Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is Session.Evict in OnPostUpdate causing "Possible nonthreadsafe access to session" exception?

Tags:

nhibernate

I have a user that has a number of roles. The user is linked to the roles using a link entity table. I have set the configuration file to cascade delete the user role link entities when a user is deleted.

We are currently using soft delete to delete entities. We have added a soft delete event listener that is triggered by a delete. When an entity is being deleted, it triggers the DeleteEntity event which marks the entity as deleted.

We also have an override the OnPostUpdate event to remove the entities from the cache by calling Evict on the entity.

If I create a user without any roles, then delete it, everything works fine (it also works if cascade disabled). However if I have a user with at least one role assigned and I delete the user, after the call to Evict in OnPostUpdate, I get a NHibernate exception "NHibernate.AssertionFailure: Possible nonthreadsafe access to session".

I have tried, in OnPostUpdate, to use the child session to Evict the entity, the exception is not thrown, however, the entity is not evicted.

public void UserDelete(.....)
{
    var user = repository.Fetch<User>(id);

    repository.Remove(user);
    repository.Connection.Commit();
}


// soft delete event listener
protected override void DeleteEntity(NHibernate.Event.IEventSource session, object entity, ..)
{               
    var repositoryEntity = entity as deletableentity;
    if (repositoryEntity != null)
    {
        if (!repositoryEntity.IsDeleted)
        {
            // this marks the entity as deleted
            repositoryEntity.isDeleted = true;

            // cascade delete
            this.CascadeBeforeDelete(session, persister, repositoryEntity, entityEntry, transientEntities);
            this.CascadeAfterDelete(session, persister, repositoryEntity, transientEntities);          
        }
    }
}

public void OnPostUpdate(PostUpdateEvent @event)
{
    if (@event == null) throw new ArgumentNullException("event");

    var entity = @event.Entity as deletableentity;

    // Evict any entities that have been set as deleted from first level cache.
    if (entity != null && entity.IsDeleted)
    {
        @event.Session.Evict(entity);
    }
}

Any ideas on how to resolve it?

like image 662
Alex M Avatar asked Oct 04 '12 11:10

Alex M


3 Answers

According to https://forum.hibernate.org/viewtopic.php?p=2424890 another way to avoid this is to basically call

          session.save(s);
          session.flush(); // allow evict to work
          session.evict(s);

and that the root of the problem is that "if I've evicted the entity from cache the commit() will not find it there" (i.e. it's not a thread safety issue at all, it's a cache was modified issue).

like image 176
rogerdpack Avatar answered Oct 11 '22 16:10

rogerdpack


Found what the problem was. Using soft delete will actually trigger update to set isDeleted flag. Because of this line in a mapping

  cascade="all"

the cascade is applied for both Update and Evict actions. My postUpdate will be fired 2 times, but the same time Evict will try to evict on child entities.

The solution was to remove Evict from cascade in the mapping file. Now it is:

  cascade="persist, merge, save-update, delete, lock, refresh"
like image 3
Alex M Avatar answered Oct 11 '22 16:10

Alex M


I ran into the same problem, but since I'm using Fluent mappings there's no option to exclude Evict from the cascade. My solution was to avoid calling Evict and just remove the entity from the session cache:

public void OnPostUpdate(PostUpdateEvent @event)
{
    var entity = @event.Entity as ISoftDeletable;
    if (entity != null && entity.Deleted)
    {
        IEventSource session = @event.Session;
        IEntityPersister persister = @event.Persister;

        var key = new EntityKey(@event.Id, persister, session.EntityMode);
        session.PersistenceContext.RemoveEntity(key);
        session.PersistenceContext.RemoveProxy(key);
    }
}
like image 1
Omer Bokhari Avatar answered Oct 11 '22 15:10

Omer Bokhari