Class: Pecorino::Adapters::MemoryAdapter
- Inherits:
-
Object
- Object
- Pecorino::Adapters::MemoryAdapter
- Defined in:
- lib/pecorino/adapters/memory_adapter.rb
Overview
A memory store for leaky buckets and blocks
Defined Under Namespace
Classes: KeyedLock
Instance Method Summary collapse
-
#add_tokens(key:, capacity:, leak_rate:, n_tokens:) ⇒ Object
Adds tokens to the leaky bucket.
-
#add_tokens_conditionally(key:, capacity:, leak_rate:, n_tokens:) ⇒ Object
Adds tokens to the leaky bucket conditionally.
-
#blocked_until(key:) ⇒ Object
Returns the time until which a block for a given key is in effect.
-
#create_tables(active_record_schema) ⇒ Object
No-op.
-
#initialize ⇒ MemoryAdapter
constructor
A new instance of MemoryAdapter.
-
#prune ⇒ Object
Deletes leaky buckets which have an expiry value prior to now and throttle blocks which have now lapsed.
-
#set_block(key:, block_for:) ⇒ Object
Sets a timed block for the given key - this is used when a throttle fires.
-
#state(key:, capacity:, leak_rate:) ⇒ Object
Returns the state of a leaky bucket.
Constructor Details
#initialize ⇒ MemoryAdapter
Returns a new instance of MemoryAdapter.
35 36 37 38 39 |
# File 'lib/pecorino/adapters/memory_adapter.rb', line 35 def initialize @buckets = {} @blocks = {} @lock = KeyedLock.new end |
Instance Method Details
#add_tokens(key:, capacity:, leak_rate:, n_tokens:) ⇒ Object
Adds tokens to the leaky bucket. The return value is a tuple of two values: the current level (Float) and whether the bucket is now at capacity (Boolean)
57 58 59 |
# File 'lib/pecorino/adapters/memory_adapter.rb', line 57 def add_tokens(key:, capacity:, leak_rate:, n_tokens:) add_tokens_with_lock(key, capacity, leak_rate, n_tokens, _conditionally = false) end |
#add_tokens_conditionally(key:, capacity:, leak_rate:, n_tokens:) ⇒ Object
Adds tokens to the leaky bucket conditionally. If there is capacity, the tokens will be added. If there isn’t - the fillup will be rejected. The return value is a triplet of the current level (Float), whether the bucket is now at capacity (Boolean) and whether the fillup was accepted (Boolean)
65 66 67 |
# File 'lib/pecorino/adapters/memory_adapter.rb', line 65 def add_tokens_conditionally(key:, capacity:, leak_rate:, n_tokens:) add_tokens_with_lock(key, capacity, leak_rate, n_tokens, _conditionally = true) end |
#blocked_until(key:) ⇒ Object
Returns the time until which a block for a given key is in effect. If there is no block in effect, the method should return ‘nil`. The return value is either a `Time` or `nil`
82 83 84 85 86 87 88 89 90 |
# File 'lib/pecorino/adapters/memory_adapter.rb', line 82 def blocked_until(key:) blocked_until_monotonic = @blocks[key] return unless blocked_until_monotonic now_monotonic = get_mono_time return unless blocked_until_monotonic > now_monotonic Time.now + (blocked_until_monotonic - now_monotonic) end |
#create_tables(active_record_schema) ⇒ Object
No-op
112 113 |
# File 'lib/pecorino/adapters/memory_adapter.rb', line 112 def create_tables(active_record_schema) end |
#prune ⇒ Object
Deletes leaky buckets which have an expiry value prior to now and throttle blocks which have now lapsed
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/pecorino/adapters/memory_adapter.rb', line 94 def prune now_monotonic = get_mono_time @blocks.keys.each do |key| @lock.with(key) do @blocks.delete(key) if @blocks[key] && @blocks[key] < now_monotonic end end @buckets.keys.each do |key| @lock.with(key) do _level, expire_at_monotonic = @buckets[key] @buckets.delete(key) if expire_at_monotonic && expire_at_monotonic < now_monotonic end end end |
#set_block(key:, block_for:) ⇒ Object
Sets a timed block for the given key - this is used when a throttle fires. The return value is not defined - the call should always succeed.
71 72 73 74 75 76 77 78 |
# File 'lib/pecorino/adapters/memory_adapter.rb', line 71 def set_block(key:, block_for:) raise ArgumentError, "block_for must be positive" unless block_for > 0 @lock.lock(key) @blocks[key] = get_mono_time + block_for.to_f Time.now + block_for.to_f ensure @lock.unlock(key) end |
#state(key:, capacity:, leak_rate:) ⇒ Object
Returns the state of a leaky bucket. The state should be a tuple of two values: the current level (Float) and whether the bucket is now at capacity (Boolean)
43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/pecorino/adapters/memory_adapter.rb', line 43 def state(key:, capacity:, leak_rate:) @lock.lock(key) level, ts = @buckets[key] @lock.unlock(key) return [0, false] unless level dt = get_mono_time - ts level_after_leak = [0, level - (leak_rate * dt)].max [level_after_leak.to_f, (level_after_leak - capacity) >= 0] end |