﻿# AWS DynamoDB persistence

<!-- Version variant: dynamodb_3; default: [/persistence/dynamodb/index.md](/persistence/dynamodb/index.md) -->


Uses the [AWS DynamoDB](https://aws.amazon.com/pm/dynamodb/) NoSQL database service for storage.

## Persistence at a glance

For a description of each feature, see the [persistence at a glance legend](/persistence/index.md#persistence-at-a-glance).

|Feature                    |   |
|:---                       |---
|Supported storage types    |Sagas, Outbox
|Transactions               |Using `TransactWriteItems`
|Concurrency control        |Optimistic concurrency, optional pessimistic concurrency
|Scripted deployment        |Not supported
|Installers                 |Table is created by installers
|Local development          |[Supported via LocalStack](/nservicebus/aws/local-development.md)

## Usage

Add a NuGet package reference to `NServiceBus.Persistence.DynamoDB`. Configure the endpoint to use the persistence with the following configuration API:

<!-- snippet: DynamoDBUsage -->

```cs
var persistence = endpointConfiguration.UsePersistence<DynamoPersistence>();
// optional client
persistence.DynamoClient(new AmazonDynamoDBClient());
```

<!-- endsnippet -->

### Customizing the table used

By default, the persister will store outbox and saga records in a shared table named `NServiceBus.Storage`.

Customize the table name and other table attributes using the following configuration API:

<!-- snippet: DynamoDBTableCustomizationShared -->

```cs
persistence.UseSharedTable(new TableConfiguration
{
    TableName = "MyTable",
    PartitionKeyName = "MyPartitionKey",
    SortKeyName = "MySortKey"
});
```

<!-- endsnippet -->

Outbox and saga data can be stored in separate tables; see the [saga](/persistence/dynamodb/sagas.md) and [outbox](/persistence/dynamodb/outbox.md) configuration documentation for further details.

#### Table creation

When [installers](/nservicebus/operations/installers.md) are enabled, NServiceBus will try to create the configured tables if they do not already exist. Table creation can explicitly be disabled, even with installers remaining enabled, using the `DisableTablesCreation` setting:

<!-- snippet: DynamoDBDisableTableCreation -->

```cs
persistence.DisableTablesCreation();
```

<!-- endsnippet -->

When the deployment process creates the table instead, see [table creation](/persistence/dynamodb/table-creation.md) for the required schema.

### Customizing the AmazonDynamoDBClient provider

In cases when the `AmazonDynamoDBClient` is configured and used via dependency injection, a custom provider can be implemented:

<!-- snippet: DynamoDBCustomClientProvider -->

```cs
class CustomDynamoClientProvider
    : IDynamoClientProvider
{
    // get fully configured via DI
    public CustomDynamoClientProvider(IAmazonDynamoDB dynamoClient)
    {
        Client = dynamoClient;
    }
    public IAmazonDynamoDB Client { get; }
}
```

<!-- endsnippet -->

and then registered on the container

<!-- snippet: DynamoDBCustomClientProviderRegistration -->

```cs
endpointConfiguration.RegisterComponents(c => c.AddTransient<IDynamoClientProvider, CustomDynamoClientProvider>());
```

<!-- endsnippet -->

## Permissions

Below is the list of minimum required [IAM policies for operating the DynamoDB persistence](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/security_iam_service-with-iam.html)

### [Installers](/nservicebus/operations/installers.md) enabled:

  - `dynamodb:CreateTable`,
  - `dynamodb:DescribeTable`,
  - `dynamodb:DescribeTimeToLive`,
  - `dynamodb:UpdateTimeToLive`,
  - `dynamodb:Query`,
  - `dynamodb:GetItem`,
  - `dynamodb:BatchWriteItem`,
  - `dynamodb:PutItem`,
  - `dynamodb:DeleteItem`

### Installers disabled (or when using `DisableTablesCreation()`)

  - `dynamodb:DescribeTimeToLive`,
  - `dynamodb:Query`,
  - `dynamodb:GetItem`,
  - `dynamodb:BatchWriteItem`,
  - `dynamodb:PutItem`,
  - `dynamodb:DeleteItem`

## Provisioned throughput rate-limiting

When using [provisioned throughput](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ProvisionedThroughput.html) it is possible for the DynamoDB service to rate-limit usage, resulting in "provisioned throughput exceeded" exceptions indicated by the 429 status code.

> [!WARNING]
> When using Dynamo DB persistence with the outbox enabled, "provisioned throughput exceeded" errors may result in handler re-execution and/or duplicate message dispatches depending on which operation is throttled.

> [!NOTE]
> AWS provides [guidance](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ProvisionedThroughput.html#ProvisionedThroughput.Troubleshooting) on how to diagnose and troubleshoot provisioned throughput exceeded exceptions.

The Dynamo DB SDK provides a mechanism to automatically retry operations when rate-limiting occurs. These settings can be adjusted to help prevent messages from failing during spikes in message volume, rather than changing the provisioned capacity or switching to autoscaling or the on-demand capacity mode.

The retry-operations settings can be set when initializing the `AmazonDynamoDBClient` via the [`AmazonDynamoDBConfig`](https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/retries-timeouts.html) properties:

<!-- snippet: DynamoDBConfigureThrottlingWithClientConfig -->

```cs
var dynamoDbClient = new AmazonDynamoDBClient(
    new AmazonDynamoDBConfig
    {
        Timeout = TimeSpan.FromSeconds(10),
        RetryMode = RequestRetryMode.Adaptive,
        MaxErrorRetry = 3
    });
persistence.DynamoClient(dynamoDbClient);
```

<!-- endsnippet -->
