Fix outbox stuck with multi-tenant RabbitMQ and durable messaging#2364
Merged
jeremydmiller merged 1 commit intomainfrom Mar 28, 2026
Merged
Fix outbox stuck with multi-tenant RabbitMQ and durable messaging#2364jeremydmiller merged 1 commit intomainfrom
jeremydmiller merged 1 commit intomainfrom
Conversation
…le outbox TenantedSender implemented ISenderRequiresCallback, which caused SendingAgent to pick the sendWithCallbackHandlingAsync code path. That path assumes the inner sender calls back on success via the registered ISenderCallback. But RabbitMqSender (and all other transport senders used with TenantedSender) does NOT implement ISenderRequiresCallback — it is a simple fire-and-forget sender. So after a successful send through a tenanted endpoint, DurableSendingAgent.MarkSuccessfulAsync() was never called, the outbox entry was never deleted, and the durability agent kept re-sending the message every 5 seconds. The fix removes ISenderRequiresCallback from TenantedSender. This makes SendingAgent use sendWithExplicitHandlingAsync instead, which explicitly calls MarkSuccessfulAsync after a successful send, properly cleaning up the outbox entry. This also fixes the Dispose() method to dispose ALL IDisposable inner senders, not just ISenderRequiresCallback ones. Fixes #2361 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This was referenced Mar 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
TenantedSenderimplementedISenderRequiresCallback, which causedSendingAgentto usesendWithCallbackHandlingAsync. That code path assumes the inner sender calls back on success viaISenderCallback, butRabbitMqSender(and all other transport senders used withTenantedSender) does NOT implementISenderRequiresCallback. SoDurableSendingAgent.MarkSuccessfulAsync()was never called after a successful send, the outbox entry was never deleted, and the durability agent re-sent messages every 5 seconds causing duplicate envelope exceptions.ISenderRequiresCallbackfromTenantedSender. This makesSendingAgentusesendWithExplicitHandlingAsyncwhich explicitly callsMarkSuccessfulAsync(envelope)after successful sends, properly deleting the outbox entry. Also fixesDispose()to dispose allIDisposableinner senders (not justISenderRequiresCallbackones).TenantedSenderwith durable outbox — RabbitMQ, NATS, and Azure Service Bus multi-tenancy configurations.Fixes #2361
Test plan
Bug_2361_outbox_stuck_with_tenanted_brokerpasses (was failing before fix — 2 stuck outbox messages, now 0)TenantedSenderTestspass (unit tests forTenantedSenderbehavior)Bug_475_durable_outbox_sending_out_of_orderpasses (durable outbox regression)Bug_2304_conventional_routing_ignores_durable_outbox_policypassesBug_1594_ReplayDeadLetterQueueandBug_DLQ_NotSavedToDatabase)🤖 Generated with Claude Code