Skip to main content

Settlement

What ack, retry, drop and retry_after become on a Redis stream, a reliable list, a simple list and Pub/Sub.

The commands each outcome sends, per form: a stream, a claiming stream, a reliable list, and the forms with no acknowledgementOutcomeStreamClaimingReliableNo ackackXACKXACKLREMnotsupportedretryXADD copy,then XACKstayspendingLPUSH,then LREMnotsupporteddropXACKXACKLREMnotsupported
The diagram as text
  1. Outcome: ack; retry; drop
  2. Stream: XACK; XADD copy, then XACK; XACK
  3. Claiming: XACK; stays pending; XACK
  4. Reliable: LREM; LPUSH, then LREM; LREM
  5. No ack: not supported; not supported; not supported

The runtime settles each delivery with the outcome its handler returned, and the crate maps the outcome onto the form's own acknowledgement. A stream acknowledges with XACK in the subscription's group. A reliable list removes the entry from its processing list with LREM. A simple list and Pub/Sub have nothing to acknowledge, and settling there answers AckError::Unsupported. The settle commands go out on the pool, not on the subscription's read client.

A retry on a stream appends a copy of the entry to the same stream, then acknowledges the original. A crash between the two leaves a duplicate, not a loss. A claiming subscription retries through the pending entries list instead. It appends and acknowledges nothing, and its next read claims the entry back once it has been idle for min_idle, under the same id, with a delivery count one higher. A reliable list pushes the entry back to its queue before it removes it from the processing list.

Retries with a delay​

The three ways a stream subscription serves retry_after: a copy in the process, a copy in a Redis sorted set, or the entry left pendingRuntime copyDelay queueClaimingretry_after(delay) on a stream entryXACK theoriginalCopy held inthe process:lost on a crashXADD the copywhen the timerfiresZADD the copy,then XACKIn Redis,scored by itsdue timeA read sweepsit: XADD backto the streamStays inthe pendingentries listClaimed backaftermin_idle
The diagram as text
  1. Runtime copy: XACK the original; Copy held in the process: lost on a crash; XADD the copy when the timer fires
  2. Delay queue: retry_after(delay) on a stream entry; ZADD the copy, then XACK; In Redis, scored by its due time; A read sweeps it: XADD back to the stream
  3. Claiming: Stays in the pending entries list; Claimed back after min_idle
  4. retry_after(delay) on a stream entry → XACK the original
  5. retry_after(delay) on a stream entry → ZADD the copy, then XACK
  6. retry_after(delay) on a stream entry → Stays in the pending entries list
  7. XACK the original → Copy held in the process: lost on a crash
  8. Copy held in the process: lost on a crash → XADD the copy when the timer fires: delay
  9. ZADD the copy, then XACK → In Redis, scored by its due time
  10. In Redis, scored by its due time → A read sweeps it: XADD back to the stream: due
  11. Stays in the pending entries list → Claimed back after min_idle: idle

Redis has no per-entry delay, so a stream subscription serves retry_after in one of three ways. By default the runtime acknowledges the original and holds a copy, which it publishes back to the stream when the delay is up. The copy's retry count is one higher. A crash before the timer fires loses it.

delayed_retry with DelayedRetry::DurableZset keeps the copy in Redis. The copy is added to a sorted set, scored by its due time, and then the original is acknowledged. Each read first moves the copies that are due back onto the stream with XADD, at most 128 per read, so a delay is honoured to within one block interval. The scores are wall-clock milliseconds, so the clocks of the services that share a queue should agree.

A claiming subscription leaves the entry pending, and its next read claims it back once min_idle has passed. A delay shorter than min_idle waits min_idle.

A list and a Pub/Sub subscription take the runtime's copy, which goes back to the list or the channel. A pattern subscription names the destination of its copies at its mount site.

A max_attempts cap counts attempts with one of two numbers, and the form decides which. The claiming and reclaim modes count with the server's delivery count from the pending entries list. Every other form counts with the retry-count header that the copies carry, which the delay queue raises on every round.