Redelivery is normal, not exceptional. Design the handler so processing twice is harmless.
@KafkaListener(topics = "payments")
void onPayment(PaymentEvent e) {
// Natural idempotency key from the event
if (processed.putIfAbsent(e.paymentId(), TRUE) != null) return;
ledger.credit(e.account(), e.amount());
}
// Better: a unique constraint in the database
// insert into processed_events(event_id) values (?) -> duplicate key = skip
A rebalance mid-batch redelivers the last few records.
Without a guard, the account is credited twice.
Run this yourself in the Online Java Compiler, spin up a live REST API in the API Sandbox, or practise with Java interview questions.
Published 2026-08-11