Skip to content
This repository has been archived by the owner on Feb 15, 2018. It is now read-only.

Commit

Permalink
base Monitor#summaries off the current time as buckets
Browse files Browse the repository at this point in the history
  • Loading branch information
qrush committed Apr 22, 2011
1 parent 4d9be85 commit d0b3135
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 36 deletions.
4 changes: 2 additions & 2 deletions lib/daikon/client.rb
Expand Up @@ -13,7 +13,7 @@ def setup(config, logger = nil)
self.config = config
self.logger = logger
self.redis = connect
self.monitor = Monitor.new(connect, logger)
self.monitor = Monitor.new
self.http = Excon.new(config.server_prefix)

log "Started Daikon v#{VERSION}"
Expand All @@ -24,7 +24,7 @@ def connect
end

def start_monitor
monitor.start
Monitor.start
end

def log(message)
Expand Down
43 changes: 26 additions & 17 deletions lib/daikon/monitor.rb
@@ -1,7 +1,5 @@
module Daikon
class Monitor
attr_accessor :summaries

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"]
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
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
Expand All @@ -18,29 +16,40 @@ def self.parse(line)

def self.reset
summaries.clear
summaries.replace(summaries_hash)
end

def self.pop
summary = self.summaries.dup
time, summary = self.summaries.first
if summary.nil?
summary = summary_hash
end
summary["start"] = summary["stop"] = Time.now
summary["keys"] = Hash[*summary["keys"].sort_by(&:last).reverse[0..99].flatten]
yield(summary)
reset
summaries.delete(time) if time
end

def self.summaries
@@summaries ||= summaries_hash
@@summaries ||= {}
end

def self.current_summary(time)
summaries[time] ||= summary_hash
end

def self.summaries_hash
def self.summary_hash
{"commands" => Hash.new(0),
"keys" => Hash.new(0),
"namespaces" => Hash.new(0),
"totals" => Hash.new(0)}
end

def summaries
self.class.summaries
def initialize
@now = Time.now.utc.strftime("%Y-%m-%d %H:%M:00 %Z")
end

def current_summary
self.class.current_summary(@now)
end

def self.start(redis)
Expand Down Expand Up @@ -80,33 +89,33 @@ def push(split_command)

def incr_namespace(key)
if marker = key =~ /:|-/
summaries["namespaces"][key[0...marker]] += 1
current_summary["namespaces"][key[0...marker]] += 1
else
incr_global_namespace
end
end

def incr_global_namespace
summaries["namespaces"]["global"] += 1
current_summary["namespaces"]["global"] += 1
end

def incr_command(command)
summaries["commands"][command] += 1
current_summary["commands"][command] += 1
end

def incr_key(key)
summaries["keys"][key] += 1
current_summary["keys"][key] += 1
end

def incr_total(command)
summaries["totals"]["all"] += 1
current_summary["totals"]["all"] += 1

if READ_COMMANDS.member?(command)
summaries["totals"]["read"] += 1
current_summary["totals"]["read"] += 1
elsif WRITE_COMMANDS.member?(command)
summaries["totals"]["write"] += 1
current_summary["totals"]["write"] += 1
elsif OTHER_COMMANDS.member?(command)
summaries["totals"]["other"] += 1
current_summary["totals"]["other"] += 1
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions spec/client_spec.rb
Expand Up @@ -19,7 +19,7 @@
end

it "sets redis to listen on the given port" do
Redis.should have_received(:connect).with(:url => url).twice
Redis.should have_received(:connect).with(:url => url).once
subject.should have_received(:redis=).with(redis)
end
end
Expand All @@ -32,7 +32,7 @@
end

it "sets redis to listen on the given port" do
Redis.should have_received(:connect).with(:url => "redis://0.0.0.0:6379").twice
Redis.should have_received(:connect).with(:url => "redis://0.0.0.0:6379").once
subject.should have_received(:redis=).with(redis)
end
end
Expand Down
1 change: 1 addition & 0 deletions spec/daemon_spec.rb
Expand Up @@ -4,6 +4,7 @@
let(:client) { stub("client") }

before do
Timecop.return
Daikon::Client.stubs(:new => client)
Daikon::Daemon.sleep_time = 0.05
client.stubs(:setup => true,
Expand Down
63 changes: 48 additions & 15 deletions spec/monitor_spec.rb
Expand Up @@ -103,14 +103,14 @@

it "parses logs" do
Daikon::Monitor.pop do |summary|
subject.summaries["commands"]["DECR"].should == 1
subject.summaries["commands"]["INCR"].should == 1
subject.summaries["commands"]["SISMEMBER"].should == 1
subject.summaries["keys"]["foo"].should == 2
subject.summaries["keys"]["project-13897-global-error-classes"].should == 1
subject.summaries["totals"]["all"].should == 3
subject.summaries["totals"]["write"].should == 2
subject.summaries["totals"]["read"].should == 1
summary["commands"]["DECR"].should == 1
summary["commands"]["INCR"].should == 1
summary["commands"]["SISMEMBER"].should == 1
summary["keys"]["foo"].should == 2
summary["keys"]["project-13897-global-error-classes"].should == 1
summary["totals"]["all"].should == 3
summary["totals"]["write"].should == 2
summary["totals"]["read"].should == 1
end
end
end
Expand All @@ -119,13 +119,13 @@
shared_examples_for "a valid parser" do
it "parses the given commands properly" do
Daikon::Monitor.pop do |summary|
subject.summaries["commands"]["DECR"].should == 1
subject.summaries["commands"]["INCR"].should == 1
subject.summaries["commands"]["SET"].should == 1
subject.summaries["keys"]["foo"].should == 2
subject.summaries["keys"]["g:2470920:mrn"].should == 1
subject.summaries["totals"]["all"].should == 3
subject.summaries["totals"]["write"].should == 3
summary["commands"]["DECR"].should == 1
summary["commands"]["INCR"].should == 1
summary["commands"]["SET"].should == 1
summary["keys"]["foo"].should == 2
summary["keys"]["g:2470920:mrn"].should == 1
summary["totals"]["all"].should == 3
summary["totals"]["write"].should == 3
end
end
end
Expand Down Expand Up @@ -193,3 +193,36 @@
end
end
end

describe Daikon::Monitor, "#parse over several minutes keeps several minutes of data" do
before do
Timecop.freeze(Time.at(Time.now - 179)) do
parse("INCR foo")
end

Timecop.freeze(Time.at(Time.now - 119)) do
parse("DECR foo")
end

Timecop.freeze(Time.at(Time.now - 60)) do
parse("INCR foo")
end
end

it "separates each into a separate minute" do
Daikon::Monitor.pop do |summary|
summary["commands"].should == {"INCR" => 1}
summary["keys"].should == {"foo" => 1}
end

Daikon::Monitor.pop do |summary|
summary["commands"].should == {"DECR" => 1}
summary["keys"].should == {"foo" => 1}
end

Daikon::Monitor.pop do |summary|
summary["commands"].should == {"INCR" => 1}
summary["keys"].should == {"foo" => 1}
end
end
end
5 changes: 5 additions & 0 deletions spec/spec_helper.rb
Expand Up @@ -20,6 +20,11 @@
config.include ParseHelper

config.before do
Timecop.freeze(DateTime.now)
Daikon::Monitor.reset
end

config.after do
Timecop.return
end
end
11 changes: 11 additions & 0 deletions spec/support/capture_helper.rb
@@ -0,0 +1,11 @@
module CaptureHelper
# http://pivotallabs.com/users/alex/blog/articles/853-capturing-standard-out-in-unit-tests
def capture
output = StringIO.new
$stderr = output
yield
output.string
ensure
$stderr = STDERR
end
end
5 changes: 5 additions & 0 deletions spec/support/parse_helper.rb
@@ -0,0 +1,5 @@
module ParseHelper
def parse(line)
Daikon::Monitor.parse(line)
end
end

0 comments on commit d0b3135

Please sign in to comment.