mirror of
https://github.com/jimeh/redistat.git
synced 2026-02-19 05:16:39 +00:00
Merge branch 'key-expiry'
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
language: ruby
|
||||
rvm:
|
||||
- 1.8.7
|
||||
- 1.9.2
|
||||
- jruby
|
||||
- 1.9.3
|
||||
- jruby-18mode
|
||||
- jruby-19mode
|
||||
- ree
|
||||
|
||||
30
README.md
30
README.md
@@ -202,6 +202,36 @@ This section does need further expanding as there's a lot to cover when it
|
||||
comes to the finder.
|
||||
|
||||
|
||||
## Key Expiry
|
||||
|
||||
Support for expiring keys from Redis is available, allowing you too keep
|
||||
varying levels of details for X period of time. This allows you easily keep
|
||||
things nice and tidy by only storing varying levels detailed stats only for as
|
||||
long as you need.
|
||||
|
||||
In the below example we define how long Redis keys for varying depths are
|
||||
stored. Second by second stats are available for 10 minutes, minute by minute
|
||||
stats for 6 hours, hourly stats for 3 months, daily stats for 2 years, and
|
||||
yearly stats are retained forever.
|
||||
|
||||
```ruby
|
||||
class ViewStats
|
||||
include Redistat::Model
|
||||
|
||||
depth :sec
|
||||
|
||||
expire \
|
||||
:sec => 10.minutes.to_i,
|
||||
:min => 6.hours.to_i,
|
||||
:hour => 3.months.to_i,
|
||||
:day => 2.years.to_i
|
||||
end
|
||||
```
|
||||
|
||||
Keep in mind that when storing stats for a custom date in the past for
|
||||
example, the expiry time for the keys will be relative to now. The values you
|
||||
specify are simply passed to the `Redis#expire` method.
|
||||
|
||||
|
||||
## Internals
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ module Redistat
|
||||
module Connection
|
||||
|
||||
REQUIRED_SERVER_VERSION = "1.3.10"
|
||||
MIN_EXPIRE_SERVER_VERSION = "2.1.3"
|
||||
|
||||
# TODO: Create a ConnectionPool instance object using Sychronize mixin to replace Connection class
|
||||
|
||||
@@ -67,6 +68,10 @@ module Redistat
|
||||
|
||||
def check_redis_version(conn)
|
||||
raise RedisServerIsTooOld if conn.info["redis_version"] < REQUIRED_SERVER_VERSION
|
||||
if conn.info["redis_version"] < MIN_EXPIRE_SERVER_VERSION
|
||||
STDOUT.puts "WARNING: You MUST upgrade Redis to v2.1.3 or later " +
|
||||
"if you are using key expiry."
|
||||
end
|
||||
conn
|
||||
end
|
||||
|
||||
|
||||
@@ -46,6 +46,14 @@ module Redistat
|
||||
|
||||
alias :class_name :scope
|
||||
|
||||
def expire(exp = nil)
|
||||
if !exp.nil?
|
||||
options[:expire] = exp.is_a?(Hash) ? exp : Hash.new(exp)
|
||||
else
|
||||
options[:expire]
|
||||
end
|
||||
end
|
||||
|
||||
def connect_to(opts = {})
|
||||
Connection.create(opts.merge(:ref => name))
|
||||
options[:connection_ref] = name
|
||||
|
||||
@@ -5,9 +5,12 @@ module Redistat
|
||||
class << self
|
||||
|
||||
def default_options
|
||||
{ :enable_grouping => true,
|
||||
:label_indexing => true,
|
||||
:connection_ref => nil }
|
||||
{
|
||||
:enable_grouping => true,
|
||||
:label_indexing => true,
|
||||
:connection_ref => nil,
|
||||
:expire => {}
|
||||
}
|
||||
end
|
||||
|
||||
def buffer
|
||||
@@ -29,30 +32,34 @@ module Redistat
|
||||
update(*args) unless buffer.store(*args)
|
||||
end
|
||||
|
||||
def update(key, stats, depth_limit, opts)
|
||||
def update(key, stats, depth_limit, opts = {})
|
||||
if opts[:enable_grouping]
|
||||
stats = inject_group_summaries(stats)
|
||||
key.groups.each do |k|
|
||||
update_key(k, stats, depth_limit, opts[:connection_ref])
|
||||
update_key(k, stats, depth_limit, opts)
|
||||
k.update_index if opts[:label_indexing]
|
||||
end
|
||||
else
|
||||
update_key(key, stats, depth_limit, opts[:connection_ref])
|
||||
update_key(key, stats, depth_limit, opts)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_key(key, stats, depth_limit, connection_ref)
|
||||
def update_key(key, stats, depth_limit, opts = {})
|
||||
Date::DEPTHS.each do |depth|
|
||||
update_fields(key, stats, depth, connection_ref)
|
||||
update_fields(key, stats, depth, opts)
|
||||
break if depth == depth_limit
|
||||
end
|
||||
end
|
||||
|
||||
def update_fields(key, stats, depth, connection_ref = nil)
|
||||
def update_fields(key, stats, depth, opts = {})
|
||||
stats.each do |field, value|
|
||||
db(connection_ref).hincrby key.to_s(depth), field, value
|
||||
db(opts[:connection_ref]).hincrby key.to_s(depth), field, value
|
||||
end
|
||||
|
||||
if opts[:expire] && !opts[:expire][depth].nil?
|
||||
db(opts[:connection_ref]).expire key.to_s(depth), opts[:expire][depth]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -26,5 +26,6 @@ class ModelHelper4
|
||||
include Redistat::Model
|
||||
|
||||
scope "FancyHelper"
|
||||
expire :hour => 24*3600
|
||||
|
||||
end
|
||||
|
||||
@@ -38,6 +38,7 @@ describe Redistat::Model do
|
||||
ModelHelper2.store_event.should == true
|
||||
ModelHelper2.hashed_label.should == true
|
||||
ModelHelper2.scope.should be_nil
|
||||
ModelHelper2.expire.should be_nil
|
||||
|
||||
ModelHelper1.depth.should == nil
|
||||
ModelHelper1.store_event.should == nil
|
||||
@@ -57,6 +58,7 @@ describe Redistat::Model do
|
||||
|
||||
ModelHelper4.scope.should == "FancyHelper"
|
||||
ModelHelper4.send(:name).should == "FancyHelper"
|
||||
ModelHelper4.expire.should == {:hour => 24*3600}
|
||||
end
|
||||
|
||||
it "should store and fetch stats" do
|
||||
|
||||
@@ -10,6 +10,7 @@ describe Redistat::Summary do
|
||||
@date = Time.now
|
||||
@key = Redistat::Key.new(@scope, @label, @date, {:depth => :day})
|
||||
@stats = {"views" => 3, "visitors" => 2}
|
||||
@expire = {:hour => 24*3600}
|
||||
end
|
||||
|
||||
it "should update a single summary properly" do
|
||||
@@ -32,6 +33,20 @@ describe Redistat::Summary do
|
||||
summary["visitors"].should == "1"
|
||||
end
|
||||
|
||||
it "should set key expiry properly" do
|
||||
Redistat::Summary.update_all(@key, @stats, :hour,{:expire => @expire})
|
||||
((24*3600)-1..(24*3600)+1).should include(db.ttl(@key.to_s(:hour)))
|
||||
[:day, :month, :year].each do |depth|
|
||||
db.ttl(@key.to_s(depth)).should == -1
|
||||
end
|
||||
|
||||
db.flushdb
|
||||
Redistat::Summary.update_all(@key, @stats, :hour, {:expire => {}})
|
||||
[:hour, :day, :month, :year].each do |depth|
|
||||
db.ttl(@key.to_s(depth)).should == -1
|
||||
end
|
||||
end
|
||||
|
||||
it "should update all summaries properly" do
|
||||
Redistat::Summary.update_all(@key, @stats, :sec)
|
||||
[:year, :month, :day, :hour, :min, :sec, :usec].each do |depth|
|
||||
|
||||
Reference in New Issue
Block a user