359. Logger Rate Limiter
easyAsked at RobinhoodDesign a logger system that returns whether a unique message should be printed in a given timestamp; the same message cannot reprint within 10 seconds. Robinhood asks rate-limit design because production trading services need request throttles that can't break under load.
By Alex Chen, Founder, InterviewChamp.AI · Last verified
Source citations
Public interview reports confirming this problem appears in Robinhood loops.
- Glassdoor (2026-Q1)— Robinhood SWE-I and SWE-II phone-screen reports list Logger Rate Limiter as a recurring system-design-coding hybrid.
- Blind (2025-11)— Robinhood backend trip reports note rate-limit-style problems as the lighter design round.
Problem
Design a logger system that receives a stream of messages along with their timestamps. Each unique message should only be printed at most every 10 seconds (i.e., a message printed at timestamp t will prevent other identical messages from being printed until timestamp t + 10). All messages will come in chronological order. Several messages may arrive at the same timestamp. Implement shouldPrintMessage(int timestamp, string message) that returns true if the message should be printed.
Constraints
0 <= timestamp <= 10^9Every timestamp will be passed in non-decreasing order (chronological order).1 <= message.length <= 30At most 10^4 calls will be made to shouldPrintMessage.
Examples
Example 1
shouldPrintMessage(1,'foo'); shouldPrintMessage(2,'bar'); shouldPrintMessage(3,'foo'); shouldPrintMessage(8,'bar'); shouldPrintMessage(10,'foo'); shouldPrintMessage(11,'foo');true, true, false, false, false, trueExplanation: 'foo' at 3 and 10 are within 10s of 'foo' at 1; rejected. 'foo' at 11 is after 10s — accepted.
Approaches
1. Hash map: message → last-printed timestamp
Store each message's last-print timestamp. On a new call, return true iff timestamp - last >= 10, and update.
- Time
- O(1) per call
- Space
- O(unique messages — unbounded)
class Logger {
constructor() { this.lastPrint = new Map(); }
shouldPrintMessage(timestamp, message) {
const last = this.lastPrint.get(message);
if (last === undefined || timestamp - last >= 10) {
this.lastPrint.set(message, timestamp);
return true;
}
return false;
}
}Tradeoff: O(1) per call but unbounded memory — every distinct message you've ever seen stays in the map forever. Robinhood interviewers will probe the memory growth, so name it.
2. Queue + set with eviction
Queue of (timestamp, message). On each call, evict head while timestamp - head.ts >= 10, removing from the set. Check membership of new message.
- Time
- O(1) amortized per call
- Space
- O(messages in last 10s)
class LoggerEvict {
constructor() {
this.queue = []; // [{ ts, msg }]
this.head = 0;
this.active = new Set();
}
shouldPrintMessage(timestamp, message) {
// Evict expired
while (this.head < this.queue.length && timestamp - this.queue[this.head].ts >= 10) {
this.active.delete(this.queue[this.head].msg);
this.head++;
}
if (this.active.has(message)) return false;
this.active.add(message);
this.queue.push({ ts: timestamp, msg: message });
return true;
}
}Tradeoff: Bounded memory — only stores messages within the active window. O(1) amortized because each message is enqueued once and dequeued once. This is the answer Robinhood expects when they probe the memory question.
Robinhood-specific tips
Robinhood interviewers reliably probe memory growth on the naive hash-map solution. Open with the hash map, name the memory issue, then pivot to the eviction-queue version. Bonus points for sketching how you'd scale to a distributed rate limiter — token bucket per node, Redis-backed counters, or sliding-log eviction via a sorted set.
Common mistakes
- Using > instead of >= 10 — the problem says 'every 10 seconds', which is >= 10.
- Iterating the entire map every call to evict — O(n) instead of O(1) amortized.
- Failing to note that timestamps are non-decreasing — if they weren't, the eviction-queue approach would need a different data structure.
Follow-up questions
An interviewer at Robinhood may pivot to one of these next:
- Distributed rate limiter — discuss token bucket and sliding window across nodes.
- Rate limit by IP rather than by message — same shape, different key.
- Variable-rate limiter — e.g. 100 req/min for free tier, 1000 req/min for paid.
Solve it now
Free. No sign-up. Python and JavaScript run instantly in your browser.
FAQ
Is the eviction-queue version strictly better than the hash map?
It's better when distinct messages keep arriving and most don't repeat — memory stays bounded to the active 10s window. The hash map version leaks every distinct message forever, which is fine for short-lived processes but ruins long-running services.
How would this generalize to a distributed system?
Replace the in-memory map with a Redis hash (key = message, value = last-printed timestamp) and a Redis EXPIRE. Or use a Redis sorted set with sliding-window semantics. Both give cross-node consistency at the cost of network round-trips.
Free learning resources
Curated free links for this problem.
Practice these live with InterviewChamp.AI
Drill Logger Rate Limiter and other Robinhood interview questions under real-loop conditions with instant feedback on your reasoning, complexity claims, and code.
Practice these live with InterviewChamp.AI →