Learning ColdFusion 9: Resetting Applications With ApplicationStop()
This morning, as I was starting to learn more about ColdFusion 9's new Object-Relational Mapping (ORM) functionality, I came across a new system function: ApplicationStop(). In the context of ORM, you can call ApplicationStop() to reset the application such that all of the ORM configuration files and settings will be rebuilt. Since these files are only built when the application starts up, ApplicationStop() gives you the ability to "force quit" an application such that the next page request will restart the application and therefore rebuild all of the ORM configuration files. While this makes less sense in production, I can see that having the ability to flush ORM settings as you make changes to your database and your model during development (much like the RefreshWSDL attribute in the CFInvoke tag), will be quite awesome.
This same functionality - the rebuild of the ORM / Hibernate configuration and mappings - can also be accomplished using the ORMReload() method. So this got me thinking: if this can be done in two different ways, then maybe ApplicationStop() has a wider benefit? I wanted to get a better sense of what was going on when this method, ApplicationStop(), was called, so I set up a really simple application to test application and session persistence.
To me, sessions are very tightly coupled to the application under which they exist. As such, the test application needed to have session management turned on:
<cfcomponent output="false" hint="I define the application settings and event handlers."> <!--- Define the application settings. ---> <cfset this.name = hash( getCurrentTemplatePath() ) /> <cfset this.applicationTimeout = createTimeSpan( 0, 0, 5, 0 ) /> <cfset this.sessionManagement = true /> <cfset this.sessionTimeout = createTimeSpan( 0, 0, 3, 0 ) /> <!--- Define the request settings. ---> <cfsetting showdebugoutput="false" /> <cffunction name="onApplicationStart" access="public" returntype="boolean" output="false" hint="I initialize the application."> <!--- Initialize the application settings. ---> <cfset application.dateInitialized = now() /> <!--- Return true so that the page can load. ---> <cfreturn true /> </cffunction> <cffunction name="onSessionStart" access="public" returntype="void" output="false" hint="I initialize the session."> <!--- Initialize the session settings. ---> <cfset session.dateInitialized = now() /> <!--- Return out. ---> <cfreturn /> </cffunction> </cfcomponent>
As you can see in this code, our Application.cfc only has two methods, OnApplicationStart() and OnSessionStart(). Each of these methods creates one scoped variable to mark the time at which the scope was initialized. We will be using these time stamps in the next page, index.cfm, to output the relative age of each scope (Application and Session):
<!--- Param a variable in the application. We are doing this outside of the OnApplicationStart() method to test whether or not the application was truly killed of if the above method was simply called. ---> <cfparam name="application.hitCount" type="numeric" default="0" /> <!--- Increment the hit count. ---> <cfset application.hitCount++ /> <cfoutput> <h1> Application And Session Overview </h1> <p> Application hit count: #application.hitCount# </p> <p> Application initialized: #dateDiff( "s", application.dateInitialized, now() )# seconds ago. </p> <p> Session initialized: #dateDiff( "s", session.dateInitialized, now() )# seconds ago. </p> <p> <a href="reset.cfm">Reset application</a> » </p> </cfoutput>
As you can see in this code, we are outputting the age of both the Application and the Session scopes based on their DateInitialized time stamp. We are also defining and incrementing a new Application variable to record the hit count to this page. I am using this hitCount variable to see if the Application is truly reset, or if the OnApplicationStart() method is simply called again. If the application was not truly reset, then this extra-method variable assignment would not be reset.
At the bottom of the page we are providing a link to reset the application. Normally, this kind of link would end up triggering code within the Application.cfc that might look like this:
<!--- Check to see if reset flag exists in URL. ---> <cfif structKeyExists( url, "reset" )> <cfset this.onApplicationStart() /> </cfif>
Here, we are manually calling the application initialization function from within the Application.cfc itself (usually from within the OnRequestStart() event handler). But, using this new ColdFusion 9 method, ApplicationStop(), we are doing this implicitly:
<!--- Stop the application. After calling this method, the next page request to the application should start it up again (resetting it). ---> <cfset applicationStop() /> <!--- Redirect back to overview. ---> <cflocation url="index.cfm" addtoken="false" />
As you can see, this code just stops the application and then redirects the user back to the overview page. At this point, the application has stopped and the next page request (precipitated by the CFLocation tag) should restart the application.
When I refresh the overview page a few times, I get this output:
Application And Session Overview
Application hit count: 5
Application initialized: 41 seconds ago.
Session initialized: 41 seconds ago.
Then, when I click on the "Reset application" link, I end up back on the overview page with the following output:
Application And Session Overview
Application hit count: 1
Application initialized: 3 seconds ago.
Session initialized: 91 seconds ago.
As you can see, the entire Application scope was wiped out and the OnApplicationStart() method in the Application.cfc must have executed to reset the DateInitialized property. But, the Session scope was not changed at all! Whether or not this was done on purpose, I think that this is a bug in the ColdFusion 9 application architecture. I strongly believe that when an application stops, all of the sessions within it should stop as well. Remember, these are not two completely independent entities - the Application might contain references or aggregates of the Session data. If the Application resets and the Sessions continue to live on, unaltered, then potentially troublesome discrepancies could form between the two memory pools.
When I saw that ColdFusion 9 provided two ways to flush the ORM / Hibernate configuration and mappings, I thought that maybe one of these methods - using ApplicationStop() - might have additional benefits in terms of allowing us to restart applications (something that has before only been available via hacks). After some testing, it seems that ApplicationStop() allows us to reset an Application, but only partially, leaving the Session scopes untouched.
Want to use code from this post? Check out the license.
I am waiting to hear some feedback before I file this issues (sessions not be reset) as a bug. Let me know what you think. I am *strongly* inclined to think it is a bug, but would love to hear other people's thoughts.
I agree, for me the expected behavior when resetting the application would be to also clear all the sessions.
I'm somewhat on the fence here. I guess it is because I'm used to using frameworks such as Model-Glue and Mach-II where I reload the framework when making changes. To do this I call onApplicationStart() which essentially resets the application. It does not affect the sessions.
It seems logical that stopping the application should clear out sessions, but unless you've coded it to do so, the application does not know about sessions. The app knows only about application scoped variables.
I'd love to comment on this, but there appears to be no documentation for ApplicationStop in Adobe's documentation:
It mentions the method, but offers no link for a full definition.
Yeah, the only other place that I could find that mentions it is in the ORM section, very briefly, as an after thought (see last line on page):
Right now, it's hard to compare this to anything because traditionally, we've only been able to "hack" this functionality. As such, I don't think we should make any decisions based on existing frameworks as this really is new functionality.
That said, my gut tells me that resetting an app should reset all sessions. Should a session ever exist outside of an application? That just seems off to me.
As a follow up to that thought, perhaps we should think about whether or not a Session should ever have a higher timeout than an Application? If we agree that a session should exist outside of an application, then *yes* a session can / might have a higher timeout. But, if *no*, then session are tightly bound to their parent application.
Hmm, maybe I'll give that a quick test.
I actually think the behavior works correctly.
If you're forcefully "stopping" an application because you've rolled out structural changes, then you should be clearing everything about the application to ensure that application works correctly.
If you were to make a change that affects the the SESSION scope, then using the applicationStop() is a way to reset the application w/out needing to restart the application server.
So, I think the behavior is the safest method for handling the issue.
However, I could see the usefulness of being able to keep sessions active, so perhaps an additional argument could be added to keep sessions alive.
I am sorry, I am not following you. Are you saying that the way ApplicationStop() is working now is what you think is correct?
Think about doing something like changing your Application-cached Factory that is responsible for creating things like Session-cached User objects. To me, it seems that if anything changes in the Application, it might have fallout that directly affects items created and cached in the session. As such, to me, the full-wipe across both memory pools seems the safest default.
The main reason I mentioned the framework reloading is because it is primarily done for the same reason: to reload any configuration changes (including ORMs such as Reactor or Transfer). I agree new functionality shouldn't be based on that practice though.
I guess it would be interesting to know if the Hibernate ORM session management is in any way tied to the traditional user session. I would guess not because you wouldn't want to restart an app to load new ORM changes but a user's existing session had old ORM info.
I'm awaiting your test results of timeout tests as that should show how the CF server defines the relationship between app & session.
Curious though, is there a way now (pre CF 9) to wipe out all existing sessions, short of restarting the CF service?
I have to disagree here, Ben. If you have your Session and your Application intermingled, that's something you need to work out. Forcing a reset of all Sessions could be suboptimal.
My counterexample would be a simple (one-server) eCommerce site. My Session stores login information, cart contents, etc. That information absolutely should survive a reset of the Application. If I need to bounce the Application, say because I'm hitting a memory/performance hit with my cache logic, or because I need to apply a security patch, I lose money if the dozen or hundred people on my site suddenly lose their cart contents.
(Yes, it's a contrived example. Presumably, I have more robust cart/login info than that. But that's not the point.)
I mean, I see what you're getting at -- if you have that level of intermingling then it could cause you serious headaches. Maybe it should have a resetSessions parameter?
Have you tried setting a reference to an app-scoped value in your session, and seeing what happens to it using your reset two different methods?
cfset application.teststruct = StructNew()
cfset application.teststruct.initialized = Now()
cfset session.teststruct = application.teststruct
Then dump the session variable in your index.cfm.
I think what is displayed in the dump will determine how I feel about the issue.
I agree with those that have said it would be nice to have an option to clear the session scopes, but I do not think it should do it automatically. Just like with everything else in ColdFusion, it would be nice to have options.
Clearing out the session is not always optimal, as Rick said, and in some cases it could be considered critical that they not be. In fact, though I have not tested this yet, in reading I believe it is possible to deploy your application in a way were sessions will even persist beyond a server restart.
As it turns out, I am quite wrong. It seems that this is completely inline with the default behavior of the ColdFusion application framework. I just did some relative timeout testing in ColdFusion 8 and found the exact same results:
Yeah, I was going to say something but you got to it before I could Ben. This new application function is great. It opens up a lot of doors, as your probally all ready know.
I personally believe that sessions are directly related to the application and should be reset with the application, however since the ColdFusion application framework is not setup that way; I am happy to second the request for a stop session flag in applicationStop().
With your cart example, how do you handle session timeouts?
I actually like the fact that it doesn't kill the sessions. I think it has many advantages for it to not kill the application. Assume that the user wishes to restart the application, or switch applications without having to login or authenticate again
Ben and I spoke briefly about this on twitter the other day but I thought I'd come and have another read through this post.
I like the idea of being able to stop an application programmatically like this, however as a few other people have said plenty of us already mimic this kind of functionality using the onApplicationStart() method called from onRequestStart() using an init key in the URL, this is also something built into many of the frameworks so I'm not quite sure what 'value' this really adds to the product.
Where I would really like to see adobe go with this is giving application run control in the admin panel on a per-application basis allowing us to stop/start/pause applications individually without the need to restart the entire cf server.
I've found on several occasions that I'll perhaps have a bug or bottleneck on an individual application which takes me a while to trace, until the issue is addressed the application just sits there hung in an infinite loop or something and this forces me to restart the entire server which hosts a whole bunch of applications all of which then have to reinit, potentially also killing any sessions which are active within those apps at the time of reset.
Being able to stop individual applications from within the admin would be such a great benefit to me and many others I'm sure and if Adobe already have this groundwork in place I'm sure it wouldn't require much work on their part to factor it in.
Does anyone else have a thought on that?
"....we've only been able to "hack" this functionality."
What kind of hacks are you referring to? Stuff like clearing out the application struct?
I'm looking for programmatic ways to stop an application so that garbage collection can reclaim its memory. So hacks may be usable.
I was probably referring to overriding the application TimeOut value on a given request (putting it down to 1 second). This is not the best approach as it can quickly be overridden by a parallel request to the server. Like @Robert is saying, though, manually executing the onApplicationStart() is probably pretty good.
As far as garbage collection goes, I am not sure that having the Application actually timeout does anything. Garbage collection is about memory references; not referencing values should be garbage collected whether or not the application is runnin?
1. Hmm, I'll have to look into how to dynamically override the application timeout (maybe it's already on your blog) cause I'd like to kill the app completely. Not to restart it again by calling onApplicationStart().
Use case is, let's say you have a production server and have a "test" section on it where you can preview some changes. After I'm done, there's no need for the "test" version of the application to exist so I want to kill it.
In other words, I'd like force CF to think that it's time for "onApplicationEnd"
2. Right. And I think clearing application or server scope values will dereference them and clean those up.
But what I'm thinking about is that I'd like CF itself to remove any references it may have to the application.
So when I start the "test" version CF knows about it, when I "kill it", I want to derefence any values but also have CF delete any references it may be keeping for the app.
Maybe dynamically timing out the app will cause CF to forget about it. Have to test.
If you have a "test" version of the application, you can probably just give it a really small application timeout (ex. 10 seconds). That way, you can test your stuff and let it die out quickly.
I'll try that.
Do you recall blogging about dynamically changing application timeouts? Thank you.
I don't think I ever blogged about dynamic application timeouts as I typically deal more with dynamic session timeouts. Basically, it just comes down to setting the timeout in the Application.cfc pseudo constructor.
Imagine, in your test app, you wanted to use the url variable, "killTest" to kill the test version of the app. Your Application.cfc could have this in the settings area:
<!--- Set regular timeout. --->
<cfset this.applicationTimeout = createTimeSpan( 2, 0, 0, 0 ) />
<!--- Check for URL flag. --->
<cfif structKeyExists( url, "killTest" )>
<!--- Kill app in one second. --->
<cfset this.applicationTimeout = createTimeSpan( 0, 0, 0, 1 ) />
Thank you! Sometimes the simplicity escapes you when you're down in the weeds.
No problem my man.
Add myself to the side supporting that calling this function SHOULD be a true application reset. I completely agree that application and sessions are intertwined. It would be wonderful if this function could be used instead of a service restart, in it's present state it cannot 100% of the time (yes you can change the app name or set a low timeout value, not elegant though).
In our framework, we initialize all application level variables inside the onapplicationstart method of application.cfc. You can call this method directly to "flush/reset" those variables. The applicationStop function seems to do the same thing, in our case (I image it will also flush variables set else where in the application).
Yeah, I assume it will flush all application-scope variables when you can applicationStop(), regardless of where they are initialized.
As far as the connectivity between application and session, I'm on the fence about this. It is clearly the existing behavior; but, do I like that? I am not sure. Part of me feels like if you need to reset the app, it's super possible that you need to reset the sessions as well.
It appears ORMReload() does not update the fields in the database either. That appears to happen on restarting the application but not on running that command. Was going to set the system to automatically update on a dev server and it didn't seem to work for me. (9.0.1).
I don't have too much experience with the ORM stuff, but that might be a bug with how it is supposed to work? Not sure.
Just wanted to say, I value your work. I'm not a "guru" at cold fusion but whenever I have an issue (well for the past 2 months, new project) your site always seems to be on the first page of any search engine and is 100% relevant to what I'm trying to accomplish.. I really appreciate your dedication and talent. Thank you
Thanks a lot my man; I really appreciate you saying that - it's always so motivating to keep at it.
Getting out my backhoe and dredging this baby back up.
It seems people prefer the onApplicationStart() method of resetting everything but that doesn't reset the session variables either.
What I have done in the past is just delete the structures session and application and then return to index.cfm to start all over. This also gives me control over just clearing the session variables or application variables without doing the other if I so want.
Is there a better way?
It took me a while to really wrap my head around the relationship between the application and session scopes. They are related, but only slightly. One can end without the other being affected (in terms of memory space):
Also, I'd be careful about actually clearing the session scope. Deleting the session-based CFID/CFTOKEN values doesn't actually end the session and can lead to a corrupt session:
Also, another thing to keep in mind is that clearing out the session only clears out *your* session. The rest of the people using the site will still have their previous session structure.
If you need to reset all session scopes, one approach that I've taken is to keep a dateInitialized time stamp in both the application and the session scopes. Then, if I re-init the application (giving it a new timestamp), I can check that on each request to re-init all sessions that are now out of date (dateInitialized is later in the session than it is in the application).
Not sure if any of that is helpful - resetting things can get a bit tricky. Sometimes, I just restart the ColdFusion service if the changes are too drastic to be managed programmatically.
Didn't read every comment but my opinion is that applicationStop() should NOT stop or reset sessions for the reason that I may want to restart the application after a change without effecting sessions. If your app crashes when it follows this manner then you should rethink your dependencies if possible, otherwise don't use the function and find another way.
I have to say that this surprised us recently.
We expcected the applicationStop() method to also killl of the session scopes that existed under the application we were restarting.
A google search of the method brought us here! (Thanks!)
Intuitively, I think it's wrong that it doesn't kill the session scopes - but I also think it is great.
We store configuration settings / site specific settings / vocabs in the application scope and so the ability to reload the ORM / application scoped structs etc and NOT kill off all the existing users session too - is a fabulous advantage for the end-user.
Super late in response to this, but because having ApplicationStart() in OnRequestStart will throw an error about ORM not being enabled (and subsequently a 505, as my ant-build was complaining about). Putting it in OnRequestEnd() seems a lot more smooth.
what if someone still using application.cfm