Blog Hoch
Mar 2026·2 tup leghDe'wI' chenmoHta'

Rate Limiting Algorithms: Token Bucket vs Sliding Window

#backend#api-design#performance

public API Hoch rate limiting poQ, algorithm choose "100 requests per minute" real meaning decide. cha' approaches dominate: token bucket 'ej sliding window - edges Daq pIm behave (real traffic hit vaj qatlh).

"requests per minute" tricky

naive approach fixed window: current calendar minute requests count, minute roll over vaj reset. simple 'ach client 0:59 100 requests 'ej 1:00 100 more send vaj - limiter 100/minute allow 'ach server 200 requests two seconds Daq. fixed windows boundary Daq leak.

token bucket 'ej sliding window gap close, memory 'ej precision cost pIm.

token bucket: refilling pool spend

token bucket maximum tokens hold. request wa' token consume; tokens continuously fixed rate refill. bucket empty vaj request reject (pagh queue). key property: bursts bucket size Dung allowed, average throughput refill rate cap.

class TokenBucket {
  constructor(capacity, refillPerSecond) {
    this.capacity = capacity;
    this.tokens = capacity;
    this.refillPerSecond = refillPerSecond;
    this.lastRefill = Date.now();
  }

  #refill() {
    const now = Date.now();
    const elapsedSeconds = (now - this.lastRefill) / 1000;
    this.tokens = Math.min(this.capacity, this.tokens + elapsedSeconds * this.refillPerSecond);
    this.lastRefill = now;
  }

  tryConsume(count = 1) {
    this.#refill();
    if (this.tokens >= count) {
      this.tokens -= count;
      return true;
    }
    return false;
  }
}

const bucket = new TokenBucket(20, 5); // burst of 20, refills at 5/sec
if (bucket.tryConsume()) {
  handleRequest();
} else {
  respondWithTooManyRequests();
}

background timer pagh - #refill() last check qaSDI' tokens chay' law' accumulate compute (elapsed time). requests Dung stateless (tokens 'ej lastRefill neH) - Redis Daq distributed API state vaj Dun.

sliding window: recently nuq qaS count

sliding window log recent requests timestamps track, trailing window Daq count (right now) - fixed calendar bucket pagh. most accurate: "last 60 seconds requests chay' law'" direct jang, boundary leak pagh.

class SlidingWindowLog {
  constructor(limit, windowMs) {
    this.limit = limit;
    this.windowMs = windowMs;
    this.timestamps = [];
  }

  tryConsume() {
    const now = Date.now();
    const cutoff = now - this.windowMs;
    this.timestamps = this.timestamps.filter((t) => t > cutoff);

    if (this.timestamps.length < this.limit) {
      this.timestamps.push(now);
      return true;
    }
    return false;
  }
}

const limiter = new SlidingWindowLog(100, 60_000); // 100 requests per rolling 60s

cost memory: request per timestamp, client per. high volume law'. production sliding window counters lo': hybrid, cha' fixed-window counters (current 'ej previous) weighted estimate:

function slidingWindowCount(currentCount, previousCount, elapsedInCurrentWindowMs, windowMs) {
  const weightOfPrevious = 1 - elapsedInCurrentWindowMs / windowMs;
  return currentCount + previousCount * weightOfPrevious;
}

precision few trade, O(1) memory per client (O(requests) pagh).

choose

  • Token bucket bursts fine 'ej average throughput qatlh - downstream service short spikes absorb 'ach sustained load pagh.
  • Sliding window log exact enforcement poQ 'ej client counts low - high-value API keys few.
  • Sliding window counter rolling-window accuracy at scale, small approximation error boundaries Daq tolerate.

fixed window alone production choice rare - boundary bursts care pagh vaj neH (coarse daily quotas).

state nuqDaq lives

algorithms interesting pagh (API more than wa' server Daq Qu'). single process memory Daq token bucket pagh timestamp log wa' process neH limit - client N instances Daq spread vaj N times limit.

standard fix: Redis Daq counter centralize, INCR 'ej PEXPIRE (fixed/sliding-window counter), Lua script (atomic token-bucket check-and-decrement):

// Atomic fixed-window counter in Redis
async function isAllowed(redisClient, key, limit, windowSeconds) {
  const count = await redisClient.incr(key);
  if (count === 1) {
    await redisClient.expire(key, windowSeconds);
  }
  return count <= limit;
}

count === 1 check new window first request Daq expiry set - INCR Hoch existing counter bump (expire, cycle restart).

client jatlh

algorithm Hoch standard headers return - well-behaved clients back off (429 hammer pagh):

  • X-RateLimit-Limit - ceiling.
  • X-RateLimit-Remaining - current window Daq nuq Dach.
  • Retry-After - seconds (429 response rap).

yaj

Fixed windows simple, boundary leak. Token bucket controlled bursts (average rate). Sliding window log exact, memory-hungry; sliding window counters cheap approximate. bursts acceptable, precision enforcement choose - counter servers Hoch legh place Daq.

chu'CORS wa'logh Hoch vaD yajqanN+1 Query Qagh 'ej chay' Qotlh