redis.replicate_commands() local injected_time = tonumber(ARGV) local redis_time = redis.call('time') local local_time = redis_time + 0.000001 * redis_time

local now = injected_time or local_time

local current_bucket_levels = {} local new_bucket_levels = {} local timeouts = {} local exceeded = false

for key_index, key in ipairs(KEYS) do

local arg_index = key_index * 5 - 3
local rate = tonumber(ARGV[arg_index])
local size = tonumber(ARGV[arg_index + 1])
local amount = tonumber(ARGV[arg_index + 2])

local bucket = redis.call('hmget', key, 'time', 'level')
local last_time = tonumber(bucket[1]) or now
local before_level = tonumber(bucket[2]) or size

local elapsed = math.max(0, now - last_time)
local gained = rate * elapsed

local current_level = math.min(size, before_level + gained)

current_bucket_levels[key_index] = current_level

if amount > 0 then
  local limit = tonumber(ARGV[arg_index + 3]) or 0

  local new_level = current_level - amount

  local seconds_to_full = (size - new_level) / rate
  timeouts[key_index] = seconds_to_full

  if new_level < limit then
    local allow_charge_adjustment = tonumber(ARGV[arg_index + 4]) or 0

    if allow_charge_adjustment > 0 then
      new_level = limit
    else
      exceeded = true
    end
  end
  new_bucket_levels[key_index] = new_level
end

end

local levels_to_report local charged

if exceeded or new_bucket_levels == 0 then

levels_to_report = current_bucket_levels
charged = 0

else

levels_to_report = new_bucket_levels
charged = 1

for key_index, key in ipairs(KEYS) do
  local new_level = new_bucket_levels[key_index]
  local timeout = timeouts[key_index]

  redis.call('hmset', key,
    'time', string.format("%.16g", now),
    'level', string.format("%.16g", new_level)
    )

  redis.call('expire', key, math.ceil(timeout))
end

end

local formatted_levels = {} for index, value in ipairs(levels_to_report) do

formatted_levels[index] = string.format("%.16g", value)

end

return {charged, formatted_levels}