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:
(*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:
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:
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.
Can you please point me at a your reference for this statement:
*In recent versions of ColdFusion there is a "Single Threaded Sessions" setting in ColdFusion administrator
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.")
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?
I usually refer to this article for locking best practices, where the author mentions the "Single Threaded Sessions" setting:
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.
There is a revised edition of the first article indicating that it only applies to version 5 and prior:
There is a follow up article explaining how the CFMX architecture is no longer vulnerable to memory corruption:
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:
Those where some great questions, thanks for keeping me on my toes!
"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.
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.
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.
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.