berater 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/berater/concurrency_limiter.rb +1 -1
- data/lib/berater/rate_limiter.rb +45 -9
- data/lib/berater/version.rb +1 -1
- data/spec/test_mode_spec.rb +3 -3
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f57aeaa9319ae38bdd7d2468443fc5c9ea9ba44cecf47bc5a558f18ecd0ab274
|
4
|
+
data.tar.gz: ff14ef90856911e9e18cbfbad6f9799727c999697c5e4d96f47b5fbf665c725c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c9d3abeeb9d35c608c194eaf505c5af1350a82583dd12257e70b9d4f96bd3a6cd30db802f57c3bf77d371b5deb5e856e9238eadd3990768205fd37ea3e2e3703
|
7
|
+
data.tar.gz: c98078a3c7d3a3eb4e2d523577f9d6dd7f1bbae76c9ecbd8d65961cc7aa1bab6e1a4b8fe84cfb30ee60f4d0ce11dcb58e3b94a3566cf50ee60d1f8e21bab82b1
|
data/lib/berater/rate_limiter.rb
CHANGED
@@ -53,20 +53,56 @@ module Berater
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
-
|
57
|
-
|
56
|
+
LUA_SCRIPT = <<~LUA.gsub(/^\s*|\s*--.*/, '')
|
57
|
+
local key = KEYS[1]
|
58
|
+
local ts_key = KEYS[2]
|
59
|
+
local ts = tonumber(ARGV[1])
|
60
|
+
local capacity = tonumber(ARGV[2])
|
61
|
+
local usec_per_drip = tonumber(ARGV[3])
|
62
|
+
local count = 0
|
63
|
+
|
64
|
+
-- timestamp of last update
|
65
|
+
local last_ts = tonumber(redis.call('GET', ts_key))
|
66
|
+
|
67
|
+
if last_ts then
|
68
|
+
count = tonumber(redis.call('GET', key)) or 0
|
69
|
+
|
70
|
+
-- adjust for time passing
|
71
|
+
local drips = math.floor((ts - last_ts) / usec_per_drip)
|
72
|
+
count = math.max(0, count - drips)
|
73
|
+
end
|
74
|
+
|
75
|
+
local allowed = count + 1 <= capacity
|
58
76
|
|
59
|
-
|
60
|
-
|
77
|
+
if allowed then
|
78
|
+
count = count + 1
|
61
79
|
|
62
|
-
|
63
|
-
|
64
|
-
|
80
|
+
-- time for bucket to empty, in milliseconds
|
81
|
+
local ttl = math.ceil((count * usec_per_drip) / 1000)
|
82
|
+
|
83
|
+
-- update count and last_ts, with expirations
|
84
|
+
redis.call('SET', key, count, 'PX', ttl)
|
85
|
+
redis.call('SET', ts_key, ts, 'PX', ttl)
|
65
86
|
end
|
66
87
|
|
67
|
-
|
88
|
+
return { count, allowed }
|
89
|
+
LUA
|
90
|
+
|
91
|
+
def limit
|
92
|
+
usec_per_drip = (@interval_sec * 10**6) / @count
|
93
|
+
|
94
|
+
# timestamp in microseconds
|
95
|
+
ts = (Time.now.to_f * 10**6).to_i
|
96
|
+
|
97
|
+
count, allowed = redis.eval(
|
98
|
+
LUA_SCRIPT,
|
99
|
+
[ cache_key(key), cache_key("#{key}-ts") ],
|
100
|
+
[ ts, @count, usec_per_drip ]
|
101
|
+
)
|
102
|
+
|
103
|
+
raise Overrated unless allowed
|
68
104
|
|
69
|
-
lock = Lock.new(self, count, count)
|
105
|
+
lock = Lock.new(self, "#{ts}-#{count}", count)
|
70
106
|
|
71
107
|
if block_given?
|
72
108
|
begin
|
data/lib/berater/version.rb
CHANGED
data/spec/test_mode_spec.rb
CHANGED
@@ -93,7 +93,7 @@ describe 'Berater.test_mode' do
|
|
93
93
|
it_behaves_like 'a RateLimiter'
|
94
94
|
|
95
95
|
it 'works per usual' do
|
96
|
-
expect(limiter.redis).to receive(:
|
96
|
+
expect(limiter.redis).to receive(:eval).twice.and_call_original
|
97
97
|
expect(limiter.limit).to be_a Berater::Lock
|
98
98
|
expect { limiter.limit }.to be_overloaded
|
99
99
|
end
|
@@ -109,7 +109,7 @@ describe 'Berater.test_mode' do
|
|
109
109
|
it_behaves_like 'a RateLimiter'
|
110
110
|
|
111
111
|
it 'always works and without calling redis' do
|
112
|
-
expect(limiter.redis).not_to receive(:
|
112
|
+
expect(limiter.redis).not_to receive(:eval)
|
113
113
|
expect {|block| limiter.limit(&block) }.to yield_control
|
114
114
|
10.times { expect(limiter.limit).to be_a Berater::Lock }
|
115
115
|
end
|
@@ -121,7 +121,7 @@ describe 'Berater.test_mode' do
|
|
121
121
|
it_behaves_like 'a RateLimiter'
|
122
122
|
|
123
123
|
it 'never works and without calling redis' do
|
124
|
-
expect(limiter.redis).not_to receive(:
|
124
|
+
expect(limiter.redis).not_to receive(:eval)
|
125
125
|
expect { limiter }.to be_overloaded
|
126
126
|
end
|
127
127
|
end
|