Skip to content

Instantly share code, notes, and snippets.

@itamarhaber
Created February 23, 2018 19:18
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save itamarhaber/254bac4283d1675c5a5569639b0322aa to your computer and use it in GitHub Desktop.
Save itamarhaber/254bac4283d1675c5a5569639b0322aa to your computer and use it in GitHub Desktop.
Some thoughts about "Building a sliding window rate limiter with Redis"

Reading Building a sliding window rate limiter with Redis, and w/o addressing the actual logic (which may or may not work).

Optimize by:

  1. Lua seems a much better choice: idiompotent, portable, server-side, less bandwidth, atomic...
  2. The call to ZRANGEBYSCORE seems to be unused, should be commented out if so
  3. Looking at the use of ZRANGE, it appears that ZCARD it what's actually needed

The (untested) Lua snippet:

local tok = KEYS[1]
local now, win = tonumber(ARGV[1]), tonumber(ARGV[2])

-- redis.call('ZRANGEBYSCORE', tok, 0, now - win)
local ret = redis.call('ZCARD', tok)
redis.call('ZADD', tok, now, now)
redis.call('EXPIRE', tok, win)

return ret

The (untested) PHP:

// composer require predis/predis
require_once __DIR__ . '/vendor/autoload.php';

$maxCallsPerHour = 5000;
$slidingWindow = 3600;

$now = microtime(true);
$accessToken = md5('access-token');

$client = new Predis\Client();
$result = $client->evalsha('the-sha1-of-the-snippet');

$remaining = max(0, $maxCallsPerHour - count($result));

if ($remaining > 0) {
    echo sprintf('%s: Allowed and %d remaining', $now, $remaining) . PHP_EOL;
} else {
    echo sprintf('%s: Not allowed', $now) . PHP_EOL;
}
@hansott
Copy link

hansott commented Jun 28, 2018

This is the lua script we're using atm:

local token = KEYS[1]
local now = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local limit = tonumber(ARGV[3])

local clearBefore = now - window
redis.call('ZREMRANGEBYSCORE', token, 0, clearBefore)

local amount = redis.call('ZCARD', token)
if amount < limit then
    redis.call('ZADD', token, now, now)
end
redis.call('EXPIRE', token, window)

return limit - amount

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment