Settlement
What ack, drop, retry and retry_after become on Pulsar: acknowledgements and redeliveries
the broker counts toward the client's dead-letter policy.
The diagram as text
- Handler: ack(); retry(); retry_after(d); drop(); no outcome
- The crate calls: ack_with_id; nack_with_id; waits d, then nack_with_id; ack_with_id; nothing; the client's timer acts later; The count rises on Shared and KeyShared subscriptions only: the other types keep none
- Broker: acknowledged, never sent again; sent again at once, count + 1; sent again after d, count + 1; acknowledged: gone; sent again after ack_timeout, count + 1
- ack() → ack_with_id
- ack_with_id → acknowledged, never sent again
- retry() → nack_with_id
- nack_with_id → sent again at once, count + 1
- retry_after(d) → waits d, then nack_with_id
- waits d, then nack_with_id → sent again after d, count + 1
- drop() → ack_with_id
- ack_with_id → acknowledged: gone
- no outcome → nothing; the client's timer acts later
- nothing; the client's timer acts later → sent again after ack_timeout, count + 1
Every outcome is a call on the subscription's client consumer, which its driver task makes. ack
and drop acknowledge the message, because Pulsar has no command that rejects one for good: a
message that should not come back is acknowledged, and one that keeps failing reaches the
dead-letter topic by the cap. retry asks the broker to deliver it again at once.
retry_after(d) holds the delivery unacknowledged in the service for d, then sends the same
negative acknowledgement. The wait runs on the runtime the broker connected on. A delay not shorter
than the subscription's ack_timeout is
refused, because the client's timer would bring the message back first.
A delivery no handler settles stays unacknowledged. With an ack_timeout, the client asks for it
again once the timeout passes, as Acknowledgement draws. Without
one, it comes back only when the consumer disconnects.
Retries
The diagram as text
- A delivery the broker counts its redeliveries → The handler returns retry(), or retry_after(d): handled
- The handler returns retry(), or retry_after(d) → retry(): negative acknowledgement at once
- The handler returns retry(), or retry_after(d) → retry_after(d): held in the process for d, then the same
- retry(): negative acknowledgement at once → The broker delivers it again count + 1
- retry_after(d): held in the process for d, then the same → The broker delivers it again count + 1
- The broker delivers it again count + 1 → The client's dead-letter policy: has the count reached max_attempts?
- The client's dead-letter policy: has the count reached max_attempts? → Below the cap: the handler gets it again: below
- The client's dead-letter policy: has the count reached max_attempts? → At the cap: produced to the dead-letter topic, original acked: at the cap
- A cap on an Exclusive or a Failover subscription refuses to start: neither counts redeliveries
- A stop during the wait loses the wait, not the message: the message stays unacknowledged
Nothing is published to bring a message back: the broker delivers the same message again and
counts it. A registration's max_attempts and
dead-letter topic become the client's dead-letter policy. The client delivers a message while its
count is below the cap, so the handler sees it max_attempts times. At the cap the client produces
it to the dead-letter topic and acknowledges the original.
The two settings go together: a registration that declares one without the other refuses to
start, and so does a cap on an Exclusive or a Failover subscription, whose messages always
arrive with a count of zero.
The count is the client's own, and a delivery does not report it. A process that stops during a
retry_after wait loses the wait and not the message: it is still unacknowledged, and the broker
delivers it again after the acknowledgement timeout or once the consumer's connection is gone.