The Azure Cosmos DB persister uses the Azure Cosmos DB NoSQL database service for storage.
It is important to read and understand partitioning in Azure Cosmos DB before using NServiceBus.
.
Persistence at a glance
For a description of each feature, see the persistence at a glance legend.
Feature | |
---|---|
Supported storage types | Sagas, Outbox |
Transactions | Using TransactionalBatch, with caveats |
Concurrency control | Optimistic concurrency, optional pessimistic concurrency |
Scripted deployment | Not supported |
Installers | Container is created by installers. |
The Outbox feature requires partition planning.
Usage
Add a NuGet package reference to NServiceBus.
. Configure the endpoint to use the persister through the following configuration API:
endpointConfiguration.UsePersistence<CosmosPersistence>()
.CosmosClient(new CosmosClient("ConnectionString"));
Token credentials
Using a TokenCredential
enables the usage of Microsoft Entra ID authentication such as managed identities for Azure resources instead of the requiring a shared secret in the connection string.
A TokenCredential
can be provided by using the corresponding CosmosClient
constructor overload when creating the client passed to the persister.
Customizing the database used
By default, the persister will store records in a database named NServiceBus
and use a container per endpoint, using the endpoint name as the name of the container.
The database name can be customized using the following configuration API:
endpointConfiguration.UsePersistence<CosmosPersistence>()
.CosmosClient(new CosmosClient("ConnectionString"))
.DatabaseName("DatabaseName");
Customizing the container used
The default container can be set by using the following configuration API:
endpointConfiguration.UsePersistence<CosmosPersistence>()
.CosmosClient(new CosmosClient("ConnectionString"))
.DefaultContainer(
containerName: "ContainerName",
partitionKeyPath: "/partition/key/path");
The container that is used by default for all incoming messages is specified via DefaultContainer(.
. When installers are enabled, this (default) container will be created if it doesn't exist.
To opt out of creating the default container, either disable the installers or use:
endpointConfiguration.UsePersistence<CosmosPersistence>()
.CosmosClient(new CosmosClient("ConnectionString"))
.DefaultContainer(
containerName: "ContainerName",
partitionKeyPath: "/partition/key/path")
.DisableContainerCreation();
Any other containers that are resolved by extracting partition information from incoming messages need to be manually created in Azure.
The transactions documentation details additional options on how to configure NServiceBus to specify the container using the incoming message headers or contents.
Customizing the CosmosClient
provider
When the CosmosClient
is configured and used via dependency injection, a custom provider can be implemented:
class CustomCosmosClientProvider
: IProvideCosmosClient
{
// get fully configured via DI
public CustomCosmosClientProvider(CosmosClient cosmosClient)
{
Client = cosmosClient;
}
public CosmosClient Client { get; }
}
and registered on the container:
endpointConfiguration.RegisterComponents(c => c.AddTransient<IProvideCosmosClient, CustomCosmosClientProvider>());
Provisioned throughput rate-limiting
When using provisioned throughput, it is possible for the CosmosDB service to rate-limit usage, resulting in "request rate too large" exceptions indicated by a 429 status code.
When using the Cosmos DB persister with the outbox enabled, "request rate too large" errors may result in handler re-execution and/or duplicate message dispatches depending on which operation is throttled.
Microsoft provides guidance on how to diagnose and troubleshoot request rate too large exceptions.
The Cosmos DB SDK provides a mechanism to automatically retry collection operations when rate-limiting occurs. Besides changing the provisioned RUs or switching to the serverless tier, those settings can be adjusted to help prevent messages from failing during spikes in message volume.
These settings may be set when initializing the CosmosClient
via the CosmosClientOptions
MaxRetryAttemptsOnRateLimitedRequests
and MaxRetryWaitTimeOnRateLimitedRequests
properties:
endpointConfiguration.UsePersistence<CosmosPersistence>()
.CosmosClient(new CosmosClient("ConnectionString", new CosmosClientOptions
{
MaxRetryWaitTimeOnRateLimitedRequests = TimeSpan.FromSeconds(30),
MaxRetryAttemptsOnRateLimitedRequests = 9
}));
They may also be set when using a CosmosClientBuilder
via the WithThrottlingRetryOptions
method:
var cosmosClientBuilder = new CosmosClientBuilder("ConnectionString")
.WithThrottlingRetryOptions(
maxRetryWaitTimeOnThrottledRequests: TimeSpan.FromSeconds(30),
maxRetryAttemptsOnThrottledRequests: 9
);
endpointConfiguration.UsePersistence<CosmosPersistence>()
.CosmosClient(cosmosClientBuilder.Build());
Transactions
The Cosmos DB persister supports using the Cosmos DB transactional batch API. However, Cosmos DB only allows operations to be batched if all operations are performed within the same logical partition key. This is due to the distributed nature of the Cosmos DB service, which does not support distributed transactions.
The transactions documentation provides additional details on how to configure NServiceBus to resolve the incoming message to a specific partition key to take advantage of this Cosmos DB feature.
Outbox
Storage format
When the default partition key is not explicitly set, outbox rows are not separated by endpoint name. As a result, multiple logical endpoints cannot share the same database and container since message identities are not unique across endpoints from a processing perspective. To avoid conflicts, either separate different endpoints into different containers or override the partition key.
Outbox cleanup
When the outbox is enabled, the deduplication data is kept for seven days by default. To customize this time frame, use the following API:
var outbox = endpointConfiguration.EnableOutbox();
outbox.TimeToKeepOutboxDeduplicationData(TimeSpan.FromDays(7));
Outbox cleanup depends on the Cosmos DB time-to-live feature. Failure to remove the expired outbox records is caused by a misconfigured collection that has time-to-live disabled. Refer to the Cosmos DB documentation to configure the collection correctly.