From f5215bfd8d13071bc5e096eb2aa135e88c7c7c1d Mon Sep 17 00:00:00 2001 From: Dan Cartoon Date: Sun, 14 Apr 2013 17:38:23 -0700 Subject: [PATCH 1/3] Changing worker test setup We have to use before(:each) rather than before(:all), or our test doubles don't end up being properly configured(Get "unexpected message" errors for every example besides the first that uses redis). This also caught me by surprise, but the reference is here: https://github.com/rspec/rspec-mocks#use-beforeeach-not-beforeall --- spec/worker_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/worker_spec.rb b/spec/worker_spec.rb index f99e0a6..46c89f7 100644 --- a/spec/worker_spec.rb +++ b/spec/worker_spec.rb @@ -2,7 +2,7 @@ describe "worker" do - before(:all) do + before(:each) do @worker = Redmon::Worker.new end From a9832988a54f16a6258957c971c51da5c4979130 Mon Sep 17 00:00:00 2001 From: Dan Cartoon Date: Sun, 14 Apr 2013 17:55:32 -0700 Subject: [PATCH 2/3] Adding configurable # stats to keep + cleanup Now supports passing the amount of sample data to keep in Redis. The default is 180 samples, which at 10 seconds/sample is half an hour's worth of samples. The dashboard now uses this amount when starting up to determine how much data to request from the server. In addition, the worker will now clean up any excess samples from Redis. Clean up is currently done every time a sample is written(since Redis operations are cheap), but this could be done less frequently if necessary. --- README.md | 1 + bin/redmon | 7 +++++++ lib/redmon/config.rb | 1 + lib/redmon/helpers.rb | 4 ++++ lib/redmon/public/redmon.js | 4 ++-- lib/redmon/views/app.haml | 1 + lib/redmon/worker.rb | 15 ++++++++++++++- spec/worker_spec.rb | 14 ++++++++++++++ 8 files changed, 44 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6e63f47..94ba374 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ $ redmon -h Usage: /Users/sean/codez/steelThread/redmon/vendor/ruby/1.9.1/bin/redmon (options) -a, --address ADDRESS The thin bind address for the app (default: 0.0.0.0) -n, --namespace NAMESPACE The root Redis namespace (default: redmon) + -k, --keep NUM_SAMPLES Number of poll samples to keep (default: 180) -i, --interval SECS Poll interval in secs for the worker (default: 10) -p, --port PORT The thin bind port for the app (default: 4567) -r, --redis URL The Redis url for monitor (default: redis://127.0.0.1:6379) diff --git a/bin/redmon b/bin/redmon index 297e037..422186b 100755 --- a/bin/redmon +++ b/bin/redmon @@ -46,6 +46,13 @@ class RedmonCLI :description => 'Poll interval in secs for the worker (default: 10)', :proc => to_i + option :num_samples_to_keep, + :short => '-k NUM_SAMPLES', + :long => '--keep NUM_SAMPLES', + :default => 180, + :description => 'Number of poll samples to keep (default: 180)', + :proc => to_i + option :app, :on => :tail, :long => '--no-app', diff --git a/lib/redmon/config.rb b/lib/redmon/config.rb index a68b093..bda55a2 100644 --- a/lib/redmon/config.rb +++ b/lib/redmon/config.rb @@ -15,6 +15,7 @@ class Config :worker => true, :web_interface => ['0.0.0.0', 4567], :poll_interval => 10, + :num_samples_to_keep => 180, :secure => false } diff --git a/lib/redmon/helpers.rb b/lib/redmon/helpers.rb index 357475f..fa848ba 100644 --- a/lib/redmon/helpers.rb +++ b/lib/redmon/helpers.rb @@ -10,6 +10,10 @@ def poll_interval Redmon.config.poll_interval * 1000 end + def num_samples_to_keep + Redmon.config.num_samples_to_keep + end + def count -(params[:count] ? params[:count].to_i : 1) end diff --git a/lib/redmon/public/redmon.js b/lib/redmon/public/redmon.js index 2f903d7..131e11e 100644 --- a/lib/redmon/public/redmon.js +++ b/lib/redmon/public/redmon.js @@ -3,13 +3,13 @@ var Redmon = (function() { , events = $({}); /** - * Loads the last 100 events and starts the periodic polling for new events. + * Loads the last numSamples events and starts the periodic polling for new events. */ function init(opts) { config = opts; toolbar.init(); cli.init(); - requestData(100, function(data) { + requestData(config.numSamples, function(data) { renderDashboard(data); poll(); }); diff --git a/lib/redmon/views/app.haml b/lib/redmon/views/app.haml index a21c176..ebc87c3 100644 --- a/lib/redmon/views/app.haml +++ b/lib/redmon/views/app.haml @@ -167,6 +167,7 @@ $(document).ready(function() { Redmon.init({ pollInterval : #{poll_interval}, + numSamples : #{num_samples_to_keep}, cliPrompt : '#{prompt}', absoluteUrl : '#{absolute_url}' }); diff --git a/lib/redmon/worker.rb b/lib/redmon/worker.rb index d9f5172..ba93f53 100644 --- a/lib/redmon/worker.rb +++ b/lib/redmon/worker.rb @@ -3,13 +3,22 @@ class Worker include Redmon::Redis def run! - EM::PeriodicTimer.new(interval) {record_stats} + EM::PeriodicTimer.new(interval) { + record_stats + cleanup_old_stats + } end def record_stats redis.zadd stats_key, *stats end + def cleanup_old_stats + # When indexing from the end of a sorted set, we start at -1, so we need to add 1 here or we'll be keeping one + # fewer samples than expected + redis.zremrangebyrank stats_key, 0, -(num_samples_to_keep + 1) + end + def stats stats = redis.info.merge! \ :dbsize => redis.dbsize, @@ -37,5 +46,9 @@ def interval Redmon.config.poll_interval end + def num_samples_to_keep + Redmon.config.num_samples_to_keep + end + end end diff --git a/spec/worker_spec.rb b/spec/worker_spec.rb index 46c89f7..6fd5280 100644 --- a/spec/worker_spec.rb +++ b/spec/worker_spec.rb @@ -24,6 +24,14 @@ def mock_timer end end + describe "#cleanup_old_stats" do + it "should remove old stats entries from a redis sorted set" do + redis = mock_redis + redis.should_receive(:zremrangebyrank).with(Redmon::Redis.stats_key, 0, -(@worker.num_samples_to_keep + 1)) + @worker.cleanup_old_stats + end + end + describe "#stats" do it "should fetch info, dbsize and slowlog from redis" do pending @@ -59,4 +67,10 @@ def mock_timer end end + describe "#num_samples_to_keep" do + it "should return the configured number of samples to keep" do + @worker.num_samples_to_keep.should == Redmon.config.num_samples_to_keep + end + end + end From 3fe65e3a9619453928086d47b92aff2259de26fd Mon Sep 17 00:00:00 2001 From: Dan Cartoon Date: Mon, 15 Apr 2013 20:04:11 -0700 Subject: [PATCH 3/3] Specify data lifespan rather than # of samples The end-user can now specify how long data should be kept. This also controls how much data is pulled for the chart. This should be a little more intuitive than specifying the number of samples to keep. --- README.md | 2 +- bin/redmon | 10 +++++----- lib/redmon.rb | 1 + lib/redmon/config.rb | 2 +- lib/redmon/helpers.rb | 4 ++-- lib/redmon/views/app.haml | 2 +- lib/redmon/worker.rb | 11 ++++++++--- spec/helpers_spec.rb | 12 ++++++++++++ spec/worker_spec.rb | 17 +++++++++++++---- 9 files changed, 44 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 94ba374..7fe7075 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ $ redmon -h Usage: /Users/sean/codez/steelThread/redmon/vendor/ruby/1.9.1/bin/redmon (options) -a, --address ADDRESS The thin bind address for the app (default: 0.0.0.0) -n, --namespace NAMESPACE The root Redis namespace (default: redmon) - -k, --keep NUM_SAMPLES Number of poll samples to keep (default: 180) + -l, --lifespan MINUTES Lifespan(in minutes) for polled data (default: 30) -i, --interval SECS Poll interval in secs for the worker (default: 10) -p, --port PORT The thin bind port for the app (default: 4567) -r, --redis URL The Redis url for monitor (default: redis://127.0.0.1:6379) diff --git a/bin/redmon b/bin/redmon index 422186b..c93020b 100755 --- a/bin/redmon +++ b/bin/redmon @@ -46,11 +46,11 @@ class RedmonCLI :description => 'Poll interval in secs for the worker (default: 10)', :proc => to_i - option :num_samples_to_keep, - :short => '-k NUM_SAMPLES', - :long => '--keep NUM_SAMPLES', - :default => 180, - :description => 'Number of poll samples to keep (default: 180)', + option :data_lifespan, + :short => '-l MINUTES', + :long => '--lifespan MINUTES', + :default => 30, + :description => 'Lifespan(in minutes) for polled data (default: 30)', :proc => to_i option :app, diff --git a/lib/redmon.rb b/lib/redmon.rb index 67585cc..bdac3d6 100644 --- a/lib/redmon.rb +++ b/lib/redmon.rb @@ -10,6 +10,7 @@ def run(opts={}) rescue Exception => e unless e.is_a?(SystemExit) log "!!! Redmon has shit the bed, restarting... #{e.message}" + e.backtrace.each { |line| log line } sleep(1) run(opts) end diff --git a/lib/redmon/config.rb b/lib/redmon/config.rb index bda55a2..8dc27be 100644 --- a/lib/redmon/config.rb +++ b/lib/redmon/config.rb @@ -15,7 +15,7 @@ class Config :worker => true, :web_interface => ['0.0.0.0', 4567], :poll_interval => 10, - :num_samples_to_keep => 180, + :data_lifespan => 30, :secure => false } diff --git a/lib/redmon/helpers.rb b/lib/redmon/helpers.rb index fa848ba..66c9bcd 100644 --- a/lib/redmon/helpers.rb +++ b/lib/redmon/helpers.rb @@ -10,8 +10,8 @@ def poll_interval Redmon.config.poll_interval * 1000 end - def num_samples_to_keep - Redmon.config.num_samples_to_keep + def num_samples_to_request + (Redmon.config.data_lifespan * 60) / Redmon.config.poll_interval end def count diff --git a/lib/redmon/views/app.haml b/lib/redmon/views/app.haml index ebc87c3..a30065b 100644 --- a/lib/redmon/views/app.haml +++ b/lib/redmon/views/app.haml @@ -167,7 +167,7 @@ $(document).ready(function() { Redmon.init({ pollInterval : #{poll_interval}, - numSamples : #{num_samples_to_keep}, + numSamples : #{num_samples_to_request}, cliPrompt : '#{prompt}', absoluteUrl : '#{absolute_url}' }); diff --git a/lib/redmon/worker.rb b/lib/redmon/worker.rb index ba93f53..3e99acb 100644 --- a/lib/redmon/worker.rb +++ b/lib/redmon/worker.rb @@ -16,7 +16,7 @@ def record_stats def cleanup_old_stats # When indexing from the end of a sorted set, we start at -1, so we need to add 1 here or we'll be keeping one # fewer samples than expected - redis.zremrangebyrank stats_key, 0, -(num_samples_to_keep + 1) + redis.zremrangebyscore stats_key, '-inf', '(' + oldest_data_to_keep.to_s end def stats @@ -46,9 +46,14 @@ def interval Redmon.config.poll_interval end - def num_samples_to_keep - Redmon.config.num_samples_to_keep + def data_lifespan + Redmon.config.data_lifespan end + def oldest_data_to_keep + lifespan_seconds = data_lifespan * 60 + oldest_time_to_keep = Time.now - lifespan_seconds + oldest_time_to_keep.to_i * 1000 + end end end diff --git a/spec/helpers_spec.rb b/spec/helpers_spec.rb index 1616d6a..5d34f93 100644 --- a/spec/helpers_spec.rb +++ b/spec/helpers_spec.rb @@ -1,4 +1,7 @@ require 'spec_helper' +require 'redmon/helpers' + +include Redmon::Helpers describe "Helpers" do @@ -83,4 +86,13 @@ def em_redis end end + describe "#num_samples_to_request" do + it "should return the number of samples to request based on poll interval and data lifespan" do + Redmon.config.stub(:data_lifespan).and_return(31) + Redmon.config.stub(:poll_interval).and_return(10) + + num_samples_to_request.should == (31 * 60) / 10 + end + end + end diff --git a/spec/worker_spec.rb b/spec/worker_spec.rb index 6fd5280..a2b71cc 100644 --- a/spec/worker_spec.rb +++ b/spec/worker_spec.rb @@ -27,7 +27,7 @@ def mock_timer describe "#cleanup_old_stats" do it "should remove old stats entries from a redis sorted set" do redis = mock_redis - redis.should_receive(:zremrangebyrank).with(Redmon::Redis.stats_key, 0, -(@worker.num_samples_to_keep + 1)) + redis.should_receive(:zremrangebyscore).with(Redmon::Redis.stats_key, '-inf', '(' + @worker.oldest_data_to_keep.to_s) @worker.cleanup_old_stats end end @@ -67,9 +67,18 @@ def mock_timer end end - describe "#num_samples_to_keep" do - it "should return the configured number of samples to keep" do - @worker.num_samples_to_keep.should == Redmon.config.num_samples_to_keep + describe "#data_lifespan" do + it "should return the data lifspan" do + @worker.data_lifespan.should == Redmon.config.data_lifespan + end + end + + describe "#oldest_data_to_keep" do + it "should return the oldest data timestamp that should be kept" do + Time.stub(:now).and_return(Time.at(1366044862)) + @worker.stub(:data_lifespan).and_return(30) + + @worker.oldest_data_to_keep.should == 1366043062000 end end