Question

When is it appropriate to use <cflock scope="application"> or it's ilk as opposed to <cflock name="foo">?

Specifically, I'm interested in using CFLock to protect shared objects in the application, session, or server scopes, but I'm also interested in finding out about different uses of locking in ColdFusion.

Was it helpful?

Solution

You should use when reading and writing from things that can change in the application scope. For example:

<cfquery name="application.myData">
    select * from myTable
</cfquery>

You are going to want to lock that with type="exclusive". Wherever application.myData is used, you need a type="readonly" lock. The exception is Application.cfc's OnApplicationStart method, which locks itself. Likewise use the same strategy with the session and server scopes.

Named locks give you more control over your locking strategy. Use a named cflock when you need to lock commands dynamically. For example:

<cflock name="write_file_#session.user_type#" type="exclusive">
    <cffile action="write" name="file_#session.user_type#" output="#content#" />
</cflock>

In this example, users of different types are allowed to write a file at the same time, but users with the same session.user_type must wait for each other. This cflock helps avoid file contention issues.

Another reason to use a named lock is if you do not know the scope of your current operation. If you are in an instantiated cfc, how do you know what scope you were instantiated into? Variables? Session? Application? Good encapsulation teaches us that objects do not know anything except what they have been told. Inside a CFC, use a named lock and name it after the CFC, or the CFC and a unique instance variable depending on your use case.

OTHER TIPS

Continuing with what @Mr. Nate said, use locks whenever you are concerned about race conditions. For example, you may want to lock a session initialization, but not subsequent reads. Similarly, you may want to lock writes to the application scope, but not reads.

Locking a read is a lot less useful since CF6, which introduced thread-safe shared variable scopes. In the bad old days, if you were not careful, you could be simultaneously reading and writing the same memory address. However, since CF became Java-powered, this is not a problem.

Named locks are useful, as he demonstrated, for locking anything that is not scoped, like file read/writes.

building upon the other suggestions here.

to be honest, since the advent of cf8 and now that duplicate() can duplicate objects, i would only use scope locks when writing to application, session or server scope (btw, writing to the server scope is a big no-no in my book).

if you need to read the data out, i would use duplicate() to do a deep copy of the data to a local variable and avoid the read lock all together. this will prevent deadlocks.

<cflock scope="application" timeout="5" type="exlusive">
 <cfset application.data = {}>
 <cfset application.data.firstname = "tony">
</cflock>

<cfset variables.firstname = duplicate(application.data.firstname)>

A great time to use named locking is when you have a "transaction" that you want to make sure happens all in one go, for example updating several tables in a database at once, or when you simply want to make sure that two users aren't updating the same database record at a time, or when reading or writing to a file on the server that might have more than one user at a time trying to access it.

Simply put, anytime there is a situation where there could be problems if two requests tried to do the same thing at the same time, then put a named lock around it (or if it strictly involves the session, application or server scope, then use a scoped lock).

Ben Nadel posted a blog entry once that 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."

You can even nest CFLOCK tags, such as have a named lock around a transaction, and session or application scoped locks nested inside, but do so with caution -- if you do it wrong, you could have a "deadlock" situation where no request can execute the locked section of the page and all the requests to the locked section of a page might be blocked until there is a timeout. (The ColdFusion manual describes best practices for nested locking.)

Typically you should always use cflock for session, application and server vars any time you're reading or changing those vars outside of an Application.cfc so as to prevent race-conditions. Here's an article that may be helpful:

http://www.horwith.com/index.cfm/2008/4/28/cflock-explained

Edit: To further answer your question about scope, I always use <cflock scope="application"> (for example) whenever interacting with shared resources.

This is an example from the ColdFusion 8 documentation that uses a page variable to create a "local flag" that can be read without locking to see if the application variables have been initialized.

What this does address is the fact that we need to conditionalize the exclusive lock because running it every time a page is loaded may create bottlenecks due to the lock taking up more processing time.

I don't know if better techniques have appeared since this has, but I figure I'll post it here anyway. The ColdFusion docs often don't provide good code, so I'm interested to see if anyone can see how this one can be improved.

  • I might put the local flag in the request scope so that it is available even in custom tags, etc. However, it's really only needed in app.cfm, so maybe that's unnecessary.
  • I also would remove isDefined() in favor of structKeyExists() so that it doesn't have to cycle through all the scopes.
  • I would also use bracket notation to set variables, so that capitalization is preserved (e.g. application['myDsn'] = "orders"). It also makes it easier to spot variable writes, which are a bit more important than variable reads. (This one's just my preference)

Source: http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=sharedVars_18.html

<!--- Initialize local flag to false. --->
<cfset app_is_initialized = False>
<!--- Get a readonly lock --->
<cflock scope="application" type="readonly">
    <!--- read init flag and store it in local variable --->
    <cfset app_is_initialized = IsDefined("APPLICATION.initialized")>
</cflock>
<!--- Check the local flag --->
<cfif not app_is_initialized >
<!--- Not initialized yet, get exclusive lock to write scope --->
    <cflock scope="application" type="exclusive">
        <!--- Check nonlocal flag since multiple requests could get to the
                exclusive lock --->
        <cfif not IsDefined("APPLICATION.initialized") >
            <!--- Do initializations --->
            <cfset APPLICATION.varible1 = someValue >
             ... 
            <!--- Set the Application scope initialization flag --->
            <cfset APPLICATION.initialized = "yes">
        </cfif>
    </cflock>
</cfif>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top