Why it's wrong to use application.dsn in your templates

and what to do about it

Last updated: June 25, 2001

(This article is a actually an extended discussion of a tip listed in tip archive VI.)

This will continue to be expanded and refined and soon offered as an article in the ColdFusion Developers Journal or the Allaire DevCenter. Your comments are welcome.

This question is similar to, but different from, two related tips also offered here, on Why you should use CFLOCK around any Verity tags, or how two tags on the same page can run at the same time and Do I really need to lock access to shared scope variables?. Be sure to check those out as well.

You've probably seen the use of a variable called "application.dsn" (or "application.datasource") in code you've encountered. Perhaps you've even been taught to use the method in a class. Maybe you've even been doing it in your own code. I'm talking about setting this variable in the application.cfm file to hold the name of your datasource for a given application.

It seems so innocuous, and it seems to offer advantages in making your code easier to maintain (change the dsn variable once in the application.cfm, and all the templates that use it under control of that application.cfm get the benefit of the change).

The Problem

But there is a problem, and it can be a nasty one. The issue has to do with locking of (or failure to lock) shared scope variables like this one, and the fact that rarely does any discussion of using this approach include a discussion of the consequences of using shared variable scopes. There have been other articles in CFDJ on the subject of shared variable locking, and there are Macromedia Knowledge Base articles on it as well.

Maybe those seem to go over your head. Let me offer this simple case which is so often seen in code, and is still taught in classes as an acceptable approach. There's really very little reason to be doing it that way, when there's an equally effective way with no shared variable locking issues at all: change your code to use request.dsn instead (there may be a little more to changing the code to solve this problem than just doing a find and replace, which is discussed later).

If you're not familiar with "request" scope variables, or perhaps don't even fully understand either shared scope locking issues, or maybe aren't quite clear on how the magic of application.cfm works (you've just been "doing what you're told"), this article will help you.

If you do understand these things, then just know this: change your references to application.dsn to request.dsn--and if you find any locks around code that was using such an application.dsn variable, you need to consider whether those should stay, as well. I'll offer insights into how to find and fix such locking references as needed.

Some Background

People are often taught to do this in their application.cfm:

<CFSET application.dsn = "whatever">

so that they have declared the variable as "global", in essence, and can therefore later use it in all their templates as in:

<CFQUERY datasource="#application.dsn#" ...>

to refer to that datasource name. Of course, there's nothing magic about the name of the variable being "dsn". You may see it also as "datasource" or "datasourcename". The name of the variable isn't important. It's just that they're trying to declare it once for use by all their templates in the application.

The upside to this is that then if they change their datasource name (from "whatever" to "whatever_test", for instance), they can simply modify their application.cfm to point to the new name, and all their templates get the benefit of the change.

It's a good plan, having one place to set/edit the DSN for your application, but the specific approach is flawed. At worst, it opens you to locking issues (for more on that, including a good explanation for why it's a problem, see "ColdFusion Locking Best Practices" at http://www.allaire.com/Handlers/index.cfm?ID=20370&Method=Full). More simply, the intended benefit can be had another equally useful and less troublesome way.

The Solution

For reasons I'll explain in a moment, if it's still unclear, I'm suggesting that you stop using the application scope to hold the datasource name. But instead of justr dropping "application." from the variable name, I'm suggesting instead that you do the following (in your application.cfm):

<CFSET request.dsn = "whatever">

and then in all your templates do:

<CFQUERY datasource="#request.dsn#" ...>

If it's not clear why this is useful, or how it works, then there may be confusion about:

  • how the application.cfm works like a CFINCLUDE (and how we could, but won't, just use a local variable called "dsn")
  • what the request scope is about (and why it's better to use "request.dsn")
  • why using application variables without LOCK's is bad (and why we shouldn't use "application.dsn")

Let me explain all three. Though it would seem many already understand the first point, I find they often don't and it's fundamental to the rest of the discussion. The use of request scoped variables is also poorly understood.

How Application.cfm works like a CFINCLUDE

First, nearly every CF developer knows that whenever a CF template is run, it first tries to execute any application.cfm in the same directory. (Most also know that if there's none there, if looks in the parent directory, and then its grandparent and so on, until it finds one.)

But what may not be obvious is that CF actually runs the application.cfm like a CFINCLUDE, which means that any variables set there, including "local" variables (such as <CFSET firstname="bob"> are then available to the template that was originally being run.

So let's say we have a template that does just the following:

<CFOUTPUT>Hello #firstname#</CFOUTPUT>

If it did this and nothing else, you might normally expect that to fail since you can't refer to variables that don't exist, but because firstname was set in application.cfm (assuming this template is in a directory controlled by that application.cfm), it can indeed refer to the variable firstname.

Knowing that, one may wonder then why the folks who promoted this solution of setting the dsn variable to the application scope even bothered. They could just as easily have said simply:

<CFSET dsn = "whatever">

and then in all their templates do:

<CFQUERY datasource="#dsn#" ...>

It would work. In effect, there's really no need to be using the application scope to pass the variable to all templates (make it global), because any variables set in the application.cfm do "trickle down" to all templates, in effect making them global. (see footnote)

So the simple example of setting a variable called "dsn" (or more what could be formally specified as variables.dsn, which is the same thing) to hold the name of the datasource would work, and would trickle down to all the templates. It's effectively "global" at least for the life of that template, and it's reset at the execution of each template by being executed in the application.cfm.

But I've recommended instead that you use "request.dsn", rather than just "dsn" or "variables.dsn". Why? And is the request scope, anyway?

What the request scope is about

Well, if you understand that setting a local variable DSN will work, can you think of any situation in which your code may expect to have access to that variable but won't be able to? Think hard. OK, custom tags. Yep, remember, custom tags have their own local variable scope. So a local variable set in the caller (or in the application.cfm) will not be available to the custom tag.

When we were setting the dsn variable to an application scope, then it was indeed available in the custom tag (as are all shared scopes and also form, url, cgi, and other variables passed to the calling template).

But by changing the application.dsn to simply dsn (or variables.dsn, same thing), while it's available in all templates under control of the application.cfm, it's not available to any custom tags called by those templates. That's why we need to use the request scope instead.

It's sole purpose, poorly understood though it is, is to create local variables in a program that are indeed available within custom tags (and vice-versa). That's it. Nothing more.

Many often confuse the request scope with some sort of persistence or shared nature (and the fact that there is a different kind "request" scope in other languages like ASP and JSP only confuses matters further). But it's best to think of it as nothing more than a scope that allows local variables to be seen in a custom tag called by the template setting the request scope variable. And since our request.dsn variable is set in the application.cfm , it trickles down to the template being executed and is therefore also available to any custom tags we call as well.

So that's why you should use the request scope rather than a local scope.

Why using application variables carelessly is bad (from a shared scope variable locking standpoint)

So why is it wrong in the first place to use the application scope rather than the request scope? If it was taught as "the way" for so long, who am I to say it's wrong? Well, I'm not alone. The practice is derided in the Best Practices article on locking mentioned above, though they don't explain WHY you should use a request scope variable. Let me provide a little more background. (And, to be fair, there is now talk about changing the course materials to address this issue.)

From the locking standpoint, using the application scope to hold the DSN leaves you open to possible problems with shared locking issues. (See that Best Practices article for a more complete explanation). You should be locking any references to a variable (as well as the setting of the variable) that is in one of the shared scopes: session, application, or server.

Sadly, while some have at least heard that message, they don't fully understand what's going on and try to "solve" the locking problem by wrapping all their queries referring to the application.dsn variable in a CFLOCK, as in:

<CFLOCK scope="application" type="readonly" timeout="10">
<CFQUERY datasource="#application.dsn#" ...>
...
</CFQUERY>
</CFLOCK>

which is probably worse still, because then it single threads the execution of this query so that only one user at a time in the entire application can run the query (and since the query is probably just local to the page, that's really bad and wasteful and may cause other templates to timeout needlessly waiting for the lock to be released). It's absolutely mistaken coding.

If they're smart, they'd realize that all they need the lock for is because they're accessing the application variable, called dsn. As such, all they really need is to lock the access to that. As the best practices article points out, one solution would be to set the application variable first to a local variable (within a lock) and then use the local variable in the CFQUERY.

But as should be clear now, all that's moot if you instead skip using an application variable and instead use a request variable in the first place.

Remediation of Application.DSN Misuse

One last thought: you may not be able to just blindly do a global search and replace changing application.dsn to request.dsn.Depending on the savvy of the coder (or you, if you did the coding), the use of application.dsn may have at least two wrinkles that require more than just replacing application.dsn with request.dsn:
  • you may be testing for existence of application.dsn before setting it in the application.cfm, in which case you need to set the request.dsn outside that test since it will never "already exist"
  • you may have CFLOCKs surrounding CFQUERY's (for the mistaken reason mentioned above), or CFLOCKs that are moving the application.dsn to a local variable.
Each of these cases require a little more careful thought.

This article will be expanded further soon to discuss these matters. It will cover:

  • Dealing with a Test for Its Existence in Application.cfm
  • Dealing with a Test for Its Existence in application.cfm that Does Still More
  • Dealing With the Code That References the Variable

Footnote from above: There are indeed uses for application variables. It's just that this is a poor use of one. One more useful example may be creating an application-scoped query, for the purposes of caching it for the life of the server for use by all templates under control of that application.cfm. That's called application-scope query caching and may indeed make sense. There's a KB article on that, too, at http://www.allaire.com/Handlers/index.cfm?ID=1582&Method=Full, though it's from 97 and makes no reference to the shared scope locking issues. It also pre-dates the newer CACHEDWITHIN and CACHEDAFTER attributes on CFQUERY, which might be more useful for caching query results in a more global and perhaps simpler manner.
Tips Contents:

| Home | ColdFusion | Articles | Presentations
| User Groups | Other Resources | Press Releases | Company

© 1998-2024, Charles Arehart, SysteManage
Our Practice Makes You Perfect