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
UnitOfWorkBehavior<TMessage>, InboxBehavior<TMessage> and OutboxProcessingBehavior<TMessage> are all ICommandBehavior<TMessage>. The transaction and the inbox's deduplication therefore live inside each Command's pipeline, not around the delivery that the broker handed us. That placement is the root cause of a family of defects:
when a broker-delivered Event's handler dispatches several Commands, only the first is deduplicated; later ones are at-least-once on retry or redelivery (ADR-0041 R1);
a direct call to the public DispatchReceivedMessageAsync bypasses the per-attempt reset (ADR-0041 R2);
broker-delivered Events are not deduplicated at all.
The standard idempotent receiver / transactional inbox pattern (as NServiceBus's outbox and MassTransit's transactional inbox apply it): the reliability envelope wraps the handling of one delivery.
For each receive attempt of a delivery:
Open the unit of work for the delivery.
Check and claim the delivery's message id in the Inbox; if it is already handled, stop.
Run every handler: the delivered Command or Event and every Command dispatched in-process during it.
Record the message id as handled and commit it atomically with all the handler work and any outbox rows.
A failed attempt rolls back its claim with its work, so a retry starts clean. Nested and fan-out dispatch need no special rule, and Events are deduplicated too.
BrokeredMessageReceiver already creates one TransactionContext per delivery, which is the natural seam.
ADRs to amend or supersede: ADR-0006 (behavior order / two-tier reliability), ADR-0025 (unit of work vs retrying execution strategy), ADR-0041 (interim Delivery Entry).
Registration surface: WithBehavior(typeof(InboxBehavior<>)), EF WithInboxBehavior<TContext>(), the Cosmos inbox registration — how they map to a delivery-level envelope, and what that means for versioning and consumer code.
Commands dispatched outside any delivery (no broker context): what, if anything, provides a unit of work for them.
Non-transactional resources inside a handler remain at-least-once; state that explicitly.
This needs a planning session before implementation.
Problem
UnitOfWorkBehavior<TMessage>,InboxBehavior<TMessage>andOutboxProcessingBehavior<TMessage>are allICommandBehavior<TMessage>. The transaction and the inbox's deduplication therefore live inside each Command's pipeline, not around the delivery that the broker handed us. That placement is the root cause of a family of defects:DispatchReceivedMessageAsyncbypasses the per-attempt reset (ADR-0041 R2);Target design
The standard idempotent receiver / transactional inbox pattern (as NServiceBus's outbox and MassTransit's transactional inbox apply it): the reliability envelope wraps the handling of one delivery.
For each receive attempt of a delivery:
A failed attempt rolls back its claim with its work, so a retry starts clean. Nested and fan-out dispatch need no special rule, and Events are deduplicated too.
BrokeredMessageReceiveralready creates oneTransactionContextper delivery, which is the natural seam.Scope and open decisions
Chatter.MessageBrokers,Chatter.MessageBrokers.Reliability.EntityFramework,Chatter.MessageBrokers.Reliability.Cosmos(including Cosmos document tier silently discards a nested participant command's writes #538's document-tier design).WithBehavior(typeof(InboxBehavior<>)), EFWithInboxBehavior<TContext>(), the Cosmos inbox registration — how they map to a delivery-level envelope, and what that means for versioning and consumer code.This needs a planning session before implementation.
Related
#534, #538, ADR-0006, ADR-0025, ADR-0041.