This page details the cost of the Ratelimit algorithms in terms of the number of Redis commands. Note that these are calculated for Regional Ratelimits. For Multi Region Ratelimit, costs will be higher. Additionally, if a Global Upstash Redis is used as the database, number of commands should be calculated as (1+readRegionCount) * writeCommandCount + readCommandCount and plus 1 if analytics is enabled.

The Rate Limit SDK minimizes Redis calls to reduce latency overhead and cost. The count of commands executed by the Rate Limit algorithm depends on the chosen algorithm, as well as the state of the algorithm and the caching.

Algorithm State

By state of the algorithm, we refer to the entry in our Redis store regarding some identifier ip1. You can imagine that there is a state for every identifier. We name these states in the following manner for the purpose of attributing costs to each one:

StateSuccessExplanation
FirsttrueFirst time the Ratelimit was called with identifier ip1
IntermediatetrueSecond or some other time the Ratelimit was called with identifier ip1
Rate-LimitedfalseRequests with identifier ip1 which are rate limited.

For instance, first time we call the algorithm with ip1, PEXPIRE is called so that the key expires after some time. In the following calls, we still use the same script but don’t call PEXPIRE. In the rate-limited state, we may avoid using Redis altogether if we can make use of the cache.

Cache Result

We distinguish the two cases when the identifier ip1 is found in cache, resulting in a “hit” and the case when the identifier ip1 is not found in the cache, resulting in a “miss”. The cache only exists in the runtime environment and is independent of the Redis database. The state of the cache is especially relevant for serverless contexts, where the cache will usually be empty because of a cold start.

ResultExplanation
HitIdentifier ip1 is found in the runtime cache
MissIdentifier ip1 is not found in cache or the value in the cache doesn’t block (rate-limit) the request

An identifier is saved in the cache only when a request is rate limited after a call to the Redis database. The request to Redis returns a timestamp for the time when such a request won’t be rate limited anymore. We save this timestamp in the cache and this allows us to reject any request before this timestamp without having to consult the Redis database.

See the section on caching for more details.

Costs

limit()

Fixed Window

Cache ResultAlgorithm StateCommand CountCommands
Hit/MissFirst3EVAL, INCR, PEXPIRE
Hit/MissIntermediate2EVAL, INCR
MissRate-Limited2EVAL, INCR
HitRate-Limited0utilized cache

Sliding Window

Cache ResultAlgorithm StateCommand CountCommands
Hit/MissFirst5EVAL, GET, GET, INCR, PEXPIRE
Hit/MissIntermediate4EVAL, GET, GET, INCR
MissRate-Limited3EVAL, GET, GET
HitRate-Limited0utilized cache

Token Bucket

Cache ResultAlgorithm StateCommand CountCommands
Hit/MissFirst/Intermediate4EVAL, HMGET, HMSET, PEXPIRE
MissRate-Limited2EVAL, HMGET
HitRate-Limited0utilized cache

getRemaining()

This method doesn’t use the cache or it doesn’t have a state it depends on. Therefore, every call results in the same number of commands in Redis.

AlgorithmCommand CountCommands
Fixed Window2EVAL, GET
Sliding Window3EVAL, GET, GET
Token Bucket2EVAL, HMGET

resetUsedTokens()

This method starts with a SCAN command and deletes every key that matches with DEL commands:

AlgorithmCommand CountCommands
Fixed Window3EVAL, SCAN, DEL
Sliding Window4EVAL, SCAN, DEL, DEL
Token Bucket3EVAL, SCAN, DEL

blockUntilReady()

Works the same as limit().

Analytics

If analytics is enabled, all calls of limit will result in 1 more command since HINCRBY will be called to update the analytics.