Class: Prop::LeakyBucketStrategy
- Inherits:
-
Object
- Object
- Prop::LeakyBucketStrategy
- Defined in:
- lib/prop/leaky_bucket_strategy.rb
Class Method Summary collapse
- .build(options) ⇒ Object
- .compare_threshold?(counter, operator, options) ⇒ Boolean
- .counter(cache_key, options) ⇒ Object
- .decrement(cache_key, amount, options) ⇒ Object
-
.increment(cache_key, amount, options) ⇒ Object
WARNING: race condition this increment is not atomic, so it might miss counts when used frequently.
- .reset(cache_key) ⇒ Object
- .threshold_reached(options) ⇒ Object
- .validate_options!(options) ⇒ Object
- .zero_counter ⇒ Object
Class Method Details
.build(options) ⇒ Object
44 45 46 47 48 49 50 51 |
# File 'lib/prop/leaky_bucket_strategy.rb', line 44 def build() key = .fetch(:key) handle = .fetch(:handle) cache_key = Prop::Key.normalize([ handle, key ]) "prop/leaky_bucket/#{Digest::MD5.hexdigest(cache_key)}" end |
.compare_threshold?(counter, operator, options) ⇒ Boolean
40 41 42 |
# File 'lib/prop/leaky_bucket_strategy.rb', line 40 def compare_threshold?(counter, operator, ) counter.fetch(:bucket).to_i.send operator, .fetch(:burst_rate) end |
.counter(cache_key, options) ⇒ Object
8 9 10 11 12 13 14 15 16 17 |
# File 'lib/prop/leaky_bucket_strategy.rb', line 8 def counter(cache_key, ) bucket = Prop::Limiter.cache.read(cache_key) || zero_counter now = Time.now.to_i leak_rate = (now - bucket.fetch(:last_updated)) / .fetch(:interval).to_f leak_amount = leak_rate * .fetch(:threshold) bucket[:bucket] = [(bucket.fetch(:bucket) - leak_amount).to_i, 0].max bucket[:last_updated] = now bucket end |
.decrement(cache_key, amount, options) ⇒ Object
28 29 30 31 32 33 34 |
# File 'lib/prop/leaky_bucket_strategy.rb', line 28 def decrement(cache_key, amount, ) counter = counter(cache_key, ) counter[:bucket] -= amount counter[:bucket] = 0 unless counter[:bucket] > 0 Prop::Limiter.cache.write(cache_key, counter) counter end |
.increment(cache_key, amount, options) ⇒ Object
WARNING: race condition this increment is not atomic, so it might miss counts when used frequently
21 22 23 24 25 26 |
# File 'lib/prop/leaky_bucket_strategy.rb', line 21 def increment(cache_key, amount, ) counter = counter(cache_key, ) counter[:bucket] += amount Prop::Limiter.cache.write(cache_key, counter) counter end |
.reset(cache_key) ⇒ Object
36 37 38 |
# File 'lib/prop/leaky_bucket_strategy.rb', line 36 def reset(cache_key) Prop::Limiter.cache.write(cache_key, zero_counter) end |
.threshold_reached(options) ⇒ Object
53 54 55 56 57 58 |
# File 'lib/prop/leaky_bucket_strategy.rb', line 53 def threshold_reached() burst_rate = .fetch(:burst_rate) threshold = .fetch(:threshold) "#{options[:handle]} threshold of #{threshold} tries per #{options[:interval]}s and burst rate #{burst_rate} tries exceeded for key #{options[:key].inspect}, hash #{options[:cache_key]}" end |
.validate_options!(options) ⇒ Object
60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/prop/leaky_bucket_strategy.rb', line 60 def () Prop::IntervalStrategy.() if ![:burst_rate].is_a?(Integer) || [:burst_rate] < [:threshold] raise ArgumentError.new(":burst_rate must be an Integer and not less than :threshold") end if [:first_throttled] raise ArgumentError.new(":first_throttled is not supported") end end |
.zero_counter ⇒ Object
72 73 74 |
# File 'lib/prop/leaky_bucket_strategy.rb', line 72 def zero_counter { bucket: 0, last_updated: 0 } end |