Files

2.7 KiB

kafka-error-handling — retries, DLT and poison pills

Companion project for Kafka Error Handling with Spring Kafka 4.1: DLT, Retry Topics and Poison Pills on ankurm.com.

Six tests against a real Kafka broker started in-process in KRaft mode. No Docker, no local install. ./scripts/run-all.sh regenerates everything under docs/output/.

Versions

Version
JDK 25 (Temurin 25.0.4.1+1)
Spring Boot 4.1.1
Spring Kafka 4.1.1
kafka-clients 4.2.1 (Boot-managed)

Profiles

Profile What it wires
dlt DefaultErrorHandler + DeadLetterPublishingRecoverer, FixedBackOff(1000, 2), PermanentFailure classified non-retryable
dltbytes the same, with a byte[]-aware template map so poison pills keep their original bytes
retrytopic @RetryableTopic non-blocking retries with a @DltHandler
defaults the stock DefaultErrorHandler, for reading its behaviour

Documentation

  1. Two kinds of failure, and why they need different machinery
  2. What the default actually does
  3. Poison pills
  4. The dead-letter topic
  5. Non-blocking retries with @RetryableTopic

Captured output

File Shows
default-backoff.txt ten deliveries, zero delay
retry-and-dlt.txt measured back-off and the DLT headers
poison-pill.txt base64 payload, and the byte-aware fix
retry-topics.txt the non-blocking delivery trace
tests.txt 6 tests

Six things this module exists to prove

  1. The default is ten deliveries, zero milliseconds apart, then the record is dropped. Not "retry with backoff", and not "dead-letter".
  2. The DLT suffix is -dlt, not .DLT. Get it wrong and the recoverer logs a WARN and the record is lost — your safety net silently drops it.
  3. kafka_dlt-exception-fqcn is always ListenerExecutionFailedException for listener failures. The useful header is -exception-cause-fqcn.
  4. A poison pill reaches the DLT base64-encoded, because the recoverer reuses the JSON producer. A per-type template map fixes it; the module shows both transcripts.
  5. @RetryableTopic names retry topics by delay-retry-500, -retry-1000 — so changing the multiplier renames them.
  6. @Backoff from spring-retry no longer exists here. Spring Kafka 4 ships its own @BackOff, and the attribute is backOff.