lib/simple_throttle.rb in simple_throttle-1.0.5 vs lib/simple_throttle.rb in simple_throttle-1.0.6
- old
+ new
@@ -14,10 +14,11 @@
local list_key = KEYS[1]
local limit = tonumber(ARGV[1])
local ttl = tonumber(ARGV[2])
local now = ARGV[3]
local push = tonumber(ARGV[4])
+ local pause_to_recover = tonumber(ARGV[5])
local size = redis.call('llen', list_key)
if size >= limit then
local expired = tonumber(now) - ttl
while size > 0 do
@@ -28,11 +29,11 @@
end
size = size - 1
end
end
- if push > 0 and size < limit then
+ if push > 0 and (size < limit or (size == limit and pause_to_recover > 0)) then
redis.call('rpush', list_key, now)
redis.call('pexpire', list_key, ttl)
end
return size
@@ -46,16 +47,19 @@
# and then use in multiple places.
#
# @param name [String] unique name for the throttle
# @param ttl [Numeric] number of seconds that the throttle will remain active
# @param limit [Integer] number of allowed requests within the throttle ttl
+ # @param pause_to_recover [Boolean] require processes calling the throttle
+ # to pause at least temporarily before freeing up the throttle. If this is true,
+ # then a throttle called constantly with no pauses will never free up.
# @param redis [Redis, Proc] Redis instance to use or a Proc that yields a Redos instance
# @return [void]
- def add(name, ttl:, limit:, redis: nil)
+ def add(name, ttl:, limit:, pause_to_recover: false, redis: nil)
@lock.synchronize do
@throttles ||= {}
- @throttles[name.to_s] = new(name, limit: limit, ttl: ttl, redis: redis)
+ @throttles[name.to_s] = new(name, limit: limit, ttl: ttl, pause_to_recover: pause_to_recover, redis: redis)
end
end
# Returns a globally defined throttle with the specfied name.
#
@@ -118,16 +122,20 @@
# Create a new throttle.
#
# @param name [String] unique name for the throttle
# @param ttl [Numeric] number of seconds that the throttle will remain active
# @param limit [Integer] number of allowed requests within the throttle ttl
+ # @param pause_to_recover [Boolean] require processes calling the throttle
+ # to pause at least temporarily before freeing up the throttle. If this is true,
+ # then a throttle called constantly with no pauses will never free up.
# @param redis [Redis, Proc] Redis instance to use or a Proc that yields a Redos instance
- def initialize(name, ttl:, limit:, redis: nil)
+ def initialize(name, ttl:, limit:, pause_to_recover: false, redis: nil)
@name = name.to_s
@name = name.dup.freeze unless name.frozen?
@limit = limit.to_i
@ttl = ttl.to_f
+ @pause_to_recover = !!pause_to_recover
@redis = redis
end
# 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.
@@ -180,12 +188,13 @@
# Evaluate and execute a Lua script on the redis server that returns the number calls currently being tracked.
# If push is set to true then a new item will be added to the list.
def current_size(push)
push_arg = (push ? 1 : 0)
+ pause_to_recover_arg = (@pause_to_recover ? 1 : 0)
time_ms = (Time.now.to_f * 1000).round
ttl_ms = (ttl * 1000).ceil
- self.class.send(:execute_lua_script, redis: redis_client, keys: [redis_key], args: [limit, ttl_ms, time_ms, push_arg])
+ self.class.send(:execute_lua_script, redis: redis_client, keys: [redis_key], args: [limit, ttl_ms, time_ms, push_arg, pause_to_recover_arg])
end
def redis_key
"simple_throttle.#{name}"
end