Java LLD: Designing a Kafka-Like Message Queue for Machine Coding Interviews
Designing a high-performance message queue is a frequent requirement in senior-level machine coding rounds. It tests your ability to balance thread safety with decoupled architecture while managing stateful consumer progress.
The mistake most candidates make
- Using a standard
java.util.Queuethat removes elements upon polling, which prevents multiple consumer groups from reading the same data. - Coupling the Producer directly to the Consumer logic, violating the Pub-Sub principle and making the system brittle to scale.
- Failing to implement independent offset management, leading to data loss or duplicate processing when one consumer lags.
The right approach
- Core mental model: An immutable, append-only log where messages are persisted per topic, allowing consumers to track their own progress independently.
- Key entities:
Topic,Message,Subscriber,ConsumerGroup,OffsetManager. - Why it wins: It enables "replayability" and allows multiple heterogeneous systems to consume the same stream at different speeds without interference.
Implementation Insight: The Thread-Safe Log
public class Topic {
private final List<Message> messages = new ArrayList<>();
private final ReentrantLock lock = new ReentrantLock();
public void addMessage(Message message) {
lock.lock();
try {
messages.add(message);
} finally {
lock.unlock();
}
}
public List<Message> getMessagesFrom(int offset) {
return (offset >= messages.size()) ? List.of() : messages.subList(offset, messages.size());
}
}
Key takeaways
- Decouple producers and consumers by using an append-only log structure instead of a destructive queue.
- Use
ReentrantLockto ensure atomic appends to the message log in a multi-threaded producer environment. - Manage consumer state via independent offsets, allowing each
ConsumerGroupto process messages at its own pace.
Full working implementation with execution trace available at https://javalld.com/problems/message-queue
Top comments (0)