daikon 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{daikon}
8
- s.version = "0.6.0"
8
+ s.version = "0.7.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Nick Quaranto"]
12
- s.date = %q{2011-01-10}
12
+ s.date = %q{2011-01-29}
13
13
  s.default_executable = %q{daikon}
14
14
  s.description = %q{daikon, a radishapp.com client}
15
15
  s.email = %q{nick@quaran.to}
@@ -40,7 +40,6 @@ Gem::Specification.new do |s|
40
40
  "spec/client_spec.rb",
41
41
  "spec/configuration_spec.rb",
42
42
  "spec/daemon_spec.rb",
43
- "spec/daikon_spec.rb",
44
43
  "spec/monitor_spec.rb",
45
44
  "spec/spec_helper.rb"
46
45
  ]
@@ -54,7 +53,6 @@ Gem::Specification.new do |s|
54
53
  "spec/client_spec.rb",
55
54
  "spec/configuration_spec.rb",
56
55
  "spec/daemon_spec.rb",
57
- "spec/daikon_spec.rb",
58
56
  "spec/monitor_spec.rb",
59
57
  "spec/spec_helper.rb"
60
58
  ]
@@ -1,10 +1,11 @@
1
1
  require 'rubygems'
2
2
 
3
3
  require 'logger'
4
- require 'pp'
5
4
  require 'shellwords'
5
+ require 'set'
6
6
  require 'socket'
7
7
  require 'stringio'
8
+ require 'thread'
8
9
 
9
10
  require 'excon'
10
11
  require 'daemons'
@@ -25,5 +26,5 @@ require 'daikon/monitor'
25
26
  require 'daikon/redis_hacks'
26
27
 
27
28
  module Daikon
28
- VERSION = "0.6.0"
29
+ VERSION = "0.7.0"
29
30
  end
@@ -6,7 +6,8 @@ module Daikon
6
6
  Errno::EINVAL,
7
7
  Errno::ECONNRESET,
8
8
  EOFError,
9
- JSON::ParserError]
9
+ JSON::ParserError,
10
+ Excon::Errors::SocketError]
10
11
 
11
12
  attr_accessor :redis, :logger, :config, :http, :monitor
12
13
 
@@ -38,7 +39,7 @@ module Daikon
38
39
  options[:headers] ||= {}
39
40
  options[:headers]['Authorization'] = config.api_key
40
41
 
41
- log "#{options[:method]} #{config.server_prefix}/#{options[:path]}"
42
+ log "#{options[:method]} #{config.server_prefix}#{options[:path]}"
42
43
  http.request(options)
43
44
  end
44
45
 
@@ -65,16 +66,19 @@ module Daikon
65
66
  log ex.to_s
66
67
  end
67
68
 
68
- def report_info
69
- push :post, "/api/v1/info.json", redis.info
69
+ def rotate_monitor(start, stop)
70
+ payload = monitor.rotate.merge({
71
+ "start" => start,
72
+ "stop" => stop
73
+ })
74
+
75
+ push :post, "/api/v1/summaries.json", payload
70
76
  rescue *EXCEPTIONS => ex
71
77
  log ex.to_s
72
78
  end
73
79
 
74
- def rotate_monitor
75
- lines = monitor.rotate
76
-
77
- push :post, "/api/v1/monitor.json", {"lines" => lines}
80
+ def report_info
81
+ push :post, "/api/v1/infos.json", redis.info
78
82
  rescue *EXCEPTIONS => ex
79
83
  log ex.to_s
80
84
  end
@@ -1,6 +1,26 @@
1
1
  module Daikon
2
2
  class Daemon
3
- def self.start(argv)
3
+ INFO_INTERVAL = ENV["INFO_INTERVAL"] || 5
4
+ SUMMARY_INTERVAL = ENV["SUMMARY_INTERVAL"] || 60
5
+
6
+ def self.sleep_time=(sleep_time)
7
+ @@sleep_time = sleep_time
8
+ end
9
+
10
+ def self.sleep_time
11
+ @@sleep_time ||= 1
12
+ end
13
+
14
+ def self.run=(run)
15
+ @@run = run
16
+ end
17
+
18
+ def self.run
19
+ @@run
20
+ end
21
+
22
+ def self.start(argv, ontop = false)
23
+ self.run = true
4
24
  config = Daikon::Configuration.new(argv)
5
25
 
6
26
  if argv.include?("-v") || argv.include?("--version")
@@ -8,31 +28,34 @@ module Daikon
8
28
  return
9
29
  end
10
30
 
11
- Daemons.run_proc("daikon", :log_output => true, :backtrace => true) do
31
+ Daemons.run_proc("daikon", :ARGV => argv, :log_output => true, :backtrace => true, :ontop => ontop) do
12
32
  if argv.include?("run")
13
33
  logger = Logger.new(STDOUT)
14
34
  else
15
35
  logger = Logger.new("/tmp/radish.log")
16
36
  end
17
37
 
18
- count = 0
19
- client = Daikon::Client.new
38
+ rotated_at = Time.now
39
+ reported_at = Time.now
40
+ client = Daikon::Client.new
41
+
20
42
  client.setup(config, logger)
21
43
  client.start_monitor
22
44
 
23
- loop do
24
- if count % 5 == 0
45
+ while self.run do
46
+ now = Time.now
47
+
48
+ if now - reported_at >= sleep_time * INFO_INTERVAL.to_i
25
49
  client.report_info
50
+ reported_at = now
26
51
  end
27
52
 
28
- client.fetch_commands
29
-
30
- if count % 10 == 9
31
- client.rotate_monitor
53
+ if now - rotated_at >= sleep_time * SUMMARY_INTERVAL.to_i
54
+ client.rotate_monitor(rotated_at, now)
55
+ rotated_at = now
32
56
  end
33
57
 
34
- count += 1
35
- sleep 1
58
+ sleep sleep_time
36
59
  end
37
60
  end
38
61
  end
@@ -1,15 +1,29 @@
1
1
  module Daikon
2
2
  class Monitor
3
- attr_accessor :queue
3
+ attr_accessor :data
4
+
5
+ NO_ARG_COMMANDS = ["BGREWRITEAOF", "BGSAVE", "CONFIG RESETSTAT", "DBSIZE", "DEBUG SEGFAULT", "DISCARD", "EXEC", "FLUSHALL", "FLUSHDB", "INFO", "LASTSAVE", "MONITOR", "MULTI", "PING", "QUIT", "RANDOMKEY", "SAVE", "SHUTDOWN", "SYNC", "UNWATCH"]
6
+ READ_COMMANDS = ["EXISTS", "GET", "GETBIT", "GETRANGE", "HEXISTS", "HGET", "HGETALL", "HKEYS", "HLEN", "HMGET", "HVALS", "KEYS", "LINDEX", "LLEN", "LRANGE", "MGET", "SCARD", "SDIFF", "SINTER", "SISMEMBER", "SMEMBERS", "SORT", "SRANDMEMBER", "STRLEN", "SUNION", "TTL", "TYPE", "ZCARD", "ZCOUNT", "ZRANGE", "ZRANGEBYSCORE", "ZRANK", "ZREVRANGE", "ZREVRANGEBYSCORE", "ZREVRANK", "ZSCORE"].to_set
7
+ WRITE_COMMANDS = ["APPEND", "BLPOP", "BRPOP", "BRPOPLPUSH", "DECR", "DECRBY", "DEL", "GETSET", "EXPIRE", "EXPIREAT", "HDEL", "HINCRBY", "HMSET", "HSET", "HSETNX", "INCR", "INCRBY", "LINSERT", "LPOP", "LPUSH", "LPUSHX", "LREM", "LSET", "LTRIM", "MOVE", "MSET", "MSETNX", "PERSIST", "RENAME", "RENAMENX", "RPOP", "RPOPLPUSH", "RPUSH", "RPUSHX", "SADD", "SDIFFSTORE", "SET", "SETBIT", "SETEX", "SETNX", "SETRANGE", "SINTERSTORE", "SMOVE", "SPOP", "SREM", "SUNIONSTORE", "ZADD", "ZINCRBY", "ZINTERSTORE", "ZREM", "ZREMRANGEBYRANK", "ZREMRANGEBYSCORE", "ZUNIONSTORE"].to_set
8
+ OTHER_COMMANDS = ["AUTH", "BGREWRITEAOF", "BGSAVE", "CONFIG GET", "CONFIG SET", "CONFIG RESETSTAT", "DBSIZE", "DEBUG OBJECT", "DEBUG SEGFAULT", "DISCARD", "ECHO", "EXEC", "FLUSHALL", "FLUSHDB", "INFO", "LASTSAVE", "MONITOR", "MULTI", "PING", "PSUBSCRIBE", "PUBLISH", "PUNSUBSCRIBE", "QUIT", "RANDOMKEY", "SAVE", "SELECT", "SHUTDOWN", "SUBSCRIBE", "SYNC", "UNSUBSCRIBE", "UNWATCH", "WATCH"].to_set
9
+ ALL_COMMANDS = READ_COMMANDS + WRITE_COMMANDS + OTHER_COMMANDS
4
10
 
5
11
  NEW_FORMAT = /^\+?(\d+\.\d+)( "[A-Z]+".*)/i
6
- OLD_SINGLE_FORMAT = /^(QUIT|RANDOMKEY|DBSIZE|EXPIRE|TTL|SAVE|BGSAVE|SHUTDOWN|BGREWRITEAOF|INFO|MONITOR|SLAVEOF)$/i
12
+ OLD_SINGLE_FORMAT = /^(#{NO_ARG_COMMANDS.join('|')})$/i
7
13
  OLD_MORE_FORMAT = /^[A-Z]+ .*$/i
8
14
 
9
15
  def initialize(redis = nil, logger = nil)
10
- @queue = []
16
+ @data = data_hash
11
17
  @redis = redis
12
18
  @logger = logger
19
+ @mutex = Mutex.new
20
+ end
21
+
22
+ def data_hash
23
+ {"commands" => Hash.new(0),
24
+ "keys" => Hash.new(0),
25
+ "namespaces" => Hash.new(0),
26
+ "totals" => Hash.new(0)}
13
27
  end
14
28
 
15
29
  def start
@@ -20,20 +34,74 @@ module Daikon
20
34
  end
21
35
  end
22
36
 
37
+ def lock(&block)
38
+ @mutex.synchronize(&block)
39
+ end
40
+
23
41
  def rotate
24
- @queue.shift(@queue.size)
42
+ this_data = nil
43
+ lock do
44
+ this_data = self.data.dup
45
+ self.data.replace(data_hash)
46
+ end
47
+ this_data["keys"] = Hash[*this_data["keys"].sort_by(&:last).reverse[0..99].flatten]
48
+ this_data
25
49
  end
26
50
 
27
51
  def parse(line)
28
52
  if line =~ NEW_FORMAT
29
- push(Float($1), $2)
53
+ push($2)
30
54
  elsif line =~ OLD_SINGLE_FORMAT || line =~ OLD_MORE_FORMAT
31
- push(Time.now.to_f, line)
55
+ push(line)
32
56
  end
33
57
  end
34
58
 
35
- def push(at, command)
36
- @queue.push({:at => at, :command => command.strip})
59
+ def push(raw_command)
60
+ command, key, *rest = raw_command.strip.gsub('"', '').split
61
+ command.upcase!
62
+ lock do
63
+ incr_command(command)
64
+ incr_total(command)
65
+ if key
66
+ incr_key(key)
67
+ incr_namespace(key)
68
+ else
69
+ incr_global_namespace
70
+ end
71
+ end
72
+ end
73
+
74
+ def incr_namespace(key)
75
+ namespace, has_namespace = key.split(/(?::|-)/, 2)
76
+ if has_namespace
77
+ data["namespaces"][namespace] += 1
78
+ else
79
+ incr_global_namespace
80
+ end
81
+ end
82
+
83
+ def incr_global_namespace
84
+ data["namespaces"]["global"] += 1
85
+ end
86
+
87
+ def incr_command(command)
88
+ data["commands"][command] += 1 if ALL_COMMANDS.member?(command)
89
+ end
90
+
91
+ def incr_key(key)
92
+ data["keys"][key.gsub(".", "{PERIOD}").gsub("$", "{DOLLAR}")] += 1
93
+ end
94
+
95
+ def incr_total(command)
96
+ data["totals"]["all"] += 1
97
+
98
+ if READ_COMMANDS.member?(command)
99
+ data["totals"]["read"] += 1
100
+ elsif WRITE_COMMANDS.member?(command)
101
+ data["totals"]["write"] += 1
102
+ elsif OTHER_COMMANDS.member?(command)
103
+ data["totals"]["other"] += 1
104
+ end
37
105
  end
38
106
  end
39
107
  end
@@ -134,32 +134,47 @@ describe Daikon::Client, "when it returns bad json" do
134
134
  end
135
135
  end
136
136
 
137
- shared_examples_for "a info api consumer" do
137
+ shared_examples_for "a summary api consumer" do
138
138
  it "shoots the results back to radish" do
139
139
  headers = {
140
140
  "Authorization" => api_key,
141
- "Content-Length" => results.to_json.size.to_s,
141
+ "Content-Length" => payload.to_json.size.to_s,
142
142
  "Content-Type" => "application/json"
143
143
  }
144
144
 
145
145
  http.should have_received(:request).with(
146
146
  :method => "POST",
147
- :path => "/api/v1/info.json",
148
- :body => results.to_json,
147
+ :path => "/api/v1/summaries.json",
148
+ :body => payload.to_json,
149
149
  :headers => headers)
150
150
  end
151
151
  end
152
152
 
153
- describe Daikon::Client, "report info" do
154
- subject { Daikon::Client.new }
155
- let(:results) { {"connected_clients"=>"1", "used_cpu_sys_childrens"=>"0.00"} }
156
- let(:redis) { stub("redis instance", :info => results) }
157
- let(:http) { stub("http", :request => Excon::Response.new) }
153
+ describe Daikon::Client, "rotate monitor" do
154
+ subject { Daikon::Client.new }
155
+ let(:http) { stub("http", :request => Excon::Response.new) }
156
+ let(:now) { "2011-01-19T18:23:55-05:00" }
157
+ let(:past) { "2011-01-19T18:23:54-05:00" }
158
+ let(:payload) do
159
+ data.merge("start" => past, "stop" => now)
160
+ end
161
+ let(:data) do
162
+ {"commands" => {"GET" => 42},
163
+ "keys" => {"foo" => 42},
164
+ "namespaces" => {"a" => 42, "global" => 42},
165
+ "totals" => {"all" => 42, "read" => 42}}
166
+ end
158
167
 
159
168
  before do
160
- subject.stubs(:redis => redis, :http => http)
169
+ Timecop.freeze DateTime.parse(now)
170
+ subject.stubs(:http => http)
161
171
  subject.setup(config)
162
- subject.report_info
172
+ subject.monitor = stub("monitor", :rotate => data)
173
+ subject.rotate_monitor(DateTime.parse(past), DateTime.parse(now))
174
+ end
175
+
176
+ after do
177
+ Timecop.return
163
178
  end
164
179
 
165
180
  context "with default configuration" do
@@ -167,7 +182,7 @@ describe Daikon::Client, "report info" do
167
182
  let(:server) { "https://radish.heroku.com" }
168
183
  let(:config) { Daikon::Configuration.new }
169
184
 
170
- it_should_behave_like "a info api consumer"
185
+ it_should_behave_like "a summary api consumer"
171
186
  end
172
187
 
173
188
  context "with custom settings" do
@@ -175,43 +190,57 @@ describe Daikon::Client, "report info" do
175
190
  let(:server) { "http://localhost:9999" }
176
191
  let(:config) { Daikon::Configuration.new(["-k", api_key, "-s", "http://localhost:9999"]) }
177
192
 
178
- it_should_behave_like "a info api consumer"
193
+ it_should_behave_like "a summary api consumer"
179
194
  end
180
195
  end
181
196
 
182
- shared_examples_for "a monitor api consumer" do
183
- it "shoots the results back to radish" do
184
- payload = {"lines" => lines}
197
+ describe Daikon::Client, "pretty printing results" do
198
+ subject { Daikon::Client.new }
199
+ let(:body) { {"13" => "LRANGE foo 0 -1"}.to_json }
200
+ let(:list) { %w[apples bananas carrots] }
201
+ let(:server) { "https://radish.heroku.com" }
202
+ let(:config) { Daikon::Configuration.new }
203
+ let(:http) { stub("http", :request => Excon::Response.new(:body => body)) }
185
204
 
205
+ before do
206
+ subject.stubs(:evaluate_redis => list, :http => http)
207
+ subject.setup(config)
208
+ subject.fetch_commands
209
+ end
210
+
211
+ it "returns pretty printed results" do
212
+ http.should have_received(:request).with(has_entry(
213
+ :body => {"response" => "[\"apples\", \"bananas\", \"carrots\"]"}.to_json
214
+ ))
215
+ end
216
+ end
217
+
218
+ shared_examples_for "a info api consumer" do
219
+ it "shoots the results back to radish" do
186
220
  headers = {
187
221
  "Authorization" => api_key,
188
- "Content-Length" => payload.to_json.size.to_s,
222
+ "Content-Length" => results.to_json.size.to_s,
189
223
  "Content-Type" => "application/json"
190
224
  }
191
225
 
192
226
  http.should have_received(:request).with(
193
227
  :method => "POST",
194
- :path => "/api/v1/monitor.json",
195
- :body => payload.to_json,
228
+ :path => "/api/v1/infos.json",
229
+ :body => results.to_json,
196
230
  :headers => headers)
197
231
  end
198
232
  end
199
233
 
200
- describe Daikon::Client, "rotate monitor" do
234
+ describe Daikon::Client, "report info" do
201
235
  subject { Daikon::Client.new }
202
- let(:results) { %{1290289048.96581 "info"\n1290289053.568815 "info"} }
236
+ let(:results) { {"connected_clients"=>"1", "used_cpu_sys_childrens"=>"0.00"} }
203
237
  let(:redis) { stub("redis instance", :info => results) }
204
238
  let(:http) { stub("http", :request => Excon::Response.new) }
205
- let(:lines) do
206
- [{"at" => 1290289048.96581, "command" => "info"},
207
- {"at" => 1290289053.568815, "command" => "info"}]
208
- end
209
239
 
210
240
  before do
211
- subject.stubs(:http => http)
241
+ subject.stubs(:redis => redis, :http => http)
212
242
  subject.setup(config)
213
- subject.monitor = stub("monitor", :rotate => lines)
214
- subject.rotate_monitor
243
+ subject.report_info
215
244
  end
216
245
 
217
246
  context "with default configuration" do
@@ -219,7 +248,7 @@ describe Daikon::Client, "rotate monitor" do
219
248
  let(:server) { "https://radish.heroku.com" }
220
249
  let(:config) { Daikon::Configuration.new }
221
250
 
222
- it_should_behave_like "a monitor api consumer"
251
+ it_should_behave_like "a info api consumer"
223
252
  end
224
253
 
225
254
  context "with custom settings" do
@@ -227,27 +256,6 @@ describe Daikon::Client, "rotate monitor" do
227
256
  let(:server) { "http://localhost:9999" }
228
257
  let(:config) { Daikon::Configuration.new(["-k", api_key, "-s", "http://localhost:9999"]) }
229
258
 
230
- it_should_behave_like "a monitor api consumer"
231
- end
232
- end
233
-
234
- describe Daikon::Client, "pretty printing results" do
235
- subject { Daikon::Client.new }
236
- let(:body) { {"13" => "LRANGE foo 0 -1"}.to_json }
237
- let(:list) { %w[apples bananas carrots] }
238
- let(:server) { "https://radish.heroku.com" }
239
- let(:config) { Daikon::Configuration.new }
240
- let(:http) { stub("http", :request => Excon::Response.new(:body => body)) }
241
-
242
- before do
243
- subject.stubs(:evaluate_redis => list, :http => http)
244
- subject.setup(config)
245
- subject.fetch_commands
246
- end
247
-
248
- it "returns pretty printed results" do
249
- http.should have_received(:request).with(has_entry(
250
- :body => {"response" => "[\"apples\", \"bananas\", \"carrots\"]"}.to_json
251
- ))
259
+ it_should_behave_like "a info api consumer"
252
260
  end
253
261
  end
@@ -1,6 +1,29 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Daikon::Daemon do
4
+ let(:client) { stub("client") }
5
+
6
+ before do
7
+ Daikon::Client.stubs(:new => client)
8
+ Daikon::Daemon.sleep_time = 0.05
9
+ client.stubs(:setup => true,
10
+ :start_monitor => true,
11
+ :rotate_monitor => true,
12
+ :report_info => true)
13
+ end
14
+
15
+ it "submits the last minute of data" do
16
+ thread = Thread.new do
17
+ Daikon::Daemon.start(["run", "--", "-k", "1234"], true)
18
+ end
19
+ sleep 3.1
20
+ Daikon::Daemon.run = false
21
+ client.should have_received(:rotate_monitor)
22
+ client.should have_received(:report_info).times(12)
23
+ end
24
+ end
25
+
26
+ describe Daikon::Daemon, "flags" do
4
27
  %w[-v --version].each do |flag|
5
28
  it "shows the daikon version with #{flag}" do
6
29
  old_stdout = $stdout
@@ -1,15 +1,52 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Daikon::Monitor, "#rotate" do
4
- it "pops off what is on the queue" do
4
+ before do
5
5
  subject.parse("INCR foo")
6
6
  subject.parse("DECR foo")
7
+ subject.parse("DECR baz")
8
+ subject.parse("HGETALL faz")
9
+ subject.parse("PING")
10
+ end
11
+
12
+ it "clears out current data" do
13
+ subject.rotate
14
+ subject.data["commands"].size.should be_zero
15
+ subject.data["totals"].size.should be_zero
16
+ subject.data["keys"].size.should be_zero
17
+ end
18
+
19
+ it "only saves the top 100 key listings" do
20
+ 150.times { |n| subject.parse("INCR foo#{n}") }
21
+ 150.times { |n| subject.parse("DECR foo#{n}") }
22
+ 100.times { |n| subject.parse("DEL foo#{n}") }
23
+ data = subject.rotate
24
+ data["keys"].size.should == 100
25
+ data["keys"].values.all? { |n| n == 3 }.should be_true
26
+ end
7
27
 
28
+ it "santizes key names" do
29
+ subject.parse("INCR $foo.zomg")
8
30
  data = subject.rotate
9
- data.first[:command].should == "INCR foo"
10
- data.last[:command].should == "DECR foo"
11
- data.size.should == 2
12
- subject.queue.size.should be_zero
31
+ data["keys"]["$foo.zomg"].should be_nil
32
+ data["keys"]["{DOLLAR}foo{PERIOD}zomg"].should == 1
33
+ end
34
+
35
+ it "increments each command type" do
36
+ subject.data["commands"]["INCR"].should == 1
37
+ subject.data["commands"]["DECR"].should == 2
38
+ end
39
+
40
+ it "keeps track of key accesses" do
41
+ subject.data["keys"]["foo"].should == 2
42
+ subject.data["keys"]["baz"].should == 1
43
+ end
44
+
45
+ it "tallies up totals of commands" do
46
+ subject.data["totals"]["all"].should == 5
47
+ subject.data["totals"]["read"].should == 1
48
+ subject.data["totals"]["write"].should == 3
49
+ subject.data["totals"]["other"].should == 1
13
50
  end
14
51
  end
15
52
 
@@ -19,7 +56,10 @@ describe Daikon::Monitor, "#parse with new format" do
19
56
 
20
57
  it "parses the log into json" do
21
58
  subject.parse(line)
22
- subject.queue.should include({:at => 1291699658.994073, :command => '"decrby" "fooz" "2000"'})
59
+ subject.data["commands"]["DECRBY"].should == 1
60
+ subject.data["keys"]["fooz"].should == 1
61
+ subject.data["totals"]["all"].should == 1
62
+ subject.data["totals"]["write"].should == 1
23
63
  end
24
64
  end
25
65
 
@@ -29,78 +69,94 @@ describe Daikon::Monitor, "#parse with new format that has reply byte" do
29
69
 
30
70
  it "parses the log into json" do
31
71
  subject.parse(line)
32
- subject.queue.should include({:at => 1291699658.994073, :command => '"decrby" "fooz" "2000"'})
33
- end
34
- end
35
-
36
- describe Daikon::Monitor, "#parse with multiple inputs" do
37
- subject { Daikon::Monitor.new }
38
- before { Timecop.freeze }
39
- after { Timecop.return }
40
-
41
- it "queues up multiple lines" do
42
- subject.parse("+OK")
43
- subject.parse("INCR foo")
44
- subject.parse("INCR fooz")
45
- subject.parse("info")
46
-
47
- subject.queue.size.should == 3
48
- subject.queue.should include({:at => Time.now.to_f, :command => 'INCR foo'})
49
- subject.queue.should include({:at => Time.now.to_f, :command => 'INCR fooz'})
50
- subject.queue.should include({:at => Time.now.to_f, :command => 'info'})
72
+ subject.data["commands"]["DECRBY"].should == 1
73
+ subject.data["keys"]["fooz"].should == 1
74
+ subject.data["totals"]["all"].should == 1
75
+ subject.data["totals"]["write"].should == 1
51
76
  end
52
77
  end
53
78
 
54
79
  describe Daikon::Monitor, "#parse with old multi line input" do
55
80
  subject { Daikon::Monitor.new }
56
- before { Timecop.freeze }
57
- after { Timecop.return }
58
81
 
59
- it "parses gzipped logs into raws" do
82
+ it "parses logs" do
60
83
  subject.parse("incr foo")
61
84
  subject.parse("sismember project-13897-global-error-classes 17")
62
85
  subject.parse("incrApiParameterError")
63
86
  subject.parse("decr foo")
64
87
 
65
- subject.queue.size.should == 3
66
- subject.queue.should include({:at => Time.now.to_f, :command => 'incr foo'})
67
- subject.queue.should include({:at => Time.now.to_f, :command => 'sismember project-13897-global-error-classes 17'})
68
- subject.queue.should include({:at => Time.now.to_f, :command => 'decr foo'})
88
+ subject.data["commands"]["DECR"].should == 1
89
+ subject.data["commands"]["INCR"].should == 1
90
+ subject.data["commands"]["SISMEMBER"].should == 1
91
+ subject.data["keys"]["foo"].should == 2
92
+ subject.data["keys"]["project-13897-global-error-classes"].should == 1
93
+ subject.data["totals"]["all"].should == 3
94
+ subject.data["totals"]["write"].should == 2
95
+ subject.data["totals"]["read"].should == 1
69
96
  end
70
97
  end
71
98
 
72
- describe Daikon::Monitor, "#parse with multi line input with numbers" do
99
+ describe Daikon::Monitor, "#parse with old input" do
73
100
  subject { Daikon::Monitor.new }
74
- before { Timecop.freeze }
75
- after { Timecop.return }
76
101
 
77
- it "parses gzipped logs into raws" do
78
- subject.parse("incr foo")
79
- subject.parse("set g:2470920:mrn 9")
80
- subject.parse("554079885")
81
- subject.parse("decr foo")
102
+ shared_examples_for "a valid parser" do
103
+ it "parses the given commands properly" do
104
+ subject.data["commands"]["DECR"].should == 1
105
+ subject.data["commands"]["INCR"].should == 1
106
+ subject.data["commands"]["SET"].should == 1
107
+ subject.data["keys"]["foo"].should == 2
108
+ subject.data["keys"]["g:2470920:mrn"].should == 1
109
+ subject.data["totals"]["all"].should == 3
110
+ subject.data["totals"]["write"].should == 3
111
+ end
112
+ end
82
113
 
83
- subject.queue.size.should == 3
84
- subject.queue.should include({:at => Time.now.to_f, :command => 'incr foo'})
85
- subject.queue.should include({:at => Time.now.to_f, :command => 'set g:2470920:mrn 9'})
86
- subject.queue.should include({:at => Time.now.to_f, :command => 'decr foo'})
114
+ context "with a bulk input that is a number" do
115
+ before do
116
+ subject.parse("incr foo")
117
+ subject.parse("set g:2470920:mrn 9")
118
+ subject.parse("554079885")
119
+ subject.parse("decr foo")
120
+ end
121
+ it_should_behave_like "a valid parser"
122
+ end
123
+
124
+ context "with a bulk input that is a number" do
125
+ before do
126
+ subject.parse("incr foo")
127
+ subject.parse("set g:2470920:mrn 9")
128
+ subject.parse("46fdcf77c1bb2108e6191602c2f5f9ae")
129
+ subject.parse("decr foo")
130
+ end
131
+ it_should_behave_like "a valid parser"
87
132
  end
88
133
  end
89
134
 
90
- describe Daikon::Monitor, "#parse with strings that may to_i to a number" do
91
- subject { Daikon::Monitor.new }
92
- before { Timecop.freeze }
93
- after { Timecop.return }
135
+ describe Daikon::Monitor, "#rotate with a bad command name" do
136
+ before do
137
+ subject.parse("gmail foo")
138
+ end
94
139
 
95
- it "parses gzipped logs into raws" do
96
- subject.parse("incr foo")
140
+ it "clears out current data" do
141
+ data = subject.rotate
142
+ data["commands"].size.should be_zero
143
+ end
144
+ end
145
+
146
+ describe Daikon::Monitor, "#rotate that collects namespaces" do
147
+ before do
97
148
  subject.parse("set g:2470920:mrn 9")
98
- subject.parse("46fdcf77c1bb2108e6191602c2f5f9ae")
99
- subject.parse("decr foo")
149
+ subject.parse("get g:2470914:mrn")
150
+ subject.parse("incr s3-queue-key")
151
+ subject.parse("info")
152
+ subject.parse("decr somehorriblynamespacedkey")
153
+ subject.parse("flushdb")
154
+ end
100
155
 
101
- subject.queue.size.should == 3
102
- subject.queue.should include({:at => Time.now.to_f, :command => 'incr foo'})
103
- subject.queue.should include({:at => Time.now.to_f, :command => 'set g:2470920:mrn 9'})
104
- subject.queue.should include({:at => Time.now.to_f, :command => 'decr foo'})
156
+ it "keeps track of namespace accesses" do
157
+ data = subject.rotate
158
+ data["namespaces"]["g"].should == 2
159
+ data["namespaces"]["global"].should == 3
160
+ data["namespaces"]["s3"].should == 1
105
161
  end
106
162
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: daikon
3
3
  version: !ruby/object:Gem::Version
4
- hash: 7
4
+ hash: 3
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 6
8
+ - 7
9
9
  - 0
10
- version: 0.6.0
10
+ version: 0.7.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Nick Quaranto
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-01-10 00:00:00 -05:00
18
+ date: 2011-01-29 00:00:00 -05:00
19
19
  default_executable: daikon
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -183,7 +183,6 @@ files:
183
183
  - spec/client_spec.rb
184
184
  - spec/configuration_spec.rb
185
185
  - spec/daemon_spec.rb
186
- - spec/daikon_spec.rb
187
186
  - spec/monitor_spec.rb
188
187
  - spec/spec_helper.rb
189
188
  has_rdoc: true
@@ -226,6 +225,5 @@ test_files:
226
225
  - spec/client_spec.rb
227
226
  - spec/configuration_spec.rb
228
227
  - spec/daemon_spec.rb
229
- - spec/daikon_spec.rb
230
228
  - spec/monitor_spec.rb
231
229
  - spec/spec_helper.rb
@@ -1,4 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe "Daikon" do
4
- end