Monedula

Kafka troubleshooting

Consumers see duplicate messages

Kafka symptom

Duplicates come from one of two places. Either the record is in the log twice, because a producer retried or resent it without idempotence, or it is in the log once and the consumer processed it twice, because it restarted or rebalanced before committing its offset. Check the offsets: the same payload at two offsets is a producer duplicate, the same offset processed twice is a consumer redelivery.

Reproduce it in the playground

The moment it happensDuplicate: O1 now at offsets 0 AND 1. t+4.000s

The re-sent copy replicates to the ISR and the high watermark advances to 2. With no sequence number to recognise the re-send, the broker treats it as a brand-new record - the log holds O1 twice. Retries without idempotence cause duplicates.

Open at this step: step 4, Duplicate: O1 now at offsets 0 AND 1

What it means

Kafka's default contract is at least once. A producer that sends a record and gets no response cannot tell whether the write failed or only the acknowledgement was lost, so it retries, and logs each retry as a warning such as "Got error produce response with correlation id 12 on topic-partition orders-0, retrying (2147483646 attempts left). Error: REQUEST_TIMED_OUT. Error Message: Disconnected from node 1 due to timeout". Without idempotence, a retry of a batch the broker had already written is a duplicate. The idempotent producer stamps every batch with a producer id and a sequence number, so the broker discards a retried batch it already has, and since Kafka 3.0 enable.idempotence defaults to true together with acks=all. On the consumer side, the committed offset is where a group resumes, so records processed after the last commit are delivered again after a crash or when the partition moves to another member. Neither side gives exactly-once processing on its own: that takes transactions for Kafka-to-Kafka pipelines, or an idempotent consumer when the output goes to another system.

Likely causes, most common first

  1. Idempotence is off. enable.idempotence=false turns it off, and so does a conflicting setting such as acks=1 or max.in.flight.requests.per.connection above 5, silently, unless idempotence was enabled explicitly. A retry after a lost acknowledgement then appends a second copy of the record.
  2. The application resends. Idempotence only covers the producer's own retries within one producer session. Code that catches a send failure and calls send() again, or a producer process that restarts and replays its input, writes new records the broker has no way to recognise as repeats.
  3. The consumer is redelivered records it already processed. With auto commit (enable.auto.commit=true, every auto.commit.interval.ms, 5 seconds by default) or with manual commits after each batch, a crash, a kill or a rebalance between processing and the next commit makes the next owner start from the last committed offset and process those records again.
  4. A read_uncommitted consumer reads aborted transactions. isolation.level defaults to read_uncommitted, which also returns records from aborted transactions. If a transactional producer aborts and retries, such a consumer sees both attempts.

How to fix it

What not to do

Configs involved

Reproduce it in the simulator

Sources

Related

All troubleshooting pages