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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 850a96785d6d002acd243a5edf5cca965bd46bd1
4
- data.tar.gz: 9ffb56011562feb0d9cf2c5ab62705420d59d46e
3
+ metadata.gz: d0fe432ea00485ac163d99cc47cc62d515bf67fb
4
+ data.tar.gz: a6edac3e0e50fdaed49664fbdb4bd46419e81a27
5
5
  SHA512:
6
- metadata.gz: 8cfee5d2c8af78a8a9273bdc873ba83ee6bb81237ca7cb253fce98dbf6180cff96f1004759e5d2fc4ee8e01364dddfae7f4b94c3909f547b08bcd3b109c9d7d2
7
- data.tar.gz: 61e6d22651a9017e827d34220511fd8dd6dc60acaa69908968082d71a0ee6926859c0509f08b12fdf5a4d4310f83b95d0cf7b4137f713323d70fb88defcd0bbc
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
  ```
@@ -3,7 +3,7 @@ require "prop/limiter"
3
3
  require "forwardable"
4
4
 
5
5
  module Prop
6
- VERSION = "2.4.0"
6
+ VERSION = "2.5.0"
7
7
 
8
8
  # Short hand for accessing Prop::Limiter methods
9
9
  class << self
@@ -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
- bucket = Prop::Limiter.cache.read(cache_key) || zero_counter
10
- now = Time.now.to_i
11
- leak_rate = (now - bucket.fetch(:last_updated)) / options.fetch(:interval).to_f
12
- leak_amount = leak_rate * options.fetch(:threshold)
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
- bucket[:bucket] = [(bucket.fetch(:bucket) - leak_amount).to_i, 0].max
15
- bucket[:last_updated] = now
16
- bucket
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
- counter = counter(cache_key, options)
23
- counter[:bucket] += amount
24
- Prop::Limiter.cache.write(cache_key, counter)
25
- counter
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
- counter = counter(cache_key, options)
30
- counter[:bucket] -= amount
31
- counter[:bucket] = 0 unless counter[:bucket] > 0
32
- Prop::Limiter.cache.write(cache_key, counter)
33
- counter
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
- Prop::Limiter.cache.write(cache_key, zero_counter)
66
+ cache.write(cache_key, zero_counter, raw: true)
38
67
  end
39
68
 
40
- def compare_threshold?(counter, operator, options)
41
- counter.fetch(:bucket).to_i.send operator, options.fetch(:burst_rate)
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, last_updated: 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
@@ -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.0
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: 2019-08-22 00:00:00.000000000 Z
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.4
119
+ rubygems_version: 2.6.14
120
120
  signing_key:
121
121
  specification_version: 4
122
122
  summary: Gem for implementing rate limits.