We ❤️ Open Source

A community education resource

8 min read

Replaying Kafka messages: Practical guide with sample code

Exploring Kafka's replay capabilities: From offsets to mirror topics and fine-tuning data reprocessing with Java.

Apache Kafka is a very popular and powerful framework for processing huge quantities of data in real time. You can use its streaming capability to keep track of logs, perform transactions, and fuel analytics. But what if you want to go back and reprocess some data? Maybe you have a bug to reproduce, lost data, or need to sort the past. You will find below a step-by-step Java code guide that shows you why and how to replay messages on Kafka.

Why replay Kafka messages?

In Kafka, the Replay function is very helpful, in cases like:

  1. Data recovery: To restore lost or damaged data.
  2. Maintenance need: To pause and restart during & after the maintenance window.
  3. Bug fixes: To reprocess messages when application bugs impact the data pipeline.
  4. Historical analysis: To examine older data without impacting the live system.
  5. Testing and debugging: To simulate production conditions in a controlled environment.

However, Kafka’s message policies message replay comes with its own rules to allow us to look at old data. Here are a few Kafka message replay techniques and Java code for each.

Kafka basics: Topics, partitions, and offsets

In Kafka, data in the form of events and messages is stored and organized in topics divided into partitions to live in different nodes in the Kafka cluster. Each message in a partition has an offset, a unique identifier of messages from the beginning of a Kafka partition. Consumers of a Kafka topic keep track of the offsets to determine where they left off; by adjusting these offsets, we can control which messages to replay.

Replay Kafka message options

Kafka provides different approaches for replaying messages:

  1. Offset-based replay: One can reset consumer offsets or position markers to reprocess messages.
  2. Mirror topics: Store messages into separate topics and replay at will.
  3. Custom replay services: Create a replay service to implement any custom replay logic.

Now, let’s revisit these strategies with sample Java code to demonstrate how these work.


1. Offset-Based Replay: Resetting Consumer Offsets

The following example shows how to seek offsets back to the beginning of a topic so you can replay all messages in that topic:.

Example with KafkaConsumer API in Java

The below sample code demonstrates how to reset offsets to the beginning of a topic, and enable to replay of all messages in the topic.

import org.apache.kafka.clients.consumer.ConsumerConfig;

import org.apache.kafka.clients.consumer.ConsumerRecord;

import org.apache.kafka.clients.consumer.ConsumerRecords;

import org.apache.kafka.clients.consumer.KafkaConsumer;

import org.apache.kafka.common.TopicPartition;

import java.time.Duration;

import java.util.Collections;

import java.util.Properties;


public class KafkaReplaySample {

 public static void main(String[] args) {

     Properties properties = new Properties();

     properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, <host:port>);

     properties.put(ConsumerConfig.GROUP_ID_CONFIG, "<replay-group-name>");

     properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");

     properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");

     properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");

     KafkaConsumer<String, String> consumer = new KafkaConsumer<>(properties);

     TopicPartition partition = new TopicPartition("my-topic-name", 0);

     consumer.assign(Collections.singletonList(partition));

     consumer.seekToBeginning(Collections.singletonList(partition));

     while (true) {

         ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));

         for (ConsumerRecord<String, String> record : records) {

             System.out.println("Replaying message: " + record.value());

         }

     }

 }

}

In the above code, seekToBeginning moves the offset of the consumer to the beginning of the partition and replays all messages. If you wanted to replay messages from a given offset, you could use consumer.seek(partition, offset).

Using Kafka CLI to reset offsets

If you don’t want to modify your code, you can use Kafka’s command-line tool to reset offsets.

kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group replay-group --topic my-topic --reset-offsets --to-earliest --execute

This command resets the offset for replay-group to the earliest point in my-topic, enabling the consumer to reprocess all messages from the beginning.


2. Mirror topics: Creating a replay-specific topic

Sometimes, performing an offset reset in a production environment isn’t ideal if you don’t want to affect active consumers. In these cases, a mirror topic could be helpful. The idea of a mirror topic is a pretty simple-a duplicate topic where messages are kept for archival purposes in order to replay data independently of the main topic.

Java code to publish messages to a mirror topic

import org.apache.kafka.clients.producer.KafkaProducer;

import org.apache.kafka.clients.producer.ProducerConfig;

import org.apache.kafka.clients.producer.ProducerRecord;

import org.apache.kafka.clients.producer.RecordMetadata;

import java.util.Properties;

import java.util.concurrent.ExecutionException;

public class MirrorTopicProducer {

  public static void main(String[] args) {

      // Configure the Kafka producer

      Properties properties = new Properties();

      properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");

      properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");

      properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");

      KafkaProducer<String, String> producer = new KafkaProducer<>(properties);

      String message = "This is a sample message";

      try {

          producer.send(new ProducerRecord<>("my-topic-name", message)).get();

          producer.send(new ProducerRecord<>("my-topic-replay-name", message)).get();

          System.out.println("Message sent to both main and mirror topics");

      } catch (InterruptedException | ExecutionException e) {

          e.printStackTrace();

      } finally {

          producer.close();

      }

  }

}

In the example above, every message is published to the main topic (my-topic-name) and additionally to a mirrored topic named (my-topic-replay-name). This topic will become an archive for replay purposes, without disturbing the original one when needed.

Replaying from the mirror topic

You can then set up a consumer to read messages from the mirror topic:

The mirror topic makes it easy to replay archived data without impacting live consumers or ongoing processing.


3. Custom replay services: Filtering and custom logic

When you have advanced replay requirements example, you might want to replay messages of a certain type or messages that meet some condition-you may use the provided custom replay service. This allows you to inject your own logic to filter messages and replay only those messages that interest you.

Custom replay service with message filtering

Suppose we wanted to replay only messages containing the keyword “error.” Here’s how you might implement such a service.

import org.apache.kafka.clients.consumer.KafkaConsumer;

import org.apache.kafka.clients.producer.KafkaProducer;

import org.apache.kafka.clients.producer.ProducerRecord;

import java.time.Duration;

import java.util.Collections;

import java.util.Properties;


public class CustomReplayService {

  public static void main(String[] args) {

      Properties consumerProperties = new Properties();

      consumerProperties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");

      consumerProperties.put(ConsumerConfig.GROUP_ID_CONFIG, "replay-group");

      consumerProperties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");

      consumerProperties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");

      KafkaConsumer<String, String> consumer = new KafkaConsumer<>(consumerProperties);

      consumer.subscribe(Collections.singletonList("my-topic-replay"));

      Properties producerProperties = new Properties();

      producerProperties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");

      producerProperties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");

      producerProperties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");

      KafkaProducer<String, String> producer = new KafkaProducer<>(producerProperties);

      String keyword = "error";

      while (true) {

          ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));

          for (ConsumerRecord<String, String> record : records) {

              if (record.value().contains(keyword)) {

                  System.out.println("Replaying filtered message: " + record.value());

                  producer.send(new ProducerRecord<>("my-topic", record.value()));

              }

          }

      }

  }

}

The following code reads messages from the mirror topic and only republishes those messages that contain the keyword “error” to the main topic. Fine-grained control over the replay behavior provided by this approach may be useful while debugging, testing, or doing some selective reprocessing.


Key considerations when replaying Kafka messages

While replaying messages is powerful, there are some key things to consider when replaying messages:

  1. Idempotency: The consumers should be able to handle duplicate messages gracefully. You may want to add logic to ignore duplicate processing in key applications.
  2. Data volume: It should be communicated that large volumes might affect both network and processing resources.
  3. Retention policies: The messages you need to replay should be within Kafka’s retention window.
  4. Consumer offsets: Resetting offsets can affect other consumers in the same consumer group. Consider using separate consumer groups for testing or selective replays.

Conclusion

Kafka supports various ways to replay messages. Each approach has different trade-offs and allows a developer to select a strategy appropriate for their application. Using offset-based replay is simple; mirror topics add some flexibility, while implementing custom replay services provide fine-grained control over replays.

The following code snippets in Java demonstrate how to implement each approach in an effective way for handling replays in Kafka and, at the same time, ensuring consistency of data across applications. The strategies given will ensure that you unlock the full potential of Kafka streaming across diverse business needs.

More from We Love Open Source

About the Author

Engineering Manager @ Capital One

Read Raja Chattopadhyay's Full Bio

The opinions expressed on this website are those of each author, not of the author's employer or All Things Open/We Love Open Source.

Want to contribute your open source content?

Contribute to We ❤️ Open Source

Help educate our community by contributing a blog post, tutorial, or how-to.

Join us next year for All Things Open 2025!

Be sure to save the date for All Things Open 2025: Sunday, October 12 through Tuesday, October 14.

Open Source Meetups

We host some of the most active open source meetups in the U.S. Get more info and RSVP to an upcoming event.