time_window_drop_collector 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|