Getting Started
Architecture
NServiceBus
Transports
Persistence
ServiceInsight
ServicePulse
ServiceControl
Monitoring
Modernization
Samples

Sagas (DynamoDB)

NuGet Package: NServiceBus.Persistence.DynamoDB (2.1)
Target Version: NServiceBus 9.x

This article describes how to configure NServiceBus sagas for use with DynamoDB

Configure the saga table

The saga data table can be configured as follows:

persistence.Sagas().Table = new TableConfiguration
{
    TableName = "MySagaTable",
    PartitionKeyName = "MySagaPartitionKey",
    SortKeyName = "MySagaSortKey"
};

Saga data mapping

Saga data is automatically mapped using the built-in mapper described in the transaction documentation.

Sagas being persisted use the built-in mapper as described above. With Version 2.1 of the persistence it is possible to override the saga persistence JsonSerializationOptions to support scenarios such as source-generated serialization contexts.

var sagas = persistence.Sagas();
sagas.MapperOptions = new JsonSerializerOptions(Mapper.Default)
{
    // customize serialization options
    Converters = { new JsonStringEnumConverter() }
};

Saga concurrency

The DynamoDB saga persister uses optimistic concurrency control by default. Concurrently processed messages modifying the same saga will fail when the saga transaction completes after executing the message handler. In high-contention scenarios, pessimistic locking can enforce sequential access to the same saga to avoid concurrency related retries. To enable pessimistic locking, use:

persistence.Sagas().UsePessimisticLocking = true;

For more information, refer to the saga concurrency documentation.

Pessimistic locking configuration

Pessimistic locking is implemented using leases. The lease duration determines the amount of time exclusive access is guaranteed before other readers are able to acquire a lease again. The default duration is 30 seconds. To change the lease duration:

persistence.Sagas().LeaseDuration = TimeSpan.FromSeconds(15);

When a client attempts to acquire a lease on a saga data record that is locked, it will retry acquiring a lease for a configurable amount of time before timing out. The default retry duration is 10 seconds and can be changed with the following code:

persistence.Sagas().LeaseAcquisitionTimeout = TimeSpan.FromSeconds(5);

Amazon DynamoDB supports two read models: strongly consistent and eventually consistent. By default, the AWS SDK uses eventual consistency, which returns the most recently committed version of a record but does not guarantee that the latest write is visible at the time of the read. While this model is sufficient for many applications, it can introduce correctness issues when used in conjunction with concurrency control mechanisms, such as optimistic locking.

To ensure safety and accuracy, the NServiceBus DynamoDB saga persister has historically defaulted to consistent reads. This ensures that the most up-to-date saga data is always retrieved, reducing the chance of version mismatches or conflicting writes. However, this approach comes with a cost: consistent reads consume twice the read capacity units compared to eventually consistent reads.

To provide more flexibility, Version 2.1 of the persistence introduces a new global configuration option that allows to opt in to eventual consistent reads for sagas when using optimistic concurrency:

var sagas = persistence.Sagas();
sagas.UseEventuallyConsistentReads = true;

When enabled, this setting allows reads to be performed using DynamoDB’s eventual consistency model, reducing read costs—particularly beneficial in high-throughput or cost-sensitive environments where occasional retries are acceptable.

Related Articles

  • AWS DynamoDB persistence
    How to use NServiceBus with AWS DynamoDB.
  • Sagas
    Maintain statefulness in distributed systems with the saga pattern and NServiceBus' event-driven architecture with built-in fault-tolerance and scalability.