Class: SimpleThrottle
- Inherits:
-
Object
- Object
- SimpleThrottle
- Defined in:
- lib/simple_throttle.rb
Overview
per time period. These objects are thread safe.
Constant Summary collapse
- LUA_SCRIPT =
Server side Lua script that maintains the throttle in redis. The throttle is stored as a list of timestamps in milliseconds. When the script is invoked it will scan the oldest entries removing any that should be expired from the list. If the list is below the specified limit then the current entry will be added. The list is marked to expire with the oldest entry so there’s no need to cleanup the lists.
"local list_key = KEYS[1]\nlocal limit = tonumber(ARGV[1])\nlocal ttl = tonumber(ARGV[2])\nlocal now = ARGV[3]\nlocal push = tonumber(ARGV[4])\n\nlocal size = redis.call('llen', list_key)\nif size >= limit then\n local expired = tonumber(now) - ttl\n while size > 0 do\n local t = redis.call('lpop', list_key)\n if tonumber(t) > expired then\n redis.call('lpush', list_key, t)\n break\n end\n size = size - 1\n end\nend\n\nif push > 0 and size < limit then\n redis.call('rpush', list_key, now)\n redis.call('pexpire', list_key, ttl)\nend\n\nreturn size\n"
Instance Attribute Summary collapse
-
#limit ⇒ Object
readonly
Returns the value of attribute limit.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#ttl ⇒ Object
readonly
Returns the value of attribute ttl.
Class Method Summary collapse
-
.[](name) ⇒ Object
Returns a globally defined throttle with the specfied name.
-
.add(name, limit:, ttl:, redis: nil) ⇒ Object
Add a global throttle that can be referenced later with the [] method.
-
.redis ⇒ Object
Return the Redis instance where the throttles are stored.
-
.set_redis(client = nil, &block) ⇒ Object
Set the Redis instance to use for maintaining the throttle.
Instance Method Summary collapse
-
#allowed! ⇒ Object
Returns true if the limit for the throttle has not been reached yet.
-
#initialize(name, ttl:, limit:, redis: nil) ⇒ SimpleThrottle
constructor
Create a new throttle.
-
#peek ⇒ Object
Peek at the current number for throttled calls being tracked.
-
#reset! ⇒ Object
Reset a throttle back to zero.
-
#wait_time ⇒ Object
Returns when the next resource call should be allowed.
Constructor Details
#initialize(name, ttl:, limit:, redis: nil) ⇒ SimpleThrottle
Create a new throttle
101 102 103 104 105 106 107 |
# File 'lib/simple_throttle.rb', line 101 def initialize(name, ttl:, limit:, redis: nil) @name = name.to_s @name = name.dup.freeze unless name.frozen? @limit = limit.to_i @ttl = ttl.to_f @redis = redis end |
Instance Attribute Details
#limit ⇒ Object (readonly)
Returns the value of attribute limit.
94 95 96 |
# File 'lib/simple_throttle.rb', line 94 def limit @limit end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
94 95 96 |
# File 'lib/simple_throttle.rb', line 94 def name @name end |
#ttl ⇒ Object (readonly)
Returns the value of attribute ttl.
94 95 96 |
# File 'lib/simple_throttle.rb', line 94 def ttl @ttl end |
Class Method Details
.[](name) ⇒ Object
Returns a globally defined throttle with the specfied name.
52 53 54 55 56 |
# File 'lib/simple_throttle.rb', line 52 def [](name) if defined?(@throttles) && @throttles @throttles[name.to_s] end end |
.add(name, limit:, ttl:, redis: nil) ⇒ Object
Add a global throttle that can be referenced later with the [] method.
44 45 46 47 48 49 |
# File 'lib/simple_throttle.rb', line 44 def add(name, limit:, ttl:, redis: nil) @lock.synchronize do @throttles ||= {} @throttles[name.to_s] = new(name, limit: limit, ttl: ttl, redis: redis) end end |
.redis ⇒ Object
Return the Redis instance where the throttles are stored.
68 69 70 71 72 73 74 75 |
# File 'lib/simple_throttle.rb', line 68 def redis @redis_client ||= Redis.new if @redis_client.is_a?(Proc) @redis_client.call else @redis_client end end |
.set_redis(client = nil, &block) ⇒ Object
Set the Redis instance to use for maintaining the throttle. This can either be set with a hard coded value or by the value yielded by a block. If the block form is used it will be invoked at runtime to get the instance. Use this method if your Redis instance isn’t constant (for example if you’re in a forking environment and re-initialize connections on fork)
63 64 65 |
# File 'lib/simple_throttle.rb', line 63 def set_redis(client = nil, &block) @redis_client = (client || block) end |
Instance Method Details
#allowed! ⇒ Object
Returns true if the limit for the throttle has not been reached yet. This method will also track the throttled resource as having been invoked on each call.
111 112 113 114 |
# File 'lib/simple_throttle.rb', line 111 def allowed! size = current_size(true) size < limit end |
#peek ⇒ Object
Peek at the current number for throttled calls being tracked.
122 123 124 |
# File 'lib/simple_throttle.rb', line 122 def peek current_size(false) end |
#reset! ⇒ Object
Reset a throttle back to zero.
117 118 119 |
# File 'lib/simple_throttle.rb', line 117 def reset! redis_client.del(redis_key) end |
#wait_time ⇒ Object
Returns when the next resource call should be allowed. Note that this doesn’t guarantee that calling allow! will return true if the wait time is zero since other processes or threads can claim the resource.
129 130 131 132 133 134 135 136 137 138 |
# File 'lib/simple_throttle.rb', line 129 def wait_time if peek < limit 0.0 else first = redis_client.lindex(redis_key, 0).to_f / 1000.0 delta = Time.now.to_f - first delta = 0.0 if delta < 0 delta end end |