Skip to main content

Settlement

What ack, drop, retry and retry_after become on Kafka, which commits one offset per partition instead of acknowledging records.

Commit::Tracked on one partition: the stored position stops below the lowest delivery that has not settledOne partition, offsets as delivered10settled11settled12running13settled14settled15runningStored position: 11below the lowestrunning deliveryCommitted offset: 12the record the groupresumes fromWhen 12 settles,the position movesto 14: 15 is stillrunning
The diagram as text
  1. Stored position: 11 below the lowest running delivery → 11 settled: stored at
  2. Stored position: 11 below the lowest running delivery → Committed offset: 12 the record the group resumes from: auto-commit
  3. One partition, offsets as delivered
  4. 10 settled
  5. 12 running
  6. 13 settled
  7. 14 settled
  8. 15 running
  9. When 12 settles, the position moves to 14: 15 is still running

Kafka keeps one committed offset per partition and group. The subscription's Commit mode decides how a handler's outcome reaches it.

Under Commit::Tracked, the crate turns librdkafka's automatic offset store off and records every offset it delivers. A settlement stores the position just below the lowest delivery still running, or the highest delivered offset once none is running. Acks from parallel lanes can arrive in any order, and the stored position never passes unfinished work. Offsets the consumer never receives, such as transaction markers or compacted records, do not hold it back. librdkafka's timer commits the stored position as usual.

OutcomeCommit::Auto (default)Commit::TrackedCommit::Transactional
ackNothing: librdkafka stored the offset when it handed the record overThe offset settles and the stored position moves upThe offset settles; the pipeline commits it with its next window
dropNothing, as for ackThe offset settles, as for ackThe offset settles, as for ack
retry()Refused (AckError::Unsupported) and logged: nothing brings the record backThe record is delivered again ahead of what is fetched next, and the position waits for itThe open window cannot commit: it aborts and is read again

A rebalance or a seek that moves a partition discards its tracked state. A settlement of a delivery read before the move changes nothing, because the partition's next reader starts from the committed offset anyway.

Under Commit::Transactional the consumer commits nothing itself. The EosPipeline of the same id adds the settled positions to its producer transaction every commit_interval (100 milliseconds), so the consumed offsets and the published records become visible together, as Kafka transactions draws.

Retries​

A retry on Kafka: the attempt cap decides between a copy to the topic, the dead-letter topic and a dropA recordx-ruststream-retry-count says which attempt it isThe handler returnsretry_after(delay), or retry() under a declarationmax_attempts: is this attempt below the cap?Below the cap:the originalsettles, a copygoes to the topicAt the cap:a copy goes tothe dead-lettertopicAt the cap,no dead letter:the recordsettles, no copyThe copy is a new recordat the end of its partitionThe delayed copy is held in the process:a crash before the timer fires loses it
The diagram as text
  1. A record x-ruststream-retry-count says which attempt it is → The handler returns retry_after(delay), or retry() under a declaration: handled
  2. The handler returns retry_after(delay), or retry() under a declaration → max_attempts: is this attempt below the cap?: a retry
  3. max_attempts: is this attempt below the cap? → Below the cap: the original settles, a copy goes to the topic: below
  4. max_attempts: is this attempt below the cap? → At the cap: a copy goes to the dead-letter topic: at the cap
  5. max_attempts: is this attempt below the cap? → At the cap, no dead letter: the record settles, no copy
  6. Below the cap: the original settles, a copy goes to the topic → The copy is a new record at the end of its partition
  7. The delayed copy is held in the process: a crash before the timer fires loses it

Kafka holds no record back and counts no deliveries, so the runtime does both. A retry_after outcome, and a retry() on a registration that declares max_attempts or a dead-letter topic, take this path in every commit mode. The attempt count travels in the copy's x-ruststream-retry-count header, and the first delivery counts as one.

The copy goes to the address the descriptor reports. A KafkaTopic subscription reports its own topic. A KafkaTopics or KafkaPartitions subscription reads a set no single publish reaches, so its registration names the destination, or the app refuses to start.

The copy keeps the record's key, so it lands on the key's partition again, behind the records written since. Every consumer group reading the topic receives the copy, not only the one that retried.