Question for the crowd. We are very strict on our team about scoping local variables inside functions in our CFC's. Recently though the question of scoping variables inside Application.cfc came up. Are unscoped variables in functions like onRequestStart() at the same risk for being accessed by other sessions running concurrently as we know that local variables in functions in other components are? Or are they somehow treated differently because of the nature of the functions in Application.cfc?

1 Answer
1

Your question borders on two entirely separate questions (both of which are important to clarify and address). These two questions are:

Should I scope my variables correctly when referring to them (ie. APPLICATION.settings vs. SESSION.settings).

The short answer to this is: Yes. It makes for cleaner, more readable / managable code, and prevents variable scope clashes that you may encounter later when variable names are re-used.

If you create APPLICATION.settings and SESSION.settings, but attempt to refer to them without scope (ie. <cfset myvar = settings />), you're going to have variable clash issues, as they'll be poured into VARIABLES by default--since neither APPLICATION nor SESSION are examined to resolve scope ambiguity.

The second question is:

Should I be worried about variables that are accessed in Application.cfc that could be potentially be shared by multiple users in a concurrent environment?

The short answer to this is: Yes. You should know & understand the ramifications of how your shared variables are accessed, and <CFLOCK> them where appropriate.

Unfortunately, exactly when and where you lock your shared variables is often never clarified to the CF community, so let me sum it up:

onApplicationStart() single-threads access to the APPLICATION scope. You do not need to lock APPLICATION vars that are read/written within this method.

onSessionStart() single-threads access to the SESSION scope. Same answer as before.

If you provide any kind of mechanism that accesses SESSION or APPLICATION from within the onRequestStart() method--or any other template afterwards (such as a URL reload parameter that directly calls onApplicationStart() )--all bets are off--you must now properly handle the locking of your shared variable reads and writes.

-- edit --

I've re-read your comments and hope to clarify this answer further. Without any example code, I'm going to assume the worst, based on how you've described the situation.

and then later, perhaps in a view template, where the previous developer has done this:

<cfoutput>#myNavImgPath#</cfoutput>

Is there a chance that two concurrent requests could possibly produce different outputs, causing User A to get a different result than User B?

Short Answer: Yes

Long Answer:

When an unscoped variable is set within onRequestStart(), it is poured into the VARIABLES scope. Seasoned CF programmers recognize this as the default (or 'template level') scope for variables. However, it has further meaning within the confines of a CFC--regardless of whether it is Application.cfc or MyCustomClass.cfc: variables that belong to the VARIABLES scope of a CFC are private to that CFC, but static/shared for the life of that CFC. In many cases, that means they die at the end of the page request, and are renewed on the next. In the case of CFCs that are cached in a stateful scope (like our aforementioned APPLICATION and SESSION), they are permanent until those scopes are re-initialized (ie. server is restarted, application is restarted, etc.)

Now, in the case of Appliation.cfc, one should never be caching it as a stateful object; Application.cfc itself drives state, and thus, has its methods (and variables) renewed on each page request--depending on, of course, what state the app is currently in (ie. onAppliationStart() is not going to fire every page request). So, that leaves us with one final edge condition: If two concurrent requests hit your CF server, and Application.cfc fires, setting unscoped variables to the VARIABLES scope within the confines of the Application.cfc, will they tread on each other?

By itself, this is reasonably safe code--Reason: it is implicitly a part of the VARIABLES scope, which is private to the Application.cfc object. If you actually try to <cfoutput>#myNavImgPath#</cfoutput> in a template--you'll get an error: variable myNavImgPath is undefined.

So, let's assume the previous developer has now tried to find a way to pour those variables into the current page context, within the body of onRequestStart():

In this case, the developer has chosen to pour variables into the request scope for convenient access across the site--a typical CF programming idiom. The problem with this setup is that ColdFusion does not single-thread access to onRequestStart(). Take a look at this execution flow, as two users hit your site. User A is unindented, User B is indented:

The damage is done on unindented/bolded step 4. While the VARIABLES scope is private to the Application.cfc, it persists throughout the life of the call--which is a very short amount of time to you and I...atomically, it is long enough for a 2nd non-single threaded request to enter the body of the method, and update that stateful/shared scope, which is inevitably copied over to the REQUEST scope for use in a template. Although each access to REQUEST is unique to the user (and therefore, saved from sharing/overwriting scopes across users)--by that point, it is too late: the damage is done during the setup of that variable, and by the time the REQUEST scope is accessed in a view template--User A is seeing a corporate logo when she shouldn't be.

Solution:

a) Scope your variables.

b) Understand the ramifications of not scoping.

c) Understand the difference between the VARIABLES scope at the page context (template) level, and the VARIABLES scope within the confines of a CFC.

in CF 8

<cfset var myNavImgPath = '/images/nav_logo.jpg' />

in CF 9

<cfset local.myNavImgPath = '/images/nav_logo.jpg' />

Now, the shared/stateful VARIABLES scope which persists for the (very short) life of the Application.cfc...is out of the picture.

My question was not about application or session scoped variables, although your explanation of the single threading nature of onApplicationStart() and onSessionStart() was something I was not aware of. We lock those scopes when changing values, but don't when reading them. We also try to only read them once per request if possible to minimize chances of any issues. I am well aware of the importance of noting the scopes in accessing them.
–
Rob BarthleNov 19 '11 at 1:10

The code in question is legacy, and when you look at it, it is used as a variables-scoped variable would be used. I guess that leads to another question, is noting them as variables.varName properly scoping them? I know the LOCAL scope is a reserved scope for other components for locally scoping variables in a function. How would that work in an onRequestStart() kind of function I wonder.
–
Rob BarthleNov 19 '11 at 1:12

@DanielMendel Good catch! I glossed over this, and have edited my response near the start to improve clarity around scopes that aren't examined during ambiguity resolution.
–
Shawn HolmesDec 18 '12 at 17:33