Rate Limiting & Throttling — System Design

Rate Limiting membatasi jumlah request yang bisa dilakukan dalam periode waktu tertentu. Ini melindungi sistem dari abuse, DDoS, dan overload. Mengapa Rate Limi

Rate Limiting membatasi jumlah request yang bisa dilakukan dalam periode waktu tertentu. Ini melindungi sistem dari abuse, DDoS, dan overload.

Mengapa Rate Limiting penting:

Tanpa rate limiting:
  Satu user jahat → 100.000 request/detik → Server crash → Semua user terkena dampak

Dengan rate limiting:
  User A → 100 req/menit ✓
  User B (jahat) → 101 req → Blocked (429 Too Many Requests)
  Server tetap sehat untuk semua user

Algoritma Rate Limiting:

1. Fixed Window Counter

Window: setiap menit
Limit: 100 request/menit

00:00 - 00:59 → Counter: 100 → Full, block!
01:00 reset   → Counter: 0   → Boleh lagi

Masalah: Burst di batas window!
00:59 → 100 request  (window 1 habis)
01:00 → 100 request  (window 2 mulai)
= 200 request dalam 2 detik!

2. Sliding Window Log

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

  isAllowed(userId) {
    const now = Date.now();
    const windowStart = now - this.windowMs;

    if (!this.requests[userId]) this.requests[userId] = [];

    // Hapus request lama di luar window
    this.requests[userId] = this.requests[userId]
      .filter(ts => ts > windowStart);

    if (this.requests[userId].length >= this.limit) return false;

    this.requests[userId].push(now);
    return true;
  }
}

3. Token Bucket

Bucket berisi token. Setiap request mengambil 1 token. Token diisi kembali dengan rate konstan.

class TokenBucket {
  constructor(capacity, refillRate) {
    this.capacity = capacity;      // max token
    this.tokens = capacity;        // mulai penuh
    this.refillRate = refillRate;  // token per detik
    this.lastRefill = Date.now();
  }

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

  refill() {
    const now = Date.now();
    const elapsed = (now - this.lastRefill) / 1000; // detik
    const newTokens = elapsed * this.refillRate;
    this.tokens = Math.min(this.capacity, this.tokens + newTokens);
    this.lastRefill = now;
  }
}

// Contoh: max 10 burst, 2 token/detik
const limiter = new TokenBucket(10, 2);

Keunggulan Token Bucket: membolehkan burst sesaat (cocok untuk API yang butuh burst tapi rata-rata rendah).

4. Leaky Bucket

Request masuk ke "bucket", keluar dengan rate konstan. Request berlebih ditolak atau diqueue.

Request masuk → [Bucket] → Keluar dengan rate tetap (10 req/detik)
Jika bucket penuh → Tolak request baru (rate limiting)

Perbandingan algoritma:

Algoritma Burst Memory Akurasi
Fixed Window Bermasalah di batas O(1) Sedang
Sliding Window Log Tidak ada burst anomaly O(n) Tinggi
Token Bucket Burst diperbolehkan O(1) Tinggi
Leaky Bucket Tidak ada burst O(n) Tinggi

Implementasi di berbagai level:

// Level 1: Per IP
limiter.check(req.ip);

// Level 2: Per User (setelah auth)
limiter.check("user:" + req.user.id);

// Level 3: Per API Key
limiter.check("apikey:" + req.headers["x-api-key"]);

// Level 4: Per Endpoint (endpoint sensitif lebih ketat)
limiter.check("user:" + userId + ":POST:/api/payments");

Response saat di-rate-limit:

// HTTP 429 Too Many Requests
res.status(429).json({
  error: "Terlalu banyak request",
  message: "Coba lagi dalam 60 detik",
  retryAfter: 60,          // detik
  limit: 100,              // limit
  remaining: 0,            // sisa request
  resetAt: "2024-01-15T10:31:00Z"
});

// Header standar
res.set("X-RateLimit-Limit", 100);
res.set("X-RateLimit-Remaining", 0);
res.set("X-RateLimit-Reset", resetTimestamp);
res.set("Retry-After", 60);

Distributed Rate Limiting:

// Dengan Redis — rate limiting yang dibagikan antar semua server
async function isAllowed(userId) {
  const key = "ratelimit:" + userId;
  const count = await redis.incr(key);
  if (count === 1) {
    await redis.expire(key, 60); // window 60 detik
  }
  return count <= 100; // limit 100
}

Rate limiting adalah komponen wajib untuk API publik, mencegah abuse sekaligus menjaga QoS (Quality of Service) untuk semua pengguna.

🎭 Analogi sehari-hari: Bouncer di klub. Total kapasitas 200 orang. Orang masuk-keluar terus. Bouncer cek: "Sekarang ada berapa di dalam? Belum 200? Masuk." Atau: "Tiap menit max 10 orang masuk." Algoritma rate limit = aturan bouncer.

💡 Algoritma rate limit klasik:

Most popular: Token Bucket (allow burst) atau Sliding Window Counter (akurat).

⚠️ Jebakan klasik:

🎯 Strategi rate limit by endpoint:

TL;DR: Rate limit = pelindung dari abuse + DDoS + overload. Token Bucket (burst) atau Sliding Window (akurat) populer. Pakai Redis untuk shared state across server. Strict di auth endpoint. Wajib Retry-After header.