KubeMQ
ConnectorsAMQP 1.0How-to guides

Reliability

AMQP 1.0 delivery guarantees on KubeMQ — settlement modes, delivery-state to Ack/NAck mapping, at-least-once vs at-most-once, and durable subscriptions.

AMQP 1.0 reliability is settlement modes + delivery state, not publisher confirms and not numeric reason codes. This guide is the practical playbook for getting the delivery guarantee you want from the KubeMQ AMQP 1.0 connector: which settlement modes exist (and which is rejected), how each delivery-state outcome maps to a KubeMQ queue action, how durable subscriptions resume, what happens on disconnect (nothing is lost), and why there is no connector dead-letter exchange.

For the credit machinery that governs when deliveries arrive — and the pre-settled-pattern data-loss footguns — see Flow control.

Settlement modes — pick your guarantee at ATTACH

Settlement is negotiated when the link attaches.

snd-settle-mode (the produce / out path)

RequestedServer behaviorGuarantee
settledhonored — each outbound TRANSFER carries settled=true and the server acks it immediately at sendat-most-once (pre-settled)
unsettled / mixed / absentserver uses unsettled — sends deliveries unsettled, tracks them, waits for your DISPOSITIONat-least-once (default)
  • At-least-once (default): leave snd-settle-mode unset (or unsettled). The server keeps each delivery tracked until you settle it; if you never do (you disconnect), it is requeued.
  • At-most-once: request snd-settle-mode=settled. Use this for high-throughput fire-and-forget where occasional loss is acceptable. (Events are always pre-settled regardless — see Events.)

rcv-settle-mode (the consume / in path)

  • The server always replies rcv-settle-mode=first.
  • rcv-settle-mode=second is NOT implemented. Requesting it closes the link with DETACH(amqp:not-implemented) before the link is built. Pin first.

There is no two-stage (second) receiver settlement. Your consumer sends a DISPOSITION with a terminal delivery state and that settles the delivery in one step.

Delivery-state outcomes → KubeMQ actions

When your client is the receiver (consuming a queue), it settles each delivery by sending a DISPOSITION carrying a terminal delivery state. The connector maps that state to a KubeMQ queue AckRange (settle/remove) or NAckRange (requeue):

Client delivery stateKubeMQ actionQueue requestEffect on the message
acceptedsettle / consumeAckRangeremoved from the queue
rejecteddiscardAckRangediscarded; poison handled by the broker MaxReceiveQueue policy — there is NO connector DLX
releasedrequeue to tailNAckRangeredelivered with a grown delivery-count, first-acquirer=false; increments receive-count toward MaxReceiveCount
modified{delivery-failed} / modified{undeliverable-here}requeue to tailNAckRangerequeued (no per-consumer exclusion)
nil state (settled, no outcome)treat as successAckRangeremoved
unknown terminal stateconservatively requeueNAckRangerequeued — never silently dropped
  • A DISPOSITION may cover a first..last delivery-id range; the connector resolves it against the per-link unsettled map, groups by RefTransactionId, and emits one AckRange/NAckRange per group. Re-settling an already-settled id is idempotent.
  • rejected does NOT dead-letter through the connector. It discards (AckRanges) the message; whether a repeatedly-failing message is moved aside is a broker-side MaxReceiveQueue poison policy, not an AMQP-controllable per-link feature.

released / modified increment the receive-count. Every NAck-for-redelivery bumps ReceiveCount, so a message you keep releasing will eventually hit the broker's MaxReceiveCount cap and be removed even though you never rejected it. There is no requeue-without-increment.

Body sections

A message body must be one of two AMQP sections:

  • Data (binary) — the default; multiple Data sections concatenate.
  • AmqpValue (a typed value) — use for typed bodies.

An empty body is valid (it becomes an empty Data body downstream).

AmqpSequence bodies are rejected. Only Data and AmqpValue are supported. A message carrying an AmqpSequence body section gets a rejected DISPOSITION then a DETACH with amqp:not-implemented. Emit Data by default and AmqpValue for typed bodies; never AmqpSequence.

Confirming a produce

When your client is the sender, the server is the receiver and settles your delivery for you:

  • On broker success it emits a settled DISPOSITION(role=receiver, accepted).
  • On failure it emits rejected{condition}: broker-not-ready → amqp:not-allowed; decode error → amqp:decode-error; translate error → amqp:invalid-field; array/broker error → amqp:internal-error.
  • A pre-settled inbound delivery gets no disposition; a failure is dropped, logged, and counted in kubemq_amqp10_transfers_in_dropped_total. If you need to know a produce succeeded, do not pre-settle — send unsettled and read the server's DISPOSITION.

Durable subscriptions

The events-store pattern is a durable, replayable subscription. To consume durably, attach a receiver from events-store/<ch> with terminus expiry-policy = never, a stable container-id, and a stable link Name. On reconnect with the same identity, the subscription resumes where it left off.

The durable identity is derived from your container-id and link name. The practical consequence: to resume, reconnect with the same container-id AND the same link name. Change either and you get a different durable subscription that starts fresh.

Durable subscriptions (and dynamic reply nodes) are node-local: a durable subscription created on node A is not visible from node B. Cluster-wide uniqueness of the durable identity is still enforced, but a durable subscriber must reconnect to the same node to resume — use load-balancer session affinity or a sticky connection.

A durable receiver's start position is set with the link property x-opt-kubemq-start (it applies only to events-store):

x-opt-kubemq-start valueMeaning
"" or new-onlyonly messages published after the subscription starts (the default)
firstreplay from the beginning of stored history
laststart from the most recent stored message
sequence:<n>start at sequence number <n> (1-based, non-negative)
time:<RFC3339|unix-seconds>start at a wall-clock time
time-delta:<seconds>start <seconds> ago from now

There is no "last N by count" — use sequence:, time:, or time-delta: to bound a replay. A malformed value (sequence:abc, time:not-a-time, or an unknown token) is rejected at attach with DETACH(amqp:invalid-field) naming the offending token.

Redelivery and teardown NAck-all

released, modified, and disconnect-with-unsettled all requeue the delivery to the tail. A fresh consumer recovers it. A redelivered copy carries a grown header.delivery-count and first-acquirer=false, so your consumer can detect a redelivery.

On link detach, connection close, or graceful shutdown, every unsettled delivery is NAckRange'd exactly once — returned to the queue tail (the client experiences it as released). A disconnecting at-least-once consumer loses nothing: whatever it had not yet accepted is simply redelivered to the next consumer. This guarantee applies to queue consume links — events and events-store are pre-settled and have their own footguns (see Flow control).

No connector DLX, no visibility timeout

The connector has no dead-letter exchange and no visibility/redelivery timeout:

  • rejected discards via AckRange; it does not route to a dead-letter destination.
  • There is no per-link "make this message invisible for N seconds" verb. Queue receive is destructive, credit-based consume only — no peek, no browse.
  • Poison handling is entirely broker-side: the broker's MaxReceiveQueue / MaxReceiveCount policy removes a message redelivered too many times.

Design your consumer accordingly: accept on success, reject for a permanently-bad message (discarded, with the broker policy as your only poison backstop), and release / modify for a transient failure you want retried — knowing each retry bumps the receive-count toward the broker's cap.

Decision guide

You want…Do this
At-least-once consume (no loss on disconnect)Consume unsettled (default rcv-settle-mode=first); accept on success; rely on teardown NAck-all
At-most-once produce (fire-and-forget)Request snd-settle-mode=settled
Confirm a produce succeededSend unsettled and read the server's DISPOSITION(accepted/rejected)
Retry a transient failurerelease (or modify) — but each retry increments the receive-count
Discard a permanently-bad messagereject — discarded via AckRange; broker MaxReceiveQueue is the poison backstop
Resume after reconnectevents-store + stable container-id + stable link name + expiry-policy=never

Was this page helpful?

On this page