redis-throttler 0.1.6 → 0.1.7
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.
- checksums.yaml +4 -4
- data/ChangeLog.md +6 -0
- data/lib/redis-throttler/model.rb +4 -7
- data/lib/redis-throttler/version.rb +1 -1
- data/spec/model_spec.rb +25 -16
- data/spec/object_spec.rb +35 -11
- data/spec/spec_helper.rb +3 -1
- data/spec/throttler_spec.rb +10 -10
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d495303bb5a7733b5fd5feb751563a505d700db5
|
4
|
+
data.tar.gz: 5c8defae79ace760ed398e9d8565ce642dfbe11c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8fc82355c9e9a62a1f06501be6eedc76231d7a0c9b90f5f3fe21730cd32cfbd476c4e6520901b69a11a5ebca9fb173d158baacf786d0f9455b8b2183e6d188c0
|
7
|
+
data.tar.gz: 9909db6de4dd0a69f075eb5ca6e91c345b4d7e5d6f35bdc2fc36d5e0efaa912a3f7aa42b145143c82cd13bb07ffcaf2407923ad13f13c812463c42ad34f7ac24
|
data/ChangeLog.md
CHANGED
@@ -16,19 +16,17 @@ module RedisThrottler
|
|
16
16
|
subject = opts[:by] || :id
|
17
17
|
limit = opts[:limit] || 5
|
18
18
|
threshold = opts[:for] || 900
|
19
|
-
interval = opts[:interval] ||
|
20
|
-
|
21
|
-
bucket_span = [interval, 600].max
|
19
|
+
interval = opts[:interval] || ([threshold,120].max)/120
|
20
|
+
bucket_span = threshold * 2
|
22
21
|
|
23
22
|
throttler = RedisThrottler::Base.new("#{klass}:#{key}", bucket_interval: interval, bucket_span: bucket_span)
|
24
|
-
@limits[key] =
|
23
|
+
@limits[key] = { :limit => limit, :threshold => threshold }
|
25
24
|
|
26
|
-
# includes('?') will return true
|
27
25
|
method = "#{key}_throttler"
|
28
26
|
|
29
27
|
%w(limits limits?).each do |string|
|
30
28
|
define_singleton_method(string) { string.include?('?') || @limits }
|
31
|
-
define_method(string) { string.include?('?') || self.class.instance_variable_get(
|
29
|
+
define_method(string) { string.include?('?') || self.class.instance_variable_get(:@limits)}
|
32
30
|
end
|
33
31
|
|
34
32
|
# i used Procs because they don't complain about arity
|
@@ -49,7 +47,6 @@ module RedisThrottler
|
|
49
47
|
define_singleton_method("#{key}_#{magic.to_s}") { |id, within = threshold| eval meth.call(id, within) }
|
50
48
|
define_method("#{key}_#{magic.to_s}") { |within = threshold| eval meth.call("#{self.send subject}", within) }
|
51
49
|
end
|
52
|
-
|
53
50
|
end
|
54
51
|
end
|
55
52
|
end
|
data/spec/model_spec.rb
CHANGED
@@ -2,29 +2,37 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe TestClass do
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
# we will test the firstly defined throttle
|
6
|
+
before do
|
7
|
+
throttle = TestClass.instance_variable_get(:@limits)
|
8
|
+
key = throttle.keys[0]
|
9
|
+
@count_to_fail = throttle[key][:limit]
|
10
|
+
@time_til_okay = throttle[key][:threshold]
|
7
11
|
end
|
8
12
|
|
9
|
-
it '
|
10
|
-
expect(TestClass
|
13
|
+
it 'Klass.throttle' do
|
14
|
+
expect(TestClass).to respond_to :throttle
|
11
15
|
end
|
12
16
|
|
13
|
-
it '
|
17
|
+
it '.limits' do
|
14
18
|
expect(TestClass.limits).to be_a(Hash)
|
15
19
|
end
|
16
20
|
|
17
|
-
it '
|
21
|
+
it '.limits?' do
|
18
22
|
expect(TestClass.limits?).to eq(true)
|
19
23
|
end
|
20
24
|
|
21
|
-
it '
|
25
|
+
it '.#{key}_throttler' do
|
26
|
+
expect(TestClass.logins_throttler.class).to eq(RedisThrottler::Base)
|
27
|
+
end
|
28
|
+
|
29
|
+
it '.#{key}_increments(id)' do
|
22
30
|
TestClass.logins_increment('testid')
|
23
31
|
TestClass.logins_increment('testid')
|
24
32
|
expect(TestClass.logins_count('testid')).to eq(2)
|
25
33
|
end
|
26
34
|
|
27
|
-
it '
|
35
|
+
it '.#{key}_exceeded?(id)' do
|
28
36
|
TestClass.logins_throttler.add('testid', 10)
|
29
37
|
expect(TestClass.logins_exceeded?('testid')).to eq(true)
|
30
38
|
|
@@ -32,17 +40,18 @@ describe TestClass do
|
|
32
40
|
expect(TestClass.logins_exceeded?('testid2')).to eq(false)
|
33
41
|
end
|
34
42
|
|
35
|
-
it '
|
43
|
+
it 'counts right' do
|
44
|
+
@count_to_fail.times { TestClass.logins_increment('test4') }
|
45
|
+
expect(TestClass.logins_count('test4')).to eq(TestClass.logins_throttler.count('test4', 60))
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'recovers after limit passed' do
|
36
49
|
expect(TestClass.logins_exceeded?('test3')).to eq(false)
|
37
|
-
|
50
|
+
@count_to_fail.times { TestClass.logins_increment('test3') }
|
51
|
+
|
38
52
|
expect(TestClass.logins_exceeded?('test3')).to eq(true)
|
39
|
-
Timecop.travel(
|
53
|
+
Timecop.travel(@time_til_okay) do
|
40
54
|
expect(TestClass.logins_exceeded?('test3')).to eq(false)
|
41
55
|
end
|
42
56
|
end
|
43
|
-
|
44
|
-
it 'should return counter value for subject within defined limits' do
|
45
|
-
10.times { TestClass.logins_increment('test4') }
|
46
|
-
expect(TestClass.logins_count('test4')).to eq(TestClass.logins_throttler.count('test4', 60))
|
47
|
-
end
|
48
57
|
end
|
data/spec/object_spec.rb
CHANGED
@@ -3,40 +3,64 @@ require 'spec_helper'
|
|
3
3
|
describe 'Instace of TestClass' do
|
4
4
|
before do
|
5
5
|
@test = TestClass.new
|
6
|
+
throttle = TestClass.instance_variable_get(:@limits)
|
7
|
+
key = throttle.keys[0]
|
8
|
+
@count_to_fail = throttle[key][:limit]
|
9
|
+
@time_til_okay = throttle[key][:threshold]
|
6
10
|
end
|
7
11
|
|
8
|
-
it '
|
9
|
-
expect(@test
|
12
|
+
it 'includes RedisThrottler' do
|
13
|
+
expect(@test).to be_a_kind_of(RedisThrottler)
|
10
14
|
end
|
11
15
|
|
12
|
-
it '
|
16
|
+
it '.limits' do
|
13
17
|
expect(@test.limits).to be_a(Hash)
|
14
18
|
end
|
15
19
|
|
16
|
-
it '
|
20
|
+
it '.limits?' do
|
17
21
|
expect(@test.limits?).to eq(true)
|
18
22
|
end
|
19
23
|
|
20
|
-
it '
|
24
|
+
it '.#{key}_throttler' do
|
25
|
+
expect(@test.logins_throttler.class).to eq(RedisThrottler::Base)
|
26
|
+
end
|
27
|
+
|
28
|
+
it '.#{key}_increment' do
|
21
29
|
@test.logins_increment
|
22
30
|
@test.logins_increment
|
23
31
|
expect(@test.logins_count).to eq(2)
|
24
32
|
end
|
25
33
|
|
26
|
-
it '
|
27
|
-
|
34
|
+
it '.#{key}_exceeded?' do
|
35
|
+
@count_to_fail.times do
|
28
36
|
@test.logins_increment
|
29
37
|
end
|
30
38
|
expect(@test.logins_exceeded?).to eq(true)
|
31
39
|
end
|
32
40
|
|
33
|
-
it '
|
34
|
-
|
41
|
+
it 'counts right' do
|
42
|
+
expect(@test.logins_count).to eq(TestClass.logins_count(@test.id))
|
43
|
+
|
44
|
+
4.times { @test.logins_increment }
|
45
|
+
|
46
|
+
Timecop.travel(@time_til_okay) do
|
47
|
+
(@count_to_fail - 1).times do
|
48
|
+
@test.logins_increment
|
49
|
+
end
|
50
|
+
|
51
|
+
expect(@test.logins_count(1)).to eq(@count_to_fail - 1)
|
35
52
|
expect(@test.logins_exceeded?).to eq(false)
|
36
53
|
end
|
37
54
|
end
|
38
55
|
|
39
|
-
|
40
|
-
|
56
|
+
|
57
|
+
it 'recovers after limit passed' do
|
58
|
+
@count_to_fail.times do
|
59
|
+
@test.logins_increment
|
60
|
+
end
|
61
|
+
expect(@test.logins_exceeded?).to eq(true)
|
62
|
+
Timecop.travel(@time_til_okay) do
|
63
|
+
expect(@test.logins_exceeded?).to eq(false)
|
64
|
+
end
|
41
65
|
end
|
42
66
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -5,7 +5,7 @@ require 'timecop'
|
|
5
5
|
|
6
6
|
class TestClass
|
7
7
|
include RedisThrottler
|
8
|
-
throttle :logins, limit: 10, for:
|
8
|
+
throttle :logins, limit: 10, for: 5000
|
9
9
|
|
10
10
|
def initialize
|
11
11
|
@id = 1234
|
@@ -15,3 +15,5 @@ class TestClass
|
|
15
15
|
@id
|
16
16
|
end
|
17
17
|
end
|
18
|
+
|
19
|
+
@test = TestClass.new
|
data/spec/throttler_spec.rb
CHANGED
@@ -7,23 +7,23 @@ describe RedisThrottler::Base do
|
|
7
7
|
@rl.send(:redis).flushdb
|
8
8
|
end
|
9
9
|
|
10
|
-
it '
|
10
|
+
it 'sets set_bucket_expiry to the bucket_span if not defined' do
|
11
11
|
expect(@rl.instance_variable_get(:@bucket_span)).to eq(@rl.instance_variable_get(:@bucket_expiry))
|
12
12
|
end
|
13
13
|
|
14
|
-
it '
|
14
|
+
it 'does not allow bucket count less than 3' do
|
15
15
|
expect do
|
16
16
|
RedisThrottler::Base.new('test', {:bucket_span => 1, :bucket_interval => 1})
|
17
17
|
end.to raise_error(ArgumentError)
|
18
18
|
end
|
19
19
|
|
20
|
-
it '
|
20
|
+
it 'does not allow bucket_expiry > bucket_span' do
|
21
21
|
expect do
|
22
22
|
RedisThrottler::Base.new("key", {:bucket_expiry => 1200})
|
23
23
|
end.to raise_error(ArgumentError)
|
24
24
|
end
|
25
25
|
|
26
|
-
it '
|
26
|
+
it '.add(\'string\', 1)' do
|
27
27
|
@rl.add('value1')
|
28
28
|
@rl.add('value1')
|
29
29
|
expect(@rl.count('value1', 1)).to eq(2)
|
@@ -33,12 +33,12 @@ describe RedisThrottler::Base do
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
it '
|
36
|
+
it '.add(\'string\', 3)' do
|
37
37
|
@rl.add('value1', 3)
|
38
38
|
expect(@rl.count('value1', 1)).to eq(3)
|
39
39
|
end
|
40
40
|
|
41
|
-
it '
|
41
|
+
it '.add(123, 1)' do
|
42
42
|
@rl.add(123)
|
43
43
|
@rl.add(123)
|
44
44
|
expect(@rl.count(123, 1)).to eq(2)
|
@@ -48,12 +48,12 @@ describe RedisThrottler::Base do
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
-
it '
|
51
|
+
it '.count' do
|
52
52
|
counter_value = @rl.add("value1")
|
53
53
|
expect(@rl.count("value1", 1)).to eq(counter_value)
|
54
54
|
end
|
55
55
|
|
56
|
-
it '
|
56
|
+
it '.exceeded?' do
|
57
57
|
5.times do
|
58
58
|
@rl.add("value1")
|
59
59
|
end
|
@@ -69,7 +69,7 @@ describe RedisThrottler::Base do
|
|
69
69
|
expect(@rl.within_bounds?("value1", {:threshold => 10, :interval => 30})).to be false
|
70
70
|
end
|
71
71
|
|
72
|
-
it
|
72
|
+
it 'accepts a threshold and a block that gets executed once it\'s below the threshold' do
|
73
73
|
expect(@rl.count("key", 30)).to eq(0)
|
74
74
|
31.times do
|
75
75
|
@rl.add("key")
|
@@ -93,7 +93,7 @@ describe RedisThrottler::Base do
|
|
93
93
|
expect(@value).to be 1
|
94
94
|
end
|
95
95
|
|
96
|
-
it 'counts
|
96
|
+
it 'counts correctly when bucket_span equals count-interval ' do
|
97
97
|
@rl = RedisThrottler::Base.new('key', {:bucket_span => 10, bucket_interval: 1})
|
98
98
|
@rl.add('value1')
|
99
99
|
expect(@rl.count('value1', 10)).to eql(1)
|