Rate LimiterStep 5 of 5

Per-Client Limiting

Step 5 β€” Per-Client Limiting

Real limiters don't throttle everyone together β€” they throttle each client, API key, or IP independently. Wrap any limiter strategy in a keyed limiter that lazily creates one limiter per key.

Implement KeyedRateLimiter:

  • __init__(factory) β€” factory is a zero-arg callable that returns a fresh limiter (e.g. lambda: TokenBucket(5, 1, clock))
  • allow(key) β€” get or create the limiter for key, then delegate to its allow()

Each key gets its own independent budget. FixedWindowLimiter is provided so your tests can build a factory.

Hints
Starter code
import time


class FixedWindowLimiter:
    def __init__(self, limit, window, clock=time.time):
        self.limit = limit
        self.window = window
        self.clock = clock
        self._window_index = None
        self._count = 0

    def allow(self):
        index = int(self.clock() // self.window)
        if index != self._window_index:
            self._window_index = index
            self._count = 0
        if self._count < self.limit:
            self._count += 1
            return True
        return False


class KeyedRateLimiter:
    def __init__(self, factory):
        self.factory = factory      # zero-arg callable -> a new limiter
        self._limiters = {}

    def allow(self, key):
        """Get or create the limiter for the key, then delegate to its allow()."""
        # TODO:
        #   if key not in self._limiters: self._limiters[key] = self.factory()
        #   return self._limiters[key].allow()
        pass
Write it on a laptop

The editor and the test runner need a wide screen. Open this page on a desktop browser and your progress will be waiting.