praroter 1.0.0 → 1.0.1
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/README.md +4 -1
- data/lib/praroter/filly_bucket/bucket.rb +57 -0
- data/lib/praroter/filly_bucket/bucket_state.rb +15 -0
- data/lib/praroter/{filly_bucket.rb → filly_bucket/creator.rb} +1 -65
- data/lib/praroter/{filly_bucket.lua → filly_bucket/filly_bucket.lua} +0 -0
- data/lib/praroter/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a14e168451294700f7c71bed654256c51bcf6742f186f8201f9aebe7a1a6e3e5
|
4
|
+
data.tar.gz: 5d1995a758edad1c5f054bf665f101fa85ba00bb34eb729415eaf4a7a5f13c1e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb3c8fb96ff6f5fdec4fe33eff582ba3db6be91caf2714e28f6ba3b4fb79dccc7dbece841a82de1c2becf507c65124cea12257bbebf7ef856cd4fdf261cef725
|
7
|
+
data.tar.gz: 4b242a9abeccf2844cdeba9a0c939f5846255db810f7cd14bafaf05eafebbf5e70f6b021d83683ca92be3e8205b796721ee1f2017990288c8a181c71e5cddfa8
|
data/README.md
CHANGED
@@ -72,6 +72,7 @@ To capture that exception, add this to the controller:
|
|
72
72
|
|
73
73
|
```ruby
|
74
74
|
rescue_from Praroter::Throttled do |e|
|
75
|
+
response.set_header('X-Ratelimit-Cost', e.bucket_state.drained)
|
75
76
|
response.set_header('X-Ratelimit-Level', e.bucket_state.level)
|
76
77
|
response.set_header('X-Ratelimit-Capacity', e.bucket_state.capacity)
|
77
78
|
response.set_header('X-Ratelimit-Retry-After', e.retry_in_seconds)
|
@@ -140,7 +141,8 @@ def index
|
|
140
141
|
sleep(2.242)
|
141
142
|
end
|
142
143
|
|
143
|
-
rescue_from Praroter::
|
144
|
+
rescue_from Praroter::Throttled do |e|
|
145
|
+
response.set_header('X-Ratelimit-Cost', e.bucket_state.drained)
|
144
146
|
response.set_header('X-Ratelimit-Level', e.bucket_state.level)
|
145
147
|
response.set_header('X-Ratelimit-Capacity', e.bucket_state.capacity)
|
146
148
|
response.set_header('X-Ratelimit-Retry-After', e.retry_in_seconds)
|
@@ -159,6 +161,7 @@ def api_ratelimit
|
|
159
161
|
bucket_state = ratelimit_bucket.drain_block do
|
160
162
|
yield
|
161
163
|
end
|
164
|
+
response.set_header('X-Ratelimit-Cost', bucket_state.drained)
|
162
165
|
response.set_header('X-Ratelimit-Level', bucket_state.level)
|
163
166
|
response.set_header('X-Ratelimit-Capacity', bucket_state.capacity)
|
164
167
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Praroter
|
2
|
+
module FillyBucket
|
3
|
+
|
4
|
+
class Bucket
|
5
|
+
attr_reader :key, :fill_rate, :capacity
|
6
|
+
|
7
|
+
def initialize(key, fill_rate, capacity, creator)
|
8
|
+
@key = key
|
9
|
+
@fill_rate = fill_rate
|
10
|
+
@capacity = capacity
|
11
|
+
@creator = creator
|
12
|
+
end
|
13
|
+
|
14
|
+
def state
|
15
|
+
@creator.run_lua_bucket_script(self, 0)
|
16
|
+
end
|
17
|
+
|
18
|
+
def empty?
|
19
|
+
state.empty?
|
20
|
+
end
|
21
|
+
|
22
|
+
def full?
|
23
|
+
state.full?
|
24
|
+
end
|
25
|
+
|
26
|
+
def throttle!
|
27
|
+
bucket_state = state
|
28
|
+
if bucket_state.empty?
|
29
|
+
remaining_block_time = ((bucket_state.capacity - bucket_state.level).abs / bucket_state.fill_rate) + 3
|
30
|
+
raise Praroter::Throttled.new(bucket_state, remaining_block_time)
|
31
|
+
end
|
32
|
+
bucket_state
|
33
|
+
end
|
34
|
+
|
35
|
+
def drain(amount)
|
36
|
+
raise ArgumentError, "drain amount must be positive" if amount < 0
|
37
|
+
@creator.run_lua_bucket_script(self, amount)
|
38
|
+
end
|
39
|
+
|
40
|
+
def drain_block
|
41
|
+
work_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
42
|
+
yield
|
43
|
+
work_end = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
44
|
+
drain(((work_end - work_start) * 1000).to_i)
|
45
|
+
end
|
46
|
+
|
47
|
+
def level_key
|
48
|
+
"filly_bucket.#{key}.bucket_level"
|
49
|
+
end
|
50
|
+
|
51
|
+
def last_updated_key
|
52
|
+
"filly_bucket.#{key}.last_updated"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -1,69 +1,6 @@
|
|
1
1
|
module Praroter
|
2
|
-
|
3
2
|
module FillyBucket
|
4
3
|
|
5
|
-
class BucketState < Struct.new(:level, :capacity, :fill_rate)
|
6
|
-
def empty?
|
7
|
-
level <= 0
|
8
|
-
end
|
9
|
-
|
10
|
-
def full?
|
11
|
-
level >= capacity
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
class Bucket
|
16
|
-
attr_reader :key, :fill_rate, :capacity
|
17
|
-
|
18
|
-
def initialize(key, fill_rate, capacity, creator)
|
19
|
-
@key = key
|
20
|
-
@fill_rate = fill_rate
|
21
|
-
@capacity = capacity
|
22
|
-
@creator = creator
|
23
|
-
end
|
24
|
-
|
25
|
-
def state
|
26
|
-
@creator.run_lua_bucket_script(self, 0)
|
27
|
-
end
|
28
|
-
|
29
|
-
def empty?
|
30
|
-
state.empty?
|
31
|
-
end
|
32
|
-
|
33
|
-
def full?
|
34
|
-
state.full?
|
35
|
-
end
|
36
|
-
|
37
|
-
def throttle!
|
38
|
-
bucket_state = state
|
39
|
-
if bucket_state.empty?
|
40
|
-
remaining_block_time = ((bucket_state.capacity - bucket_state.level).abs / bucket_state.fill_rate) + 3
|
41
|
-
raise Praroter::Throttled.new(bucket_state, remaining_block_time)
|
42
|
-
end
|
43
|
-
bucket_state
|
44
|
-
end
|
45
|
-
|
46
|
-
def drain(amount)
|
47
|
-
raise ArgumentError, "drain amount must be positive" if amount < 0
|
48
|
-
@creator.run_lua_bucket_script(self, amount)
|
49
|
-
end
|
50
|
-
|
51
|
-
def drain_block
|
52
|
-
work_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
53
|
-
yield
|
54
|
-
work_end = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
55
|
-
drain(((work_end - work_start) * 1000).to_i)
|
56
|
-
end
|
57
|
-
|
58
|
-
def level_key
|
59
|
-
"filly_bucket.#{key}.bucket_level"
|
60
|
-
end
|
61
|
-
|
62
|
-
def last_updated_key
|
63
|
-
"filly_bucket.#{key}.last_updated"
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
4
|
class Creator
|
68
5
|
LUA_SCRIPT_CODE = File.read(File.join(__dir__, "filly_bucket.lua"))
|
69
6
|
LUA_SCRIPT_HASH = Digest::SHA1.hexdigest(LUA_SCRIPT_CODE)
|
@@ -86,7 +23,7 @@ module Praroter
|
|
86
23
|
keys: [bucket.level_key, bucket.last_updated_key],
|
87
24
|
argv: [bucket.capacity, bucket.fill_rate, amount]
|
88
25
|
)
|
89
|
-
BucketState.new(new_bucket_level, bucket_capacity, fill_rate)
|
26
|
+
BucketState.new(new_bucket_level, bucket_capacity, fill_rate, amount)
|
90
27
|
rescue Redis::CommandError => e
|
91
28
|
if e.message.include? "NOSCRIPT"
|
92
29
|
# The Redis server has never seen this script before. Needs to run only once in the entire lifetime
|
@@ -103,5 +40,4 @@ module Praroter
|
|
103
40
|
end
|
104
41
|
|
105
42
|
end
|
106
|
-
|
107
43
|
end
|
File without changes
|
data/lib/praroter/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: praroter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kasper Grubbe
|
@@ -176,8 +176,10 @@ files:
|
|
176
176
|
- bin/setup
|
177
177
|
- docker-compose.yml
|
178
178
|
- lib/praroter.rb
|
179
|
-
- lib/praroter/filly_bucket.
|
180
|
-
- lib/praroter/filly_bucket.rb
|
179
|
+
- lib/praroter/filly_bucket/bucket.rb
|
180
|
+
- lib/praroter/filly_bucket/bucket_state.rb
|
181
|
+
- lib/praroter/filly_bucket/creator.rb
|
182
|
+
- lib/praroter/filly_bucket/filly_bucket.lua
|
181
183
|
- lib/praroter/null_logger.rb
|
182
184
|
- lib/praroter/null_pool.rb
|
183
185
|
- lib/praroter/throttled.rb
|