﻿# Azure Storage Queues Transport Configuration



## Configuration parameters

The Azure Storage Queues Transport can be configured using the following parameters:


#### ConnectionString

Defaults: none

> [!NOTE]
> It is recommended to register Queue, Blob and Table clients configured in the desired way instead of providing connection string(s).

Note that multiple connection string formats apply when working with Azure storage services. When running against the emulated environment the format is `UseDevelopmentStorage=true`, but when running against a cloud hosted storage account the format is `DefaultEndpointsProtocol=https;AccountName=myAccount;AccountKey=myKey;`

For more details refer to [Configuring Azure Connection Strings](https://learn.microsoft.com/en-us/azure/storage/storage-configure-connection-string) document.

#### PeekInterval

The amount of time that the transport waits before polling the input queue, in milliseconds.

 Defaults: 125 ms


#### MaximumWaitTimeWhenIdle

In order to save money on the transaction operations, the transport optimizes wait times according to the expected load. The transport will back off when no messages can be found on the queue. The wait time will be increased linearly, but it will never exceed the value specified here, in milliseconds.

 Defaults: 30000 ms (30 seconds)


#### PurgeOnStartup

Instructs the transport to remove any existing messages from the input queue on startup.

Defaults: `false`, i.e. messages are not removed when endpoint starts.


#### MessageInvisibleTime

The [visibilitytimeout mechanism](https://learn.microsoft.com/en-us/rest/api/storageservices/get-messages), supported by Azure Storage Queues, causes the message to become *invisible* after read for a specified period of time. If the processing unit fails to delete the message in the specified time, the message will reappear on the queue. Then another process can retry the message.

Defaults: 30,000 ms (i.e. 30 seconds)


#### BatchSize

The number of messages that the transport tries to pull at once from the storage queue. Depending on the expected load, the value should vary between 1 and 32 (the maximum).

Starting from version 8.1.3 of the transport (and from version 7.5.6 for the 7.x version of the transport), the batch size is dynamically calculated based on the endpoints [message processing concurrency limit](/nservicebus/operations/tuning.md) unless explicitly specified. The batch size is calculated based on the following formula

|`MaxConcurrency` | `Batch Size` |
| :-: |:-:|
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| ... | ... |
| 32 | 32  [max] |

If the message processing concurrency limit is higher than the maximum batch size the [degree of parallelism](/transports/azure-storage-queues/configuration.md#configuration-parameters-degreeofreceiveparallelism) is dynamically increased, unless explicitly specified, to fulfill the concurrency needs of the endpoint while avoiding over fetching of messages. This is done to decrease the likelihood of message visibility timeouts.

Versions 7.x to 7.5.5 and 8.x to 8.1.2 default to a batch size of 32.


#### DegreeOfReceiveParallelism

The number of parallel receive operations that the transport is issuing against the storage queue to pull messages out of it.

Versions 8.1.3 and higher of the transport (or versions 7.5.6 and higher for the 7.x version of the transport) calculate the value at start-up based on the endpoints [message processing concurrency limit](/nservicebus/operations/tuning.md), using the following formula:

|`MaxConcurrency` | `DegreeOfReceiveParallelism` | `Batch Size` |
| :-: | :-:| :-:
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| ... | 1 | ... |
| 10 | 1 | 10 |
| ... | 1 | ... |
| 20 | 1 | 20 |
| ... | 1 | ... |
| 32 | 1 | 32 |
| 50 | 2 | 32 / 18 |
| 100 | 4 | 32 / 32 / 32 / 4 |
| 200 | 7 | 32 / 32 / 32 / 32 / 32 / 32 / 32 / 8 |
| 1000 | 31 | 31 x 32 / 8 |

When the `DegreeOfReceiveParallelism` is explicitly set, the batch size is dynamically adjusted to fulfill the endpoints message processing limits, if possible, up to the allowed maximum of 32. For example, with a maximum concurrency set to `100` and a `DegreeOfReceiveParallelism` fixed to `3` an underfetching of `4` messages might occur. Therefore it is advised to tweak both `DegreeOfReceiveParallelism` to be in alignment with the `BatchSize` if required. In most cases, the dynamic calculation should be sufficient and no adjustment is required.

Versions 7.x to 7.5.5, and 8.x to 8.1.2, dynamically calculate the value based on the endpoints [message processing concurrency limit](/nservicebus/operations/tuning.md), using the following equation:

```
Degree of parallelism = square root of MaxConcurrency
```

|`MaxConcurrency` | `DegreeOfReceiveParallelism` |
| :-: |:-:|
| 1 | 1 |
| 10 | 3 |
| 20 | 4 |
| 50 | 7 |
| 100 | 10 |
| 200 | 14 |
| 1000 | 32 [max] |

This means that `DegreeOfReceiveParallelism` message processing loops will receive up to the configured `BatchSize` number of messages in parallel. For example, using a `BatchSize` of 32 (the default) and parallelism set to 10 will allow the transport to receive 320 messages from the storage queue at the same time.

> [!WARNING]
> Changing the value of `DegreeOfReceiveParallelism` will influence the total number of storage operations against Azure Storage Services and can result in higher costs.

> [!WARNING]
> The values of `BatchSize` , `DegreeOfParallelism`, `Concurrency`, [ServicePointManager Settings](/persistence/azure-table/performance-tuning.md) and the other parameters like `MaximumWaitTimeWhenIdle` must be selected carefully to get the desired speed from the transport without exceeding [the boundaries](https://learn.microsoft.com/en-us/azure/azure-subscription-service-limits) of the allowed number of operations per second.


#### SerializeMessageWrapperWith

Messages are wrapped in a transport specific structure containing message metadata. By default, Azure Storage Queues Transport uses the same serializer for the message wrapper as configured for the contained message. It is possible to configure a different serializer for the wrapper using the `SerializeMessageWrapperWith` option

<!-- snippet: SerializerAndMessageWrapperSerializer -->

```cs
// serialize the messages using the XML serializer:
endpointConfiguration.UseSerialization<XmlSerializer>();

var transport = new AzureStorageQueueTransport("connection string")
{
    // wrap messages in JSON
    MessageWrapperSerializationDefinition = new SystemJsonSerializer()
};

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

> [!NOTE]
> All endpoints in the same system must use the same serializer for the message wrapper. This can be achieved by using the same serializer or the above `SerializeMessageWrapperWith` API.



## Setting configuration parameters


Settings should be set when instantiating the transport:

<!-- snippet: AzureStorageQueueConfigCodeOnly -->

```cs
var queueServiceClient = new QueueServiceClient("connectionString", new QueueClientOptions());
var blobServiceClient = new BlobServiceClient("connectionString", new BlobClientOptions());
var tableServiceClient = new TableServiceClient("connectionString", new TableClientOptions());

var transport = new AzureStorageQueueTransport(queueServiceClient, blobServiceClient, tableServiceClient)
{
    ReceiverBatchSize = 20,
    MaximumWaitTimeWhenIdle = TimeSpan.FromSeconds(1),
    DegreeOfReceiveParallelism = 16,
    PeekInterval = TimeSpan.FromMilliseconds(100),
    MessageInvisibleTime = TimeSpan.FromSeconds(30)
};

endpointConfiguration.UseTransport(transport);
```

<!-- endsnippet -->

Use the [`QueueServiceClient`](https://learn.microsoft.com/en-us/dotnet/api/azure.storage.queues.queueserviceclient.-ctor?view=azure-dotnet), [`BlobServiceClient`](https://learn.microsoft.com/en-us/dotnet/api/azure.storage.blobs.blobserviceclient.-ctor?view=azure-dotnet) or [`TableServiceClient`](https://learn.microsoft.com/en-us/dotnet/api/azure.data.tables.tableserviceclient.-ctor?view=azure-dotnet) constructor overload that suits the authentication needs when creating the clients passed to the transport.

## Native publish/subscribe

#### SubscriptionTableName

To control the subscription table name, use

<!-- snippet: storage_account_subscription_table_name -->

```cs
var transport = new AzureStorageQueueTransport("connectionString");
transport.Subscriptions.SubscriptionTableName = "NewName";
```

<!-- endsnippet -->

#### Caching

Configure the duration of subscription caching using. The default value is 5 seconds.

<!-- snippet: storage_account_configure_subscription_caching -->

```cs
var transport = new AzureStorageQueueTransport("connectionString");
transport.Subscriptions.CacheInvalidationPeriod = TimeSpan.FromSeconds(10);
```

<!-- endsnippet -->

Or disable caching entirely

<!-- snippet: storage_account_disable_subscription_caching -->

```cs
var transport = new AzureStorageQueueTransport("connectionString");
transport.Subscriptions.DisableCaching = true;
```

<!-- endsnippet -->

## Using multiple storage accounts

It is possible to configure the transport to use [multiple storage accounts](/transports/azure-storage-queues/multi-storageaccount-support.md#configuring-multiple-storage-accounts) when additional scaling is needed.

## Token-credentials

Enables usage of Microsoft Entra ID authentication such as [managed identities for Azure resources](https://learn.microsoft.com/en-us/azure/storage/blobs/authorize-access-azure-active-directory) instead of the shared secret in the connection string.

Use the corresponding [`QueueServiceClient`](https://learn.microsoft.com/en-us/dotnet/api/azure.storage.queues.queueserviceclient.-ctor?view=azure-dotnet), [`BlobServiceClient`](https://learn.microsoft.com/en-us/dotnet/api/azure.storage.blobs.blobserviceclient.-ctor?view=azure-dotnet) or [`TableServiceClient`](https://learn.microsoft.com/en-us/dotnet/api/azure.data.tables.tableserviceclient.-ctor?view=azure-dotnet) constructor overload when creating the clients passed to the transport.



## Sanitization

If a queue name is longer than [63 characters](https://learn.microsoft.com/en-us/rest/api/storageservices/naming-queues-and-metadata), the Azure Storage Queues Transport will fail to start. The endpoint name would needs to be shortened following the sanitization rules enforced by the Storage queue service.

For more details refer to [Sanitization](/transports/azure-storage-queues/sanitization.md) document.

## Serialization

Azure Storage Queues Transport changes the default serializer to JSON. The serializer can be changed using the [serialization API](/nservicebus/serialization/index.md).
