Skip to content

Preserve transport receive properties through message dispatch#7662

Merged
danielmarbach merged 24 commits intomasterfrom
feature/receive-properties
May 5, 2026
Merged

Preserve transport receive properties through message dispatch#7662
danielmarbach merged 24 commits intomasterfrom
feature/receive-properties

Conversation

@danielmarbach
Copy link
Copy Markdown
Contributor

@danielmarbach danielmarbach commented Mar 19, 2026

Extension of:

This pull request adds support for carrying transport-native receive metadata through the NServiceBus receive pipeline and propagating it to outgoing dispatch operations when the incoming message is copied.

Some transports receive messages with native properties that are not headers but still need to be preserved when the message is forwarded, audited, moved to the error queue, or otherwise copied to another transport operation. A concrete example is AWS SQS FIFO metadata such as message group or deduplication information. Until now, there was no first-class way for transports to expose that metadata to the pipeline or for the core to safely carry it forward.

What changed

  • Introduces ReceiveProperties as a read-only representation of transport-specific metadata received with a message.
  • Adds ReceiveProperties to IncomingMessage, making received transport metadata part of the message being processed rather than a separate pipeline extension.
  • Updates MessageContext and ErrorContext so receive properties can be supplied by transports and retained through normal processing and recoverability flows.
  • Updates the routing-to-dispatch stage to copy receive properties from the incoming message onto outgoing dispatch operations when the outgoing operation represents a copy of that incoming message.
  • Ensures explicit user-provided DispatchProperties always take precedence over propagated receive properties.
  • Removes the separate transport-level “property names to preserve” declaration. The transport now makes that decision directly by choosing which properties to include in ReceiveProperties for the received message.
  • Updates the learning transport to dogfood the new mechanism by preserving file metadata through receive, audit, and error queue flows.
  • Adds coverage for receive property propagation, precedence, immutability/read-only behavior, nullable annotations, and learning transport behavior.

Behavior and precedence

Receive properties are now treated as the transport’s per-message preservation decision:

  • If a transport includes a value in ReceiveProperties, it is eligible to be propagated when the incoming message is copied.
  • No separate allow-list needs to be declared on the transport definition.
  • If an outgoing operation already has a dispatch property with the same name, the user-specified dispatch property wins.
  • Receive properties are tied to the IncomingMessage, which avoids accidental propagation to unrelated outgoing messages.

Why this matters

This provides a transport-agnostic foundation for preserving native transport metadata without turning those values into NServiceBus headers or requiring each transport to implement custom forwarding behavior.

It enables advanced transport scenarios, such as preserving SQS FIFO-related metadata, while keeping the decision of what to preserve with the transport that received the message.

@danielmarbach
Copy link
Copy Markdown
Contributor Author

This would allow us to have the transports store and reuse receive properties natively without having to double store temporary headers. ServiceControl can simply "uplift" those into header storage and when returning to the queuing, turn them into dispatch properties again. That means they are available in the tooling and never lost on the wire plus the transports don't need to take extra care anymore to make sure they are not lost. They only need to take care of serializing and deserializing the properties. I checked, and it has no impact on the envelope unwrapper; plus, if needed, those properties should still be accessible from the context bag.

@mauroservienti
Copy link
Copy Markdown
Member

ServiceControl can simply "uplift" those into header storage and when returning to the queuing, turn them into dispatch properties again.

We need to be careful in releasing this until we have proper support in ServiceControl; there is a chance of data loss if we start adopting it.

Comment thread src/NServiceBus.Core/Pipeline/Outgoing/RoutingToDispatchConnector.cs Outdated
@poornimanayar
Copy link
Copy Markdown
Contributor

We need to be careful in releasing this until we have proper support in ServiceControl; there is a chance of data loss if we start adopting it.

@mauroservienti Can you explain this further.

Comment thread src/NServiceBus.Core/Pipeline/Outgoing/RoutingToDispatchConnector.cs Outdated
Comment thread src/NServiceBus.Core/Pipeline/Outgoing/RoutingToDispatchConnector.cs Outdated
@SzymonPobiega SzymonPobiega marked this pull request as ready for review April 24, 2026 09:29
danielmarbach and others added 12 commits April 30, 2026 15:26
…perties and a way for the transport to preserve properties
…transport for demonstration purposes

- LearningTransport now populates ReceiveProperties with FileCreatedAt
- Declares DispatchPropertyNamesToPreserve for auto-propagation to audit/error
- Dogfoods the new ReceiveProperties feature with real integration test
- Fully backward compatible - no wire format changes
@danielmarbach danielmarbach force-pushed the feature/receive-properties branch from 14c0ee9 to b162bbd Compare April 30, 2026 13:47
…DispatchPropertyNamesToPreserve

- ReceiveProperties now implements IReadOnlyDictionary<string, string> with
  static Empty singleton instead of inheriting Dictionary<string, string>
- IncomingMessage gains ReceiveProperties property and 4-param ctor overload
- MessageContext gains ReceiveProperties property, reordered ctor params
  (receiveProperties after body, context last)
- ErrorContext gains receiveProperties overload, same param order convention
- RoutingToDispatchConnector reads from IncomingMessage.ReceiveProperties
  instead of ContextBag
- EnvelopeUnwrapper passes ReceiveProperties from MessageContext to
  IncomingMessage
- Renamed DispatchPropertyNamesToPreserve to ReceivePropertyNamesToPreserve
  on TransportDefinition
- LearningTransport and LearningTransportMessagePump updated accordingly
@danielmarbach danielmarbach changed the title Support for auto attaching incoming message properties to dispatch properties Preserve transport receive properties through message dispatch May 1, 2026
@danielmarbach danielmarbach modified the milestone: 10.2.0 May 1, 2026
@danielmarbach danielmarbach force-pushed the feature/receive-properties branch from 4cb13bb to 658e000 Compare May 1, 2026 13:29
@danielmarbach danielmarbach force-pushed the feature/receive-properties branch from 658e000 to 3835df7 Compare May 1, 2026 13:36
…parameter and simplify receive properties propagation logic
@danielmarbach danielmarbach force-pushed the feature/receive-properties branch from 57ef333 to ffaf6a1 Compare May 1, 2026 14:39
@danielmarbach
Copy link
Copy Markdown
Contributor Author

During the review, I realized, given we want to preserve information from the IncomingMessage, then the receive properties actually belong there. From that perspective, I went down the path to double check the message context and error context and realized the transport has to give us the properties, and then it is silly having to redeclare what Core preserves because the transport is responsible for pushing it towards Core, so whatever the transport has decided to put into the receive properties should be preserved unless overridden by a dispatch property

@danielmarbach
Copy link
Copy Markdown
Contributor Author

@andreasohlund and I discussed that as a follow up it probably makes sense to no longer expose IncomingMessage on the ErrorContext and recoverability interfaces but expose the raw elements of IncomingMessage since the incoming message is something that the Core transport integration is concerned about (for example, the envelope unwrapper) and not the error context itself. It should be more aligned in how MessageContext currently exposes the raw properties.

Comment thread src/NServiceBus.Core.Tests/Transports/TransportDefinitionTests.cs Outdated
@danielmarbach danielmarbach merged commit 4d49025 into master May 5, 2026
4 checks passed
@danielmarbach danielmarbach deleted the feature/receive-properties branch May 5, 2026 12:52
@danielmarbach
Copy link
Copy Markdown
Contributor Author

During the review, I realized, given we want to preserve information from the IncomingMessage, then the receive properties actually belong there. From that perspective, I went down the path to double check the message context and error context and realized the transport has to give us the properties, and then it is silly having to redeclare what Core preserves because the transport is responsible for pushing it towards Core, so whatever the transport has decided to put into the receive properties should be preserved unless overridden by a dispatch property

See #7741

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants