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 吧!⭐ 你的支持是我们最大的动力 🚀