Ayende @ Rahien

Hi!
My name is Oren Eini
Founder of Hibernating Rhinos LTD and RavenDB.
You can reach me by phone or email:

ayende@ayende.com

+972 52-548-6969

, @ Q c

Posts: 5,953 | Comments: 44,408

filter by tags archive

RavenDB Session Management with NServiceBus


I got a few questions about this in the past, and I thought that I might as well just post how I recommend deal with RavenDB session management for NServiceBus:

public class RavenSessionMessageModule : IMessageModule
{
  ThreadLocal<IDocumentSession> currentSession = new ThreadLocal<IDocumentSession>();

  IDocumentStore store;
  
  public RavenSessionMessageModule(IDocumentStore store)
  {
    this.store = store;
  }

  public IDocumentSession CurrentSession
  {
    get{ return currentSession.Value; }
  }

  public void HandleBeginMessage()
  {
    currentSession.Value = store.OpenSession();
  }
  
  public void HandleEndMessage()
  {
    using(var old = currentSession.Value)
    {
      currentSession.Value = null;
      old.SaveChanges();
    }
  }
  
  public void HandleError()
  {
    using(var old = currentSession.Value)
      currentSession.Value = null;
  }
}

When any of your message handlers needs to get the session, you need to wire the container in such a way that it would provide the RavenSessionMessageModule.CurrentSession to it.


Comments

Scooletz

I do prefer to use one message module for all the object with PerRequest lifecycle. What if you needed another one, you'd copy the module?

Ayende Rahien

Scooletz, That depend on what scenarios you have. In our case, we need to call SaveChanges if it didn't error. The NSB API doesn't give us much other choices.

Ryan Heath

HandleBeingMessage should be HandleBeginMessage

// Ryan

JarrettV

I was worried when I first saw this post but after re-reading the following it seems ok:

"The biggest challenge relates to not having a “final”-type method. While message modules do expose HandleEndMessage and HandleError, we have no way of knowing which will be called last. True, HandleError will always be called after HandleEndMessage, but we have no way to determine if HandleError will be called once HandleEndMessage is invoked. Note that HandleEndMessage is always called, even in error conditions."

http://blog.jonathanoliver.com/2010/04/extending-nservicebus-thread-specific-message-modules/

afif

would it make sense if the message handlers get injected an interface to an IDocumentSession that does not have functionality to open, close, commit, dispose etc. for e.g. an ICurrentDocumentSession or something like that. that way we are more explicitly modelling the fact that the session instance injected to message handlers is only providing a window into an open session... your thoughts?

Ayende Rahien

Afif, That makes absolutely no sense, I am afraid. The only thing in the IDocumentSession that relates to the lifetime of the session is the Dispose method, and trying to create a new interface just to hide that seems silly

viktor

what do you think about this approach? https://github.com/NServiceBus/NServiceBus-Contrib/blob/master/src/SagaPersisters/RavenDB/NServiceBus.SagaPersisters.RavenDB/RavenDBMessageModule.cs

btw. i think "RavenSessionModuleStore store;" should be "IDocumentStore store;" viktor

Ayende Rahien

Viktor, I don't see any RavenSessionModuleStore there, I see the DocumentStoreFactory, which seems to be mostly about preserving the current session, which is fine

Viktor

sorry for my bad explanation. with "RavenSessionModuleStore store;" i mean that your RavenSessionMessageModule will not compile, because "RavenSessionMessageModule store;" should be "IDocumentStore store;" or see below Viktor

public class RavenSessionMessageModule : IMessageModule { readonly ThreadLocal currentSession = new ThreadLocal();

    readonly IDocumentStore store;
    public RavenSessionMessageModule(IDocumentStore store)
    {
        this.store = store;
    }

    public IDocumentSession CurrentSession
    {
        get { return currentSession.Value; }
    }

    public void HandleBeginMessage()
    {
        currentSession.Value = store.OpenSession();
        currentSession.Value.Advanced.UseOptimisticConcurrency = true;
    }

    public void HandleEndMessage()
    {
        using (var old = currentSession.Value)
        {
            currentSession.Value = null;
            old.SaveChanges();
        }
    }

    public void HandleError()
    {
        using (var old = currentSession.Value)
            currentSession.Value = null;
    }
}
Ayende Rahien

Oh, yeah, I am sorry, you are correct, this is a bug in the code in the post, fixed now

Angel Blanco

The code shown does not work, since nservice bus calls HandleEndMessage() before HandleError() and thus, even if an error occurs, changes would get saved.

Comment preview

Comments have been closed on this topic.

FUTURE POSTS

No future posts left, oh my!

RECENT SERIES

  1. The RavenDB Comic Strip (3):
    28 May 2015 - Part III – High availability & sleeping soundly
  2. Special Offer (2):
    27 May 2015 - 29% discount for all our products
  3. RavenDB Sharding (3):
    22 May 2015 - Adding a new shard to an existing cluster, splitting the shard
  4. Challenge (45):
    28 Apr 2015 - What is the meaning of this change?
  5. Interview question (2):
    30 Mar 2015 - fix the index
View all series

Syndication

Main feed Feed Stats
Comments feed   Comments Feed Stats