359. Logger Rate Limiter
easyAsked at TwilioLogger Rate Limiter is Twilio's signature rate-limit warm-up: implement shouldPrintMessage(timestamp, message) that returns true iff the message hasn't been printed in the last 10 seconds. Twilio's API-platform interviews use this as the entry point to the full rate-limiter design family. The grading bar is the hash-map approach with O(1) per call.
By Alex Chen, Founder, InterviewChamp.AI · Last verified
Source citations
Public interview reports confirming this problem appears in Twilio loops.
- Glassdoor (2026-Q1)— Heavily-cited in Twilio API-platform and developer-experience on-site reports as a domain-aligned question.
- LeetCode Discuss (2025-12)— Recurring in Twilio interview reports — explicitly named as 'rate-limit-style' question.
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 the Logger class: Logger() initializes the logger object; bool shouldPrintMessage(int timestamp, string message) returns true if the message should be printed in the given timestamp, otherwise returns false.
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, true]Approaches
1. Hash map of next-allowed timestamps (canonical optimal)
Store message -> next-allowed-timestamp. On each call, allow if current >= next-allowed; update next-allowed = current + 10.
- Time
- O(1) per call
- Space
- O(distinct messages)
class Logger {
constructor() {
this.nextAllowed = new Map();
}
shouldPrintMessage(timestamp, message) {
const allowedAt = this.nextAllowed.get(message);
if (allowedAt !== undefined && timestamp < allowedAt) return false;
this.nextAllowed.set(message, timestamp + 10);
return true;
}
}Tradeoff: O(1) lookup and update. Memory grows with distinct messages — never shrinks. The optimal answer for the LC contract but production-fragile (memory leak for unbounded message space).
2. Sliding window queue + set (memory-bounded alternative)
Maintain a queue of (timestamp, message) and a set of messages in the window. On each call, evict expired entries from the queue head, then check the set.
- Time
- amortized O(1) per call
- Space
- O(messages in last 10s)
class LoggerBounded {
constructor() {
this.queue = []; // [timestamp, message]
this.window = new Set();
}
shouldPrintMessage(timestamp, message) {
// Evict expired
while (this.queue.length > 0 && this.queue[0][0] + 10 <= timestamp) {
const [_, msg] = this.queue.shift();
this.window.delete(msg);
}
if (this.window.has(message)) return false;
this.queue.push([timestamp, message]);
this.window.add(message);
return true;
}
}Tradeoff: Memory bounded by the number of messages in the active 10-second window — won't leak under unbounded message space. Amortized O(1) per call. Queue.shift() is O(n) in JS arrays; use an index pointer for production. This is the answer Twilio interviewers actually want when they extend to 'how do you handle unbounded message identifiers?'
Twilio-specific tips
Twilio's API platform team explicitly uses this question to grade rate-limiter intuition. State both approaches AND the memory tradeoff. The hash-map version is cleaner code; the queue+set version is what you'd ship to production. Be ready for the follow-up 'now generalize to N requests per K seconds' (LC 1188 — Logger Rate Limiter II or 933 — Number of Recent Calls) — the queue+set extends naturally, the hash-map doesn't.
Common mistakes
- Using `<` instead of `>=` for the timestamp comparison — at exactly t + 10 the message SHOULD print, not be blocked.
- Forgetting to update next-allowed when a message is allowed — defeats the rate limit on repeat messages.
- Returning false-as-side-effect by updating the timestamp even when blocked — extends the block window incorrectly past the original 10 seconds.
Follow-up questions
An interviewer at Twilio may pivot to one of these next:
- Generalize to N calls per K seconds per user (LC 1188). (Use queue+set per user, drop expired on each call.)
- What if timestamps could arrive out of order? (Stable sort by timestamp on a sliding buffer; or use an interval tree.)
- How would you adapt this to a distributed rate-limiter across N machines? (Token bucket in Redis with atomic INCR + EXPIRE, or a shared sliding-window log.)
Solve it now
Free. No sign-up. Python and JavaScript run instantly in your browser.
FAQ
Why is the hash-map version called 'production-fragile'?
Because keys never get evicted. If your message space is unbounded — e.g., distinct request IDs — the map grows without bound for the lifetime of the process. The queue+set version self-cleans because the set membership is gated on a moving time window.
How does this relate to Twilio's actual rate-limiting code?
Public Twilio engineering posts describe their rate-limiting stack as token-bucket per route + sliding-window log per user. The Logger Rate Limiter problem is a minimal version of the sliding-window log — the same data structure scales up to per-user, per-endpoint, multi-region production rate limiters.
Free learning resources
Curated free links for this problem.
Practice these live with InterviewChamp.AI
Drill Logger Rate Limiter and other Twilio interview questions under real-loop conditions with instant feedback on your reasoning, complexity claims, and code.
Practice these live with InterviewChamp.AI →