In my first year or so of developing ColdFusion applications, I didn't really understand how to use CFLock. That caused me a lot of headaches, and I have learned to use it properly since then. In the last year or so I have had several calls and emails from people who's sites aren't performing well under load where I was able to identify their problems were due to improper use of CFLock. It is important to use CFLock to protect the integrity of data that is shared across requests in your application. However, you need to make sure you do it right. Improper use of CFLock may not cause any noticeable problem in your development environment, but once your application starts to experience a significant amount of load, poor locking implementations can cause your application to grind to a halt.

A few examples of data that could be corrupted if not properly locked are:

  • Shared scope variables (variables in the application, server, and session* scopes
  • Verity collections
  • Files that are manipulated via CFFile or the ColdFusion File functions

(*In versions 4.5 and 5 of ColdFusion there is a "Single Threaded Sessions" setting in ColdFusion administrator where you can force each browser session to only use one thread. Enabling this feature will negate the need for locking session scoped variables, although it may cause slowness if your application utilizes frames)

Another valuable use for CFLock is to ensure that sensitive processes in your application do not run concurrently. For example if you are building a shopping cart and at the end of the checkout process you use an API call to authorize a payment then save the payment result to your database. You may want to put a named lock to make sure that if a use accidentally clicks twice on the "Pay Now" button, they do no accidentally get double charged.

Basically, anytime you are in a situation where you are developing some code that could cause problems if two requests tried to do the same thing at the same time, you want to slap a CFLock around it.

The CFLock tag has 5 attributes:

  • timeout - Required - Number of seconds for the request to wait to obtain a lock. If the lock is obtained within the specified time, the code in the body of the CFLock tag is processed. If the lock is not obtained within the specified time, the CFLock tag will react based on how you set the throwOnTimeout attribute value. If you set timeout="0", then the request will wait to obtain a lock until the request times out. If the "Timeout Requests after (seconds)" setting on the ColdFusion Administrator Settings page is not enabled, and you set timeout="0", the request will never timeout and will wait indefinitely to obtain the lock.

  • name - Optional - The name of the lock. This attribute is used when you are locking anything other than one of the shared variable scopes. The name cannot be an empty string. Named locks permit synchronizing access to resources such as verity collections or files that your application reads and writes to. Lock names are global to a ColdFusion server, so they are shared among applications and user sessions, but not clustered servers. The Name and Scope attributes cannot be used together in the same CFLock.

  • scope - Optional - The variable scope to be locked. This attribute is used when you need to lock the one of the following variable scopes:
    • Application
    • Request
    • Server
    • Session

  • throwOnTimeout - Optional - Specifies whether or not to throw an error if the lock cannot be obtained within the timeout period. If you enter "Yes", an error will occur. If you enter "No" the request will skip the code within the body of the CFLock tag and continue processing past that point

  • type - Optional - Specifies whether you want the lock to be "ReadOnly", or "Exclusive".

A "ReadOnly" CFLock will allow multiple requests with the same name/scope to process their code at the same time, as long as all the requests are using "ReadOnly" locks. You should use "ReadOnly" locks every where you are reading from (but not adding, updating or deleting) shared variable scopes, and everywhere you are using named locks around your CFSearch tags or around code that reads files which could be manipulated elsewhere in your code.

An "Exclusive" CFLock will wait until all currently running "ReadOnly" locks are released, then it will obtain a lock that will not allow any other CFLocks with the same name/scope to process until it has completed it's task and released the lock. You should use "Exclusive" locks to block access to the resource while you are adding, updating, deleting variables in the shared scope, and when you are performing CFIndex or CFCollection operations to your verity index, or when you are manipulating data in a file that could be read in other parts of your application.

Be very careful not to use too many locks in your application. As I alluded to earlier, using too many locks can really slow down an application under load. If you are going to be reading several application variables throughout a particular process, then you should write one CFLock to read all the variables you will need from the application scope at the beginning of the process, then save the data to the request scope or the local variables scope and end the lock. This way you don't have to lock the application scope 15 times throughout the process every time an application variable is needed, you can just get the data from the request or local variables scope instead.

One thing to keep in mind is if you are going to use CFLock to protect a data resource, you need to make sure you use CFLock every time you use that resource in your code. It doesn't do you much good to put CFLocks around your application scope variables in some places, but not in others.

You will also want to make sure that if there is any possible chance that an error could occur with the code that is in the body of the CFLock tag you are writing, that you use CFTry and CFCatch to handle the errors so that the lock always gets released. When an error occurs in code that is within a lock, the lock never releases, so none of the subsequent requests will be able to obtain a lock with the same name, and they will throw timeout errors until the ColdFusion Application Service on that machine is restarted.

Another thing to note is that the names of named CFLocks can be dynamically generated. For instance if are you building an auction site and you want to ensure that when users are bidding on an item that only on bid process runs at a time per item, and you have a set of queries and processes that get run when a person places a bid. You can use an exclusive named CFLock, dynamically generating the name from the auction id so that it only locks the bidding process for that particular auction instead of all auctions, like this:

<cflock name="BidLock#Form.auctionID#" type="EXCLUSIVE" timeout="3">
<cftry>
<!---
Here is where the code would go to add the bid, calculate the new high bidder,
check to see if the bidder who placed the bid is the high bidder or not, send an
email notification to another bidder if he just got outbid, etc, etc, etc.
--->

<cfcatch type="Any">
   <!--- here is where you handle errors so the lock always get's released --->
</cfcatch>
</cftry>
</cflock>

A good locking strategy is a vital part of the health of an application. Make sure you do it right so your blood pressure will go down and won't need to eat so many Rolaids.

Related Blog Entries

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Adam Cameron's Gravatar Hi Scott
Can you please point me at a your reference for this statement:
{quote}
*In recent versions of ColdFusion there is a "Single Threaded Sessions" setting in ColdFusion administrator
{quote}

My understanding was that shared scope access in CFMX and beyond were intrinsically synchronised in the underlying Java code, which was the reason for not needing to lock all accesses to them any more (cf CF5 and lower). I was not aware of any setting in CF Admin required to enable this. It concerns me if my understanding is wrong.

Or is this more a way of synchronising blocks of code at session-thread level, so as to prevent race conditions with session-scoped variable processing? If so, I think it would be better to stick with the CFLOCK approach *when needed* rather than single-threading all sessions automatically, "just in case" (I'm not suggesting you suggested otherwise, btw).

Also, as of CFMX7, there's no need to lock Verity interaction (according to the online docs for CFINDEX: "Removed suggested cflock usage.")

http://livedocs.adobe.com/coldfusion/8/htmldocs/he...

I find the addition in CF8 of the ability to lock the REQUEST scope interesting. What's a use case for that? All I can think is that threads fired by CFTHREAD share the same request (which doesn't strike me as being likely/desirable/sensible). Any idea?

--
Adam
# Posted By Adam Cameron | 1/12/08 8:42 AM
Scott Bennett's Gravatar @Adam,

I usually refer to this article for locking best practices, where the author mentions the "Single Threaded Sessions" setting:

http://www.adobe.com/devnet/server_archive/article...

After a little further research it looks like this article has become outdated. I never noticed that they took out the "Single Threaded Sessions" setting because I never use it. According to this next article, it looks like it was only available in ColdFusion 4.5 and 5.

http://kb.adobe.com/selfservice/viewContent.do?ext...

There is a revised edition of the first article indicating that it only applies to version 5 and prior:

http://kb.adobe.com/selfservice/viewContent.do?ext...

There is a follow up article explaining how the CFMX architecture is no longer vulnerable to memory corruption:

http://kb.adobe.com/selfservice/viewContent.do?ext...

but it still recommends you follow the same best practices as before. I have seen this article before, but it never mentioned that the single threaded session setting was removed. I have updated my article to indicate the the "Single Threaded Session" setting was only available in certain versions. Thanks for clearing me up on that part.

As to locking verity collections, I don't think there are the same issues with verity collections getting corrupted like they used to in the past, but I still lock them anyways because my verity collections take about 30-40 seconds to rebuild and I don't want anyone searching my site to get incomplete results while the collection is being rebuilt. Instead they get a nice little message saying "we are currently rebuilding our index please try your search again in a few seconds"


The addition of the request scope to the lockable scopes in CF 8 is indeed related to cfthread. The Request scope is shared across threads if you use cfthread to run simultaneous processes within a request. Here is some good info on cfthread and how it relates to the various variable scopes:

http://livedocs.adobe.com/coldfusion/8/htmldocs/he...

Those where some great questions, thanks for keeping me on my toes!
# Posted By Scott Bennett | 1/12/08 2:33 PM
Scott Bennett's Gravatar Ben Nadel posted an entry on his blog today that clarifies nicely the conditions where you would want to use a cflock, he said:

"The way I see it, TWO conditions must be met in order to require the use of CFLock:

1. A shared resource is being accessed or updated.
2. There must be the possibility of a race condition resulting in a NEGATIVE outcome."

This is a great rule of thumb. If there is no possible way anything bad can happen because you are not locking your shared data resource, you should not lock it. Unnecessary locks are not helping you, they are just a potential slow down point for your application when its under load.

http://www.bennadel.com/blog/1123-CFLock-And-Negat...
# Posted By Scott Bennett | 1/14/08 2:33 PM
Wim Lemmens's Gravatar Hi Scott,

In your post you mention the following:

" When an error occurs in code that is within a lock, the lock never releases, so none of the subsequent requests will be able to obtain a lock with the same name, and they will throw timeout errors until the ColdFusion Application Service on that machine is restarted. "

Where does that come from? The ColdFusion documentation says: "The cflock tag uses kernel level synchronization objects that are released automatically upon time out and/or the abnormal termination of the thread that owns them."

It seems that your statement contradicts the one from Adobe.

I wrote a simple test script that generates an error inside a lock, but the lock is released and subsequent requests can obtain it afterwards.

Can you clarify where you got your information? I'm having some issues with locking and if you are right, it would explain a lot.

Thanks,
Wim
Belgium
# Posted By Wim Lemmens | 5/9/08 12:59 PM
Scott Bennett's Gravatar @Wim,

I learned this by experience not from documentation, but I just did some testing on my CF8 server and I couldn't duplicate the problem there, so perhaps it has been fixed, or perhaps I am just not generating the right kind of error to cause the lock not to release. It happened to me many times until I got in the habit of using CFTRY/CFCATCH within all my named CFLOCK tags.

The problem I was describing only happened in cflocks tags where the name attribute is used like:

<cflock name="MyLockName" timeout="3">

In previous versions of ColdFusion, if an un-handled error occurred within a named lock, it would not release until the server was restarted. However, if you did a scope lock like:

<cflock scope="application" type="EXCLUSIVE" timeout="3">

then the lock would release even if there was an error.

So perhaps my personal "Best Practice" is no longer necessary, or perhaps it is only certain kinds of errors that cause the lock not to release, and not all errors like I thought. However, if you are experiencing this problem, I would suggest that you use CFTRY/CFCATCH within the problematic lock code to avoid the problem.

-Scott
# Posted By Scott Bennett | 5/9/08 4:25 PM
Allen's Gravatar Time for a post with a revised best practices for locking?
# Posted By Allen | 1/27/09 2:36 AM
Scott Bennett's Gravatar @Allen,
I think that this post and it's comments are still accurate, as they were all written after CF8 was released. What do you see that is missing or innaccurate? If you point it out I will happily correct the post to make sure it is not leading anyone astray.
# Posted By Scott Bennett | 1/27/09 12:26 PM
Allen's Gravatar Thanks Scott. You and others have been kind enough to take the time to explain things and I threw that question out there without anything more. It just seems that from others comments and your own versus the post itself that the original post didn't match the outcome. It's close but seems like some refinement in order. Maybe it's just how I'm interpreting some things. I was thinking it would be useful for the community. Then again, this is something who could go ahead and do it too and I haven't. So I'm doing the ol' too lazy to do it myself and instead asking something else to do it.
# Posted By Allen | 1/28/09 6:29 PM
Scott Bennett's Gravatar I see =).... well I may take another stab at it in the future to clear a few points up, and to tie in some of the points that came up in our conversations on Ben Nadels follow up blog entery to this article, but for now, I just got a new job where I actually have work to do all day so I don't have as much down time for blogging (hence the lack of any recent posts to my blog).
# Posted By Scott Bennett | 1/28/09 6:42 PM