RavenDB Persistence Upgrade from 3 to 4

Component: RavenDB Persistence
RavenDB's implementation of distributed transactions contains a bug that could cause an endpoint, in certain (rare) conditions, to lose data. If RavenDB is configured to enlist in distributed transactions, read DTC not supported for RavenDB Persistence.

As part of this update NServiceBus Version 6 will be required.

Migrate saga data if using AllowStaleSagaReads

Due to changes in how NServiceBus Version 6 handles saga correlation properties, solutions that previously used the AllowStaleSagaReads() option in RavenDB Persistence will not work properly and need to be migrated during upgrade.

Failure to migrate saga data when using the AllowStaleSagaReads() option will result in NServiceBus being unable to locate saga data, and then creating duplicate saga data erroneously.

To enable efficient saga loading in one server round-trip, RavenDB persistence will use a pointer document to load the saga data document by the correlation property in an atomic and consistent manner, without using RavenDB indexes that are (by design) not updated atomically with the document store.

When using the AllowStaleSagaReads() option in previous versions, which was sometimes used to support correlating saga data on multiple properties, pointer documents were not used and RavenDB indexes (which might be stale) were used instead.

Starting in NServiceBus Version 6, only one correlation id is supported, and the AllowStaleSagaReads() option is deprecated. However, saga data stored using this option in previous versions will not have the unique identity pointer document, and will not be able to load. The result is that NServiceBus will not be able to find the saga data document. If the message handler is implemented in the saga as IHandleMessages<T> then the message will be incorrectly discarded as belonging to a saga that has already completed. If the message handler is implemented in the saga as IAmStartedByMessages<T> then a new saga data will (incorrectly) be created, leading to duplicate saga data documents and incorrect business execution.

When upgrading, if the AllowStaleSagaReads option is in use, contact support@particular.net for assistance in identifying the scope of the problem and migration of data.

Namespace changes

Namespaces for public types have been consolidated to make customizations more discoverable. The primary namespaces are NServiceBus for customization options that need to be discoverable, and NServiceBus.Persistence.RavenDB for advanced APIs. A single using NServiceBus directive should be sufficient to find all necessary options.

As part of this move, the following classes were moved to different namespaces:

  • NServiceBus.Persistence.RavenDBPersistence to the NServiceBus namespace.
  • NServiceBus.RavenDB.Outbox.RavenDBOutboxExtensions to the NServiceBus namespace.
  • NServiceBus.RavenDB.ConnectionParameters to the NServiceBus.Persistence.RavenDB namespace.

Use of RavenDB Async API

NServiceBus now uses the asynchronous RavenDB API for all operations. If sharing the session between NServiceBus and handler code is required, then handler code will need to be adjusted to utilize the asynchronous RavenDB API as well.

Previously the API exposed an IDocumentSession, but now exposes IAsyncDocumentSession instead, which contains the same operations but using a Task-based API.

Configuring a shared session

Configuring a shared raven session now requires a Func<IAsyncDocumentSession> instead of a Func<IDocumentSession>.

4.x NServiceBus.RavenDB
Func<IAsyncDocumentSession> sessionFactory = () => someAsyncSession;

var persistence = endpointConfiguration.UsePersistence<RavenDBPersistence>();
persistence.UseSharedAsyncSession(sessionFactory);
3.x NServiceBus.RavenDB
Func<IDocumentSession> sessionFactory = () => someSession;

var persistence = busConfiguration.UsePersistence<RavenDBPersistence>();
persistence.UseSharedSession(sessionFactory);

ISessionProvider is obsolete

In Version 3 of NServiceBus.RavenDB an ISessionProvider was available for dependency injection. The new method of accessing the raven session is the SynchronizedStorageSession.

4.x NServiceBus.RavenDB
public class HandlerWithRavenSession :
    IHandleMessages<MyMessage>
{
    public Task Handle(MyMessage message, IMessageHandlerContext context)
    {
        var ravenSession = context.SynchronizedStorageSession
            .RavenSession();
        return SomeLibrary.SomeAsyncMethod(message, ravenSession);
    }
}
3.x NServiceBus.RavenDB
public class HandlerWithRavenSession :
    IHandleMessages<MyMessage>
{
    ISessionProvider sessionProvider;

    public HandlerWithRavenSession(ISessionProvider sessionProvider)
    {
        this.sessionProvider = sessionProvider;
    }

    public void Handle(MyMessage message)
    {
        var ravenSession = sessionProvider.Session;
        SomeLibrary.SomeMethod(message, ravenSession);
    }
}

Session is available regardless of features enabled

In Version 3, the RavenStorageSession was only registered if at least one out of Outbox and Sagas were enabled. There are possible use cases for using the NServiceBus wrapped RavenDB session so the prerequisites have been removed.

Related Articles


Last modified