github.com/rudderlabs/rudder-go-kit@v0.30.0/throttling/lua/gcra.lua (about)

     1  --[[
     2  To debug this script you can add these entries here in the script and then check the Redis server output:
     3  redis.log(redis.LOG_NOTICE, "some label", some_variable)
     4  
     5  For more information please refer to: https://redis.io/docs/manual/programmability/lua-debugging/
     6  --]]
     7  
     8  local rate_limit_key = KEYS[1]
     9  local burst = ARGV[1]
    10  local rate = ARGV[2]
    11  local period = ARGV[3] * 1000 * 1000 -- converting to microseconds
    12  local cost = tonumber(ARGV[4])
    13  local emission_interval = period / rate
    14  local increment = emission_interval * cost
    15  local burst_offset = emission_interval * burst
    16  
    17  -- Redis returns time as an array containing two integers: seconds of the epoch
    18  -- time (10 digits) and microseconds (~5-6 digits). for convenience we need to
    19  -- convert them to a floating point number. The resulting number is 16 digits,
    20  -- bordering on the limits of a 64-bit double-precision floating point number.
    21  -- Adjust the epoch to be relative to Jan 1, 2017 00:00:00 GMT to avoid floating
    22  -- point problems. This approach is good until "now" is 2,483,228,799 (Wed, 09
    23  -- Sep 2048 01:46:39 GMT), when the adjusted value is 16 digits.
    24  local jan_1_2017 = 1483228800 * 1000 * 1000 -- in microseconds precision
    25  local current_time = redis.call("TIME")
    26  local microseconds = current_time[2]
    27  while string.len(microseconds) < 6 do
    28      -- in case the microseconds part (i.e. current_time[2]) is less than 6 digits
    29      microseconds = "0" .. microseconds
    30  end
    31  
    32  local current_time_micro = tonumber(current_time[1] .. microseconds)
    33  current_time = current_time_micro - jan_1_2017
    34  
    35  local tat = redis.call("GET", rate_limit_key)
    36  if not tat then
    37      tat = current_time
    38  else
    39      tat = tonumber(tat)
    40  end
    41  tat = math.max(tat, current_time)
    42  
    43  local new_tat = tat + increment
    44  local allow_at = new_tat - burst_offset
    45  local diff = current_time - allow_at
    46  local remaining = diff / emission_interval
    47  if remaining < 0 then
    48      local reset_after = tat - current_time
    49      local retry_after = diff * -1
    50      return {
    51          current_time_micro,
    52          0, -- allowed
    53          0, -- remaining
    54          tonumber(retry_after),
    55          tonumber(reset_after),
    56      }
    57  end
    58  
    59  local reset_after = new_tat - current_time
    60  if reset_after > 0 then
    61      redis.call("SET", rate_limit_key, new_tat, "EX", math.ceil(reset_after))
    62  end
    63  
    64  local retry_after = -1
    65  return { current_time_micro, cost, remaining, tonumber(retry_after), tonumber(reset_after) }