Service Fabric Persistence Sagas

Component: Service Fabric Persistence
NuGet Package NServiceBus.Persistence.ServiceFabric (1.x)
Target NServiceBus Version: 6.x

Reliable collections

Saga data is stored in reliable dictionaries.

Saga data serialization

Saga data in stored in JSON format using Json.NET.

Saga data serialization can be configured by providing custom JsonSerializerSettings instance

var persistence = endpointConfiguration.UsePersistence<ServiceFabricPersistence>();

var sagaSettings = persistence.SagaSettings();
sagaSettings.JsonSettings(new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.Auto,
    Converters =
    {
        new IsoDateTimeConverter
        {
            DateTimeStyles = DateTimeStyles.RoundtripKind
        }
    }
});

custom JsonReader instance

var persistence = endpointConfiguration.UsePersistence<ServiceFabricPersistence>();

var sagaSettings = persistence.SagaSettings();
sagaSettings.ReaderCreator(
    readerCreator: textReader =>
    {
        return new JsonTextReader(textReader);
    });

or custom JsonWriter instance

var persistence = endpointConfiguration.UsePersistence<ServiceFabricPersistence>();

var sagaSettings = persistence.SagaSettings();
sagaSettings.WriterCreator(
    writerCreator: builder =>
    {
        var writer = new StringWriter(builder);
        return new JsonTextWriter(writer)
        {
            Formatting = Formatting.None
        };
    });

Saga data storage

By default saga data stored in multiple reliable collections - one per saga data type. Reliable collection name can be changed at the level of saga data type

[ServiceFabricSaga(CollectionName = "custom-collection-name")]
public class CustomCollectionNameSaga :
    Saga<CustomCollectionNameSaga.SagaData>,
    IHandleMessages<Message>
{
    public Task Handle(Message message, IMessageHandlerContext context)
    {
        return Task.CompletedTask;
    }

Saga data identifier used as a key in reliable dictionary gets calculated, among others, from saga data type name. As a result, renaming saga data class name changes storage identifier for every saga data instance. This is a problem especially when some saga data instances have already been stored for a given saga data type. In such scenarios it is necessary to provide stable saga data type name

[ServiceFabricSaga(SagaDataName = "saga-data-name")]
public class ExplicitSagaDataNameSaga :
    Saga<ExplicitSagaDataNameSaga.SagaData>,
    IHandleMessages<Message>
{
    public Task Handle(Message message, IMessageHandlerContext context)
    {
        return Task.CompletedTask;
    }

Saga concurrency

When simultaneously handling messages, conflicts may occur. See below for examples of the exceptions which are thrown. Saga concurrency explains how these conflicts are handled, and contains guidance for high-load scenarios.

This means that the relevant Handle method on the saga will be invoked, even though the message might be later rolled back. Hence it is important to ensure not to perform any work in saga handlers that can't roll back together with the message. This also means that should there be high levels of concurrency there will be N-1 rollbacks where N is the number of concurrent messages. This can cause throughput issues and might require design changes.

Creating saga data

Example exception:

The saga with the correlation id 'Name: {correlationProperty.Name} Value: {correlationProperty.Value}' already exists.

Updating or deleting saga data

ServiceFabric persistence uses optimistic concurrency control when updating or deleting saga data.

Example exception:

{nameof(SagaPersister)} concurrency violation: saga entity Id[{sagaData.Id}] already saved.

Last modified