Skip to content

Pulsar: transport-default DLQ/retry precedence + resolve DLQ-sender TODOs (#3186)#3194

Merged
jeremydmiller merged 1 commit into
mainfrom
feat/pulsar-dlq-sender-3186
Jun 22, 2026
Merged

Pulsar: transport-default DLQ/retry precedence + resolve DLQ-sender TODOs (#3186)#3194
jeremydmiller merged 1 commit into
mainfrom
feat/pulsar-dlq-sender-3186

Conversation

@jeremydmiller

Copy link
Copy Markdown
Member

First PR of the Pulsar re-evaluation epic #3176. Fixes #3186.

Resolves the two loose-end TODOs in the Pulsar transport.

Transport-vs-endpoint defaults (PulsarTransport.cs:31 TODO)

The transport-level DeadLetterTopic/RetryLetterTopic are now real defaults that each endpoint inherits unless it sets its own, resolved per-endpoint-override-wins through the new PulsarEndpoint.EffectiveDeadLetterTopic / EffectiveRetryLetterTopic — mirroring the Kafka transport-default + per-endpoint override shape. UsePulsar(...) now returns a PulsarConfiguration exposing DeadLetterQueueing(...) / RetryLetterQueueing(...) to set the defaults. PulsarListener keys native DLQ/retry enablement and the resolved topic names off the effective values, so a transport default actually takes effect.

DLQ sender (PulsarEndpoint.cs:118 TODO) — resolved by decision

The stub looked like missing functionality, but native dead-lettering is already fully handled by PulsarListener (ISupportDeadLetterQueue), which produces to the {topic}-DLQ topic with the native reconsume metadata and retry-letter-topic chaining; WolverineStorage mode uses the durable store.

Implementing a competing endpoint-level TryBuildDeadLetterSender actively regresses this: BufferedReceiver/DurableReceiver then report NativeDeadLetterQueueEnabled, and MessageContext.tryGetDeadLetterQueue prefers the receiver (_channel) over the listener — hijacking the richer native path and dropping the reconsume metadata. This was caught by PulsarNativeReliabilityTests (differential: passes on main, failed with the naive sender impl). So TryBuildDeadLetterSender intentionally defers to the base, documented inline.

DeadLetterStorage now reports Native when a native DLQ is in effect (for monitoring).

Tests

  • New unit tests: effective-topic precedence (transport default vs per-endpoint override) for both DLQ and retry, and TryBuildDeadLetterSender is always false (native owned by the listener; WolverineStorage by the durable store).
  • Native DLQ end-to-end + exception headers stays covered by the existing PulsarNativeReliabilityTests.

Note: the PulsarNativeReliabilityTests retry-delay assertions are timing-flaky on a loaded/fresh broker (a different one intermittently fails per combined run; each passes in isolation). Not introduced by this PR.

🤖 Generated with Claude Code

…default TODOs (#3186)

Resolves the two loose-end TODOs in the Pulsar transport.

- Transport-vs-endpoint defaults (PulsarTransport.cs): the transport-level DeadLetterTopic
  and RetryLetterTopic are now true defaults that each endpoint inherits unless it sets its
  own, resolved per-endpoint-override-wins via the new
  PulsarEndpoint.EffectiveDeadLetterTopic / EffectiveRetryLetterTopic (mirrors the Kafka
  transport-default + per-endpoint override shape). UsePulsar(...) now returns a
  PulsarConfiguration exposing DeadLetterQueueing(...) / RetryLetterQueueing(...) to set the
  defaults. The listener now keys native DLQ/retry enablement and topic names off the
  effective values, so a transport default actually takes effect.

- DLQ sender (PulsarEndpoint.cs): the stub is resolved by decision rather than a competing
  implementation. Pulsar native dead-lettering is owned by PulsarListener
  (ISupportDeadLetterQueue), which produces to the {topic}-DLQ topic WITH the native reconsume
  metadata and retry-letter-topic chaining; WolverineStorage mode uses the durable store.
  Returning an endpoint-level sender here makes BufferedReceiver/DurableReceiver report
  NativeDeadLetterQueueEnabled, which MessageContext.tryGetDeadLetterQueue prefers over the
  listener — hijacking the richer native path and dropping the reconsume metadata (caught by
  PulsarNativeReliabilityTests). So TryBuildDeadLetterSender intentionally defers to the base.

- DeadLetterStorage now reports Native when a native DLQ is in effect (for monitoring).

Tests: precedence + effective-topic resolution and TryBuildDeadLetterSender-always-false unit
tests added; existing PulsarNativeReliabilityTests continue to cover native DLQ end-to-end
with exception headers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jeremydmiller
jeremydmiller merged commit b751825 into main Jun 22, 2026
46 of 48 checks passed
This was referenced Jun 23, 2026
This was referenced Jul 9, 2026
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.

Pulsar: finish DLQ sender + resolve transport-vs-endpoint default TODOs

1 participant