views:

516

answers:

3

Some context for the question

  • All objects in this question are persistent.
  • All requests will be from a Silverlight client talking to an app server via a binary protocol (Hessian) and not WCF.
  • Each user will have a session key (not an ASP.NET session) which will be a string, integer, or GUID (undecided so far).

Some objects might take a long time to edit (30 or more minutes) so we have decided to use pessimistic offline locking. Pessimistic because having to reconcile conflicts would be far too annoying for users, offline because the client is not permanently connected to the server.

Rather than storing session/object locking information in the object itself I have decided that any aggregate root that may have its instances locked should implement an interface ILockable

public interface ILockable
{
  Guid LockID { get; }
}

This LockID will be the identity of a "Lock" object which holds the information of which session is locking it.

Now, if this were simple pessimistic locking I'd be able to achieve this very simply (using an incrementing version number on Lock to identify update conflicts), but what I actually need is ReaderWriter pessimistic offline locking.

The reason is that some parts of the application will perform actions that read these complex structures. These include things like

  • Reading a single structure to clone it.
  • Reading multiple structures in order to create a binary file to "publish" the data to an external source.

Read locks will be held for a very short period of time, typically less than a second, although in some circumstances they could be held for about 5 seconds at a guess.

Write locks will mostly be held for a long time as they are mostly held by humans.

There is a high probability of two users trying to edit the same aggregate at the same time, and a high probability of many users needing to temporarily read-lock at the same time too. I'm looking for suggestions as to how I might implement this.

One additional point to make is that if I want to place a write lock and there are some read locks, I would like to "queue" the write lock so that no new read locks are placed. If the read locks are removed withing X seconds then the write lock is obtained, if not then the write lock backs off; no new read-locks would be placed while a write lock is queued.

So far I have this idea

  1. The Lock object will have a version number (int) so I can detect multi-update conflicts, reload, try again.
  2. It will have a string[] for read locks
  3. A string to hold the session ID that has a write lock
  4. A string to hold the queued write lock
  5. Possibly a recursion counter to allow the same session to lock multiple times (for both read and write locks), but not sure about this yet.

Rules:

  • Can't place a read lock if there is a write lock or queued write lock.
  • Can't place a write lock if there is a write lock or queued write lock.
  • If there are no locks at all then a write lock may be placed.
  • If there are read locks then a write lock will be queued instead of a full write lock placed. (If after X time the read locks are not gone the lock backs off, otherwise it is upgraded).
  • Can't queue a write lock for a session that has a read lock.

Can anyone see any problems? Suggest alternatives? Anything? I'd appreciate feedback before deciding on what approach to take.

A: 

Have you considered an mvcc implementation:

Granted, it would be harder to implement but seems much more concurrent than what you have explained. Just throwing the idea out there.

Logicalmind
In this scenario it wouldn't be an acceptable approach, thanks :-)
Peter Morris
A: 

(Edited to be phrased more as an answer, rather than a bunch of questions)

I replied to this on the Yahoo DDD list as well, but may as well here too for completeness.

You don't explicitly mention (that I can see) any sort of timeouts for write locks. I understand that they'll be able to be held for a long time, but it would be a prudent strategy to have some sort of timeout (even if it comes with a notification).

Barring that (or even as well as that), you may want to consider having a method to manually release the locks if you haven't already considered that - depending on the users/roles of the system, perhaps some sort of easy-to-use admin interface.

Also, and you've no doubt thought of this as well, ensure that the read lock array is thread safe.

Michael Hart
It wont need to be thread safe because the objects in question are persistent and will be loaded into separate object spaces. I only need to check for conflicts on update.As for time-outs. A worker thread will check for timeouts and release locks.
Peter Morris
A: 

If you are really looking to roll your own locking, I would follow a locking pattern something similar to thread locking, such as in ReaderWriterLockSlim.

http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx

Brian Rudolph