Sign in to keep reading
“Phase Three: The Limits” is free with an account. Your progress and bookmarks follow you across devices.
Sign in to continueNEW: ML Mock & Coaching with a 20+ year experience coach. Save $20 on sessions for Premium.ML Mock & Coaching now available
NEW: ML Mock & Coaching with a 20+ year experience coach. Save $20 on sessions for Premium.ML Mock & Coaching now available
Minutes 34 to 40. The concurrency conversation, the memory question you prepared for, and how to land the session instead of being cut off mid-edit.
“Phase Three: The Limits” is free with an account. Your progress and bookmarks follow you across devices.
Sign in to continueBefore the Debrief
Could you answer these today? Tick what you already know.
"Suppose this is behind a service taking a few thousand requests a second, across a thread pool. What happens?"
Six minutes left. Almost none of what follows involves writing code, and that is not a failure mode — it is what this phase is for. Phase three is usually a conversation, and the candidates who treat it as one do better than the candidates who start typing.
Reaching this phase at all is a positive signal. Plenty of sessions are still fighting phase one when the clock runs out.
The honest answer to the interviewer's question is that the limiter breaks, and specifically where:
def allow(self, user_id):
bucket = self._bucket_for(user_id)
bucket._refill()
if bucket.tokens >= 1: # thread A reads 1.0
bucket.tokens -= 1 # thread B also read 1.0, before A wrote back
return True
return False
Two threads can both evaluate tokens >= 1 before either decrements. Both are allowed through. One token was spent twice.
Worth naming the symmetry, because it is genuinely interesting rather than just correct: this is the same over-admission as the original bug, arriving by a completely different route. Phase one let extra requests through because the arithmetic was wrong. Phase three lets them through because the arithmetic is not atomic. Same observable failure, unrelated cause — which is exactly why the reproduction you wrote in phase one would not catch it.
The fix is a lock around refill-and-decrement. Say what it costs rather than presenting it as free:
"A lock around the refill and the decrement closes it. But that serialises every call to
allow, and for a rate limiter sitting in front of everything on a hot path, that's a real cost rather than a theoretical one — I'd want to measure it under load before assuming it's acceptable. If contention turned out to be a problem, the next move is a lock per user rather than one global lock, since two users' buckets never touch each other."
That last sentence matters more than the lock. It shows you can see past the first correct answer to the one that actually scales, which is the question underneath the question.
Proposing a lock is what everyone does. Proposing a lock, naming what it serialises, saying you would measure rather than assume, and having a second option ready if the measurement is bad — that is four sentences, and it is the difference between a correct answer and a senior one.