CFLOCK Confusions and to be used in Correct Way

Following article was delivered by Simon Horwith at (http://www.horwith.com)

It’s recently come to my attention that a lot of us ColdFusion Developers, including those who think they understand CFLOCK, don’t fully understand or appreciate how the CFLOCK tag actually works and when it should be used, so I thought I’d explain… starting at the begining with a very brief CFLOCK history lesson and a short overview of how CFLOCK works.

The ColdFusion Application Server runs on top of Java and though this underlying Java engine is inherently thread safe, there are occasions when developers need to programmatically control execution of code. The two most common reasons to do this are:

1.To control access to a shared resource
2.To prevent race conditions in business logic
In order to allow developers to control the execution of code blocks, CF Developers have a tag known as CFLOCK. Prior to ColdFusion MX the CFML engine wasn’t thread safe and scope level locking had to be performed in order to ensure data integrity when accessing shared memory scopes. As I already mentioned, as of CFMX the server is inherently thread safe, which means we don’t need to perform scope level locking in our code anymore. In the interest of being thorough I will mention that scope level locking is implemented by providing a “scope” attribute to the CFLOCK tag (the value being either “application” or “session”). From this point further we won’t discuss scope locks since their use has not been recommended since ColdFusion 5.

The other type of locking, which continues to be useful in CF(MX) 6 and all subsequent versions of the server, is known as “named locking”. A named lock is a CFLOCK that, rather than being passed a “scope” attribute, is passed a “name” attribute – the value of which is a user-defined string. The CFLOCK tag has a “timeout” attribute as well as a “throwontimeout” attribute. “Timeout” is a number representing the number of seconds that the CFLOCK should wait for other locks to complete. If a CFLOCK waits beyond that number of seconds and the “throwontimeout” attribute is “true” then an error is thrown – otherwise the server skips that CFLOCK block and continues processing.

As I mentioned, the CFLOCK tag has a “name” attribute. Named locks obey other locks with the same name. In order to define how locks obey each other, developers pass the CFLOCK tag a “type” attribute – the value of which can either be “readonly” or “exclusive”.

When the ColdFusion server encounters an “exclusive” CFLOCK it checks to see if any other thread is already executing an exclusive lock with that name and, if so, it waits until it’s timeout value is reached at which point an error is thrown or the CFLOCK block is skipped. If no other exclusive CFLOCK with the same name is executing, then the current thread creates the exclusive lock and executes the code within the CFLOCK block (releasing the lock when it’s done).

When the ColdFusion server encounters a “readonly” lock, it immediately executes the code in that block UNLESS an exclusive lock with the same name is currently executing. If another thread is already executing an exclusive lock with that name then the thread waits until no exclusive locks with the same name are running or until its timeout value is reached (at which point an error is thrown or the CFLOCK block is skipped)

So those are the basics of how CFLOCK works, and that overview is an accurate description of how most developers who know CFLOCK would explain it’s use. Now let’s discuss some specifics about CFLOCK that many developers don’t know, are unsure of, or are mistaken about.

First we’ll revisit the two most common reasons to use CFLOCK that I mentioned earlier, and discuss specifics about when and why you may want to use CFLOCK in a ColdFusion application. The first occasion for CFLOCK use is controlling access to shared resources. Though databases are the most common form of shared resources in web applications, using CFLOCK to control database access is generally not recommended. This is because database systems have support for transactional processing and, unlike CFLOCK, database transactions will be obeyed by every application that attempts to access the database. On the other hand, there is no native transaction manager for the file system so applications that access files and directories should use CFLOCK appropriately to prevent file corruption, dirty reads, or other possibly undesirable results when reading/writing shared file system resources.

The second occasion developers should use CFLOCK is to prevent “race conditions” in business logic. In simple terms a “race condition” is when the results of one process are dependent on the timing of other processes in order to ensure accuracy. For the most part, though not always, race conditions in applications occur when two processes are accessing and manipulating the same data in memory. So a race condition is really no different than our first common reason for using CFLOCK – accessing a shared resource. In the case of a race condition the shared resource we’re controlling access to is some data (variables) in memory. In either case our concern is the integrity of the resource in question – in the case of race conditions specifically, the concern is usually the assignment of a false value to shared data. In my experience, the most commonly occuring need for CFLOCK in CF applications is to “thread-safe” access to shared data.

It is commonly accepted that ColdFusion Components (CFCs) are an invaluable tool in the construction of ColdFusion applications and their use is strongly recommended in applications. The more complex the business logic and the more heavy the reliance on CFCs for both business logic and data encapsulation and persistance, the more likely the need to use CFLOCK to prevent race conditions and ensure data integrity. Let’s look at a simple example. Suppose you want to programatically track how many sessions are active in an application using a CFC. Let’s say you want to keep track of the session ID, timestamp the session began, and timestamp of last request. The CFC has an init method that acts like a constructor – it simply creates a private variable which will be used to store information about the active sessions. This CFC is a singleton – an application has One instance of it in the application scope – so we can create that instance and call init() within the onApplicationStart() method of Application.cfc. To add a session or update a session’s last request timestamp our session tracker object has a method called logRequest() which accepts the session ID as it’s only argument. LogRequest is called in the Application.cfc onSessionStart() method when a session starts and it’s also called in the onRequestStart() method to record the timestamp of each request. The CFC has a removeSession() method which also accepts a session ID – that method simply removes the session information from the object’s internal data. It is called from the Application.cfc onSessionEnd() method. Lastly, our object has a method called getSessionCount() that returns a number to tell me how many sessions are active. This function could be called from anywhere in our application. The code inside of the init(), logRequest(), and removeSession() methods each requires an exclusive named lock because each of them contains code that modifies the persisted data. The code inside the getSessionCount() method requires a readonly lock in order to ensure that the value returned reflect an accurate count – that it waited for any code that modifies the data to complete before returning a value.

It’s worth noting that the possibility that a race condition will rear its ugly head increases in direct proportion to increase in the length of time that blocks of code take to run. In our simple example, none of the methods described should realistically take more than a few milliseconds to execute because there’s very little actual business logic involved. This means that even without CFLOCK the odds are fairly good that the methods will return accurate values and that data values are accurate. If we were to change the requirements so that rather than just tracking session information in memory, session information is also persisted in a database or XML file somewhere, the code in these methods would most likely take much longer to execute.
We’ve discussed the history of CFLOCK, how it works from a simple point of view, and when/why/how to use CFLOCK in an application… but there’s a fairly common misconception about CFLOCK that still needs clarification which is the rules of engagement for CFLOCK in terms of how and when one CFLOCK obeys the rules of another. This misconception is what prompted me to write this rather lengthy essay. I often times hear CFLOCK explained by developers like so: “the code in an exclusive named lock or a readonly named lock will wait for any code currently executing in an exclusive named lock with the same name before it will run”. In other words, any lock with a name will wait for an exclusive lock with the same name to complete first. This is a misleading description that, without clarification, is inaccurate. The reality is that this description of how CFLOCK works is only accurate when you’re describing how locking is handled by separate requests. One user’s request will obey the locking rules as described with regards to currently executing requests initiated by other users. But what about locked code that calls other locked code in the same request? The rules don’t apply quite the same.

The truth is that it’s entirely possible and even commonplace in some applications, for the code in two exclusive CFLOCKs with the same name to execute at the same time. It’s even possible for the code in a readonly named lock to execute at the same time as the code in an exclusive lock with the same name (so long as the readonly CFLOCK block was called by the exclusive CFLOCK block code). Why? When ColdFusion is running code in a named CFLOCK block and that code attempts to run code containing a CFLOCK with the same name, if both CFLOCK tags have the same level of locking (both are exclusive or readonly) then the the second lock can safely be ignored (the currently executing thread aleady has a lock that follows the same rules). If, however, the second CFLOCK has a different level of locking then the behavior changes. From an exclusive readonly lock, attempting to execute a readonly lock of the same name results in the second lock being ignored. In this case the readonly locked code executes in an exclusive locked mode (the lock is “updgraded”). On the other hand, if the first CFLOCK executed by a server thread is a readonly named lock and it in turn attempts to run code within an exclusive lock of the same name, the exclusive lock code will never execute. This is because in order for code in a named CFLOCK to be called from code in another CFLOCK with the same name, the second cFLOCK must be able to run as if it is a lock of the same type as the first CFLOCK (it must be able to “upgrade” to the level of the first CFLOCK). Readonly locks from different requests can execete in parellel and they obey exclusive locks of other requests – so from within a readonly lock it would be irresponsible for the server to assume that it is OK to execute an exclusive lock. Here is a simple code example illustrating how exclusive locks can be executed from each other:

<cfscript>
goUntil = 100;
start = 0;
time1 = getTickCount();
test();
totalTime = getTickCount() - variables.time1;
</cfscript> 

<cffunction name="test" returntype="void">
<cflock type="exclusive" name="foo" timeout="10" throwontimeout="true">
<cfscript>
variables.start++;
if (variables.start lt variables.goUntil){
test2();
}
</cfscript>
</cflock>
<cfreturn>
</cffunction>

<cffunction name="test2" returntype="void">
<cflock type="exclusive" name="foo" timeout="10" throwontimeout="true">
<cfscript>
variables.start++;
if (variables.start lt variables.goUntil){
test();
}
</cfscript>
</cflock>
<cfreturn>
</cffunction>

<cfoutput>
code took #variables.totalTime# ms to run<br />
current value of start is #variables.start#<br />
</cfoutput>

In the preceding code example code in one function, from with an exclusive CFLOCK, executes another function whose code is in an exclusive lock with the same name. Paste the contents into a new file an run it and you’ll see that it works just fine – the second CFLOCK (the one in test2) doesn’t wait for the CFLOCK already in progress to complete. Change the first CFLOCK to be readonly like in the following example, and you’ll see that because the current thread is executing inside a readonly lock, it is unable to run code inside an exclusive lock with the same name… thus an error is thrown.

<cfscript>
goUntil = 100;
start = 0;
time1 = getTickCount();
test();
totalTime = getTickCount() - variables.time1;
</cfscript> 

<cffunction name="test" returntype="void">
<cflock type="readonly" name="foo" timeout="10" throwontimeout="true">
<cfscript>
variables.start++;
if (variables.start lt variables.goUntil){
test2();
}
</cfscript>
</cflock>
<cfreturn>
</cffunction>

<cffunction name="test2" returntype="void">
<cflock type="exclusive" name="foo" timeout="10" throwontimeout="true">
<cfscript>
variables.start++;
if (variables.start lt variables.goUntil){
test2();
}
</cfscript>
</cflock>
<cfreturn>
</cffunction>

<cfoutput>
code took #variables.totalTime# ms to run<br />
current value of start is #variables.start#<br />
</cfoutput>

The important lesson here is that, when developing complex logic that needs to be thread safe, always ensure that no code inside of a readonly named lock attempts to execute code inside of an exclusive named lock of the same name. You will generally be safe following the old rule of putting exclusive locks around code that modifies data and readonly locks around code that only reads the data, but not 100% of the time. It’s not unheard of for a method that returns a ‘count’ value to first call a maintenance or ‘clean-up’ method first. Following the ‘golden rule’ could get you into trouble in this scenario. This brings up another question I’ve heard in response to learning all this, “can’t I just use exclusive named CFLOCKs for everything and not have to worry about it?”. The short answer is “yes” – if all the named locks in CFC methods use the same name and are exclusive, you won’t have to worry about locks timing out because of the way in which the code is calling it. It is not ideal to use exclusive locks alone however, because readonly locks executed by different user requests are allowed to execute simultaneously. If you have methods that are called from outside your CFC and that only return data then you want readonly locks – to use exclusive locks would single-thread the execution of those requests, which means a performance hit as well as risk of CFLOCK timeouts – something you don’t have when the lock is readonly.

The documentation for CFLOCK does describe all of this, except that it’s not entirely clear about code in two different places that call each other (i.e. CFLOCK tags in methods that call each other) – it describes the rules for CFLOCK as they pertain to different user requests and as they pertain to nested CFLOCK tags. Though methods calling each other don’t have their respected CFLOCK blocks nested, the rules in the CF documentation for CFLOCK pertaining to nesting one CFLOCK inside of another also apply to this method call locking scenario. It’s a nuance, but one I felt was worth mentioning. Hopefully, in the process of writing so much in one sitting, I have succeeded in making my point clear and haven’t confused the issue for anyone.