Skip to content
2 changes: 1 addition & 1 deletion Snippets/ASBS/ASBS_6/ASBS_6.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.*" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.*" />
<PackageReference Include="NServiceBus.Transport.AzureServiceBus" Version="6.1.0" />
<PackageReference Include="NServiceBus.Transport.AzureServiceBus" Version="6.*" />
<PackageReference Include="NUnit" Version="4.*" />
<PackageReference Include="NUnit3TestAdapter" Version="5.*" />
</ItemGroup>
Expand Down
67 changes: 67 additions & 0 deletions Snippets/ASBS/ASBS_6/DeadLettering.cs
Original file line number Diff line number Diff line change
@@ -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<string, object>
{
["FailureCategory"] = "Validation"
});
}

return DefaultRecoverabilityPolicy.Invoke(config, errorContext);

});
#endregion
}

class PoisonMessageException : Exception
{
}

class MyBusinessException : Exception
{
}
}
6 changes: 5 additions & 1 deletion Snippets/ASBS/ASBS_6/Usage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ class Usage
// exclude all types that inherit an interface or base type
transport.HierarchyNamespaceOptions.ExcludeMessageType<IAmExcludedFromTheHierarchy>();
#endregion

#region enable-dlq-auto-forwarding
transport.AutoForwardDeadLetteredMessagesToErrorQueue = true;
#endregion
}


Expand All @@ -155,7 +159,7 @@ public class MyExcludedMessage {}
public interface IAmExcludedFromTheHierarchy {}

public class MyExcludedMessageByInterface : IAmExcludedFromTheHierarchy { }

public class MyOtherExcludedMessageByInterface : IAmExcludedFromTheHierarchy {}
#endregion
class MyEvent;
Expand Down
2 changes: 2 additions & 0 deletions transports/azure-service-bus/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
@@ -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.
Comment thread
andreasohlund marked this conversation as resolved.

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).
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Creates infrastructure for an endpoint -- input queue.
asb-transport endpoint create name
[--size]
[--partitioned]
[--forward-dlq-to]
```

#### options
Expand All @@ -20,6 +21,8 @@ asb-transport endpoint create name

`-h` | `--hierarchy-namespace`: Sets the hierarchy namespace for prefixing destinations in the format `<hierarchy-namespace>/<topic-or-queue>` (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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,72 +2,90 @@

Create the topology for an endpoint named `MyEndpoint`:

```
```txt
Comment thread
andreasohlund marked this conversation as resolved.
asb-transport endpoint create MyEndpoint -c "<connection-string>"
```

### 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 "<connection-string>"
```

Create a queue and configure dead-letter forwarding to the `error` queue:

```txt
asb-transport queue create MyEndpoint --forward-dlq-to error -c "<connection-string>"
```

### 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 "<connection-string>"
```

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 "<connection-string>"
```

### Provisioning endpoints that use migration topology

Create the topology for an endpoint named `MyEndpoint` using the default settings:

```
```txt
asb-transport migration endpoint create MyEndpoint -c "<connection-string>"
```

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 "<connection-string>"
```

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 "<connection-string>"
```

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 "<connection-string>"
```

### Subscribing to events

Subscribe `MyOtherEndpoint` to the event `Contracts.Events.SomeEvent` using the default settings:

```
```txt
asb-transport migration endpoint subscribe MyOtherEndpoint Contracts.Events.SomeEvent -c "<connection-string>"
```

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 "<connection-string>"
```

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 "<connection-string>"
```

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 "<connection-string>"
```

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 "<connection-string>"
```
```
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ asb-transport migration endpoint create name
[--topic]
[--topic-to-publish-to] [--topic-to-subscribe-on]
[--subscription]
[--forward-dlq-to]
```

#### options
Expand All @@ -41,6 +42,8 @@ asb-transport migration endpoint create name

`-h` | `--hierarchy-namespace`: Sets the hierarchy namespace for prefixing destinations in the format `<hierarchy-namespace>/<topic-or-queue>` (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.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Create a queue using:
asb-transport queue create name
[--size]
[--partitioned]
[--forward-dlq-to]
```

#### options
Expand All @@ -20,6 +21,10 @@ asb-transport queue create name

`-h` | `--hierarchy-namespace`: Sets the hierarchy namespace for prefixing destinations in the format `<hierarchy-namespace>/<topic-or-queue>` (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

Expand Down
Loading