![]() |
Home | Articles | Talks | Links | Contact Me | ISA | ThoughtWorks |
by David Rice
Allow framework or layer supertype code to acquire offline locks.
Key to any locking scheme is that there are no gaps in its use. A developer's forgetting to write a single line of code that acquires a lock can render an entire offline locking scheme useless. Failing to retrieve a read lock where other transactions use write locks means you might not get up-to-date session data. Failing to use a version count properly can result in unknowingly writing over someone's changes. Generally, if an item might be locked anywhere it must be locked everywhere. Ignoring its application's locking strategy allows a business transaction to create inconsistent data. Not releasing locks will not corrupt your record data but will eventually bring productivity to a halt. As offline concurrency management is a difficult concept to test such errors might go undetected by all of your test suites.
One solution is to not allow developers to make such a mistake. Locking tasks that cannot be overlooked should be handled implicitly by the application rather than explicitly by developers. The fact that most enterprise applications make use of some combination of framework, Layer Supertype, and code generation provides us with ample opportunity to facilitate Implicit Lock.
Implementing Implicit Lock is a matter of factoring your code such that any locking mechanics that absolutely cannot be skipped can be carried out by your application framework. For lack of a better word we'll use the word framework to mean any combination of Layer Supertype, framework classes, and any other 'plumbing' code. Code generation tools are another avenue to enforce proper use of a locking strategy. OK, this is by no means a ground-breaking idea. You are very likely to head down this path once you have coded the same locking mechanics a few times over for in your application. But we have seen it done poorly often enough that it merits a quick look.
The first step is to assemble a list of what tasks are mandatory for a business transaction to work within your locking strategy. For Optimistic Offline Lock the list will include items such as storing a version count for each record, including the version in update SQL criteria, and storing an incremented version when changing the record. The Pessimistic Offline Lock list will include items along the lines of acquiring any lock necessary to load a piece of data, typically the exclusive read lock or the read portion of the read/write lock, and the release of all locks when the business transaction or session completes.
Note that the Pessimistic Offline Lock list did not include acquiring any lock only necessary for editing a piece of data. This is the exclusive write lock and the write portion of the read/write lock. Yes, this is indeed mandatory should your business transaction wish to edit the data. However, implicitly acquiring these locks would present a couple of difficulties. First, the only points where we might implicitly acquire a write lock, such as the registration of a dirty object within a Unit of Work, offer us no promise that the transaction will abort as soon as the user begins to work should the locks be unavailable. The application cannot figure out on its own when is a good time to acquire these locks. A transaction not failing rapidly conflicts with an intent of Pessimistic Offline Lock: that a user not have to perform work twice. Second, and just as important, is that these are the types of locks that most greatly limit the system's concurrency. Avoiding Implicit Lock here helps us think about how we are impacting concurrency by forcing the issue out of the technical arena and into the business domain. But still we must enforce that locks necessary for writing are acquired before changes are committed. What your framework can do is assure that a write lock has already been obtained before committing any changes. Not having acquired the lock by commit time is a programmer error and the code should at least throw an assertion failure. We'd advise skipping the assertion and throwing a concurrency exception here as you really don't want any such errors in your production system when assertions are turned off.
A word of caution with using the Implicit Lock. While Implicit Lock allows developers to ignore a large part of locking mechanics it does not allow them to ignore consequences. For example, if using Implicit Lock with a pessimistic locking scheme that waits for locks the developers still need to think about deadlock possibilities. The danger with Implicit Lock is that business transactions can fail in unexpected fashion once developers stop thinking about locking.
Making it work is a matter of determining the best way to get your framework to implicitly carry out locking mechanics. Please see Optimistic Offline Lock for samples of implicit handling of that lock type. Below is an example of an implicit Pessimistic Offline Lock. The possibilities for a quality Implicit Lock implementation are far too numerous to demonstrate here.
Implicit Lock should be used except in the simplest of applications that have no concept of framework. The risk of a single forgotten lock is too great.
![]() | ![]() |