How does Zookeeper/Kafka retain offset for a consumer?
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Apache Kafka is a high-throughput, distributed messaging system that is utilized by many organizations to handle real-time data feeds. ZooKeeper, on the other hand, plays a critical role in managing this ensemble by providing a distributed configuration service, synchronization service, and naming registry for large distributed systems like Kafka. One of the fundamental aspects in Kafka's ability to process streams of data efficiently lies in how it manages the consumer offsets. Understanding how Kafka maintains these offsets with the assistance of ZooKeeper is central to grasping Kafka's operational efficacy.
Understanding Offsets in Kafka
In Kafka, an offset is a unique identifier of a record in a Kafka partition. This offset allows Kafka's consumers to keep track of which messages have been consumed and which have not. Each Kafka message within a partition has an offset that acts like an address that provides the location information of a message within that partition.
When a consumer in Kafka reads messages, it needs to track the offsets of messages to ascertain the point up to which the messages have been consumed. Thus, maintaining accurate offsets ensures that the consumers are able to manage their operations without redundancy or message loss.
Role of Kafka in Managing Offsets
Kafka manages offsets by storing the offsets themselves within Kafka topics. Created in version 0.9.0, the __consumer_offsets topic came into existence, marking a turn from older versions where ZooKeeper itself was used to store these offsets.
When a consumer in a consumer group reads messages from a partition of a topic, it commits the offsets of messages it has consumed. By doing so, it indicates to Kafka that any message up to this offset has been consumed by that consumer group, and any new consumer joining the group should start consuming from the next offset.
Committing Offsets
Offsets can be committed in two ways:
- Automatic Commit: Here, offsets are committed automatically at intervals specified in the consumer configuration by
auto.commit.interval.ms. - Manual Commit: This allows consumers to control when to commit offsets and can be more precise about when offsets are considered safe and saved.
Interaction with ZooKeeper
ZooKeeper used to play a primary role in managing consumer offsets in Kafka protocols before version 0.9.0. Consumer metadata like offsets were stored in ZooKeeper, but this led to scalability issues as the volume of offsets and consumer metadata grew with the number of topics and partitions.
Post Kafka 0.9.0, for enhanced performance and scalability, this responsibility has been shifted from ZooKeeper to Kafka itself. This change relegated ZooKeeper to managing and coordinating brokers within the Kafka ecosystem rather than handling offsets directly. Now, ZooKeeper helps in leader election for Kafka broker partitions and in managing membership within the broker cluster, among other operational tasks.
Technical Insights into Offset Storage
As mentioned, offsets are stored in the __consumer_offsets topic, where each message in this topic denotes an offset commit. This topic is a compacted Kafka topic where the server retains only the last offset commit per partition and per consumer group. This compaction ensures efficient storage even as the number of consumers and offsets grows.
Format of Storage
Each record in the __consumer_offsets consists of:
- Key: The key contains information about the consumer group and partition.
- Value: The value stores the offset position, metadata, and a timestamp.
Summary
Here is a brief table summarizing how offsets are handled differently with the progression of Kafka versions:
| Feature | Pre Kafka 0.9.0 | Post Kafka 0.9.0 |
| Offset Storage Location | ZooKeeper | Kafka internal topic |
| Scalability and Performance | Lower | Higher |
| Control | Less (automatic) | More (manual or automatic) |
| Reliability and Fault Tolerance | Dependent on ZK | Self-managed within Kafka |
Conclusion
The shift from using ZooKeeper for offset management to using Kafka’s internal topic has helped Kafka become more scalable and reliable concerning message consumption. Understanding these core functionalities provides insights into Kafka’s design and architecture strategies which are pivotal for developing robust streaming applications.
Related reading
- How is ordering guaranteed during failures in Kafka Async Producer?
- How is spark.streaming.kafka.maxRatePerPartition related to spark.streaming.backpressure.enabled incase of spark streaming with Kafka?
- How kafka balances partitions load?
- How Kafka broadcast to many Consumer Groups
- How is Cassandra designed to avoid the need for load balancers?
- How is ETCD a highly available system, even though it uses Raft which is a CP algorithm?
- How Kafka distributes the topic partitions among the brokers
- How KafKa guarantee Consistency and Availability?

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.