Redis Tutorial 0/42 lessons ~6 min read Lesson 35

    Rate Limiting

    Shared Redis counters enforce API rate limits across all pods — without central store each instance allows full quota and attackers multiply allowance by replica count.

    Course progress0%
    Focus
    10 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    Shared Redis counters enforce API rate limits across all pods — without central store each instance allows full quota and attackers multiply allowance by replica count. Sliding window via ZSET timestamps or fixed window INCR+EXPIRE are common patterns.

    Lua scripts make check-and-increment atomic. Return 429 with Retry-After when limit exceeded; log metric for abuse detection.

    Rate limit keys per userId, API key, or IP — tier limits by subscription plan in key suffix.

    Understanding the topic

    Key concepts

    • Fixed window: INCR key EXPIRE window.
    • Sliding window: ZSET of request timestamps.
    • Token bucket via Lua periodic refill.
    • Key: ratelimit:{tier}:{id}:{window}.
    • Atomic increment in Lua or MULTI.
    • Fail open vs closed on Redis outage policy.

    Step-by-step explanation

    1. Request arrives; compute limit key.
    2. Lua or INCR checks count vs threshold.
    3. Under limit — increment and allow.
    4. Over limit — reject 429.
    5. TTL expires window automatically.

    Syntax reference

    Common commands

    • Fixed window burst at edges — sliding smoother.
    • Lua combines INCR+EXPIRE atomically.
    • Document fail-open policy for payments vs fail-closed for auth.
    bash
    # Fixed window 100 req/min
    INCR ratelimit:user:42:202607011045
    EXPIRE ratelimit:user:42:202607011045 60
    # if count > 100 → 429

    Informative example

    Sliding window rate limiter with Lua in Spring service:

    java
    @Service
    public class RateLimiter {
    private final StringRedisTemplate redis;
    public RateLimiter(StringRedisTemplate redis) {
    this.redis = redis;
    }
    private static final String SCRIPT = """
    local key = KEYS[1]
    local limit = tonumber(ARGV[1])
    local window = tonumber(ARGV[2])
    local now = tonumber(ARGV[3])
    redis.call('ZREMRANGEBYSCORE', key, 0, now - window)
    if redis.call('ZCARD', key) >= limit then return 0 end
    redis.call('ZADD', key, now, now .. ':' .. ARGV[4])
    redis.call('EXPIRE', key, window)
    return 1
    """;
    public boolean allow(String userId) {
    Long ok = redis.execute(RedisScript.of(SCRIPT, Long.class),
    List.of("rl:" + userId), "100", "60", String.valueOf(System.currentTimeMillis()), UUID.randomUUID().toString());
    return ok != null && ok == 1;
    }
    }

    Return 429 in controller when allow false. Add metric tag userId hash for dashboards.

    Real-world use

    Real-world use cases

    • Public API 1000 req/hour per key.
    • Login brute-force throttle per IP.
    • SMS OTP send limit per phone.
    • GraphQL query cost limiter.
    • Webhook receiver protection.

    Best practices

    • Lua for atomic check-increment.
    • Return Retry-After header.
    • Tier limits in key structure.
    • Monitor 429 rate and top offenders.
    • Fail closed for auth; consider fail open for non-critical reads.
    • Combine with WAF edge limits.

    Common mistakes

    • In-memory Guava limiter per pod only.
    • Fixed window double burst at boundary.
    • No TTL on counter keys.
    • Rate limiting after expensive work not before.

    Advanced interview questions

    Q1BeginnerWhy Redis for rate limiting?
    Shared atomic counters across all app instances.
    Q2BeginnerINCR rate limit pattern?
    INCR key; set EXPIRE on first hit; compare to max.
    Q3IntermediateFixed vs sliding window?
    Fixed simpler edge burst; sliding accurate uses ZSET timestamps.
    Q4IntermediateRedis down — allow or deny?
    Product decision: fail open preserves uptime; fail closed protects resources.
    Q5AdvancedDesign API rate limit for 1M developers?
    Token bucket per API key in Redis Cluster; 429 + Retry-After; analytics stream; tier in key.

    Summary

    Central Redis counters enforce global limits.

    Ready to mark this lesson complete?Track your journey across the entire course.