diff --git a/Snippets/ASBS/ASBS_6/ASBS_6.csproj b/Snippets/ASBS/ASBS_6/ASBS_6.csproj index f8ee3d00bf5..73167a00efb 100644 --- a/Snippets/ASBS/ASBS_6/ASBS_6.csproj +++ b/Snippets/ASBS/ASBS_6/ASBS_6.csproj @@ -5,7 +5,7 @@ - + diff --git a/Snippets/ASBS/ASBS_6/DeadLettering.cs b/Snippets/ASBS/ASBS_6/DeadLettering.cs new file mode 100644 index 00000000000..7b17d6f2850 --- /dev/null +++ b/Snippets/ASBS/ASBS_6/DeadLettering.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using NServiceBus; +using NServiceBus.Transport.AzureServiceBus; + +class DeadLettering +{ + void MoveErrorsToDeadLetterQueue(EndpointConfiguration endpointConfiguration) + { + #region dlq-all-errors + + endpointConfiguration.Recoverability() + .MoveErrorsToAzureServiceBusDeadLetterQueue(); + + #endregion + } + + void ExplicitDlq(EndpointConfiguration endpointConfiguration) + { + #region explicit-dlq + + endpointConfiguration.Recoverability() + .CustomPolicy((config, errorContext) => + { + if (errorContext.Exception is PoisonMessageException) + { + return RecoverabilityAction.DeadLetter(); + } + + return DefaultRecoverabilityPolicy.Invoke(config, errorContext); + }); + + #endregion + } + + void ExplicitDlqFullControl(EndpointConfiguration endpointConfiguration) + { + #region explicit-dlq-full-control + + endpointConfiguration.Recoverability() + .CustomPolicy((config, errorContext) => + { + if (errorContext.Exception is MyBusinessException ex) + { + return RecoverabilityAction.DeadLetter( + deadLetterReason: "Business rule validation failed", + deadLetterErrorDescription: ex.Message, + propertiesToModify: new Dictionary + { + ["FailureCategory"] = "Validation" + }); + } + + return DefaultRecoverabilityPolicy.Invoke(config, errorContext); + + }); + #endregion + } + + class PoisonMessageException : Exception + { + } + + class MyBusinessException : Exception + { + } +} \ No newline at end of file diff --git a/Snippets/ASBS/ASBS_6/Usage.cs b/Snippets/ASBS/ASBS_6/Usage.cs index 1b5ed398c36..52e3a19a3f5 100644 --- a/Snippets/ASBS/ASBS_6/Usage.cs +++ b/Snippets/ASBS/ASBS_6/Usage.cs @@ -146,6 +146,10 @@ class Usage // exclude all types that inherit an interface or base type transport.HierarchyNamespaceOptions.ExcludeMessageType(); #endregion + + #region enable-dlq-auto-forwarding + transport.AutoForwardDeadLetteredMessagesToErrorQueue = true; + #endregion } @@ -155,7 +159,7 @@ public class MyExcludedMessage {} public interface IAmExcludedFromTheHierarchy {} public class MyExcludedMessageByInterface : IAmExcludedFromTheHierarchy { } - + public class MyOtherExcludedMessageByInterface : IAmExcludedFromTheHierarchy {} #endregion class MyEvent; diff --git a/transports/azure-service-bus/configuration.md b/transports/azure-service-bus/configuration.md index 18785ec4a6d..8d71b589d55 100644 --- a/transports/azure-service-bus/configuration.md +++ b/transports/azure-service-bus/configuration.md @@ -73,3 +73,5 @@ partial: lockrenewal > > - Optimise the message handlers to reduce their execution time. > - Reduce the prefetch count. All messages are locked on peek, so when they are prefetched, they remain locked until they are all processed. + +partial: dead-lettering diff --git a/transports/azure-service-bus/configuration_dead-lettering_asbs_[6,).partial.md b/transports/azure-service-bus/configuration_dead-lettering_asbs_[6,).partial.md new file mode 100644 index 00000000000..e2999f4565c --- /dev/null +++ b/transports/azure-service-bus/configuration_dead-lettering_asbs_[6,).partial.md @@ -0,0 +1,58 @@ +## Dead lettering + +> [!NOTE] +> Support for dead lettering is available in Version 6.3.0 and higher. + +Azure Service Bus provides a native dead-letter queue (DLQ) for each queue and subscription. NServiceBus can integrate with this mechanism, allowing failed messages to be dead-lettered natively and forwarded to the central NServiceBus error queue. + +For background information on Azure Service Bus dead-letter queues, see [Overview of Service Bus dead-letter queues](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-dead-letter-queues). + +### Forward dead-lettered messages to the error queue + +When queues are created by the transport, native dead-lettered messages can be auto-forwarded to the configured error queue: + +snippet: enable-dlq-auto-forwarding + +This setting is opt-in and affects only queues created by the transport. See the [`asb-transport` provisioning commands](/transports/azure-service-bus/operational-scripting.md) for more details on scripting options. + +### Route all failed messages to the native DLQ + +To route failed messages to the native Azure Service Bus dead-letter queue instead of the NServiceBus error queue, enable: + +snippet: dlq-all-errors + +### Request dead lettering from recoverability + +Use a [custom recoverability policy](/nservicebus/recoverability/custom-recoverability-policy.md) to explicitly request dead lettering for selected failures. + +Dead-letter with standard NServiceBus fault metadata: + +snippet: explicit-dlq + +Dead-letter with custom reason, description, and modified application properties: + +> [!NOTE] +> Using this option does note automatically add the NServiceBus faults metadata to the application properties. + +snippet: explicit-dlq-full-control + +### Fault header mapping + +When processing dead-lettered messages, the transport maps native dead-letter properties to [error forwarding headers](/nservicebus/messaging/headers.md#error-forwarding-headers) when those headers are not already present: + +- `DeadLetterSource` -> `NServiceBus.FailedQ` +- `DeadLetterReason` -> `NServiceBus.ExceptionInfo.Message` +- `DeadLetterErrorDescription` -> `NServiceBus.ExceptionInfo.StackTrace` + +This mapping helps tools such as ServiceControl and ServicePulse present failure information consistently. + +### Monitoring and operations + +[ServicePulse failed message monitoring](/servicepulse/intro-failed-messages.md) tracks messages in the NServiceBus error queue. If endpoint failures are kept in native Azure Service Bus dead-letter queues without forwarding, those failures require Azure-native operational tooling. + +Enable DLQ forwarding as described above when you want to centralized native dead lettering and failed-message handling. + +### Caveats + +- `TransportTransactionMode.None` uses receive-and-delete semantics, so dead-lettering actions cannot be performed in that mode. See [transport transactions](/transports/transactions.md#transaction-modes-unreliable-transactions-disabled). +- The transport truncates dead-letter reason and description to 1024 characters to reduce oversized message risk. Review Azure limits in [Service Bus quotas](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-quotas). diff --git a/transports/azure-service-bus/configuration_entity-settings_asbs_[6,).partial.md b/transports/azure-service-bus/configuration_entity-settings_asbs_[6,).partial.md index 6d7c6d2b2a4..bffef8a1800 100644 --- a/transports/azure-service-bus/configuration_entity-settings_asbs_[6,).partial.md +++ b/transports/azure-service-bus/configuration_entity-settings_asbs_[6,).partial.md @@ -3,4 +3,7 @@ * `EntityMaximumSize`: The maximum entity size in GB. The value must correspond to a valid value for the namespace type. Defaults to 5. See [the Microsoft documentation on quotas and limits](https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-quotas) for valid values. * `EnablePartitioning`: Partitioned entities offer higher availability, reliability, and throughput over conventional non-partitioned queues and topics. For more information about partitioned entities [see the Microsoft documentation on partitioned messaging entities](https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-partitioning). * `AutoDeleteOnIdle`: A `TimeSpan` representing the [AutoDeleteOnIdle](https://learn.microsoft.com/en-us/azure/service-bus-messaging/advanced-features-overview#autodelete-on-idle) setting for instance-specific input queues (such as when using [`MakeInstanceUniquelyAddressable`](/nservicebus/messaging/routing.md#make-instance-uniquely-addressable)) created by the transport. This value is the maximum time period that a queue can remain [idle](https://learn.microsoft.com/en-us/azure/service-bus-messaging/message-expiration#idleness) before Azure Service Bus automatically deletes it. Defaults to `TimeSpan.MaxValue` in Azure Service Bus if this setting is not specified within the transport. The minimum allowed value is 5 minutes. The transport will not apply this setting to topics or subscriptions as these are considered shared infrastructure (along with shared queues such as error and audit). +* `AutoForwardDeadLetteredMessagesToErrorQueue`: When enabled, dead-lettered messages from transport-created receive queues are auto-forwarded to the configured NServiceBus error queue. It's recommended to enable this option to centralize failed-message handling for dead-letter queues. For general Azure Service Bus forwarding behavior, see [Enable auto forwarding for Azure Service Bus queues and subscriptions](https://learn.microsoft.com/en-us/azure/service-bus-messaging/enable-auto-forward). This setting is nullable; when not explicitly configured, a warning is logged at runtime if a message is dead-lettered. * `HierarchyNamespaceOptions`: Beginning version 6.1, [hierarchical entities](https://learn.microsoft.com/en-us/rest/api/servicebus/addressing-and-protocol) can be [configured using a hierarchical namespace](#entity-creation-hierarchy-namespace). Setting this with the required `HierarchyNamespace` property will prefix all entity paths in the format `{HierarchyNamespace}/{original-entity-path}`. Defaults to `HierarchyNamespaceOptions.None`, which disables prefixing with a hierarchy namespace. +* `MaxDeliveryCount`: The maximum delivery count applied to queues created during endpoint infrastructure setup. Defaults to `int.MaxValue`, which effectively disables Azure Service Bus's built-in delivery count limits and defers all retry decisions to NServiceBus recoverability. The Azure Service Bus Emulator requires a value of `10`. When choosing a value, ensure it is high enough to allow the configured recoverability policy to eventually move the message to the error queue, but not so high that it creates effectively infinite retries. +* `ThrowOnMissingTopicWhenPublishing`: When enabled, the transport re-throws the underlying `ServiceBusException` when publishing to a non-existent topic. The transport always logs a warning when publishing to a non-existent topic. Defaults to `false` for backward compatibility. diff --git a/transports/azure-service-bus/operational-scripting_endpoint-command_asbs_[6,).partial.md b/transports/azure-service-bus/operational-scripting_endpoint-command_asbs_[6,).partial.md index 0dd5b7cc42c..d00040b93b7 100644 --- a/transports/azure-service-bus/operational-scripting_endpoint-command_asbs_[6,).partial.md +++ b/transports/azure-service-bus/operational-scripting_endpoint-command_asbs_[6,).partial.md @@ -6,6 +6,7 @@ Creates infrastructure for an endpoint -- input queue. asb-transport endpoint create name [--size] [--partitioned] + [--forward-dlq-to] ``` #### options @@ -20,6 +21,8 @@ asb-transport endpoint create name `-h` | `--hierarchy-namespace`: Sets the hierarchy namespace for prefixing destinations in the format `/` (available from version 6.1) +`-f` | `--forward-dlq-to`: Queue name to auto-forward dead-lettered messages to. The queue will be created if it does not exist. The resolved queue name cannot be the same as the endpoint queue. + ### asb-transport endpoint subscribe Creates a new subscription for an endpoint. diff --git a/transports/azure-service-bus/operational-scripting_examples_asbs_[5,).partial.md b/transports/azure-service-bus/operational-scripting_examples_asbs_[5,).partial.md index 596e7e470ef..9684c8c32d0 100644 --- a/transports/azure-service-bus/operational-scripting_examples_asbs_[5,).partial.md +++ b/transports/azure-service-bus/operational-scripting_examples_asbs_[5,).partial.md @@ -2,21 +2,33 @@ Create the topology for an endpoint named `MyEndpoint`: -``` +```txt asb-transport endpoint create MyEndpoint -c "" ``` -### Subscribing to events +Create the topology for an endpoint named `MyEndpoint` and forward dead-lettered messages to the `error` queue: -Subscribe `MyOtherEndpoint` to the event `Contracts.Events.SomeEvent` using the default settings: +```txt +asb-transport endpoint create MyEndpoint --forward-dlq-to error -c "" +``` + +Create a queue and configure dead-letter forwarding to the `error` queue: +```txt +asb-transport queue create MyEndpoint --forward-dlq-to error -c "" ``` + +### Subscribing to events (migration topology) + +Subscribe `MyOtherEndpoint` to the event `Contracts.Events.SomeEvent` using the default settings: + +```txt asb-transport endpoint subscribe MyOtherEndpoint Contracts.Events.SomeEvent -c "" ``` Subscribe `MyOtherEndpoint` to the event `Contracts.Events.SomeEvent` and override the subscription name to be `my-other-endpoint` -``` +```txt asb-transport endpoint subscribe MyOtherEndpoint Contracts.Events.SomeEvent -s my-other-endpoint -c "" ``` @@ -24,19 +36,25 @@ asb-transport endpoint subscribe MyOtherEndpoint Contracts.Events.SomeEvent -s m Create the topology for an endpoint named `MyEndpoint` using the default settings: -``` +```txt asb-transport migration endpoint create MyEndpoint -c "" ``` -Create the topology for an endpoint named `MyEndpoint` and override the topic name to be `custom-topic` and the subscription name to be `my-endpoint`: +Create migration topology and forward dead-lettered messages to the `error` queue: +```txt +asb-transport migration endpoint create MyEndpoint --forward-dlq-to error -c "" ``` + +Create the topology for an endpoint named `MyEndpoint` and override the topic name to be `custom-topic` and the subscription name to be `my-endpoint`: + +```txt asb-transport migration endpoint create MyEndpoint -t custom-topic -s my-endpoint -c "" ``` Create the topology for an endpoint named `MyEndpoint` and override the publish topic name to be `custom-publish-topic` and the subscription topic name to be `custom-subscribe-topic`: -``` +```txt asb-transport migration endpoint create MyEndpoint -tp custom-publish-topic -ts custom-subscribe-topic -c "" ``` @@ -44,30 +62,30 @@ asb-transport migration endpoint create MyEndpoint -tp custom-publish-topic -ts Subscribe `MyOtherEndpoint` to the event `Contracts.Events.SomeEvent` using the default settings: -``` +```txt asb-transport migration endpoint subscribe MyOtherEndpoint Contracts.Events.SomeEvent -c "" ``` Subscribe `MyOtherEndpoint` to the event `Contracts.Events.SomeEvent` using the topic-per-event apprach (after migration): -``` +```txt asb-transport migration endpoint subscribe-migrated MyOtherEndpoint Contracts.Events.SomeEvent -c "" ``` Subscribe `MyOtherEndpoint` to the event `Contracts.Events.SomeEvent` and override the topic name to be `custom-topic`: -``` +```txt asb-transport migration endpoint subscribe MyOtherEndpoint Contracts.Events.SomeEvent -t custom-topic -c "" ``` Subscribe `MyOtherEndpoint` to the event `Contracts.Events.SomeEvent` and override the subscription name to be `my-other-endpoint` -``` +```txt asb-transport migration endpoint subscribe MyOtherEndpoint Contracts.Events.SomeEvent -s my-other-endpoint -c "" ``` Subscribe `MyOtherEndpoint` to the event `Contracts.Events.SomeEvent` and override the subscription rule name to be `SomeEvent`: -``` +```txt asb-transport migration endpoint subscribe MyOtherEndpoint Contracts.Events.SomeEvent -r SomeEvent -c "" -``` \ No newline at end of file +``` diff --git a/transports/azure-service-bus/operational-scripting_migration-endpoint-command_asbs_[6,).partial.md b/transports/azure-service-bus/operational-scripting_migration-endpoint-command_asbs_[6,).partial.md index 80969fa9198..ea2602d0b9b 100644 --- a/transports/azure-service-bus/operational-scripting_migration-endpoint-command_asbs_[6,).partial.md +++ b/transports/azure-service-bus/operational-scripting_migration-endpoint-command_asbs_[6,).partial.md @@ -19,6 +19,7 @@ asb-transport migration endpoint create name [--topic] [--topic-to-publish-to] [--topic-to-subscribe-on] [--subscription] + [--forward-dlq-to] ``` #### options @@ -41,6 +42,8 @@ asb-transport migration endpoint create name `-h` | `--hierarchy-namespace`: Sets the hierarchy namespace for prefixing destinations in the format `/` (available from version 6.1) +`-f` | `--forward-dlq-to`: Queue name to auto-forward dead-lettered messages to. The queue will be created if it does not exist. The resolved queue name cannot be the same as the endpoint queue. + > [!NOTE] > The hierarchy namespace option shifts the migration endpoint consistently into the hierarchy meaning the endpoint name and topics will have the hierarchy name applied. diff --git a/transports/azure-service-bus/operational-scripting_queue-command_asbs_[6,).partial.md b/transports/azure-service-bus/operational-scripting_queue-command_asbs_[6,).partial.md index 12eb2aa63f9..7a852ad504d 100644 --- a/transports/azure-service-bus/operational-scripting_queue-command_asbs_[6,).partial.md +++ b/transports/azure-service-bus/operational-scripting_queue-command_asbs_[6,).partial.md @@ -6,6 +6,7 @@ Create a queue using: asb-transport queue create name [--size] [--partitioned] + [--forward-dlq-to] ``` #### options @@ -20,6 +21,10 @@ asb-transport queue create name `-h` | `--hierarchy-namespace`: Sets the hierarchy namespace for prefixing destinations in the format `/` (available from version 6.1) +`-f` | `--forward-dlq-to`: Queue name to auto-forward dead-lettered messages to. The queue will be created if it does not exist. The resolved queue name cannot be the same as the source queue. + +See also the Azure CLI option [`--forward-dead-lettered-messages-to`](https://learn.microsoft.com/en-us/cli/azure/servicebus/queue?view=azure-cli-latest#az-servicebus-queue-create). + ### asb-transport queue delete