How to save latest offset that Spark consumed to ZK or Kafka and can read back after restart
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Apache Spark is a powerful open-source processing engine built around speed, ease of use, and sophisticated analytics. Among its many capabilities, Spark can be integrated with Apache Kafka—a distributed streaming platform—to process large streams of data efficiently. A critical aspect of managing streaming data effectively is the ability to save and restore the offsets (positions within the stream of data) that a Spark application has consumed. This ensures that in case of a restart or failure, the system can continue processing from the last known point rather than starting over or duplicating work.
Understanding Offsets
In Kafka, each message in a given topic partition is assigned a unique sequential ID known as an offset. For effective stream processing, Spark needs to track these offsets to maintain the state of each partition it is processing. After a failure or planned restart, recovering these offsets allows Spark to resume processing exactly where it left off.
Storing Offsets in ZooKeeper
ZooKeeper plays a crucial role in Kafka's architecture, mainly for coordinating and managing Kafka brokers. It can also be used to store the offsets of messages that Spark has already consumed.
Steps to Store Offsets in ZooKeeper:
- Configuration: You must configure Spark to use ZooKeeper by setting up the ZooKeeper hosts in the Spark configuration file or through the SparkContext.
- Saving Offsets: Post message consumption, the offsets can be manually stored in ZooKeeper nodes. This is done by serializing the offset data into a string or JSON format and writing it under a specific path that you define in ZooKeeper.
- Reading Offsets on Restart: When Spark restarts after a failure or a stop, it reads the offset information from the specified path in ZooKeeper, deserializes it, and starts consuming messages from the offsets stored.
Storing Offsets in Kafka
Storing offsets in Kafka itself is generally more robust and simpler compared with ZooKeeper due to Kafka’s built-in offset management capabilities.
Steps to Store Offsets in Kafka:
- Configuration: To have Spark manage offsets within Kafka, use the subscribe method of KafkaUtils to subscribe to the topics, and configure the parameters like
'auto.offset.reset'to determine where to start reading data in case of missing offsets.'enable.auto.commit'set to false to manually manage offsets.
- Offset Commits: Periodically commit the offsets using either synchronous or asynchronous commit API of Kafka. This can be configured in the Spark streaming job itself to ensure that offsets are committed after a batch is processed successfully.
- State Recovery: Upon restart, the Kafka consumer can be configured to automatically read from the last committed offsets. This is managed internally by Kafka based on the committed offset data.
Use Cases and Considerations
While both methods serve the basic purpose of storing and retrieving offsets, using Kafka for offset management is preferred in environments where Kafka is the primary source of data. It simplifies operations by not requiring an external system (like ZooKeeper) to manage offsets. However, when additional coordination or metadata management is needed beyond offsets, combining Kafka with ZooKeeper might be advantageous.
Example Code Snippet
Here's a basic example in Scala showing how to commit offsets in Kafka within a Spark application:
Summary Table
| Feature | ZooKeeper | Kafka |
| Dependency | External ZooKeeper | Built into Kafka |
| Complexity | High | Low |
| Scalability | Limited | High |
| Failover | Manual Configuration | Automatic by Kafka |
| Community Support | Limited | Broad and Growing |
In conclusion, managing Kafka offsets within Spark streams is crucial for efficient fault tolerance and seamless data processing continuity. Choosing between ZooKeeper and Kafka for offset management depends largely on your architectural needs and existing setups. The newer versions of Kafka, with enhanced capabilities for offset management, make Kafka the more straightforward choice in most cases.
Related reading
- How to scale k8s pods according to rabbitmq queue message rate?
- How to scale Kafka Connect effectively?
- How to send and consume json messages using confluent-kafka in Python
- how to send batched data with Spring Kafka producer
- How to serve a Spark MLlib model?
- how to set Hadoop DistributedCache?
- How to send final kafka-streams aggregation result of a time windowed KTable?
- How to send headers using KStream

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.