Rate Limiter Sliding Window With Per-Entity Limits
Reported by candidates from Roblox's online assessment. Pattern, common pitfall, and the honest play if you blank under the timer.
Roblox reported this one in September 2026, and the whole problem hinges on one data structure: a per-key queue of accepted timestamps. If you've got an OA invite for Roblox, expect a rate limiter where every request has to pass two checks at once, one for the user and one for the experience. It looks like a design question but it's a sliding window in disguise. Timestamps arrive sorted, which is the gift. The traps are small and they cost you hidden test cases. StealthCoder sits invisibly on your screen as a safety net if you blank mid-assessment, but the pattern below is simple enough to carry in your head.
The problem
You are given a stream of requests in chronological order. Each request has a timestamp, a user id, and an experience id. A request is accepted only when both of these limits still have capacity: the number of accepted requests for the same user inside the active sliding window the number of accepted requests for the same experience inside the active sliding window Only accepted requests count toward future capacity. Denied requests are not stored. For a request at timestamp t, the active window is (t - windowLength, t], so accepted requests at timestamps less than or equal to t - windowLength are expired before checking the new request. Return an integer array where 1 means the corresponding request is accepted and 0 means it is denied. Function rateLimiter(requestTimestamps: int[], userIds: int[], experienceIds: String[], windowLength: int, maxRequests: int) → int[] Examples Example 1 requestTimestamps = [1, 2, 3, 4, 5] userIds = [1, 1, 2, 1, 2] experienceIds = ["A", "A", "A", "A", "B"] windowLength = 3 maxRequests = 1 return = [1, 0, 0, 1, 1] The second request is denied because user 1 already has an accepted request in the window. The third request is denied because experience A already has an accepted request in the window. At timestamp 4, the timestamp 1 request has expired, so user 1 and experience A both have capacity. Example 2 requestTimestamps = [10, 11] userIds = [1, 2] experienceIds = ["game-1", "game-1"] windowLength = 10 maxRequests = 1 return = [1, 0] The second user has no accepted request yet, but the shared experience has already reached its limit. Example 3 requestTimestamps = [1, 2, 3, 4] userIds = [7, 7, 7, 7] experienceIds = ["A", "A", "A", "A"] windowLength = 3 maxRequests = 2 return = [1, 1, 0, 1] The denied request at timestamp 3 is not stored. Before timestamp 4 is checked, the accepted timestamp 1 request expires. Constraints requestTimestamps.length == userIds.length == experienceIds.length requestTimestamps is sorted in nondecreasing order. windowLength > 0 maxRequests > 0
Reported by candidates. Source: FastPrep
Pattern and pitfall
Keep two hash maps. One maps user id to a deque of accepted timestamps, the other maps experience id to a deque. For each request at time t, pop from the front of both deques while the front is <= t - windowLength. Then check whether both deque sizes are below maxRequests. If so, push t onto both and output 1. Otherwise output 0 and store nothing. Sorted timestamps mean each deque stays sorted, so expiry is amortized O(1) and the whole thing runs in O(n). The classic pitfalls: using < instead of <= on expiry, so the boundary request lingers. Storing denied requests, which Example 3 explicitly tests. Only pushing into one map when both must update. Evict from both maps before checking either. If you freeze on the live OA, StealthCoder can surface this deque structure fast.
If you see this problem in your OA tomorrow, the play is to recognize the pattern in 30 seconds. StealthCoder buys you that recognition.
You can drill Rate Limiter Sliding Window With Per-Entity Limits cold, or you can hedge it. StealthCoder runs invisibly during screen share and surfaces a working solution in under 2 seconds. The proctor sees the IDE. They don't see what's behind it. Built by an Amazon engineer who passed his OA cold and still thinks the filter is broken.
Get StealthCoderRelated leaked OAs
You've seen the question.
Make sure you actually pass Roblox's OA.
Roblox reuses patterns across OAs. Built by an Amazon engineer who passed his OA cold and still thinks the filter is broken. Works on HackerRank, CodeSignal, CoderPad, and Karat.
Rate Limiter Sliding Window With Per-Entity Limits FAQ
What's the trick in the Roblox rate limiter problem?+
Track accepted timestamps per user and per experience in separate queues. Expire old entries first, then check both sizes against maxRequests. Only when both have room do you accept and push the timestamp into both. Denied requests never get stored.
How hard is this really?+
Medium at most. There's no fancy algorithm. The difficulty is carefully following the rules: boundary expiry, dual limits, and not storing denied requests. If you've written a sliding window with a deque before, it's about 15 lines.
Where do people lose points on this one?+
Off-by-one on expiry is the big one. Timestamps equal to t - windowLength must be removed, so use <=. Next is storing denied requests, which breaks later decisions. Last is checking the limit before evicting expired entries from both maps.
Do I need a heap or a fancy structure?+
No. Timestamps are sorted in nondecreasing order, so a plain deque per key works. Appends go to the back and expiries come off the front. A heap or sorted set only adds log factors and complexity you don't need.
How do I prepare for this in 48 hours?+
Write the two-map deque solution from scratch twice, then run all three examples by hand. Test the edge cases: same timestamp repeated, window boundary equality, and a denied request that must not count. Then do a couple of generic sliding window problems for rhythm.