Skip to content

Commit

Permalink
oh hai rrrdtool
Browse files Browse the repository at this point in the history
  • Loading branch information
igrigorik committed May 9, 2010
1 parent 80f77ac commit 48fba22
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 27 deletions.
28 changes: 15 additions & 13 deletions README.md
@@ -1,28 +1,30 @@
# RRStat
# RRRDTool

Implements a round-robin database pattern on top of Redis sorted sets. Ideal for
answering top/last N queries in (almost) fixed (memory) space. Specify the period
and precision of each collection bucket, and RRStat will do the rest.
Implements a round-robin database (circular buffer) pattern on top of Redis sorted
sets. Ideal for answering top/last N queries in (almost) fixed (memory) space - actual
footprint depends on the number of unique keys you are tracking. Specify the period
and precision (step) of each collection bucket, and RRStat will do the rest.

Memory footprint will be limited to number of buckets * number of keys in each. New
samples will be automatically placed into correct epoch/bucket. Ex:

## Store up to 60s worth of samples, in 10s buckets:
rr = RRStat.new(:precision => 10, :buckets => 6)
## Store up to 5s worth of samples, in 1s buckets:
rr = RRRDTool.new(:step => 1, :buckets => 5)

rr.incr("namespace", "key")
rr.incr("namespace", "key", 5)
rr.score("namespace", "key") => 6
p rr.score("namespace", "key") # => 6

sleep (10)
sleep (1)

rr.incr("namespace", "key")
rr.score("namespace", "key") => 7
rr.first("namespace", 1, :with_scores => true) => {"key" => 7}
p rr.score("namespace", "key") # => 7
p rr.first("namespace", 1, :with_scores => true) # => {"key"=>"7"}

sleep(50)
sleep(4)

# epoch will rollover, eliminating first bucket
rr.score("namespace", "key") => 1
p rr.score("namespace", "key") # => 1
p rr.stats("namespace") # => {:buckets=>5, :unique_keys=>1, :key_count=>{0=>0, 1=>0, 2=>0, 3=>1, 4=>0}}

# License

Expand Down
10 changes: 5 additions & 5 deletions Rakefile
Expand Up @@ -3,15 +3,15 @@ require "rake"
begin
require "jeweler"
Jeweler::Tasks.new do |gemspec|
gemspec.name = "rrstat"
gemspec.summary = "Round robin database via Redis sorted sets"
gemspec.name = "rrrdtool"
gemspec.summary = "Round robin database pattern via Redis sorted sets"
gemspec.description = gemspec.summary
gemspec.email = "ilya@igvita.com"
gemspec.homepage = "http://github.com/igrigorik/rrstat"
gemspec.homepage = "http://github.com/igrigorik/rrrdtool"
gemspec.authors = ["Ilya Grigorik"]
gemspec.required_ruby_version = ">= 1.8"
gemspec.add_dependency("redis", ">= 2.0.0")
gemspec.rubyforge_project = "rrstat"
gemspec.add_dependency("redis", ">= 1.9")
gemspec.rubyforge_project = "rrrdtool"
end

Jeweler::GemcutterTasks.new
Expand Down
19 changes: 19 additions & 0 deletions examples/simple.rb
@@ -0,0 +1,19 @@
require 'lib/rrrdtool'

rr = RRRDTool.new(:step => 1, :buckets => 5)
rr.flushdb

rr.incr("namespace", "key")
rr.incr("namespace", "key", 5)
p rr.score("namespace", "key") # => 6

sleep (1)

rr.incr("namespace", "key")
p rr.score("namespace", "key") # => 7
p rr.first("namespace", 1, :with_scores => true) # => {"key"=>"7"}

sleep(4)

p rr.score("namespace", "key") # => 1
p rr.stats("namespace") # => {:buckets=>5, :unique_keys=>1, :key_count=>{0=>0, 1=>0, 2=>0, 3=>1, 4=>0}}
10 changes: 5 additions & 5 deletions lib/rrstat.rb → lib/rrrdtool.rb
@@ -1,9 +1,9 @@
require 'redis'

class RRStat
class RRRDTool
def initialize(opts)
@precision = opts[:precision]
@buckets = opts[:buckets]
@step = opts[:step]
@debug = opts[:debug] || false

@current = nil
Expand All @@ -12,7 +12,7 @@ def initialize(opts)
@db = Redis.new
end

def time_epoch; (Time.now.to_i / @precision) % @buckets; end
def time_epoch; (Time.now.to_i / @step) % @buckets; end
def epochs_ago(set, num)
b = time_epoch-num
b = (b < 0) ? @buckets + b : b
Expand All @@ -26,13 +26,13 @@ def buckets(set)

def epoch(set)
e = time_epoch
s = Time.now.to_i / @precision
s = Time.now.to_i / @step
now = set + ":" + e.to_s

if now != @current and s != @signature
debug [:new_epoch, e]
@current = now
@signature = Time.now.to_i / @precision
@signature = Time.now.to_i / @step

clear_bucket(epochs_ago(set, @buckets))
end
Expand Down
11 changes: 7 additions & 4 deletions spec/rrstat_spec.rb → spec/rrrdtool_spec.rb
Expand Up @@ -2,19 +2,19 @@
require 'delorean'
require 'time'

require 'lib/rrstat'
require 'lib/rrrdtool'

describe RRStat do
describe RRRDTool do
include Delorean

let(:rr) { RRStat.new(:precision => 10, :buckets => 6) }
let(:rr) { RRRDTool.new(:step => 10, :buckets => 6) }

before(:each) { time_travel_to("Jan 1 2010") }
before(:each) { rr.clear("test") }

it "should initialize db" do
lambda {
RRStat.new(:precision => 10, :buckets => 6)
RRRDTool.new(:step => 10, :buckets => 6)
}.should_not raise_error
end

Expand Down Expand Up @@ -112,6 +112,9 @@
:key_count => { 0 => 0, 1 => 1, 2 => 0, 3 => 0, 4 => 0, 5 => 0 }
}
end
end

it "should store & verify epoch signatures for each bucket" do
pending "otherwise, if we skip several buckets, they won't get cleared"
end
end

0 comments on commit 48fba22

Please sign in to comment.