Implement a per-user API rate limiter
Company: Read.Ai
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: medium
Interview Round: Onsite
Quick Answer: This question evaluates the ability to implement per-user API rate limiting, testing competency in time-windowed request accounting, per-entity state management, and designing efficient data structures for time-based constraints.
Constraints
- 1 <= len(requests) <= 10^6 (and may be empty)
- 0 <= maxRequests <= 10^4
- 1 <= windowSeconds <= 10^9
- timestampSeconds are non-negative integers in non-decreasing order
- userId is a non-empty string
- A denied request does not count toward the limit (it is not recorded)
Examples
Input: (3, 10, [['u1', 1], ['u1', 2], ['u1', 3], ['u1', 4], ['u1', 12]])
Expected Output: [True, True, True, False, True]
Explanation: The given example: 1,2,3 fill the limit; 4 is the 4th in window [-5,4] so denied (and not recorded); by 12 only the request at 3 remains in window [3,12], so 12 is allowed.
Input: (2, 5, [['a', 1], ['b', 1], ['a', 2], ['a', 3], ['b', 2]])
Expected Output: [True, True, True, False, True]
Explanation: Users are limited independently. User a: 1,2 allowed, then 3 denied (window [-1,3] already has 1,2). User b: 1 and 2 allowed (only 2 in its window).
Input: (1, 1, [['x', 5], ['x', 5], ['x', 6], ['x', 6]])
Expected Output: [True, False, True, False]
Explanation: maxRequests=1, window=1. The first request each second is allowed; the duplicate at the same second is denied. Denied requests do not block the next second.
Input: (3, 10, [])
Expected Output: []
Explanation: Empty request stream yields an empty decision list.
Input: (0, 10, [['u', 1], ['u', 2]])
Expected Output: [False, False]
Explanation: maxRequests=0 means no request can ever be allowed, since size < 0 is never true.
Input: (2, 100, [['solo', 50]])
Expected Output: [True]
Explanation: A single request from a single user is always allowed when maxRequests >= 1.
Input: (2, 3, [['u', 1], ['u', 2], ['u', 3], ['u', 4], ['u', 5]])
Expected Output: [True, True, False, True, True]
Explanation: Window=3, max=2. At t=3 the window [1,3] already holds the allowed 1 and 2, so 3 is denied (not recorded). At t=4 the 1 has aged out, leaving only 2, so 4 is allowed; similarly 5 is allowed after 2 ages out.
Hints
- Track state per user independently — a hash map from userId to that user's recent allowed timestamps.
- A rolling time window with FIFO eviction is naturally a deque (monotonic queue): on each request, drop timestamps older than t - windowSeconds + 1 from the front.
- Allow the request iff the deque size after eviction is strictly less than maxRequests; only then append t. Never record a denied request.
- Watch the boundary: the window is inclusive on both ends and spans exactly windowSeconds seconds, so the lower bound is t - windowSeconds + 1, not t - windowSeconds.