prop 2.4.0 → 2.5.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/README.md +2 -0
- data/lib/prop.rb +1 -1
- data/lib/prop/leaky_bucket_strategy.rb +53 -20
- data/lib/prop/limiter.rb +8 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0fe432ea00485ac163d99cc47cc62d515bf67fb
|
4
|
+
data.tar.gz: a6edac3e0e50fdaed49664fbdb4bd46419e81a27
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 67390eaadb265153db7845785824a3f27b3bcb97db533b277d14b44a121c70deca9105bee5565541dabd53e374f67adff6da539346ea4f98f5bc8d730d156960
|
7
|
+
data.tar.gz: 66e0df0d0db24d5baf3ca3791b36dd0c9bf2f5bacc8d56637fb96d0f47266788483b752292e3b07337efc2d03c2e30d1b72e8330051ff64fa37984906566ad38
|
data/README.md
CHANGED
@@ -227,6 +227,8 @@ You can add two additional configurations: `:strategy` and `:burst_rate` to use
|
|
227
227
|
Prop will handle the details after configured, and you don't have to specify `:strategy`
|
228
228
|
again when using `throttle`, `throttle!` or any other methods.
|
229
229
|
|
230
|
+
The leaky bucket algorithm used is "leaky bucket as a meter".
|
231
|
+
|
230
232
|
```ruby
|
231
233
|
Prop.configure(:api_request, strategy: :leaky_bucket, burst_rate: 20, threshold: 5, interval: 1.minute)
|
232
234
|
```
|
data/lib/prop.rb
CHANGED
@@ -5,40 +5,69 @@ require 'prop/key'
|
|
5
5
|
module Prop
|
6
6
|
class LeakyBucketStrategy
|
7
7
|
class << self
|
8
|
+
def _throttle_leaky_bucket(handle, key, cache_key, options)
|
9
|
+
(over_limit, bucket) = options.key?(:decrement) ?
|
10
|
+
decrement(cache_key, options.fetch(:decrement), options) :
|
11
|
+
increment(cache_key, options.fetch(:increment, 1), options)
|
12
|
+
|
13
|
+
[over_limit, bucket]
|
14
|
+
end
|
15
|
+
|
8
16
|
def counter(cache_key, options)
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
17
|
+
cache.read(cache_key) || zero_counter
|
18
|
+
end
|
19
|
+
|
20
|
+
def leak_amount(bucket, amount, options, now)
|
21
|
+
leak_rate = (now - bucket.fetch(:last_leak_time, 0)) / options.fetch(:interval).to_f
|
22
|
+
leak_amount = (leak_rate * options.fetch(:threshold).to_f)
|
23
|
+
leak_amount.to_i
|
24
|
+
end
|
13
25
|
|
14
|
-
|
15
|
-
|
16
|
-
|
26
|
+
def update_bucket(current_bucket_size, max_bucket_size, amount)
|
27
|
+
over_limit = (max_bucket_size-current_bucket_size) < amount
|
28
|
+
updated_bucket_size = over_limit ? current_bucket_size : current_bucket_size + amount
|
29
|
+
[over_limit, updated_bucket_size]
|
17
30
|
end
|
18
31
|
|
19
32
|
# WARNING: race condition
|
20
33
|
# this increment is not atomic, so it might miss counts when used frequently
|
21
34
|
def increment(cache_key, amount, options)
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
35
|
+
bucket = counter(cache_key, options)
|
36
|
+
now = Time.now.to_i
|
37
|
+
max_bucket_size = options.fetch(:burst_rate)
|
38
|
+
current_bucket_size = bucket.fetch(:bucket, 0)
|
39
|
+
leak_amount = leak_amount(bucket, amount, options, now)
|
40
|
+
if leak_amount > 0
|
41
|
+
# maybe TODO, update last_leak_time to reflect the exact time for the current leak amount
|
42
|
+
# the current strategy will always reflect a little less leakage, probably not an issue though
|
43
|
+
bucket[:last_leak_time] = now
|
44
|
+
current_bucket_size = [(current_bucket_size - leak_amount), 0].max
|
45
|
+
end
|
46
|
+
|
47
|
+
over_limit, updated_bucket_size = update_bucket(current_bucket_size, max_bucket_size, amount)
|
48
|
+
bucket[:bucket] = updated_bucket_size
|
49
|
+
bucket[:over_limit] = over_limit
|
50
|
+
cache.write(cache_key, bucket)
|
51
|
+
[over_limit, bucket]
|
26
52
|
end
|
27
53
|
|
28
54
|
def decrement(cache_key, amount, options)
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
55
|
+
now = Time.now.to_i
|
56
|
+
bucket = counter(cache_key, options)
|
57
|
+
leak_amount = leak_amount(bucket, amount, options, now)
|
58
|
+
bucket[:bucket] = [bucket[:bucket] - amount - leak_amount, 0].max
|
59
|
+
bucket[:last_leak_time] = now if leak_amount > 0
|
60
|
+
bucket[:over_limit] = false
|
61
|
+
cache.write(cache_key, bucket)
|
62
|
+
[false, bucket]
|
34
63
|
end
|
35
64
|
|
36
65
|
def reset(cache_key)
|
37
|
-
|
66
|
+
cache.write(cache_key, zero_counter, raw: true)
|
38
67
|
end
|
39
68
|
|
40
|
-
def compare_threshold?(
|
41
|
-
|
69
|
+
def compare_threshold?(bucket, operator, options)
|
70
|
+
bucket.fetch(:over_limit, false)
|
42
71
|
end
|
43
72
|
|
44
73
|
def build(options)
|
@@ -70,7 +99,11 @@ module Prop
|
|
70
99
|
end
|
71
100
|
|
72
101
|
def zero_counter
|
73
|
-
{ bucket: 0,
|
102
|
+
{ bucket: 0, last_leak_time: 0, over_limit: false }
|
103
|
+
end
|
104
|
+
|
105
|
+
def cache
|
106
|
+
Prop::Limiter.cache
|
74
107
|
end
|
75
108
|
end
|
76
109
|
end
|
data/lib/prop/limiter.rb
CHANGED
@@ -142,9 +142,17 @@ module Prop
|
|
142
142
|
|
143
143
|
private
|
144
144
|
|
145
|
+
def leaky_bucket_strategy?(strategy)
|
146
|
+
strategy == Prop::LeakyBucketStrategy
|
147
|
+
end
|
148
|
+
|
145
149
|
def _throttle(strategy, handle, key, cache_key, options)
|
146
150
|
return [false, strategy.zero_counter] if disabled?
|
147
151
|
|
152
|
+
if leaky_bucket_strategy?(strategy)
|
153
|
+
return Prop::LeakyBucketStrategy._throttle_leaky_bucket(handle, key, cache_key, options)
|
154
|
+
end
|
155
|
+
|
148
156
|
counter = options.key?(:decrement) ?
|
149
157
|
strategy.decrement(cache_key, options.fetch(:decrement), options) :
|
150
158
|
strategy.increment(cache_key, options.fetch(:increment, 1), options)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Morten Primdahl
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-06-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -116,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
116
116
|
version: '0'
|
117
117
|
requirements: []
|
118
118
|
rubyforge_project:
|
119
|
-
rubygems_version: 2.6.14
|
119
|
+
rubygems_version: 2.6.14
|
120
120
|
signing_key:
|
121
121
|
specification_version: 4
|
122
122
|
summary: Gem for implementing rate limits.
|