NHibernate, MultiThreading and Shared Object Update Conflict

I understand that ISession is not thread safe and SessionFactory is thread safe. As such, I've wrapped and confirmed that I have one session per thread.

I'm receiving an error under the following situation and was wondering if this is something not supported, or I'm still missing something with my ISession thread isolation.

I am running NUnit tests. I have a scenario where my Entity is stubbed out as a field variable. I have a test that runs 2 parallel tasks.

• Each parallel task creates its own session from the same SessionFactory and begins an NHibernate Transaction. • They each update the entity and perform a SaveOrUpdate on it. • Then Commit and close the transaction. Each task does this about 10k times.

During this test I get a message:

System.AggregateException : One or more errors occurred.
  ----> NHibernate.HibernateException : identifier of an instance of Domain.Entity.MyEntity was altered from 2 to 1

This makes sense because the MyEntity is a field object and consumed by both threads. So a single object created in the NUnit class is reference and updated by both threads.

My question is whether a scenario like this can be avoided by pessimistic locking or other NHibernate features? Or is this just not do-able and I have to make sure this situation (i.e my Entity object is not referenced and updated by more than one thread at a time) never occurs in my code?

I've tired some options in NHibernate, like ensuring versioning of the entity and tried some locking calls, but I'm guessing in the dark through the documentation which is the right way, if any to handle this scenario.

Edit: Thanks for the comments! Here is the code in the unit test:

private PluginConfiguration _configStub1;

    [SetUp]
    public void Setup()
    {
        new FluentMapper().Configuration().ExposeConfiguration(
            e => new SchemaExport(e).Drop(false, true)
            );

        _configStub1 = new PluginConfiguration()
            {
                Enabled = true,
                Keys = "Name",
                Value = "Fred",
                PluginName = "red",
                RuntimeId = 1
            };
     }

    [Test]
    [Explicit]
    public void HighVolume_Saves_MultiManager_SameDataRecord_SameInstance_MultiThread()
    {
        Action action1 = () =>
        {
            var dal = new DataAccessManager();
            for (int i = 0; i < 10000; i++)
            {
                dal.Begin();
                dal.Current.Session.SaveOrUpdate(_configStub1);
                dal.Current.Commit();
                dal.End();
            }
        };

        Action action2 = () =>
        {
            var dal = new DataAccessManager();
            for (int i = 0; i < 10000; i++)
            {
                dal.Begin();
                dal.Current.Session.SaveOrUpdate(_configStub1);
                dal.Current.Commit();
                dal.End();
            }
        };

        var task1 = Task.Factory.StartNew(action1);
        var task2 = Task.Factory.StartNew(action2);

        task1.Wait();
        task2.Wait();
    }

The DataAccess Manager referenced in the test is as follows:

public class DataAccessManager : IDataAccessManager
{
    private readonly ThreadLocal<ISessionManager> _current = new ThreadLocal<ISessionManager>();

    public void Begin()
    {
        Current = new SessionManager();
    }
    public ISessionManager Current
    {
        get { return _current.Value; }
        set { _current.Value = value; }
    }
    public void End(bool doComplete = true)
    {
        bool isActive = Current.Transaction != null && Current.Transaction.IsActive;

        if (doComplete && isActive) Current.Commit();
        else if (!doComplete && isActive) Current.Transaction.Rollback();

        Current.Dispose();
    }
}

The SessionManager is as such:

public class SessionManager : ISessionManager
{
    /// <summary>
    /// Initializes a new instance of the <see cref="SessionManager"/> class.
    /// </summary>
    public SessionManager()
    {
        Session = ContextFactory.OpenSession();
        Transaction = Session.BeginTransaction();
    }

    public ITransaction Transaction { get; private set; }
    public ISession Session { get; private set; }

    public void Commit()
    {
        try
        {
            Transaction.Commit();
        }
        catch (Exception ex)
        {
            Transaction.Rollback();
            throw;
        }
    }
}

Answers


As stated in the comments, it seems there are two points to consider to ensure thread safety, as well a non conflicting sessions :

  • calling Session.Evict(_configStub1) once your object has been handled by the session. This way you prevent non Garbage-Collected Sessions from generating conflicting interactions with your object.
  • lock your object before it is attached to a session, and until it has been evicted from this session. This way, you ensure thread safety on your object.

Interacting with an entity object currently active in a session (i.e. known by the session) should be regarded the same as interacting with the session. Since the session is not safe for concurrent use from different threads, neither is access to entity objects known by the session.

This is because interacting with an entity instance may cause the session to lazy load some data, and during the call to Save() (or similar methods) NHibernate will mutate the object to set the id (depending on the choosen identity assignment strategy).

The above applies in the general case. In more narrow scenarios it might be possible to do it safely, but I would argue that you should be really sure you need such a thing before you introduce that complexity.


Need Your Help

Cocos2D OR libgdx for Android Game Developement

android game-engine libgdx cocos2d-android

I just want to know, that in the long run, using which of these engines will be better. Although I feel that using Cocos2D will be a better option, as it can also be used for iphone developement,

Create “check all” / “select all” checkbox with jQuery?

javascript jquery checkbox

I have few checkboxes , out of which one is "Select All" and the others are some standalone's.

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.