Please note, new blog at http://www.acheron.org/darryl/

AJAX Diary: Race conditions and using cflock

Within traditional web applications, it is highly unlikely that you will come across a scenario where a race condition will ocurr in the current session, unless you are accessing shared scopes. However, within an AJAX application, which is more like a frame-based Web site (remember them?), they can occur only too often.

Background

In our CRM application, there is a method that returns a "client ID". This method checks to see if a record exists in the database, and if not, creates it, and then returns the ID. If it already exists, then it simply returns the record. This record is in a tracking table that we use to reference records in an external system. All tables in our schema join to the tracking table, not the external database. Suffice to say, this method is called in a lot of places where client information is accessed.

<cffunction name="getClientID" returntype="string">
<cfset var local = StructNew()>
<cfset local.qClient = getClientID(arguments.account)>

<cfif NOT local.qClient.recordcount>
// Some logic to create a new tracking ID -- insert into db, etc.
</cfif>

<cfreturn local.qClient.ID>
</cffunction>

Now, you would probably state that there should be a scoped or named lock around this code. However, the likelihood of two sessions trying to create this record at the same time is very remote. Furthermore, most locking in the past has been where you are making changes to shared scopes such as SESSION, APPLICATION or SERVER.

Problem

Our new AJAX interface to our CRM has shown us that we need to be more careful, and use locking more judiciously. The scenario is this. The user clicks on a client record in a search result. This in turn loads the client information card. Three seperate, almost simultaneous, requests are sent to the server to retrieve different pieces of data. Each of the requests just happens to call the aforementioned method at some point. Even though the requests themselves are not occurring at the same time, a race condition is still created...

All three requests are now essentially calling the method, let's call it getClient(), at the same time. The database lookup to determine whether or not the record exists are being performed within a millisecond of each other. All three lookups return no records, so the conditional block of code is executed to insert a new record! So, the end result is three records in the database where there should have only be one!

Solution

Now, we pretty much had two options -- change the calls on the client so that the record check is performed once, or put a named lock around the code, so that we force requests to queue for execution.

We went for the later. Changing all of the calls to the method would require significant refactoring of the model, so that just wasn't an option.

<cffunction name="getClient" returntype="string">
<cfset var local = StructNew()>
<cflock name="getClient.getClientID" timeout="10">
<cfset local.qClient = getClientID(arguments.account)>

<cfif NOT local.qClient.recordcount>

<cflock name="getClient.getClientID.insert" timeout="10">
<cfset local.qClient = getClientID(arguments.account)>
<cfif NOT local.qClient.recordcount>
// Some logic to create a new tracking ID -- insert into db, etc.
</cfif>
</cflock>

</cfif>
</cflock>

<cfreturn local.qClient.ID>
</cffunction>

In the example above, I've put a named lock around the query to the database, and the check of the recordcount. If the recordcount is zero, then we perform another lock, and then do the check all over again. The reason for this is that in the time between acquiring the lock and performing the first check, a database record could have been inserted. The double lock technique just minimises the chance of this happening.

So, why was this only an issue now? Well, the old "traditional" user interface was a HTML page. The processing of the single request was top-down. By that I mean that if various elements of the user interface happened to call the getClient() method, they would be synchronously executed, thereby avoiding the scenario I have described.

Some other resources

By Blogger Unknown, at 6/22/2010 03:34:00 pm  

While the Louis vuitton bags
is the one which makes you admirable wherever you go. This is not abandoned due to the architectonics of the aberant louis vuitton handbags
. lv
acquire got their own emphasis in our lives, for arcade or anywhere away afterwards acclimatized a louis vuitton
with us.



By Blogger Unknown, at 6/29/2010 01:47:00 pm  

Tiffany jewellery
are a household word more than a century later and remain a great choice. The Tiffany necklaces
designs are timeless. silver necklaces
drew upon his love of nature for his original designs, Tiffany pendants
saw the opportunity to take his beautiful stained glass creations and offer them in a functional art object.
Before buying lida
, you will have to consult with your doctor first. Ephedra was acclimated by lots of accepted lida daidaihua
aback in the day. daidaihua
is based on ancient Chinese formula. The lida slimming
stick to the basic theme. slimming capsule
is purely natural as it is made from plants no acids. slimming capsules
burns our fat naturally.



By Blogger cherrywei, at 3/24/2011 05:39:00 pm  

R4DS
r4i gold
r4i-sdhc
acekard 2i
ps3 move
ps3 controller
ps3 games
ps3 wireless controller
ps3 move accessories
cheap ps3 controller
ps3 hdd
ps3 games hdd
Acekard 2i
nds card
Acekard
r4 dsi
r4 revolution
r4i gold
M3
M3i Zero
Acekard
Acekard 2i
R4i
R4 SDHC
Acekard 2i
supercard ds2
ps3 external hard drive
ps3 accessories
CycloDS



» Post a Comment