Fix Bug in Concurrent Rate Limiter Implementation
Given a broken Python implementation of a token bucket rate limiter intended to handle concurrent requests with thread safety, identify and fix the bugs causing incorrect rate limiting behavior and race conditions.
Challenge prompt
The provided Python code is intended to implement a token bucket rate limiter for an API, enforcing a max request rate over time and supporting concurrency with thread safety. However, it currently fails under concurrent load, either allowing more requests than allowed or deadlocking. Your task is to identify and fix the concurrency and logic bugs so that the rate limiter correctly enforces the limit in multithreaded environments.
Guidance
- • Review thread synchronization mechanisms used in the code and ensure proper locking to avoid race conditions.
- • Verify that token refill logic accounts for time and concurrency correctly and only adds tokens once per period.
- • Test with concurrent threads to confirm that no more than the allowed requests pass within the specified window.
Hints
- • Look for improper use or missing use of thread locks around shared state modifications.
- • Check if the token refill timing is calculated correctly and whether multiple threads might simultaneously refill tokens improperly.
- • Consider the atomicity of checking and decrementing token counts under concurrency.
Starter code
import threading
import time
class TokenBucket:
def __init__(self, rate, capacity):
self.capacity = capacity
self.tokens = capacity
self.rate = rate
self.timestamp = time.time()
self.lock = threading.Lock()
def allow_request(self):
now = time.time()
elapsed = now - self.timestamp
add_tokens = elapsed * self.rate
if add_tokens > 0:
self.tokens = min(self.capacity, self.tokens + add_tokens)
self.timestamp = now
if self.tokens >= 1:
self.tokens -= 1
return True
else:
return FalseExpected output
The fixed TokenBucket should correctly limit requests so that no more than capacity tokens are used per second regardless of concurrency. For example, in a test with 10 threads attempting to call allow_request 100 times each with rate=5 and capacity=10, roughly 10-15 requests per second are permitted, reflecting the rate limiter's constraints without race conditions.
Core concepts
Challenge a Friend
Send this duel to someone else and see if they can solve it.