berater 0.3.0 → 0.4.0
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/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
|