Kafka
Spark
Cassandra
Offset Management
Data Processing

How to fetch offset id while consuming Kafka from Spark, save it in Cassandra and use it to restart Kafka?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

In modern data processing, Apache Kafka and Apache Spark are often used in combination due to their ability to process large streams of data in a scalable and efficient manner. In many use cases, it's critical to manage Kafka offsets properly to ensure that your Spark streaming applications can recover seamlessly in case of failures. This article focuses on fetching Kafka offsets while consuming data in Spark, storing these offsets in Cassandra, and using them to restart Kafka streaming applications with the exact state they left off.

Understanding Kafka Offsets and Spark Streaming

Kafka Offsets

Kafka offsets are integral to tracking messages in Kafka partitions. Each message within a Kafka partition is assigned a unique offset, which identifies the position of the message. When consuming messages, the consumer's progress is tracked by recording the offsets of messages that have been read.

Spark Streaming with Kafka

Apache Spark provides two approaches for consuming messages from Kafka:

  1. Direct Stream Approach: Spark directly reads the offset range from Kafka, bypassing Kafka's consumer group mechanism. This approach offers better performance and allows for explicit control over which offsets are consumed, making offset management more flexible.
  2. Receiver-based Approach: Messages are received using Kafka's high-level consumer API, which automatically manages offset tracking within consumer groups.

For managing offsets explicitly, the Direct Stream approach is preferred as it does not depend on Kafka's consumer group offset tracking.

Fetching Kafka Offsets in Spark

In Spark, when using the Direct Stream approach, you can leverage createDirectStream to gain control over Kafka offsets.

scala
1import org.apache.spark.streaming.kafka010._
2import org.apache.kafka.common.TopicPartition
3
4// Define Kafka parameters
5val kafkaParams = Map[String, Object](
6  "bootstrap.servers" -> "localhost:9092",
7  "key.deserializer" -> classOf[StringDeserializer],
8  "value.deserializer" -> classOf[StringDeserializer],
9  "group.id" -> "use_a_separate_group_id_for_each_stream",
10  "auto.offset.reset" -> "latest",
11  "enable.auto.commit" -> (false: java.lang.Boolean)
12)
13
14// Define the topics and offsets to consume
15val topics = Array("my_topic")
16val fromOffsets = Map(new TopicPartition("my_topic", 0) -> 2L)
17
18val stream = KafkaUtils.createDirectStream[String, String](
19  streamingContext,
20  PreferConsistent,
21  Assign[String, String](fromOffsets.keys.toList, kafkaParams, fromOffsets)
22)

Saving Offsets in Cassandra

To have a robust system, storing offsets in an external system like Cassandra can provide resiliency against failures. Here's how you can configure Cassandra to store offsets:

  1. Cassandra Configuration: First, ensure your Spark application is configured to connect to the Cassandra cluster:
scala
1import com.datastax.spark.connector._
2
3val conf = new SparkConf(true)
4  .set("spark.cassandra.connection.host", "127.0.0.1")
  1. Save Offsets: Extract offsets after processing and store them in Cassandra.
scala
1import com.datastax.spark.connector.cql.CassandraConnector
2
3stream.foreachRDD { rdd => 
4  val offsetRanges = rdd.asInstanceOf[HasOffsetRanges].offsetRanges
5  
6  // Write offsetRanges to Cassandra
7  val cassandraConnector = CassandraConnector(rdd.sparkContext.getConf)
8  
9  cassandraConnector.withSessionDo { session =>
10    offsetRanges.foreach { offsetRange =>
11      val query = s"INSERT INTO my_keyspace.kafka_offsets (topic, partition, offset) VALUES ('${offsetRange.topic}', ${offsetRange.partition}, ${offsetRange.untilOffset})"
12      session.execute(query)
13    }
14  }
15}

Restarting Kafka Using Saved Offsets

Upon restarting your Spark application, you can fetch the stored offsets from Cassandra to start consuming from where you left off:

scala
1import org.apache.spark.sql.cassandra._
2
3val spark = SparkSession.builder().appName("KafkaConsumer").getOrCreate()
4
5// Fetch saved offsets from Cassandra
6val offsetData = spark.read
7  .cassandraFormat("kafka_offsets", "my_keyspace")
8  .load()
9
10val fromOffsets = offsetData.collect().map { row =>
11  new TopicPartition(row.getString("topic"), row.getInt("partition")) -> row.getLong("offset")
12}.toMap
13
14// Reuse fromOffsets in KafkaUtils.createDirectStream
15val stream = KafkaUtils.createDirectStream[String, String](
16  streamingContext,
17  PreferConsistent,
18  Assign[String, String](fromOffsets.keys.toList, kafkaParams, fromOffsets)
19)

Summary of Key Points

Providing a clear overview, here's a table summarizing the key points covered in this article:

TopicDescription
Kafka OffsetsUnique identifiers for messages in Kafka partitions, used for tracking consumer progress.
Spark Stream ApproachesDirect Stream vs Receiver-based approaches for consuming Kafka messages.
Storing OffsetsUse Cassandra to store offsets for fault-tolerant and scalable offset storage.
Offset RetrievalFetch offsets from Cassandra upon restart to ensure seamless Kafka stream reprocessing.

Advantages of the Approach

  • Fault Tolerance: By storing offsets in Cassandra, you ensure that Kafka consumers can continue from their last processed message even in the case of failures.
  • Scalability: Both Kafka and Cassandra handle large volumes of data efficiently, making this approach suitable for scalable workloads.
  • Flexibility: The separation of offset management from Kafka allows for more flexible stream processing implementations.

By utilizing Kafka's offsets, storing them in Cassandra, and managing their retrieval upon application restart, you ensure robustness and continuity in Spark-based data streaming applications. This approach is particularly useful in systems where data consistency and fault tolerance are non-negotiable.


Course illustration
Course illustration

All Rights Reserved.