Monday, May 07, 2007

NHibernate and Coarse Grained Locking

One of the requirements of the aggregate pattern is that the aggregate has a root and a boundary and that the aggregate as a whole is the unit that we consider when looking at concurrency.

This has bothered me for a while as NHibernates concurrency is based on a version column at the row level, which is a completely different way of doing things.

For example if a Customer has a CustomerIdentification and both are in one row then we're OK, modifying either and saving the Customer will cause NHibernate to check that the row hasn't changed since we loaded it.

But lets say that the CustomerIdentification is in it's own table, now if we modify the CustomerIdentification and save then the save will pass even if someone else has modified the Customer. That can cause problems (see Eric Evans' book on DDD for concrete examples of the issues it can cause).

I looked it up in Nilssons "Applying Domain-Driven Design and Patterns" and was in luck as page 127 had the following:

"...an Order including its OrderLines is a concurrency conflict detection unit of it's own or - more conceptually - an Aggregate...".

He then goes on say that because the aggregate is the unit as far as concurrency we don't need to worry about other users interfering. Exactly what we need. I have read the book before so I was surprised that if it had a solution to this I'd missed it so I looked on, page 309 indicates two things:

  1. We want to use coarse-grained locks at the aggregate root level.
  2. We want to use a version field on the aggregate roots

The second bit confused me but its only for optimistic offline locking (though I would have thought you'd use that for all objects or for none). It doesn't seem to get me any closer to actually getting coarse grained locking working though so I skipped to page 341 covers how NHibernate works with concurrency and it tells me that NHibernate doesn't give us coarse grained locking. And thats all, no more reference to how we can get this important capability, even though the earlier examples assumed that we had a way of doing it. Back on the shelf for you.

Having said that I was particularly interested in the discussion of coarse grained locking of aggregates in Fowlers excellent "Patterns Of Enterprise Application Architecture". On page 439 the idea of a "root lock" is introduced, the root of the aggregate would have a lock and all parts of the aggregate would use it.

The book points out that for this to work we need to be able to navigate from any part of the aggregate back to the root, indirectly (with each one having a reference to its "parent") or directly (each has a reference to the root).

For me for now this is too much, having to couple each part of the aggregate directly to the root is too much and even coupling each part to the parent is troublesome, not least as to ensure that relationship is maintained when reloaded from persistence I'm going to need to store the relationship in the database. Unless I'm missing something obvious, which is quite possible.

For now I'll forget it :)

Share This - Digg It Save to del.icio.us Stumble It! Kick It DZone

3 comments:

  1. I had the same issue a time ago. Our customer wanted opt locks on every aggregate root in the system.

    I solved it traversing the graph inside an Interceptor and set the root as dirty as soon as a children was dirty. Fortunatly we needed bi-directional references everywhere inside an aggregate for other reasons (sometimes impl private though).

    /Roger

    ReplyDelete
  2. Anonymous1:43 am

    An approach that may work would be to only save root level objects with children objects saved via cascades.
    Combine this with versioning or a timestamp on the root object.

    This alone does not direcly prevent someone from directly modifying the child object but you could drop a DAO layer between the application and NHibernate to only expose saving for root layer objects.

    You're basically getting NHibernate to traverse the graph downwards for you rather than traversing upwards yourself as suggested by Roger.

    I'd probably use this approach if starting a new project & Roger's approach if adding coarse-grained locking to an existing codebase.

    ReplyDelete
  3. @Roger
    Ta for replying, glad to hear you solved it but the bi-directional references are something I tend to avoid. Its one smell against another here though.

    @brendan
    I thin the problem is then that you end up being scuppered by the unit of work in your ORM.

    ReplyDelete