mctop 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -8,16 +8,18 @@ bandwidth.
8
8
  You can read more detail about why this tool evovled over on our
9
9
  [code as craft](http://codeascraft.etsy.com/2012/12/13/mctop-a-tool-for-analyzing-memcache-get-traffic) blog.
10
10
 
11
- mctop depends on the [ruby-pcap](https://rubygems.org/gems/ruby-pcap) gem, if you don't have
12
- this installed you'll need to ensure you have the development pcap libraries (libpcap-devel
11
+ mctop depends on the [ruby-pcap](https://rubygems.org/gems/ruby-pcap) gem, if you don't have
12
+ this installed you'll need to ensure you have the development pcap libraries (libpcap-devel
13
13
  package on most linux distros) to build the native gem.
14
14
 
15
+ ![](http://etsycodeascraft.files.wordpress.com/2012/12/mctop.jpg)
16
+
15
17
  ## How it works
16
18
 
17
19
  mctop sniffs network traffic collecting memcache `VALUE` responses and calculates from
18
20
  traffic statistics for each key seen. It currently reports on the following metrics per key:
19
21
 
20
- * **calls** - the number of times the key has been called since mctop started
22
+ * **calls** - the number of times the key has been called since mctop started
21
23
  * **objsize** - the size of the object stored for that key
22
24
  * **req/sec** - the number of requests per second for the key
23
25
  * **bw (kbps)** - the estimated netowrk bandwidth consumed by this key in kilobits-per-second
@@ -37,6 +39,7 @@ the quickest way to get it running is to:
37
39
 
38
40
  Usage: mctop [options]
39
41
  -i, --interface=NIC Network interface to sniff (required)
42
+ -p, --port=PORT Network port to sniff on (default 11211)
40
43
  -d, --discard=THRESH Discard keys with request/sec rate below THRESH
41
44
  -r, --refresh=MS Refresh the stats display every MS milliseconds
42
45
  -h, --help Show usage info
@@ -61,8 +64,12 @@ The following details are displayed in the status bar
61
64
  * `packets` - packets received and dropped by libpcap (% is percentage of packets dropped)
62
65
  * `rt` - the time taken to sort and render the stats
63
66
 
67
+ ## Changelog
68
+
69
+ * 2012-12-14 - Now compatible with Ruby 1.8.x (tested on 1.8.7-p371)
70
+
64
71
  ## Known issues / Gotchas
65
72
 
66
73
  ### ruby-pcap drops packets at high volume
67
- from my testing the ruby-pcap native interface to libpcap struggles to keep up with high packet rates (in what we see on a production memcache instance) you can keep an eye on the packets recv/drop and loss percentage on the status bar at the bottom of the UI to get an idea of the packet
74
+ from my testing the ruby-pcap native interface to libpcap struggles to keep up with high packet rates (in what we see on a production memcache instance) you can keep an eye on the packets recv/drop and loss percentage on the status bar at the bottom of the UI to get an idea of the packet
68
75
 
data/bin/mctop CHANGED
@@ -6,6 +6,7 @@
6
6
 
7
7
  $:.unshift File.join(File.dirname(__FILE__),'..','lib')
8
8
 
9
+ require 'rubygems'
9
10
  require 'cmdline'
10
11
  require 'sniffer'
11
12
  require 'ui'
@@ -10,15 +10,21 @@ class CmdLine
10
10
  @config[:nic] = nic
11
11
  end
12
12
 
13
+ opt.on('-p', '--port=PORT', 'Network port to sniff on (default 11211)') do |port|
14
+ @config[:port] = port
15
+ end
16
+
17
+ @config[:discard_thresh] = 0
13
18
  opt.on '-d', '--discard=THRESH', Float, 'Discard keys with request/sec rate below THRESH' do |discard_thresh|
14
19
  @config[:discard_thresh] = discard_thresh
15
20
  end
16
21
 
22
+ @config[:refresh_rate] = 500
17
23
  opt.on '-r', '--refresh=MS', Float, 'Refresh the stats display every MS milliseconds' do |refresh_rate|
18
24
  @config[:refresh_rate] = refresh_rate
19
25
  end
20
26
 
21
- opt.on_tail '-h', '--help', 'Show usage info' do
27
+ opt.on_tail '-h', '--help', 'Show usage info' do
22
28
  puts opts
23
29
  exit
24
30
  end
@@ -1,10 +1,12 @@
1
1
  require 'pcap'
2
+ require 'thread'
2
3
 
3
- class MemcacheSniffer
4
+ class MemcacheSniffer
4
5
  attr_accessor :metrics, :semaphore
5
6
 
6
7
  def initialize(config)
7
8
  @source = config[:nic]
9
+ @port = config.has_key?(:port) ? config[:port] : 11211
8
10
 
9
11
  @metrics = {}
10
12
  @metrics[:calls] = {}
@@ -18,12 +20,12 @@ class MemcacheSniffer
18
20
 
19
21
  def start
20
22
  cap = Pcap::Capture.open_live(@source, 1500)
21
-
23
+
22
24
  @metrics[:start_time] = Time.new.to_f
23
25
 
24
26
  @done = false
25
27
 
26
- cap.setfilter('port 11211')
28
+ cap.setfilter("port #{@port}")
27
29
  cap.loop do |packet|
28
30
  @metrics[:stats] = cap.stats
29
31
 
data/lib/ui.rb CHANGED
@@ -4,12 +4,14 @@ include Curses
4
4
 
5
5
  class UI
6
6
  def initialize(config)
7
+ @config = config
8
+
7
9
  init_screen
8
10
  cbreak
9
11
  curs_set(0)
10
12
 
11
13
  # set keyboard input timeout - sneaky way to manage refresh rate
12
- Curses.timeout = config.has_key?(:refresh_rate) ? config[:refresh_rate] : 500
14
+ Curses.timeout = @config[:refresh_rate]
13
15
 
14
16
  if can_change_color?
15
17
  start_color
@@ -22,9 +24,6 @@ class UI
22
24
  @stat_col_width = 10
23
25
  @key_col_width = 0
24
26
 
25
- # we will delete any keys from the metrics table whose req/sec rate is below discard_rate
26
- @discard_thresh = config.has_key?(:discard_thresh) ? config[:discard_thresh] : 0
27
-
28
27
  @commands = {
29
28
  'Q' => "quit",
30
29
  'C' => "sort by calls",
@@ -63,7 +62,7 @@ class UI
63
62
 
64
63
  # calculate packet loss ratio
65
64
  if sniffer.metrics[:stats][:recv] > 0
66
- loss = (sniffer.metrics[:stats][:drop].to_f / sniffer.metrics[:stats][:recv].to_f) * 100
65
+ loss = sprintf("%5.2f", (sniffer.metrics[:stats][:drop].to_f / sniffer.metrics[:stats][:recv].to_f) * 100)
67
66
  else
68
67
  loss = 0
69
68
  end
@@ -74,7 +73,7 @@ class UI
74
73
  header_summary = sprintf "%-28s %-14s %-30s",
75
74
  "sort mode: #{sort_mode.to_s} (#{sort_order.to_s})",
76
75
  "keys: #{sniffer.metrics[:calls].keys.count}",
77
- "packets (recv/dropped): #{sniffer.metrics[:stats][:recv]} / #{sniffer.metrics[:stats][:drop]} (#{loss.round(2)}%)"
76
+ "packets (recv/dropped): #{sniffer.metrics[:stats][:recv]} / #{sniffer.metrics[:stats][:drop]} (#{loss}%)"
78
77
  addstr(sprintf "%-#{cols}s", header_summary)
79
78
 
80
79
  # reset colours for main key display
@@ -95,7 +94,7 @@ class UI
95
94
  # if req/sec is <= the discard threshold delete those keys from
96
95
  # the metrics hash - this is a hack to manage the size of the
97
96
  # metrics hash in high volume environments
98
- if reqsec <= @discard_thresh
97
+ if reqsec <= @config[:discard_thresh]
99
98
  sniffer.metrics[:calls].delete(k)
100
99
  sniffer.metrics[:objsize].delete(k)
101
100
  sniffer.metrics[:reqsec].delete(k)
@@ -151,7 +150,20 @@ class UI
151
150
  end
152
151
 
153
152
  def input_handler
154
- getch
153
+ # Curses.getch has a bug in 1.8.x causing non-blocking
154
+ # calls to block reimplemented using IO.select
155
+ if RUBY_VERSION =~ /^1.8/
156
+ refresh_secs = @config[:refresh_rate].to_f / 1000
157
+
158
+ if IO.select([STDIN], nil, nil, refresh_secs)
159
+ c = getch
160
+ c.chr
161
+ else
162
+ nil
163
+ end
164
+ else
165
+ getch
166
+ end
155
167
  end
156
168
 
157
169
  def done
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |gem|
6
6
  gem.name = "mctop"
7
- gem.version = "0.0.3"
7
+ gem.version = "0.0.4"
8
8
  gem.authors = ["Marcus Barczak"]
9
9
  gem.email = ["marcus@etsy.com"]
10
10
  gem.description = %q{mctop - a realtime memcache key analyzer}
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mctop
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-13 00:00:00.000000000 Z
12
+ date: 2012-12-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ruby-pcap