fwreinit under heavy load - workaround

I wanted to post this to the community, hope it helps someone. Please
give feedback if you notice anything that could be done better.
Thanks!

Problem:

Under heavy load, we have had trouble reinitting the framework.
Recently during a large auction for one of our biggest clients, I
accidentally hit a link in my browser's URL history and fwreinit'ed
the site! Since we had so many people on the site, the reinit
actually took quite a bit of time, and a bunch of people saw error
pages. That sucked! Luis suggested a maintenance flag that would
override processColdBoxRequest() outside of ColdBox. Here is what I
came up with:

Application.cfc:

    <!--- on Application Start --->
    <cffunction name="onApplicationStart" returnType="boolean"
output="false">
      <cfscript>
        application.maintFlag = false; //maintenance flag for reinits,
bypasses processColdBoxRequest()
        application.lastReinitTime = now(); //last time app was started
        loadColdBox();
        return true;
      </cfscript>
    </cffunction>

    <!--- on Request Start --->
    <cffunction name="onRequestStart" returnType="boolean"
output="true">
      <cfargument name="targetPage" type="string" required="true" />
      <cfscript>
        if (not application.maintFlag or (application.maintFlag and
isDefined("url.fwreinit"))) { //allows any request with "fwreinit" to
go through, but stops anything else
          reloadChecks();
          if (findNoCase('index.cfm', listLast(arguments.targetPage, '/')))
{
            processColdBoxRequest();
          }
        }
        return true;
      </cfscript>
    </cffunction>

This sets up the maintFlag application variable and skips the
processColdBox request if it is enabled (and fwreinit is not in the
URL). I actually am sending them a nice error, but didn't include it
in this code.

Then I used 2 interceptors, preReinit and afterAspectsLoad (Notice:
preReinit is 3.0+)

  <cffunction name="preReinit" output="false" access="public"
returntype="void" hint="Fires right before the reinit occurs">
    <cfargument name="interceptData" required="true" type="struct"
hint="A structure containing intercepted information.">
    <cfscript>
      //set app to maintFlag = true
      application.maintFlag = true;
    </cfscript>
  </cffunction>

  <cffunction name="afterAspectsLoad" output="false" access="public"
returntype="void" hint="Loads after all objects are created">
    <cfargument name="interceptData" required="true" type="struct"
hint="A structure containing intercepted information.">
    <cfscript>
      application.lastReinitTime = now(); //set last reinit time
      //set app to maintFlag = false
      application.maintFlag = false;
    </cfscript>
  </cffunction>

So basically, when you call fwreinit, the preReinit interceptor sets
maintFlag to true, then the afterAspectsLoad interceptor sets it back
to false. This single threads the application during an fwreinit, and
works really well. Now, while sending a boatload (that's more than a
buttload, but less than an assload) of hits to the site, we can reinit
to our hearts content and it works great. fwreinit is a lot faster as
well.

The only issue I currently have is that when sending a lot of hits to
a remote proxy, I will see 2 - 5 errors happen before it cleans up and
everything is working fine. Can anyone think of why the remote proxy
would act different than a "normal" event? They all go through the
application.cfc onRequestStart function.

I'm thinking possibly these requests are in the middle of being
processed when the reinit fires, so there is really no way to keep
them from happening, but I never see them in any other handler except
the remote proxy. If anyone can think of why, I would appreciate it!

The errors are:

- ColdBox Controller Not Found - The coldbox main controller has not
been initialized - The error occurred in C:\inetpub\amp\www\coldbox
\system\core\util\Util.cfc: line 128
- Element cbController is undefined in a Java object of type class
coldfusion.runtime.ApplicationScope - The error occurred in C:\inetpub
\amp\www\coldbox\system\Coldbox.cfc: line 334
- Element COLDBOXOCM is undefined in INSTANCE. - The error occurred in
C:\inetpub\amp\www\coldbox\system\web\Controller.cfc: line 109

Thanks, and hope this helps someone else in the same situation.

This is great Ken.

The proxy still goes via application.cfc so it seems weird

Ken's code works great for reiniting through the url. Thank you.

I also needed this to work when the app reinits through the deploy
interceptor so here's my proof of concept if anyone is interest. It's
a little cumbersome but it is working although I'm open to feedback.

1. In my custom deploy interceptor (based on the core one), I set the
following values in my "preProcess.

     application.maintFlag = true;
     application.maintFlagFromDeploy = true;

     // Reload ColdBox
     getController().setColdboxInitiated(false);
     getController().setAspectsInitiated(false);

"maintFlag" flag is set to true because this is what happens on
"preReinit" but that interception point isn't going to fire when using
the deploy interceptor.

"maintFlagFromDeploy" is a flag that lets me know that the deploy
interceptor is causing a reinit.

2. In application.cfc, both of the values above are set to false in
onApplicationStart like in ken's example. But my onRequestStart looks
something like below. In addition ken's checks, I add an additional
check to see if "maintFlagFromDeploy" is true. If it is, allow that
request to go process and then set the value to false so that other
requests will not process and instead show the nice message in the
cfelse.

<cfif application.maintFlagFromDeploy
      OR (NOT application.maintFlag
      OR (application.maintFlag
      AND isDefined("url.fwreinit")
      )
)>
      
     <cfset application.maintFlagFromDeploy = false>
        
     <!--- Reload Checks --->
     <cfset application.cbBootstrap.reloadChecks()>
    
     <!--- Process A ColdBox Request Only --->
     <cfif findNoCase('index.cfm', listLast(arguments.targetPage, '/'))>
          <cfset application.cbBootstrap.processColdBoxRequest()>
     </cfif>
    
<cfelse>
       <!--- Show a nice message. We'll be right back after reinit --->
</cfif>

3. The deploy interceptor works in 2 requests as far as I understand
it. In the first request, coldboxinitated and aspectasinitiated is set
to false. In the second request, it reinits.

For my changes, I set my two application values in the deploy
interceptor to true in the first request. In the second request, it
sees that "maintFlagFromDeploy" is true, allows the request to
continue processing, and then immediately sets it to false so other
requests don't process.

Recall that the deploy interceptor works across 2 requests. So if I
only set "maintFlag" in the first deploy request, then my app will
always display the "nice message" and never reinit because "maintFlag"
= true in the second request of a deploy always goes into the "nice
message.". This is why I used a second value of "maintFlagFromDeploy"
to say: "I know we're currently reiniting, but you need to let this
request through". That request being the second one involved in the
deploy process.

- Gabriel