Skip to content

Idempotence

The idempotent producer guarantees that a message is not written more than once when the producer retries. RobustMQ fully supports the default idempotent producer (enable.idempotence=true, the default in modern Kafka clients), giving exactly-once write behavior with no extra configuration.

Transactions are not supported: an InitProducerId carrying a transactional_id is rejected. Idempotence is a subset of transactions; RobustMQ currently offers idempotence only, not cross-partition / cross-session transactions. See Compatibility and Limitations.

How It Works

RobustMQ Kafka idempotent producer

Producer ID Allocation

On startup the producer sends InitProducerId; the broker allocates a producer id and returns epoch 0.

  • The producer id is monotonic per broker — a node-local range, not a globally coordinated allocation.
  • The epoch distinguishes incarnations of the same producer id; a restarted producer arrives with a higher epoch.

Dedup State

The broker keeps dedup state per (producer_id, shard):

FieldMeaning
epochThe latest producer epoch seen
next_seqThe expected next base sequence
windowThe last 5 accepted batches, as (base_seq, base_offset)

The window size 5 matches Kafka's default max.in.flight.requests.per.connection — up to 5 in-flight requests, so the broker remembers the last 5 batches to recognize their retries.

Sequence Decision

Each batch carries a base_sequence. The broker decides as follows (source: check_producer_sequence):

ConditionResultReturns
epoch older than seenFenced (stale incarnation)INVALID_PRODUCER_EPOCH
epoch newer than seenNew incarnation, reset windowAccept and write
base_seq == next_seqIn orderAccept and write
base_seq hits a batch in the windowDuplicate (retry)Return the original base_offset, no rewrite
otherwise (gap ahead, or stale beyond window)Out of orderOUT_OF_ORDER_SEQUENCE_NUMBER

The key point: a retry that hits the window returns the original base_offset and produces no new record, which is what makes it idempotent. After a successful write, next_seq advances to last_seq + 1 and the batch is appended to the window (evicting the oldest beyond 5).

Relation to the Produce Flow

The idempotence check sits in the produce path "after the size check, before the write" (see Producer). A non-idempotent producer (producer_id < 0) skips the whole check.

Limits

ItemStatus
Idempotent produceFully supported
Producer id scopeBroker-local (single node), not a cross-node global range
Transactions (transactional_id)Unsupported; InitProducerId is rejected
Cross-session idempotenceRelies on the producer id/epoch held by the client, per Kafka semantics
🎉 既然都登录了 GitHub,不如顺手给我们点个 Star 吧!⭐ 你的支持是我们最大的动力 🚀