You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 31ccb70
Browse filesBrowse the repository at this point in the historyBrowse files
* Leave SQS and ASB poison to the native mechanism when dead-lettering is off (ADR-0037)
With dead-lettering off, version-gated and poison bodies on SQS and Azure Service Bus are no longer dropped; they are left to RedrivePolicy or MaxDeliveryCount. AsbTransport gains a narrow Abandoner used only when a version-gate DLQ write fails. Pin conformance v1.8.0 and run the version_gate sections.
* Default the SQS dead-letter queue to <queue>-dlq (ADR-0037)
SQS queue names cannot contain a dot, so the old <queue>.dlq default could never exist on AWS. The SQS transport now defaults to <queue>-dlq and places any suffix before .fifo; an explicit dead-letter queue still wins. Other transports keep .dlq.
* Release v1.24.0
-*Version-gated message* (an unsupported `bq-schema-version`), `dead_letter` off and a strategy
25
+
other than `DELETE` or `DEAD_LETTER`: it used to be acknowledged and dropped. It is now **not**
26
+
acknowledged. SQS: no `DeleteMessage`; visibility expiry redelivers it (the gate rejects it
27
+
again) and the queue's `RedrivePolicy` moves it to the native DLQ after `maxReceiveCount`. Azure
28
+
Service Bus: an explicit native `DeadLetter` (reason `unsupported schema version`) into
29
+
`$DeadLetterQueue` instead of `Complete`.
30
+
-*Poison body* (invalid JSON or an identity-less object), `dead_letter` off and a strategy other
31
+
than `DELETE` or `DEAD_LETTER`: it used to take the bounded `FAIL` path (re-publish an empty
32
+
envelope up to `max_attempts`, then drop). On SQS and Azure Service Bus it is now settled
33
+
natively **on the first delivery** exactly as above (Azure Service Bus reason `poison`): no
34
+
re-publish, no retry, no ack, body unchanged, the event is logged. Every other transport
35
+
(Pulsar, Artemis, AMQP, Kafka, Redis, memory) keeps its behaviour; `DELETE` (ack and drop),
36
+
`dead_letter=True` and the `DEAD_LETTER` strategy (the raw record, then ack) are unchanged on
37
+
SQS and Azure Service Bus too.
38
+
- A deployment that relied on SQS/Azure Service Bus silently dropping these messages now needs a
39
+
`RedrivePolicy` (SQS) or accepts them in `$DeadLetterQueue` (Azure Service Bus), or sets
40
+
`dead_letter=True` / `on_unknown_urn=DELETE`.
41
+
42
+
### Fixed
43
+
-**SQS: the default dead-letter queue name is now `<queue>-dlq` (FIFO `<queue>-dlq.fifo`) (ADR-0037
44
+
decision 7).** The old default `<queue>.dlq` was never a valid Amazon SQS queue name (a name may hold only
45
+
alphanumerics, hyphens and underscores, plus one `.fifo` suffix), so a DLQ write by default could not find a
46
+
queue on real SQS. `dead_letter_suffix` now defaults to `None` ("not set"); unless `dead_letter_queue` is set
47
+
the runtime asks the transport's new optional `DeadLetterNamer` capability
48
+
(`dead_letter_queue_name(queue, suffix)`, exported from `babelqueue`), which `SqsTransport` implements. The
49
+
runtime also hands it an explicit `dead_letter_suffix`; on a FIFO queue the `.fifo` suffix always stays last,
50
+
an explicit suffix included: `orders.fifo` with `dead_letter_suffix="-x"` is `orders-x.fifo`, never
51
+
`orders.fifo-x` (an SQS FIFO name must end in `.fifo`; before, that explicit suffix produced the uncreatable
52
+
`orders.fifo-x`). An explicit queue name behaves as before; every other transport keeps `<queue>.dlq` (the
53
+
attribute `App.dead_letter_suffix` is `None` instead of `".dlq"` until set). Deployments
54
+
that relied on the SQS default must name the DLQ `<queue>-dlq`, or pass `dead_letter_suffix=".dlq"` /
55
+
`dead_letter_queue=` to keep the old name. An explicitly empty `dead_letter_suffix=""` now counts as "no suffix" on SQS (`-dlq`); before, it made the DLQ the source queue itself.
56
+
57
+
### Added
58
+
-**`babelqueue.transport.NativeSettler`**, an optional `Transport` capability
59
+
(`settle_native(message, reason, description)`). With the DLQ off the runtime calls it instead of
60
+
acknowledging a version-gated message (and, on the poison route, instead of the bounded retry) on
61
+
a transport that offers it; the decision follows the capability, never the transport's name.
62
+
`SqsTransport.settle_native` is a deliberate no-op on the wire; `AsbTransport.settle_native`
63
+
dead-letters natively (falling back to `abandon_message` for a receiver without
64
+
`dead_letter_message`). Transports without it are unchanged.
65
+
-**`babelqueue.transport.Abandoner` and `AsbTransport.abandon`.** When the dead-letter write of a
66
+
*version-gated* message fails, Azure Service Bus now `Abandon`s the peek-lock (broker-bindings 4.5,
67
+
"A failed write `Abandon`s it") so the broker redelivers it at once, instead of leaving it locked until
68
+
the lock lapses (up to 5 minutes). Only that branch abandons (owner decision 2026-10-25): `AsbTransport`
69
+
is deliberately **not** a `Rewinder`, so a graceful-shutdown release and a failed retry re-publish keep
70
+
their previous handling (a copy is re-published and the original completed), and a failed dead-letter
71
+
write of a poison body is still left to its lock. Tests pin all three. The `version_gate_asb` runner
72
+
asserts the `Abandon` on its `unsettled` rows.
73
+
- Conformance `version_gate_*` runners (`version_gate_pulsar` / `version_gate_artemis` through the
74
+
runtime, `version_gate_sqs` / `version_gate_asb` through the real transports) and their entries in
75
+
the results writer.
76
+
12
77
## [1.23.0] - 2026-10-25
13
78
14
79
Implements ADR-0036 (post-R1 parity) decisions 2, 4, 5 and 6. A MINOR: every change is a
Copy file name to clipboardExpand all lines: README.md
+15-3Lines changed: 15 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -158,8 +158,13 @@ untrimmed; Azure Service Bus and Artemis also accept the integer `1`). A message
158
158
anything else is not decoded: it reaches the runtime on the reserved route
159
159
`babelqueue:unsupported-schema-version:<v>` (the version percent-escaped) and is **never
160
160
retried** — it goes straight to the dead-letter queue (the incoming body, byte-identical,
161
-
with no `dead_letter` block) when `dead_letter` is enabled, and is dropped otherwise, whatever `max_attempts` is.
162
-
It does not depend on a broker `RedrivePolicy`. If the dead-letter write fails the message is
161
+
with no `dead_letter` block) when `dead_letter` is enabled (or `on_unknown_urn=DEAD_LETTER`), and
162
+
`on_unknown_urn=DELETE` drops it, whatever `max_attempts` is. With `dead_letter` off, **SQS and Azure
163
+
Service Bus no longer drop it** (ADR-0037): it is left to the broker's native mechanism and logged
164
+
(SQS: no `DeleteMessage`, so visibility expiry redelivers it — the gate rejects it again — and the
165
+
queue's `RedrivePolicy` moves it to the native DLQ after `maxReceiveCount`; Azure Service Bus: an
166
+
explicit native `DeadLetter` with reason `unsupported schema version` into `$DeadLetterQueue`
167
+
instead of `Complete`). Kafka, Artemis and Pulsar still acknowledge and drop it. If the dead-letter write fails the message is
163
168
left unacknowledged (an error is logged), is never retried and never reaches a handler; the
164
169
dead-letter write is attempted again when the broker brings it back, which depends on the
165
170
broker:
@@ -199,10 +204,17 @@ a warning. A **per-message delay** at publish time is
199
204
standard queue; a larger value, or any per-message delay on a FIFO queue, raises
200
205
`BabelQueueError` instead of being truncated or dropped).
201
206
207
+
**Poison bodies.** A body that does not decode, or has no identity, is never retried by the SDK
208
+
(ADR-0037): `dead_letter=True` writes the ADR-0035 record to the DLQ (`<queue>-dlq` on SQS, `<queue>.dlq` elsewhere) and then deletes it,
209
+
`on_unknown_urn=DELETE` deletes it, and with the DLQ off it is **not deleted and not re-sent**: the
210
+
transport's `settle_native` capability leaves it to SQS on the first delivery (visibility expiry,
211
+
then the queue's `RedrivePolicy`; the event is logged). Azure Service Bus does the same with a native
212
+
`DeadLetter` (reason `poison`). Every other transport keeps the bounded retry up to `max_attempts`.
213
+
202
214
**Poison-loop risk:** a message that always fails is redelivered at once until
203
215
`max_attempts` is reached. If the receive count is unavailable the SDK cannot
204
216
advance attempts at all. **Configure a native `RedrivePolicy`**
205
-
(`maxReceiveCount` ≥ `max_attempts`, pointing at `<queue>.dlq`) on every SQS
217
+
(`maxReceiveCount` ≥ `max_attempts`, pointing at `<queue>-dlq`, the SQS default DLQ name: FIFO `<queue>-dlq.fifo`, because an SQS queue name cannot contain a dot; `dead_letter_queue` overrides it, and `dead_letter_suffix` replaces `-dlq`, placed before `.fifo` on a FIFO queue) on every SQS
206
218
queue as the broker-side backstop, and set a non-zero `retry_backoff` if the
0 commit comments