Skip to content

feat: DLQ support for Azure Storage Queues#53

Merged
markt-sublime merged 11 commits into
masterfrom
mt.asq-dlq-insertion-logic
May 12, 2026
Merged

feat: DLQ support for Azure Storage Queues#53
markt-sublime merged 11 commits into
masterfrom
mt.asq-dlq-insertion-logic

Conversation

@markt-sublime

Copy link
Copy Markdown
Contributor

Support configuring a DLQ for our Azure Storage Queue brokers. Config requires a queue client bound to the destination URL, accepts a max receive count (defaults to 10), and accepts a queue TTL (defaults to 30 days). If configured, consumeOne will (upon seeing a message with a dequeue count larger than the max receive count) insert the message using the DLQ client, and upon successful insertion delete from the source queue (does not delete on failed insertion). This will insert duplicate messages into the DLQ on successful insertion followed by failed delete, which in practice should be relatively harmless.

markt-sublime and others added 11 commits May 7, 2026 16:40
When AzureConfig.DLQ is set, messages whose DequeueCount exceeds
MaxReceives (default 10) are enqueued to the DLQ and deleted from the
source queue before processing is attempted, mirroring SQS redrive
semantics. Infra failures (DLQ enqueue or source delete) are logged and
return nil so transient errors do not exit the consume loop.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Covers the seven cases from the plan: below/above threshold, enqueue
failure with retry, source-delete failure with retry (verifying the
at-least-once duplicate), DLQ disabled, and the MaxReceives/DLQTTL
defaults. Retry cases swap the failing mock mid-test to confirm the
message actually lands in the DLQ on the subsequent attempt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Avoids the int32→int64 cast in the DequeueCount comparison.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
On JSON decode failure, only delete from source once DequeueCount reaches
maxReceiveCountBeforeDelete (15), mirroring the SQS broker. Below that
threshold the message is left on the queue so a configured DLQ (threshold
10) can catch it first. Above the threshold it is deleted as a last resort.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Negative values (e.g. from misconfigured callers) now fall through to
defaults rather than being used as-is. time.Duration is int64 so it can
be negative; int64 MaxReceives could also be set negatively by mistake.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TestNew_DLQ_Defaults (internal) exercises the zero-value defaulting in
New() itself rather than via the test helper. The redundant DefaultTTL
test is removed; a TTL assertion is added to the existing above-threshold
happy-path test instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DLQ tests now use a valid JSON body (validDLQTaskBody const) instead of
invalid JSON. Above-threshold tests pass nil processor since the DLQ path
returns before decode. The disabled-DLQ test uses a countingProcessor and
a registered task name to assert normal processing ran, replacing the
IgnoreWhenTaskNotRegistered workaround. Decode-failure coverage remains in
the dedicated InvalidJSON_* tests in the internal file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…reshold

The old name implied the threshold applied generally; the new name makes
clear it only governs deletion of messages that failed JSON decoding.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each DLQ test now wires a countingProcessor and asserts whether the task
was processed. Above-threshold cases assert count=0 (DLQ path returns
before decode). BelowThreshold and DefaultMaxReceives register the task
and assert count=1 to confirm normal processing ran.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ransition

Single test exercises count=10 (processes normally) then count=11 (redrives
to DLQ) using an explicit maxReceives=10; the zero-default is already
covered by TestNew_DLQ_Defaults in the internal file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@markt-sublime markt-sublime merged commit 219e0ff into master May 12, 2026
3 checks passed
@markt-sublime markt-sublime deleted the mt.asq-dlq-insertion-logic branch May 12, 2026 22:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants