3

I keep getting a ConcurrencyException trying to update the same document multiple times in succession. PUT attempted on document '<id>' using a non current etag is the message.

On every save from our UI we publish an event using MassTransit. This event is sent to the subscriberqueues, but I put the Eventhandlers offline (testing offline subscribers). Once the eventhandler comes online the queue is read and the messages are processed as intended.

However since the same object is in the queue multiple times the first write succeeds, the next doesn't and throws this concurrencyexception.

I use a factory class to have a consistent IDocumentStore and IDocumentSession in all my applications. I specifically set the UseOptimisticConcurrency = false in the GetSession() method.

public static class RavenFactory
{
    public static IDocumentStore CreateDocumentStore()
    {
        var store = new DocumentStore() { ConnectionStringName = "RavenDB" };

        // Setting Conventions
        store.Conventions.RegisterIdConvention<MyType>((db, cmd, e) => e.MyProperty.ToString());
        store.Conventions.RegisterAsyncIdConvention<MyType>((db, cmd, e) => new CompletedTask<string>(e.MyProperty.ToString()));

        // Registering Listeners
        store
            .RegisterListener(new TakeNewestConflictResolutionListener())
            .RegisterListener(new DocumentConversionListener())
            .RegisterListener(new DocumentStoreListener());

        // Initialize and return
        store.Initialize();
        return store;
    }

    public static IDocumentSession GetSession(IDocumentStore store)
    {
        var session = store.OpenSession();
        session.Advanced.UseOptimisticConcurrency = false;
        return session;
    }
}

The eventhandler looks like this. The IDocumentSession gets injected using Dependency Injection. Here is the logic to get an instance of IDocumentSession.

private static void InitializeRavenDB(IUnityContainer container)
{
    container.RegisterInstance<IDocumentStore>(RavenFactory.CreateDocumentStore(), new ContainerControlledLifetimeManager());
    container.RegisterType<IDocumentSession, DocumentSession>(new PerResolveLifetimeManager(), new InjectionFactory(c => RavenFactory.GetSession(c.Resolve<IDocumentStore>())));
}

And here is the actual EventHandler which has the ConcurrencyException.

public class MyEventHandler:Consumes<MyEvent>.All, IConsumer
{
    private readonly IDocumentSession _session;

    public MyEventHandler(IDocumentSession session)
    {
        if (session == null) throw new ArgumentNullException("session");

        _session = session;
    }

    public void Consume(MyEvent message)
    {
        Console.WriteLine("MyEvent received: Id = '{0}'", message.MyProperty);

        try
        {
        _session.Store(message);
    _session.SaveChanges();
        }
        catch (Exception ex)
        {
            var exc = ex.ToString();
            // Deal with concurrent writes ...
            throw;
        }
    }
}

I want to ignore any concurrencyexception for now until we can sort out with the business on how to tackle concurrency.

So, any ideas why I get the ConcurrencyException? I want the save to happen no matter whether the document has been updated before or not.

Guillaume Schuermans
  • 906
  • 2
  • 12
  • 28
  • Take a look at this, it might help you with concurrency problems http://johnculviner.com/achieving-named-lock-locker-functionality-in-c-4-0/ – Alexandru Puiu Jan 16 '14 at 20:58
  • 1
    This is getting on for nearly a year old now, did you find a solution? – Simon Apr 23 '14 at 15:23
  • Has anyone figured this out? It seems to be on only one of my document types that get saved in the handler - all the others seem to save fine! –  Sep 08 '14 at 09:31
  • @GuillaumeSchuermans: Is this Unity Container returning a Singleton for the entire application? – Dominic Zukiewicz Oct 14 '14 at 12:58

1 Answers1

0

I am unfamiliar with configuring Unity, but you always want Singleton of the IDocumentStore. Below, I have coded the Singleton out manually, but I'm sure Unity would support it:

public static class RavenFactory
{
    private static IDocumentStore store;
    private static object syncLock = new object();

    public static IDocumentStore CreateDocumentStore()
    {
        if(RavenFactory.store != null) 
           return RavenFactory.store;

        lock(syncLock)
        {
           if(RavenFactory.store != null) 
              return RavenFactory.store;

           var localStore = new DocumentStore() { ConnectionStringName = "RavenDB" };

           // Setting Conventions
           localStore .Conventions.RegisterIdConvention<MyType>((db, cmd, e) => e.MyProperty.ToString());
           localStore .Conventions.RegisterAsyncIdConvention<MyType>((db, cmd, e) => new CompletedTask<string>(e.MyProperty.ToString()));

           // Registering Listeners
           localStore 
              .RegisterListener(new TakeNewestConflictResolutionListener())
              .RegisterListener(new DocumentConversionListener())
              .RegisterListener(new DocumentStoreListener());

           // Initialize and return
           localStore.Initialize();
           RavenFactory.store = localStore;
           return RavenFactory.store;
       }
    }

    //      As before
    //     public static IDocumentSession GetSession(IDocumentStore store)
    //
}
Dominic Zukiewicz
  • 8,258
  • 8
  • 43
  • 61