Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
name: Conformance suite in sync
runs-on: ubuntu-latest
env:
BQ_CONFORMANCE_VERSION: 1.6.0
BQ_CONFORMANCE_VERSION: 1.8.0
# Integrity pin for fetch.sh (sha256 of conformance/fetch.sh at the pinned tag).
BQ_FETCH_SH_SHA256: 8bb6ba567b288fb0ac71a70fd17041b2503df119e01998d76fdad372570bf849
steps:
Expand Down
40 changes: 40 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,46 @@ The envelope wire format is versioned separately by `meta.schema_version`

## [Unreleased]

## [1.4.0] - 2026-10-26

MINOR (ADR-0037): the consumer gains an opt-in BabelQueue DLQ and settles poison bodies and version-gated
deliveries on their first delivery. The envelope is unchanged (`schema_version` stays `1`).

### Added

- `SqsConsumer.Builder.deadLetterQueueUrl(url)` (opt-in, like the .NET `DeadLetterQueueUrl` and the Node
`deadLetterQueueUrl`): the `<queue>-dlq` queue (a FIFO one is `<queue>-dlq.fifo`; the consumer sends it
the queue name as message group and the SQS message id as deduplication id). "Dead-lettering on" means this
URL is set. The queue must exist.
- With a DLQ queue set, the `dead_letter` unknown-URN strategy now writes the envelope, annotated with a
`dead_letter` block (reason `unknown_urn`), to it and deletes the message only after the send succeeded.
Without one it still degrades to `delete`.

### Changed (behaviour)

- **Poison body** (not JSON, not an envelope, no valid identity, or an unsupported `meta.schema_version`):
never handed to a handler and never retried. With a DLQ queue it is sent as the ADR-0035 record
(`PoisonRecord`, `reason: "poison"`, `original_queue` = this queue) and deleted **only after** the send
succeeded; a failed send leaves it unsettled and is reported. Under an explicit `delete` strategy it is
deleted. With no DLQ queue nothing changes: it is reported and left to the queue's `RedrivePolicy`.
- **Version-gated delivery** (`bq-schema-version` present, not blank, not `1`): settled on the first delivery,
body never decoded, no handler or hook, no retry. With a DLQ queue the **raw body, byte for byte** (no
`dead_letter` block) is sent to it and the message deleted only after the send succeeded (a failed send
leaves it unsettled, so the write is retried on redelivery); under an explicit `delete` strategy it is
deleted (**dropped**); with no DLQ queue nothing changes (reported, not deleted, native redrive).
- The poison and version-gate rules read only the **explicitly configured** `unknownUrnStrategy`
(`fail` when none is set). An `onUnknownUrn` hook, and the `delete` it implies for an unknown URN, no
longer reach them: a hook-only consumer never drops a poison or version-gated delivery silently (owner
decision, 2026-10-25: the poison route reads only the explicitly configured strategy, like the version
gate). The unknown-URN path is unchanged.
- Built against `babelqueue-core` 1.13.0 (`PoisonRecord`), unchanged.
- Vendored the cross-SDK conformance suite v1.8.0 (`version_gate_sqs`, `poison_dlq` runners added) and moved
the CI pin to it.

### Docs

- The BabelQueue DLQ on SQS is `<queue>-dlq` (FIFO `<queue>-dlq.fifo`), not `<queue>.dlq`: an SQS queue name cannot contain a dot (ADR-0037 decision 7). README and Javadoc only; `deadLetterQueueUrl` is an explicit URL, so no code change.

## [1.3.0] - 2026-10-24

### Added
Expand Down
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ as the PHP/Laravel, Python, Go, Node and .NET SDKs. Implements
<dependency>
<groupId>com.babelqueue</groupId>
<artifactId>babelqueue-sqs</artifactId>
<version>1.3.0</version>
<version>1.4.0</version>
</dependency>
```

Expand Down Expand Up @@ -70,8 +70,17 @@ delete — observe via `onError` / `onUnknownUrn`. The envelope is unchanged
**Schema-version gate (§3.7).** Before decoding the body the consumer reads the
`bq-schema-version` attribute. Missing or blank (empty / ASCII whitespace only) → normal decode.
Any other value that is not exactly `1` (no trimming — `"2"`, `"x"`, `" 1"`) → the body is **not**
decoded, the handler is not called, `onError` is notified with a `null` envelope, and the message is
left undeleted so SQS redrives it to the DLQ.
decoded, the handler is not called, no hook runs, `onError` is notified with a `null` envelope, and the
message is settled on its **first** delivery (ADR-0037): with a `deadLetterQueueUrl` the body is sent to
`<queue>-dlq` **exactly as received** (no `dead_letter` block) and deleted after the send succeeded (a failed
send leaves it for redelivery); under an explicit `delete` strategy it is deleted; without a DLQ queue it is
left undeleted so SQS redrives it (`RedrivePolicy`). Only an explicitly set `unknownUrnStrategy` is read — an
`onUnknownUrn` hook never drops a gated message.

**Poison bodies (ADR-0037).** A body that is not a valid envelope (not JSON, no identity, unsupported
`meta.schema_version`) never reaches a handler and is never retried. With a `deadLetterQueueUrl` it is written
to the DLQ as the ADR-0035 record (`reason: "poison"`; the original body kept, spliced or under `raw`) and
deleted after the send succeeded; without one it is reported and left to the queue's `RedrivePolicy`.

```java
SqsConsumer.builder(sqs, url)
Expand All @@ -81,6 +90,7 @@ SqsConsumer.builder(sqs, url)
//.retryBackoff(5).retryBackoffMultiplier(2).retryBackoffMax(300).retryJitter("full")
.unknownUrnStrategy(UnknownUrnStrategy.RELEASE) // fail | delete | release | dead_letter
.unknownUrnReleaseDelaySeconds(60) // default 0
.deadLetterQueueUrl(dlqUrl) // opt-in BabelQueue DLQ (<queue>-dlq / <queue>-dlq.fifo)
.build();
```

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.babelqueue</groupId>
<artifactId>babelqueue-sqs</artifactId>
<version>1.3.0</version>
<version>1.4.0</version>
<packaging>jar</packaging>

<name>BabelQueue Amazon SQS</name>
Expand Down
Loading
Loading