time_window_drop_collector 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/.rvmrc.example +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +2 -0
- data/README.md +68 -0
- data/Rakefile +18 -0
- data/lib/time_window_drop_collector/config.rb +20 -0
- data/lib/time_window_drop_collector/logger.rb +5 -0
- data/lib/time_window_drop_collector/storage.rb +46 -0
- data/lib/time_window_drop_collector/version.rb +3 -0
- data/lib/time_window_drop_collector/wrapper.rb +16 -0
- data/lib/time_window_drop_collector/wrappers/memcache.rb +19 -0
- data/lib/time_window_drop_collector/wrappers/mock.rb +19 -0
- data/lib/time_window_drop_collector/wrappers/rails_cache.rb +21 -0
- data/lib/time_window_drop_collector/wrappers/redis.rb +20 -0
- data/lib/time_window_drop_collector.rb +37 -0
- data/test/config_test.rb +23 -0
- data/test/logger_test.rb +11 -0
- data/test/memcache_wrapper_test.rb +21 -0
- data/test/mock_wrapper_test.rb +21 -0
- data/test/rails_cache_wrapper_test.rb +28 -0
- data/test/redis_wrapper_test.rb +26 -0
- data/test/storage_test.rb +234 -0
- data/test/test_helper.rb +16 -0
- data/test/time_window_drop_collector_test.rb +70 -0
- data/test/wrapper_test.rb +23 -0
- data/time_window_drop_collector.gemspec +28 -0
- metadata +150 -0
data/.gitignore
ADDED
data/.rvmrc.example
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.2@time_window_drop_collector
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# Time Window Drop Collector
|
2
|
+
|
3
|
+
System to keep record of an _amount_ for a concrete duration.
|
4
|
+
|
5
|
+
With _Time Window Drop Collector_ you can define a **maximun time** you want to keep the record.
|
6
|
+
|
7
|
+
You can also keep record for **different keys**.
|
8
|
+
|
9
|
+
## How to use
|
10
|
+
|
11
|
+
### Install
|
12
|
+
|
13
|
+
gem install time_window_drop_collector
|
14
|
+
|
15
|
+
### Config
|
16
|
+
|
17
|
+
These are the default values
|
18
|
+
|
19
|
+
twdc =
|
20
|
+
TimeWindowDropCollector.new do
|
21
|
+
client :memcache, "localhost:11211" # underneeth client
|
22
|
+
window 600 # in seconds
|
23
|
+
slices 10 # one slice every minute
|
24
|
+
end
|
25
|
+
|
26
|
+
### Use
|
27
|
+
|
28
|
+
twdc.drop( "id1" )
|
29
|
+
twdc.drop( "id1" )
|
30
|
+
twdc.drop( "id2" )
|
31
|
+
twdc.drop( ["id1", "id2"] )
|
32
|
+
|
33
|
+
twdc.count( "id1" ) # => 3
|
34
|
+
|
35
|
+
# after 10 minutes
|
36
|
+
twdc.count( "id1" ) # => 0
|
37
|
+
|
38
|
+
## Cache clients wrappers
|
39
|
+
|
40
|
+
Now we have implementation for 3 diferent underneeth cache clients.
|
41
|
+
|
42
|
+
### Memcache
|
43
|
+
|
44
|
+
It uses the Dalli Client for memcache.
|
45
|
+
|
46
|
+
twdc =
|
47
|
+
TimeWindowDropCollector.new do
|
48
|
+
client :memcache, "localhost:11211"
|
49
|
+
end
|
50
|
+
|
51
|
+
### Rails cache
|
52
|
+
|
53
|
+
It uses the `Rails.cache` accesible
|
54
|
+
|
55
|
+
twdc =
|
56
|
+
TimeWindowDropCollector.new do
|
57
|
+
client :rails_cache
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
### Redis
|
62
|
+
|
63
|
+
twdc =
|
64
|
+
TimeWindowDropCollector.new do
|
65
|
+
client :redis, { :host => "host", :port => "port" }
|
66
|
+
end
|
67
|
+
|
68
|
+
At the moment this wrapper does not support auto-key-clean so the stored keys will be there until anyone delete them.
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# require 'bundler/gem_tasks'
|
2
|
+
|
3
|
+
require 'rake'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'bundler'
|
6
|
+
|
7
|
+
include Rake::DSL
|
8
|
+
|
9
|
+
Bundler::GemHelper.install_tasks
|
10
|
+
|
11
|
+
task :default => :test
|
12
|
+
|
13
|
+
Rake::TestTask.new do |t|
|
14
|
+
t.libs << '.'
|
15
|
+
t.test_files = FileList['test/*_test.rb']
|
16
|
+
t.verbose = true
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module TimeWindowDropCollector::Config
|
2
|
+
def self.extract( block )
|
3
|
+
@opts = {}
|
4
|
+
instance_eval( &block )
|
5
|
+
@opts
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.client( type, *opts )
|
9
|
+
@opts[:client] = type
|
10
|
+
@opts[:client_opts] = opts
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.window( seconds )
|
14
|
+
@opts[:window] = seconds
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.slices( num )
|
18
|
+
@opts[:slices] = num
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class TimeWindowDropCollector::Storage
|
2
|
+
attr_reader :wrapper, :window, :slices
|
3
|
+
|
4
|
+
def initialize( wrapper, window, slices )
|
5
|
+
@wrapper = wrapper
|
6
|
+
@window = window
|
7
|
+
@slices = slices
|
8
|
+
end
|
9
|
+
|
10
|
+
def incr( key )
|
11
|
+
wrapper.incr( time_key( key ), window )
|
12
|
+
end
|
13
|
+
|
14
|
+
def count( key )
|
15
|
+
time_keys = time_keys( key )
|
16
|
+
values = wrapper.values_for( time_keys )
|
17
|
+
|
18
|
+
values.map( &:to_i ).inject( :+ ).to_i
|
19
|
+
end
|
20
|
+
|
21
|
+
def time_key( key, time = timestamp )
|
22
|
+
"drop_window_#{key}_#{slice_start_timestamp( time )}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def time_keys( key )
|
26
|
+
now = timestamp
|
27
|
+
|
28
|
+
( 0..( slices - 1 ) ).map{ |i|
|
29
|
+
time_key( key, now - ( ( i*slice_milliseconds ) / 1000 ) )
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def timestamp
|
34
|
+
Time.now
|
35
|
+
end
|
36
|
+
|
37
|
+
def slice_milliseconds
|
38
|
+
( window * 1000 ) / slices
|
39
|
+
end
|
40
|
+
|
41
|
+
def slice_start_timestamp( time )
|
42
|
+
time_milliseconds = ( time.to_f * 1000 ).truncate
|
43
|
+
|
44
|
+
( time_milliseconds / slice_milliseconds ) * slice_milliseconds
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module TimeWindowDropCollector::Wrapper
|
2
|
+
def self.instance( type, opts = nil )
|
3
|
+
case type
|
4
|
+
when :memcache
|
5
|
+
TimeWindowDropCollector::Wrappers::Memcache.new( opts )
|
6
|
+
when :redis
|
7
|
+
TimeWindowDropCollector::Wrappers::Redis.new( opts )
|
8
|
+
when :rails_cache
|
9
|
+
TimeWindowDropCollector::Wrappers::RailsCache.new( opts )
|
10
|
+
when :mock
|
11
|
+
TimeWindowDropCollector::Wrappers::Mock.new( opts )
|
12
|
+
else
|
13
|
+
raise ArgumentError, "type not supported: '#{type}'"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class TimeWindowDropCollector
|
2
|
+
module Wrappers
|
3
|
+
class Memcache
|
4
|
+
attr_reader :client
|
5
|
+
|
6
|
+
def initialize( opts )
|
7
|
+
@client = Dalli::Client.new( *opts )
|
8
|
+
end
|
9
|
+
|
10
|
+
def incr( key, expire_time )
|
11
|
+
client.incr( key, 1, expire_time, 1 )
|
12
|
+
end
|
13
|
+
|
14
|
+
def values_for( keys )
|
15
|
+
client.get_multi( keys ).values
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class TimeWindowDropCollector
|
2
|
+
module Wrappers
|
3
|
+
class Mock
|
4
|
+
attr_reader :client
|
5
|
+
|
6
|
+
def initialize( opts )
|
7
|
+
@client = MemcacheMock.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def incr( key, expire_time )
|
11
|
+
client.incr( key, 1, nil, 1 )
|
12
|
+
end
|
13
|
+
|
14
|
+
def values_for( keys )
|
15
|
+
client.get_multi( keys ).values
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class TimeWindowDropCollector
|
2
|
+
module Wrappers
|
3
|
+
class RailsCache
|
4
|
+
attr_reader :client
|
5
|
+
|
6
|
+
def initialize( opts )
|
7
|
+
@client = Rails.cache
|
8
|
+
end
|
9
|
+
|
10
|
+
def incr( key, expire_time )
|
11
|
+
value = client.read( key ).to_i + 1
|
12
|
+
client.write( key, value, :expires_in => expire_time )
|
13
|
+
end
|
14
|
+
|
15
|
+
def values_for( keys )
|
16
|
+
client.read_multi( keys ).values
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class TimeWindowDropCollector
|
2
|
+
module Wrappers
|
3
|
+
class Redis
|
4
|
+
attr_reader :client
|
5
|
+
|
6
|
+
def initialize( opts )
|
7
|
+
@client = ::Redis.new( *opts )
|
8
|
+
end
|
9
|
+
|
10
|
+
def incr( key, expire_time )
|
11
|
+
client.incr( key )
|
12
|
+
end
|
13
|
+
|
14
|
+
def values_for( keys )
|
15
|
+
client.mget( *keys )
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "dalli"
|
2
|
+
|
3
|
+
require_relative "time_window_drop_collector/version"
|
4
|
+
require_relative "time_window_drop_collector/config"
|
5
|
+
require_relative "time_window_drop_collector/logger"
|
6
|
+
require_relative "time_window_drop_collector/storage"
|
7
|
+
require_relative "time_window_drop_collector/wrapper"
|
8
|
+
require_relative "time_window_drop_collector/wrappers/memcache"
|
9
|
+
require_relative "time_window_drop_collector/wrappers/mock"
|
10
|
+
require_relative "time_window_drop_collector/wrappers/redis"
|
11
|
+
require_relative "time_window_drop_collector/wrappers/rails_cache"
|
12
|
+
|
13
|
+
class TimeWindowDropCollector
|
14
|
+
attr_reader :config, :wrapper, :storage
|
15
|
+
|
16
|
+
def initialize( &block )
|
17
|
+
@config = {
|
18
|
+
:window => 600,
|
19
|
+
:slices => 10,
|
20
|
+
:client => :memcache,
|
21
|
+
:client_opts => "localhost:11211"
|
22
|
+
}
|
23
|
+
|
24
|
+
@config.merge!( TimeWindowDropCollector::Config.extract( block ) ) if block_given?
|
25
|
+
|
26
|
+
@wrapper = TimeWindowDropCollector::Wrapper.instance( config[:client], config[:client_opts] )
|
27
|
+
@storage = TimeWindowDropCollector::Storage.new( wrapper, config[:window], config[:slices] )
|
28
|
+
end
|
29
|
+
|
30
|
+
def drop( key )
|
31
|
+
storage.incr( key )
|
32
|
+
end
|
33
|
+
|
34
|
+
def count( key )
|
35
|
+
storage.count( key )
|
36
|
+
end
|
37
|
+
end
|
data/test/config_test.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
|
3
|
+
class ConfigTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
TimeWindowDropCollector::Logger.stubs( :log )
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_config
|
9
|
+
proc =
|
10
|
+
Proc.new do
|
11
|
+
client "client", "client_opt1", "client_opt2"
|
12
|
+
window "window"
|
13
|
+
slices "slices"
|
14
|
+
end
|
15
|
+
|
16
|
+
config = TimeWindowDropCollector::Config.extract( proc )
|
17
|
+
|
18
|
+
assert_equal( "client", config[:client] )
|
19
|
+
assert_equal( ["client_opt1", "client_opt2"], config[:client_opts] )
|
20
|
+
assert_equal( "window", config[:window] )
|
21
|
+
assert_equal( "slices", config[:slices] )
|
22
|
+
end
|
23
|
+
end
|
data/test/logger_test.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
|
3
|
+
class LoggerTest < Test::Unit::TestCase
|
4
|
+
def test_log
|
5
|
+
IO.any_instance.expects( :puts ).with( "[TWDC 2001-02-01 04:05:06] hello!" )
|
6
|
+
|
7
|
+
Delorean.time_travel_to( "2001-02-01 04:05:06" ) do
|
8
|
+
TimeWindowDropCollector::Logger.log( "hello!" )
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
|
3
|
+
class MemcacheWrapperTest < Test::Unit::TestCase
|
4
|
+
def test_initialize
|
5
|
+
Dalli::Client.expects( :new ).with( "arg1" ).returns( "client" )
|
6
|
+
wrapper = TimeWindowDropCollector::Wrappers::Memcache.new( ["arg1"] )
|
7
|
+
assert_equal( "client", wrapper.client )
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_incr
|
11
|
+
wrapper = TimeWindowDropCollector::Wrappers::Memcache.new( ["arg1"] )
|
12
|
+
wrapper.client.expects( :incr ).with( "key", 1, "expire_time", 1 )
|
13
|
+
wrapper.incr( "key", "expire_time" )
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_values_for
|
17
|
+
wrapper = TimeWindowDropCollector::Wrappers::Memcache.new( ["arg1"] )
|
18
|
+
wrapper.client.expects( :get_multi ).with( "keys" ).returns( {:a => 1, :b => 2} )
|
19
|
+
assert_equal( [1, 2], wrapper.values_for( "keys" ))
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
|
3
|
+
class MockWrapperTest < Test::Unit::TestCase
|
4
|
+
def test_initialize
|
5
|
+
MemcacheMock.expects( :new ).returns( "client" )
|
6
|
+
wrapper = TimeWindowDropCollector::Wrappers::Mock.new( ["arg1"] )
|
7
|
+
assert_equal( "client", wrapper.client )
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_incr
|
11
|
+
wrapper = TimeWindowDropCollector::Wrappers::Mock.new( ["arg1"] )
|
12
|
+
wrapper.client.expects( :incr ).with( "key", 1, nil, 1 )
|
13
|
+
wrapper.incr( "key", "expire_time" )
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_values_for
|
17
|
+
wrapper = TimeWindowDropCollector::Wrappers::Mock.new( ["arg1"] )
|
18
|
+
wrapper.client.expects( :get_multi ).with( "keys" ).returns( {:a => 1, :b => 2} )
|
19
|
+
assert_equal( [1, 2], wrapper.values_for( "keys" ))
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
|
3
|
+
class MemcacheWrapperTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@rails_client = mock()
|
6
|
+
Rails.stubs( :cache ).returns( @rails_cache )
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_initialize
|
10
|
+
Rails.expects( :cache ).returns( "client" )
|
11
|
+
wrapper = TimeWindowDropCollector::Wrappers::RailsCache.new( ["arg1"] )
|
12
|
+
assert_equal( "client", wrapper.client )
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_incr
|
16
|
+
wrapper = TimeWindowDropCollector::Wrappers::RailsCache.new( ["arg1"] )
|
17
|
+
wrapper.client.expects( :read ).with( "key" ).returns( 2 )
|
18
|
+
wrapper.client.expects( :write ).with( "key", 3, :expires_in => "expire_time" )
|
19
|
+
|
20
|
+
wrapper.incr( "key", "expire_time" )
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_values_for
|
24
|
+
wrapper = TimeWindowDropCollector::Wrappers::RailsCache.new( ["arg1"] )
|
25
|
+
wrapper.client.expects( :read_multi ).with( "keys" ).returns( {:a => 1, :b => 2} )
|
26
|
+
assert_equal( [1, 2], wrapper.values_for( "keys" ))
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
|
3
|
+
class Redis
|
4
|
+
def initialize( *opts )
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class MemcacheWrapperTest < Test::Unit::TestCase
|
9
|
+
def test_initialize
|
10
|
+
Redis.expects( :new ).with( "arg1", "arg2" ).returns( "client" )
|
11
|
+
wrapper = TimeWindowDropCollector::Wrappers::Redis.new( ["arg1", "arg2"] )
|
12
|
+
assert_equal( "client", wrapper.client )
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_incr
|
16
|
+
wrapper = TimeWindowDropCollector::Wrappers::Redis.new( ["arg1"] )
|
17
|
+
wrapper.client.expects( :incr ).with( "key" )
|
18
|
+
wrapper.incr( "key", "expire_time" )
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_values_for
|
22
|
+
wrapper = TimeWindowDropCollector::Wrappers::Redis.new( ["arg1"] )
|
23
|
+
wrapper.client.expects( :mget ).with( "key1", "key2" ).returns( [1, 2] )
|
24
|
+
assert_equal( [1, 2], wrapper.values_for( ["key1", "key2"] ))
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,234 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
|
3
|
+
class StorageTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@client = mock()
|
6
|
+
@storage = TimeWindowDropCollector::Storage.new( @client, 600, 10 )
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_time_key_when_time_is_present
|
10
|
+
timestamp = Time.new( 2001, 2, 3, 4, 5 )
|
11
|
+
assert_equal( "drop_window_key_981169500000", @storage.time_key( "key", timestamp ) )
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_time_key_when_time_is_not_present
|
15
|
+
@storage.stubs( :timestamp ).returns( Time.new( 2012, 4, 3, 2, 1 ))
|
16
|
+
assert_equal( "drop_window_key_1333411260000", @storage.time_key( "key" ))
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_time_keys_should_return_10_keys_for_the_last_10_minutes
|
20
|
+
keys = [
|
21
|
+
"drop_window_key_1325560800000",
|
22
|
+
"drop_window_key_1325560740000",
|
23
|
+
"drop_window_key_1325560680000",
|
24
|
+
"drop_window_key_1325560620000",
|
25
|
+
"drop_window_key_1325560560000",
|
26
|
+
"drop_window_key_1325560500000",
|
27
|
+
"drop_window_key_1325560440000",
|
28
|
+
"drop_window_key_1325560380000",
|
29
|
+
"drop_window_key_1325560320000",
|
30
|
+
"drop_window_key_1325560260000"
|
31
|
+
]
|
32
|
+
|
33
|
+
Delorean.time_travel_to( '2012-01-03 04:20' ) do
|
34
|
+
assert_equal( keys, @storage.time_keys( "key" ))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_incr
|
39
|
+
@storage.expects( :time_key ).with( "key" ).returns( "time_key" )
|
40
|
+
@client.expects( :incr ).with( "time_key", 600 )
|
41
|
+
|
42
|
+
@storage.incr( "key" )
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_count
|
46
|
+
keys = [
|
47
|
+
"drop_window_key_201201031416",
|
48
|
+
"drop_window_key_201201031415",
|
49
|
+
"drop_window_key_201201031414",
|
50
|
+
"drop_window_key_201201031413",
|
51
|
+
"drop_window_key_201201031412"
|
52
|
+
]
|
53
|
+
|
54
|
+
values = [nil, 1, "2", 3, 4, 5]
|
55
|
+
|
56
|
+
@storage.expects( :time_keys ).with( "key" ).returns( "keys" )
|
57
|
+
@client.expects( :values_for ).with( "keys" ).returns( values )
|
58
|
+
|
59
|
+
assert_equal( 15, @storage.count( "key" ))
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_count_when_empty_values
|
63
|
+
@storage.stubs( :time_keys )
|
64
|
+
@client.expects( :values_for ).returns( [] )
|
65
|
+
assert_equal( 0, @storage.count( "key" ))
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_integration_count
|
69
|
+
client = TimeWindowDropCollector::Wrapper.instance( :mock )
|
70
|
+
storage = TimeWindowDropCollector::Storage.new( client, 600, 10 )
|
71
|
+
|
72
|
+
key_1 = 1
|
73
|
+
key_2 = 2
|
74
|
+
key_3 = 3
|
75
|
+
|
76
|
+
storage.incr( key_1 )
|
77
|
+
storage.incr( key_2 )
|
78
|
+
storage.incr( key_3 )
|
79
|
+
storage.incr( key_2 )
|
80
|
+
|
81
|
+
assert_equal( 1, storage.count( key_1 ))
|
82
|
+
assert_equal( 2, storage.count( key_2 ))
|
83
|
+
assert_equal( 1, storage.count( key_3 ))
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_slice_start_timestamp
|
87
|
+
storage = TimeWindowDropCollector::Storage.new( nil, 100, 10 )
|
88
|
+
time = Time.new( 2001, 2, 3, 4, 5 )
|
89
|
+
|
90
|
+
assert_equal( 981169500000, storage.slice_start_timestamp( time ) )
|
91
|
+
assert_equal( 981169500000, storage.slice_start_timestamp( time + 1 ) )
|
92
|
+
assert_equal( 981169500000, storage.slice_start_timestamp( time + 9 ) )
|
93
|
+
assert_equal( 981169520000, storage.slice_start_timestamp( time + 25 ) )
|
94
|
+
assert_equal( 981169600000, storage.slice_start_timestamp( time + 100 ) )
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_slice_start_timestamp_with_slice_sizes_no_integer
|
98
|
+
storage = TimeWindowDropCollector::Storage.new( nil, 100, 12 )
|
99
|
+
time = Time.new( 2001, 2, 3, 4, 5 )
|
100
|
+
|
101
|
+
assert_equal( 981169493317, storage.slice_start_timestamp( time ) )
|
102
|
+
assert_equal( 981169493317, storage.slice_start_timestamp( time + 1 ) )
|
103
|
+
assert_equal( 981169501650, storage.slice_start_timestamp( time + 9 ) )
|
104
|
+
assert_equal( 981169518316, storage.slice_start_timestamp( time + 25 ) )
|
105
|
+
assert_equal( 981169593313, storage.slice_start_timestamp( time + 100 ) )
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_slice_start_timestamp_with_slice_size_less_than_a_second
|
109
|
+
storage = TimeWindowDropCollector::Storage.new( nil, 100, 101 )
|
110
|
+
time = Time.new( 2001, 2, 3, 4, 5 )
|
111
|
+
|
112
|
+
assert_equal( 981169499970, storage.slice_start_timestamp( time ) )
|
113
|
+
assert_equal( 981169500960, storage.slice_start_timestamp( time + 1 ) )
|
114
|
+
assert_equal( 981169508880, storage.slice_start_timestamp( time + 9 ) )
|
115
|
+
assert_equal( 981169524720, storage.slice_start_timestamp( time + 25 ) )
|
116
|
+
assert_equal( 981169599960, storage.slice_start_timestamp( time + 100 ) )
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_window_size_consistency
|
120
|
+
window = 100
|
121
|
+
slices = 10
|
122
|
+
storage = TimeWindowDropCollector::Storage.new( nil, window, slices )
|
123
|
+
time = Time.new( 2001, 2, 3, 4, 5 )
|
124
|
+
|
125
|
+
first_slice = storage.slice_start_timestamp( time )
|
126
|
+
last_slice = storage.slice_start_timestamp( time + 101 )
|
127
|
+
|
128
|
+
assert_equal( window, ( last_slice - first_slice ) / 1000 )
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_window_size_consistency_with_slice_sizes_no_integer
|
132
|
+
window = 100
|
133
|
+
slices = 12
|
134
|
+
storage = TimeWindowDropCollector::Storage.new( nil, window, slices )
|
135
|
+
time = Time.new( 2001, 2, 3, 4, 5 )
|
136
|
+
|
137
|
+
first_slice = storage.slice_start_timestamp( time )
|
138
|
+
last_slice = storage.slice_start_timestamp( time + 101 )
|
139
|
+
|
140
|
+
assert_equal( 99, ( last_slice - first_slice ) / 1000 )
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_window_size_consistency_with_slice_sizes_less_than_a_second
|
144
|
+
window = 100
|
145
|
+
slices = 101
|
146
|
+
storage = TimeWindowDropCollector::Storage.new( nil, window, slices )
|
147
|
+
time = Time.new( 2001, 2, 3, 4, 5 )
|
148
|
+
|
149
|
+
first_slice = storage.slice_start_timestamp( time )
|
150
|
+
last_slice = storage.slice_start_timestamp( time + 101 )
|
151
|
+
|
152
|
+
assert_equal( window, ( last_slice - first_slice ) / 1000 )
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_integration_store_of_the_count_for_10_minutes
|
156
|
+
client = TimeWindowDropCollector::Wrapper.instance( :mock )
|
157
|
+
storage = TimeWindowDropCollector::Storage.new( client, 600, 10 )
|
158
|
+
|
159
|
+
key_1 = 1
|
160
|
+
|
161
|
+
Delorean.time_travel_to( '2012-01-03 11:00' ) do
|
162
|
+
storage.incr( key_1 )
|
163
|
+
end
|
164
|
+
|
165
|
+
Delorean.time_travel_to( '2012-01-03 11:01' ) do
|
166
|
+
storage.incr( key_1 )
|
167
|
+
end
|
168
|
+
|
169
|
+
Delorean.time_travel_to( '2012-01-03 11:02' ) do
|
170
|
+
storage.incr( key_1 )
|
171
|
+
end
|
172
|
+
|
173
|
+
Delorean.time_travel_to( '2012-01-03 11:03' ) do
|
174
|
+
storage.incr( key_1 )
|
175
|
+
end
|
176
|
+
|
177
|
+
Delorean.time_travel_to( '2012-01-03 11:04' ) do
|
178
|
+
storage.incr( key_1 )
|
179
|
+
end
|
180
|
+
|
181
|
+
Delorean.time_travel_to( '2012-01-03 11:05' ) do
|
182
|
+
storage.incr( key_1 )
|
183
|
+
end
|
184
|
+
|
185
|
+
Delorean.time_travel_to( '2012-01-03 11:06' ) do
|
186
|
+
storage.incr( key_1 )
|
187
|
+
end
|
188
|
+
|
189
|
+
Delorean.time_travel_to( '2012-01-03 11:07' ) do
|
190
|
+
storage.incr( key_1 )
|
191
|
+
end
|
192
|
+
|
193
|
+
Delorean.time_travel_to( '2012-01-03 11:08' ) do
|
194
|
+
storage.incr( key_1 )
|
195
|
+
end
|
196
|
+
|
197
|
+
Delorean.time_travel_to( '2012-01-03 11:09' ) do
|
198
|
+
storage.incr( key_1 )
|
199
|
+
end
|
200
|
+
|
201
|
+
Delorean.time_travel_to( '2012-01-03 11:10' ) do
|
202
|
+
storage.incr( key_1 )
|
203
|
+
end
|
204
|
+
|
205
|
+
# counters
|
206
|
+
Delorean.time_travel_to( '2012-01-03 10:59' ) do
|
207
|
+
assert_equal( 0, storage.count( key_1 ))
|
208
|
+
end
|
209
|
+
|
210
|
+
Delorean.time_travel_to( '2012-01-03 11:00' ) do
|
211
|
+
assert_equal( 1, storage.count( key_1 ))
|
212
|
+
end
|
213
|
+
|
214
|
+
Delorean.time_travel_to( '2012-01-03 11:05' ) do
|
215
|
+
assert_equal( 6, storage.count( key_1 ))
|
216
|
+
end
|
217
|
+
|
218
|
+
Delorean.time_travel_to( '2012-01-03 11:10' ) do
|
219
|
+
assert_equal( 10, storage.count( key_1 ))
|
220
|
+
end
|
221
|
+
|
222
|
+
Delorean.time_travel_to( '2012-01-03 11:15' ) do
|
223
|
+
assert_equal( 5, storage.count( key_1 ))
|
224
|
+
end
|
225
|
+
|
226
|
+
Delorean.time_travel_to( '2012-01-03 11:19' ) do
|
227
|
+
assert_equal( 1, storage.count( key_1 ))
|
228
|
+
end
|
229
|
+
|
230
|
+
Delorean.time_travel_to( '2012-01-03 11:20' ) do
|
231
|
+
assert_equal( 0, storage.count( key_1 ))
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
|
3
|
+
class TimeWindowDropCollectorTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@twdc = TimeWindowDropCollector::Logger.stubs( :log )
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_initialize_with_empty_config
|
9
|
+
TimeWindowDropCollector::Config.expects( :extract ).never
|
10
|
+
TimeWindowDropCollector::Wrapper.expects( :instance ).with( :memcache, "localhost:11211" ).returns( "wrapper" )
|
11
|
+
TimeWindowDropCollector::Storage.expects( :new ).with( "wrapper", 600, 10 ).returns( "storage" )
|
12
|
+
|
13
|
+
twdc = TimeWindowDropCollector.new
|
14
|
+
|
15
|
+
assert_equal( "wrapper", twdc.wrapper )
|
16
|
+
assert_equal( "storage", twdc.storage )
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def test_initialize_with_block_config
|
21
|
+
config = {
|
22
|
+
:window => "window",
|
23
|
+
:slices => "slices",
|
24
|
+
:client => "client",
|
25
|
+
:client_opts => "client_opts"
|
26
|
+
}
|
27
|
+
|
28
|
+
TimeWindowDropCollector::Config.expects( :extract ).returns( config )
|
29
|
+
TimeWindowDropCollector::Wrapper.expects( :instance ).with( "client", "client_opts" ).returns( "wrapper" )
|
30
|
+
TimeWindowDropCollector::Storage.expects( :new ).with( "wrapper", "window", "slices" ).returns( "storage" )
|
31
|
+
|
32
|
+
twdc = TimeWindowDropCollector.new {}
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_drop
|
36
|
+
storage = mock()
|
37
|
+
storage.expects( :incr ).with( "key" )
|
38
|
+
|
39
|
+
twdc = TimeWindowDropCollector.new
|
40
|
+
twdc.stubs( :storage ).returns( storage )
|
41
|
+
|
42
|
+
twdc.drop( "key" )
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_count
|
46
|
+
storage = mock()
|
47
|
+
storage.expects( :count ).with( "key" )
|
48
|
+
|
49
|
+
twdc = TimeWindowDropCollector.new
|
50
|
+
twdc.stubs( :storage ).returns( storage )
|
51
|
+
|
52
|
+
twdc.count( "key" )
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_integration_new
|
56
|
+
twdc =
|
57
|
+
TimeWindowDropCollector.new do
|
58
|
+
client :memcache
|
59
|
+
window 100
|
60
|
+
slices 20
|
61
|
+
end
|
62
|
+
|
63
|
+
assert( twdc.wrapper.is_a? TimeWindowDropCollector::Wrappers::Memcache )
|
64
|
+
assert( twdc.wrapper.client.is_a? Dalli::Client )
|
65
|
+
assert( twdc.storage.is_a? TimeWindowDropCollector::Storage )
|
66
|
+
|
67
|
+
assert_equal( 100, twdc.storage.window )
|
68
|
+
assert_equal( 20, twdc.storage.slices )
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
|
3
|
+
class WrapperTest < Test::Unit::TestCase
|
4
|
+
def test_instance_when_memcache
|
5
|
+
instance = TimeWindowDropCollector::Wrapper.instance( :memcache, "localhost:11211" )
|
6
|
+
assert( instance.is_a? TimeWindowDropCollector::Wrappers::Memcache )
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_instance_when_mock
|
10
|
+
instance = TimeWindowDropCollector::Wrapper.instance( :mock )
|
11
|
+
assert( instance.is_a? TimeWindowDropCollector::Wrappers::Mock )
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_instance_when_redis
|
15
|
+
instance = TimeWindowDropCollector::Wrapper.instance( :redis, ["server", "port"] )
|
16
|
+
assert( instance.is_a? TimeWindowDropCollector::Wrappers::Redis )
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_instance_when_rails_cache
|
20
|
+
instance = TimeWindowDropCollector::Wrapper.instance( :rails_cache )
|
21
|
+
assert( instance.is_a? TimeWindowDropCollector::Wrappers::RailsCache )
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path( "../lib", __FILE__ )
|
3
|
+
require "time_window_drop_collector/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "time_window_drop_collector"
|
7
|
+
s.version = TimeWindowDropCollector::VERSION
|
8
|
+
s.authors = ["Fernando Guillen", "Carlos Moutinho"]
|
9
|
+
s.email = ["fguillen.mail@gmail.com", "carlosmoutinho@gmail.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = "Counter storage system for a concrete time window"
|
12
|
+
s.description = "Counter storage system for a concrete time window"
|
13
|
+
|
14
|
+
s.rubyforge_project = "time_window_drop_collector"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split( "\n" )
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split( "\n" )
|
18
|
+
s.executables = `git ls-files -- bin/*`.split( "\n" ).map{ |f| File.basename( f ) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_development_dependency "bundler", "1.0.21"
|
22
|
+
s.add_development_dependency "rake", "0.9.2.2"
|
23
|
+
s.add_development_dependency "mocha", "0.10.0"
|
24
|
+
s.add_development_dependency "delorean", "1.2.0"
|
25
|
+
s.add_development_dependency "memcache_mock", "0.0.1"
|
26
|
+
|
27
|
+
s.add_dependency "dalli"
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: time_window_drop_collector
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.5
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Fernando Guillen
|
9
|
+
- Carlos Moutinho
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2012-01-23 00:00:00.000000000Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: bundler
|
17
|
+
requirement: &70242052766820 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - =
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.0.21
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *70242052766820
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rake
|
28
|
+
requirement: &70242052766320 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - =
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.9.2.2
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *70242052766320
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: mocha
|
39
|
+
requirement: &70242052765860 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - =
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: 0.10.0
|
45
|
+
type: :development
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *70242052765860
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: delorean
|
50
|
+
requirement: &70242052765400 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - =
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 1.2.0
|
56
|
+
type: :development
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: *70242052765400
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: memcache_mock
|
61
|
+
requirement: &70242052764940 !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - =
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: 0.0.1
|
67
|
+
type: :development
|
68
|
+
prerelease: false
|
69
|
+
version_requirements: *70242052764940
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: dalli
|
72
|
+
requirement: &70242052764560 !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
type: :runtime
|
79
|
+
prerelease: false
|
80
|
+
version_requirements: *70242052764560
|
81
|
+
description: Counter storage system for a concrete time window
|
82
|
+
email:
|
83
|
+
- fguillen.mail@gmail.com
|
84
|
+
- carlosmoutinho@gmail.com
|
85
|
+
executables: []
|
86
|
+
extensions: []
|
87
|
+
extra_rdoc_files: []
|
88
|
+
files:
|
89
|
+
- .gitignore
|
90
|
+
- .rvmrc.example
|
91
|
+
- .travis.yml
|
92
|
+
- Gemfile
|
93
|
+
- README.md
|
94
|
+
- Rakefile
|
95
|
+
- lib/time_window_drop_collector.rb
|
96
|
+
- lib/time_window_drop_collector/config.rb
|
97
|
+
- lib/time_window_drop_collector/logger.rb
|
98
|
+
- lib/time_window_drop_collector/storage.rb
|
99
|
+
- lib/time_window_drop_collector/version.rb
|
100
|
+
- lib/time_window_drop_collector/wrapper.rb
|
101
|
+
- lib/time_window_drop_collector/wrappers/memcache.rb
|
102
|
+
- lib/time_window_drop_collector/wrappers/mock.rb
|
103
|
+
- lib/time_window_drop_collector/wrappers/rails_cache.rb
|
104
|
+
- lib/time_window_drop_collector/wrappers/redis.rb
|
105
|
+
- test/config_test.rb
|
106
|
+
- test/logger_test.rb
|
107
|
+
- test/memcache_wrapper_test.rb
|
108
|
+
- test/mock_wrapper_test.rb
|
109
|
+
- test/rails_cache_wrapper_test.rb
|
110
|
+
- test/redis_wrapper_test.rb
|
111
|
+
- test/storage_test.rb
|
112
|
+
- test/test_helper.rb
|
113
|
+
- test/time_window_drop_collector_test.rb
|
114
|
+
- test/wrapper_test.rb
|
115
|
+
- time_window_drop_collector.gemspec
|
116
|
+
homepage: ''
|
117
|
+
licenses: []
|
118
|
+
post_install_message:
|
119
|
+
rdoc_options: []
|
120
|
+
require_paths:
|
121
|
+
- lib
|
122
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
123
|
+
none: false
|
124
|
+
requirements:
|
125
|
+
- - ! '>='
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '0'
|
128
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
requirements: []
|
135
|
+
rubyforge_project: time_window_drop_collector
|
136
|
+
rubygems_version: 1.8.15
|
137
|
+
signing_key:
|
138
|
+
specification_version: 3
|
139
|
+
summary: Counter storage system for a concrete time window
|
140
|
+
test_files:
|
141
|
+
- test/config_test.rb
|
142
|
+
- test/logger_test.rb
|
143
|
+
- test/memcache_wrapper_test.rb
|
144
|
+
- test/mock_wrapper_test.rb
|
145
|
+
- test/rails_cache_wrapper_test.rb
|
146
|
+
- test/redis_wrapper_test.rb
|
147
|
+
- test/storage_test.rb
|
148
|
+
- test/test_helper.rb
|
149
|
+
- test/time_window_drop_collector_test.rb
|
150
|
+
- test/wrapper_test.rb
|