Exercise 2: Verifying the Rate Limiter Recovers After Refill — Possible Solution ==================================================================== THE SEQUENCE ------------------------------ limiter = TokenBucket(capacity=5, refill_rate_per_sec=1) first_batch = [limiter.allow_request() for _ in range(5)] # exhausts the bucket sixth = limiter.allow_request() # immediate 6th - should fail time.sleep(2.1) # refill ~2 tokens at 1/sec recovery_batch = [limiter.allow_request() for _ in range(2)] third_after_wait = limiter.allow_request() # a 3rd should fail again RESULTS ------------------------------ first 5 requests: [True, True, True, True, True] 6th immediate request (bucket empty): False 2 requests after waiting 2.1s (should refill ~2 tokens): [True, True] 3rd request after the wait (should fail, only 2 tokens refilled): False VERIFYING THE LIMITER GENUINELY RECOVERS, NOT JUST STAYS EXHAUSTED ------------------------------ After being fully drained (5 successes, then a rejection), the bucket correctly allowed exactly 2 new requests after waiting 2.1 seconds - matching the expected refill of roughly 2.1 * 1/sec ~= 2.1 tokens (enough for exactly 2 full requests, with a small fractional remainder insufficient for a 3rd). This confirms the limiter isn't a one-time budget that permanently locks out a client after being exhausted once - it's a genuinely renewing resource, exactly as this chapter's own UrlShortener needs for real, ongoing traffic rather than a single burst. WHY THE 3RD RECOVERY REQUEST CORRECTLY FAILS ------------------------------ 2.1 seconds at a refill rate of 1 token/sec produces roughly 2.1 tokens - just barely enough for 2 requests (consuming 2.0 of the 2.1 available), with roughly 0.1 tokens left over, not enough for a 3rd. This third rejection isn't a bug or an inconsistency - it's the exact same "not enough tokens available yet" logic that correctly rejected the original 6th request, now correctly re-applied after a genuine, if still-insufficient, amount of recovery time. WHY THIS WORKS AS AN ANSWER ------------------------------ The test explicitly verifies both directions of the limiter's own behavior - that it enforces its limit AND that it recovers over real time - using this chapter's own unmodified TokenBucket, with the exact refill arithmetic checked against the observed pass/fail pattern rather than only confirming "some recovery happens."