What is Idempotent Producer
An Idempotent Producer in Apache Kafka is a client operational mode that mathematically guarantees that each message is written to a topic partition exactly once (exactly-once w...
🟢 Junior Level
What is an Idempotent Producer in Simple Terms?
An Idempotent Producer in Apache Kafka is a client operational mode that mathematically guarantees that each message is written to a topic partition exactly once (exactly-once write per partition), even if network disruptions cause the producer to issue automatic retry attempts (retries).
In distributed systems, transient network packet drops are routine. Without idempotence, network retries lead to duplicate records in the log:
Scenario WITHOUT Idempotence:
1. Producer ──send(Message A)──> Broker (Appends to partition at Offset 42)
2. Broker ──ACK (TCP)──X (Network timeout / packet dropped)
3. Producer (Retry timeout) ──retry(Message A)──> Broker (Appends duplicate at Offset 43)
Result: Message A is persisted twice! Causes duplicate financial charges or duplicate orders.
Scenario WITH Idempotence (enable.idempotence = true):
1. Producer ──send(PID=101, Seq=0, Message A)──> Broker (Appends at Offset 42, remembers Seq=0)
2. Broker ──ACK (TCP)──X (Network drop / timeout)
3. Producer (Retry timeout) ──retry(PID=101, Seq=0, Message A)──> Broker
4. Broker detects: Record with PID=101 and Seq=0 is ALREADY COMMITTED!
-> Broker drops duplicate disk write, but returns SUCCESS ACK with original Offset 42!
Result: Exactly one record in the log. Zero duplicates!
Enabling Idempotence in Java
Starting with Kafka 3.0, producer idempotence is enabled by default (enable.idempotence = true). For legacy Kafka 0.11–2.8 clients, it can be declared explicitly:
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "kafka-broker1:9092,kafka-broker2:9092");
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
// Explicitly enable idempotence (Default in Kafka 3.0+)
props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, "true");
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
Core Identifiers
Producer deduplication is powered by two lightweight metadata fields automatically attached to batch headers:
- Producer ID (PID): A unique 64-bit integer allocated to the client by the broker upon initialization.
- Sequence Number (SeqNum): A monotonic 32-bit counter ($0, 1, 2, \dots$) maintained per
(PID, TopicPartition)pair.
🟡 Middle Level
Enforced Safety Invariants
When enable.idempotence is set to true, the Kafka client automatically validates and enforces three critical configuration invariants:
acks = all(acks = -1): Requires acknowledgment from the Leader and all active ISR members. Ifacks=1were allowed, an un-replicated write on a failed Leader would destroy sequence ordering on the promoted follower.retries = Integer.MAX_VALUE: Enables effectively infinite retry attempts (bounded only bydelivery.timeout.ms). Network retries are completely safe since duplicates are rejected by design.max.in.flight.requests.per.connection <= 5: Bounds the number of unacknowledged TCP requests simultaneously in flight to a broker. Modern Kafka brokers can track and reorder up to 5 concurrent in-flight batches per partition without violating sequence order.
[!WARNING] If a developer configures
enable.idempotence=truewhile explicitly settingacks=1ormax.in.flight.requests.per.connection=10, theKafkaProducerconstructor will fail immediately with aConfigException:org.apache.kafka.common.config.ConfigException: Must set acks to all when enable.idempotence is true.
Delivery Guarantees Comparison
| Producer Mode | Configuration | Delivery Guarantee | Network Failure Behavior | Duplicate Risk |
| :— | :— | :— | :— | :— |
| At-Most-Once | acks=0 or retries=0 | Fire-and-forget; delivery at most once | Message dropped permanently | Zero (no retries) |
| At-Least-Once | acks=1/all, retries>0, idempotence=false | Delivered at least once | Retries introduce duplicates | High |
| Idempotent | enable.idempotence=true | Exactly once per partition | Retries deduplicated by broker | Zero (within session) |
| Transactional (EOS) | Idempotence + transactional.id | Atomic multi-partition / multi-topic writes | Transactions abort or commit atomically | Zero (end-to-end) |
Boundaries and Limitations of the Idempotent Producer
While the Idempotent Producer eliminates network retry duplicates, it has strict architectural boundaries:
- Single-Partition Scope: Sequence numbers are tracked strictly per
TopicPartition. Deduplication across different partitions or topics is not supported. - Session-Scoped (Ephemeral Lifecycle): The PID lives exclusively in producer client memory. If a producer process crashes, restarts, or redeploys, it is assigned a brand new PID. Any in-flight duplicate generated across the crash boundary will not be detected by the broker.
- Application-Level Duplicates: If a user double-clicks “Submit Order” in a web interface and the backend triggers two distinct
producer.send()calls with new object instances, Kafka treats them as two distinct messages with different sequence numbers.
🔴 Senior Level
Broker Architectural Internals: KIP-98 and ProducerStateManager
On the Kafka broker, idempotence validation is managed inside kafka.log.ProducerStateManager:
Producer RecordBatch (PID: 5042, Epoch: 0, BaseSeq: 10, LastSeq: 14)
│
▼
Broker: Partition Leader Replica
│
▼
ProducerStateManager
┌────────────────────────────────────────────────────────────────────────┐
│ In-Memory State Cache: Map<Long, ProducerStateEntry> │
│ Key: PID (5042) │
│ Value: │
│ - ProducerEpoch: 0 │
│ - LastSequence: 9 (Expected Next: 10) │
│ - Sliding Queue of Last 5 Batches: [Seq 5, Seq 6, Seq 7, Seq 8, Seq 9] │
│ - LastUpdatedTimestamp: 1711456000000 │
└────────────────────────────────────────────────────────────────────────┘
│
┌──────────────────────────┴──────────────────────────┐
▼ ▼
BaseSeq == LastSequence + 1 (10 == 9 + 1) BaseSeq <= LastSequence (10 <= 9)
│ │
▼ ▼
[VALID NEW BATCH] [DUPLICATE DETECTED]
- Appended to Active Segment (.log) - DISK WRITE SKIPPED
- Update LastSequence = 14 - Returns SUCCESS ACK
- State checkpointed to *.snapshot file with original baseOffset
Client Bootstrap: InitProducerIdRequest
When the producer initializes:
- It dispatches an
InitProducerIdRequestto the broker acting as Transaction Coordinator. - The coordinator allocates a monotonically increasing 64-bit
ProducerId(PID) and setsProducerEpochto0. - The client attaches this PID and Epoch to every outgoing
RecordBatch.
Batch Header Structure (KIP-98)
Each RecordBatch contains:
ProducerId(int64)ProducerEpoch(int16)BaseSequence(int32) — Sequence number of the first record in the batch.
Individual records inside the batch do not store absolute 64-bit offsets; they store lightweight relative offsets (offsetDelta) and sequenceDelta ($0, 1, \dots, N-1$), minimizing wire framing overhead.
Broker Sequence Verification Logic
When a batch arrives at the partition Leader, ProducerStateManager validates BaseSequence against the cached LastSequence:
- $BaseSequence = LastSequence + 1$ (Sequential): The batch is valid. The broker appends the records to the active
.logsegment, setsLastSequence = BaseSequence + recordCount - 1, and returnsRecordMetadata. - $BaseSequence \le LastSequence$ (Duplicate): The batch was already persisted. The broker skips disk I/O, locates the original starting offset, and sends an immediate success
ProduceResponse. - $BaseSequence > LastSequence + 1$ (Gap / Missing Data): The broker detects a sequence gap (e.g., expected Seq 10, received Seq 12). A preceding batch was lost. The broker rejects the write with a fatal
OutOfOrderSequenceException.
Sliding Window Mechanics: Why max.in.flight.requests <= 5
In Kafka 0.11, idempotence required max.in.flight.requests.per.connection = 1. If two requests ($R_1, R_2$) were dispatched concurrently and $R_1$ failed while $R_2$ succeeded, records committed out of order on disk.
Introduced in Kafka 1.1 (KIP-180), the broker maintains a sliding window of the last 5 in-flight batches:
- If request $R_2$ (Seq 5–9) arrives at the broker before the retry of request $R_1$ (Seq 0–4) completes, the broker buffers $R_2$ in memory.
- As soon as $R_1$ arrives and appends, $R_2$ is immediately appended in correct order without throwing
OutOfOrderSequenceException. - Because the broker’s sliding queue tracks up to 5 entries, setting
max.in.flight > 5exceeds buffer capacity, leading to configuration rejection.
State Snapshots and Failover Resilience: .snapshot Files
How does Kafka survive broker restarts and leader elections without losing sequence history?
- Alongside log segments (
.log) and sparse index files (.index), Kafka persists state snapshots:00000000000000010000.snapshot. - The snapshot contains the serialized state of
ProducerStateManager(active PIDs, epochs, and the last 5 sequence numbers) captured at segment rollover. - Follower replicas replicate
RecordBatchheaders intact (including PID and SeqNum) and maintain their own localProducerStateManager. - During Leader failover, the newly elected Leader already possesses identical sequence state and seamlessly deduplicates incoming retries.
- Inactive PIDs are purged after
producer.id.expiration.ms(default: 7 days).
4 Tricky Questions
1. Can a producer.send() call with enable.idempotence=true ever fail with a DuplicateSequenceException on the client?
Answer:
No. The Kafka client will never throw DuplicateSequenceException to the application layer.
When the broker detects a duplicate batch ($BaseSequence \le LastSequence$), it classifies it as a normal network retry following a dropped ACK. The broker discards the duplicate payload from disk, retrieves the original assigned offset, and returns a standard success ProduceResponse. The application’s callback.onCompletion(metadata, null) or future.get() resolves cleanly and successfully.
2. Why does a Pod restart in Kubernetes break the idempotence guarantee, and how do you achieve deduplication across restarts?
Answer:
- When a Kubernetes pod restarts (OOM, rolling deployment, node drain), the JVM process terminates and the
KafkaProducerinstance is destroyed. - The newly instantiated producer issues a fresh
InitProducerIdRequestand is assigned a brand new random PID. - The broker associates this new PID with a starting sequence number of
0and has no knowledge of its predecessor. - If the crashed process pushed a batch to Kafka before dying but crashed before saving progress locally, the new process will re-emit the message under the new PID, causing a duplicate record.
- Architectural Remedy: To survive process restarts, use a Transactional Producer by assigning a stable
transactional.id(e.g.,props.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG, "order-service-pod-0")). The broker coordinator recognizes the existingtransactional.id, maps it to the existing PID, incrementsProducerEpoch(fencing zombie pods withProducerFencedException), and maintains continuous deduplication.
3. What happens if a producer attempts to publish with max.in.flight.requests.per.connection = 7 with idempotence enabled?
Answer:
In modern Kafka clients (2.0+), initializing a producer with enable.idempotence = true and max.in.flight.requests.per.connection > 5 immediately fails during client construction with:
ConfigException: Must set max.in.flight.requests.per.connection to at most 5 to use the idempotent producer.
This constraint exists because the broker’s ProducerStateManager allocates memory for a fixed 5-slot sliding window per PID. Exceeding 5 would cause false-positive OutOfOrderSequenceException errors under network reordering.
4. Why is acks=all mathematically mandatory for idempotence rather than acks=1?
Answer:
Consider what happens under acks=1:
- Producer sends batch (PID 100, Seq 1) to Leader Broker A.
- Leader A writes locally and confirms success with an ACK without waiting for follower replication.
- Producer advances its counter to Seq 2.
- Leader A immediately crashes. Follower Broker B is elected the new Leader, but it never received Seq 1; its
ProducerStateManageronly knows up to Seq 0. - Producer publishes the next batch (PID 100, Seq 2).
- New Leader B inspects the request: $BaseSequence (2) > LastSequence (0) + 1$.
- Leader B detects a sequence gap and aborts with a fatal
OutOfOrderSequenceException. The producer is permanently blocked from writing to that partition without restarting. - Therefore,
acks=allis mathematically necessary to guarantee that sequence state is replicated across the entire ISR before acknowledgment.
🎯 Interview Cheat Sheet
Core Concepts
- Definition: Eliminates duplicate messages within a partition caused by producer network retries.
- Key Identifiers:
ProducerId(64-bit PID) assigned by broker, plus monotonicSequenceNumberper partition. - Default Status: Enabled by default in Apache Kafka 3.0+.
- Automatic Invariants: Forces
acks = all,retries = Integer.MAX_VALUE, andmax.in.flight <= 5. - Deduplication Behavior: Duplicates are discarded silently on the broker; the producer receives a standard success ACK.
Production Monitoring Metrics
| JMX Metric | Production Meaning | Healthy Target |
| :— | :— | :— |
| record-error-rate | Rate of record failures dispatched by client | Strictly 0 |
| record-retry-rate | Frequency of network retry attempts | Non-zero during transient drops; idempotence handles cleanly |
| ErrorsPerSec (OutOfOrderSequenceException) | Sequence mismatch errors on broker | Strictly 0; non-zero indicates client state corruption |
Red Flags to Avoid
- ❌ “Idempotent Producer eliminates duplicates across the entire cluster and all services.” (Deduplication operates strictly per partition within a single producer session).
- ❌ “If a microservice pod restarts in Kubernetes, the idempotent producer will prevent duplicates.” (A new process receives a new PID; cross-restart deduplication requires
transactional.id). - ❌ “Idempotent Producer requires
max.in.flight.requests = 1, making it slow.” (Kafka 1.1+ supportsmax.in.flight = 5with high throughput). - ❌ “When the broker detects a duplicate, it throws an exception to the producer.” (The broker silently discards the duplicate and returns a success ACK).
Related Topics
- How to Configure Exactly-Once Semantics — Transactional EOS
- What Message Delivery Guarantees Does Kafka Provide — Delivery semantics
- What is Producer Acknowledgment and What Modes Exist (acks=0, 1, all) — Producer ACKs
- What is Batch in Kafka Producer — Batch accumulation