locked
detect when an app domain is being shut down RRS feed

  • Question

  • User-324641873 posted
    Hi,

    We have HttpHandlers that can process requests for up to 50 minutes.  Those
    HttpHandlers are running in an app domain inside the DefaultAppPool.  We'd
    like the HttpHandlers to be able to detect when its containing app pool or
    app domain is requesting a shutdown (when it is recycling, for example).  I
    looked at the Application_End method in global.asax, as well as the System.
    AppDomain.CurrentDomain.DomainUnload event.  Neither get called as long as
    the HttpHandlers are handling a client request.  Is there a way to
    programatically know when an app domain is about to shut down?  We need to
    know so that we can force the request to end more quickly and gracefully.

    Thanks for your help. [:)]

    Bubba

    Monday, December 18, 2006 2:30 PM

All replies

  • User-1683291434 posted

    hi,

    i had a similar problem recently, didn't know exactly when the application shut down. 

    According to the documentation the application is shut down in case of 20 minutes idleness - no user request. So until you are processing a request you will never get your application shut down. Do you need actually ?

    From my point of view, you should not force the request to finish more quickly but to process is in the right and complete way.

    Personally, i needed to have a web application that is not restarted. So I took advantage of the new application pools in IIS 6.0.

    I configured my web to not be restarted because of this inactiveness and the process to be recylcled on regular basis(once a day for example).

    Cheers,

    Yani 

    Monday, December 18, 2006 5:03 PM
  • User1347187933 posted

    Hi, 

    IIS application pool can be recycled manually, can you test if Application_End is fired when manually recycling IIS?

    <CENTER>Recycle Application Pool</CENTER> <CENTER> </CENTER>
    Tuesday, December 19, 2006 1:43 AM
  • User-324641873 posted

    Hi,

    Application_End is not called until the requests being handled are completed.

     Thanks,

    Bubba

    Tuesday, December 19, 2006 8:34 AM
  • User-1683291434 posted

    Well, man, I still don't understand what is your problem with the application end event.

    Could you clarify?

    Cheers,

    Yani
     

    Tuesday, December 19, 2006 10:43 AM
  • User-324641873 posted

    Hi Yani,

     The Application_End event will not get called until AFTER all executing requests are finished.  Remember, those requests might take up to 50 minutes.  That's too late for my purposes.

     Thanks,

    Bubba

    Tuesday, December 19, 2006 11:14 AM
  • User-1683291434 posted

    What are your purposes ?

    What is the problem that the worker process is recycled 50 minutes later? 

    Tuesday, December 19, 2006 11:18 AM
  • User-324641873 posted

    The problem is that any new requests coming in get queued up in another instance of w3wp.exe.  Those new requests will not get processed until after the first w3wp.exe shuts down.

    Tuesday, December 19, 2006 11:25 AM
  • User-1683291434 posted

    Okay, now i can see the problem :)

    A solution i could suggest is to run your web application into a new your own defined application pool. This feature is available in IIS 6.0. In the configuration of the application pool you could configure that you worker process is *not* recycled under no circumstances. This way your user requests will be  processed okay. However, not recycling the process at all could bring your application running to slow. If you experience such problems you could schedule recycling to appear at particular date time ( day + hour) and you could programmatically deny user requests when the time for recycling is about to come.

    In case you don't know how to make the configuration for recycling, feel free to ask.

     
    Cheers,

    Yani
     

    Tuesday, December 19, 2006 12:49 PM
  • User808892575 posted

    Hi Yani,

    PMFJI (a little late).  I need to also detect when the AppDomain is shutsdown.  Basically I am using an Asynchronous HTTP Handler to pass jobs to a single (Infinate) running thread.  If the thread is not running the request tries to start it.  My issues are this:

    1) I do not want auto-recycling to force this thread down (I haven't seen this happen actually).

    2) I want to shut this thread down safely (finish processing) and quit.

    Can I use Application_End in this case?

    Basically the thread sits idle most of the time holding onto requests asynchronously for upto 45 minutes (Use of AutoReset events).  It holds on to these requests by adding them to a shared ArrayList that the thread periodically looks at to respond to.  If I'm holding on to requests does that prevent recycling?  Secondly I need to trigger this thread to wake up if the ASP.NET process is going to die so I can gracefully free this thread.  At the moment this app just dies in a big mess!!

     My testing so far shows that these requests are held by this thread successfully for up to 40 minutes.  What I haven't done is stressed tested this yet and I am worried all the automatic management of ASP.NET and the CLR is going to cause me some trouble.  I am also worried about not knowing when ASP shutsdown. 

     I thought I could use finalizers here but this is a bad idea as they should not wait for anything and they have a max of 2 seconds when a .NET app is shutting down.

    Could you offer any advice here?

    Many Thanks

    NozFx  

    Wednesday, May 2, 2007 7:33 AM
  • User-1683291434 posted

    Hi NozFx,

    I'm not sure I fully understood your scenario.

    But here is some information that may by helpful for you:

    1) I do not want auto-recycling to force this thread down (I haven't seen this happen actually). -> unfortunately I've seen that. Actually, if you have any background process running and not serving a user request but just doing some other background task (I also use AutoReset events and have a lot of things going on in the background) , your thread will recieve 'Abort' message (an exception will be thrown ThreadAbortException or sth like that , don't remember it by heart), so it won't wait till your thread finishes

    2) I want to shut this thread down safely (finish processing) and quit. -> if you know a way of doing that in the Application_End event handler that you could use it, the web server will definitely wait until that code is executed before shutting down the whole application.

    I am also worried about not knowing when ASP shutsdown -> That worries me either :) I was able to find out concrete specification when the Application_End event is fired (or the web app is shut down) , but basically from exp I could say that this happens in some (not fully able to define how long) interval of time (I have noticed this happens in 3 minutes idleness, also in 20 minutes idleness - so not being able to tell what's that period)

    The way I solved my problem is to configure that application pool not to recycle the process on any circumstatnces. And the application should run in infinite loop :)

    Fortunately, our application supports shut downs ( I just wanted to make them as less as possible) and it is being able to recover after such, despite being shut down

    in a middle of something. So you could either implement a similar to ours mechanism for handling such situations.

    Cheers,

    Yani

    Wednesday, May 2, 2007 4:06 PM
  • User-225114762 posted
    Check the HostingEnvironment.RegisterObject and IRegisteredObject interface. I have not actuallly done this, but the docs seems to state that the Stop() method of the registered object will be called when the app domain is about to end. Presumably this is done on different threads for the application itself and other registered objects. The Stop() method in turn can signal the handlers in whatever way and ask them to shutdown gracefully.
    Thursday, May 3, 2007 10:44 AM
  • User808892575 posted

    Hi Yani, Svante,

     Thank you for both your replies.

    I've actually implemented the IRegisteredObject interface successfully.  It works like a charm.  The problem I got is that it works too good ;) 

    What I actually mean here is the App Domain is shutting down frequently killing my thread every time.  Ahh, I was trying to play nice but its bitten me hard and I'm getten taken advantage off!!!  The weird thing is without this registered object the thread (appears to operate OK but my guess is that I'm getting multiple Domains launched for each request.

    Its a weird result I'm seeing here.  If I launch one request the app runs fine for a long time.  As soon as I launch a simultaneous request the AppDomain dies and start anew?!?!?  Why?  This kind of indicates each request attempts to shutdown the old Domain and start a new? Huh?

    Yani, I have a problem testing here as I'm using XP IIS 5.  Have I got no choice but to move to IIS 6 to test this properly with no auto-recycling?  Am I correct in saying this problem I'm saying is a auto-recycle problem?

    Thanks

    NozFx

    Friday, May 4, 2007 5:06 AM
  • User-225114762 posted

    Just a guess - have you just discovered a latent problem that's been there the whole time, except before you didn't see it because it waited for your longrunning requests to finish? There are many reasons for it to recycle - one rather subtle one in ASP.NET 2.0 is that if you create or delete files or directories anywhere in the file system under your application root, the app will recycle...

    You can control process recycling to some extent in IIS 5 too, via Web.Config/Machine.config. See http://msdn.microsoft.com/en-us/library/7w2sway1.aspx and http://msdn.microsoft.com/en-us/library/aa719566(VS.71).aspx for example.

    Friday, May 4, 2007 5:31 AM
  • User808892575 posted

    Hi Svante,

     I think your right, it looks like I'm seeing a problem that has always been there I think?  Saying that though, early testing I was responding back to client with ThreadID and this was not changing between requests.  This is definately happening when a second request comes in!  As soon as a new request comes in Stop() method gets called.  The odd thing is that when I turn on Lifetime Events loggin the App Domain doesn't appear to be getting closed down.  I can see the log entry for it starting but when my app reports the request to shutdown ASP does not log that the App Domain is shut down.  This is really weird!!!  Its almost like the App Domain is not actually shutting down but my app is!?!

    Thanks for the links I'll check em' out.

    Friday, May 4, 2007 6:24 AM
  • User-225114762 posted

    You are not using the method described in the HostingEnvironment.RegisterObject documentation, calling ApplicationManager.CreateObject etc? I think you should just instantiate your object implementing IRegisteredObject, and then call RegisterObject with it. The description there is, I believe, to register your own application. You just want to be a part of the existing application, and notified when it exits.

    Check the event log also, worker process recycling will be shown there.

    Friday, May 4, 2007 6:46 AM
  • User808892575 posted

    Hi Svante,

     Maybe my implementation of IRegisteredObject is incorrect?

     I have a singleton class that inherits the IRegisteredObject interface.  The way I've implemented the singleton is with a private constructor and a public static readonly field that initialises to a new instance of the Object itself by calling the private constructor.  Within the private constructor I call the HostingEnviroment.RegisterObject() method passing (this) as the object ref to register.  To initantiate the singleton within the Async Handler class I have a private static member that just reads the public static readonly field to get the object ref.  Do you think there is anything wrong with doing this?  Code example below:

    public class AUTDRequest : IHttpAsyncHandler
    {
        // Required property
        public bool IsReusable { get { return false; } }

        // The IRegisteredObject Singleton
        private static AppDomainShutdown AppCloseNotification = AppDomainShutdown.Instance;

        // More class code. . .
    }

    sealed class AppDomainShutdown : IRegisteredObject
    {

        // Singleton reference
        public static readonly AppDomainShutdown Instance = new AppDomainShutdown();

        // Singleton private Constructor
        private AppDomainShutdown()
        {
            // Register the object
            HostingEnvironment.RegisterObject(this);
        }

        // Interface method
        public void Stop(bool bImmediate)
        {
           // Whats it want us to do?
           if (!bImmediate)
           {
               // Do some code to handle graceful
               // if OK

               HostingEnviroment.UnRegisterObject(this);
           }
           else
           {
               // Do some code to force down (THREAD.ABORT)
               // Mandatory

               HostingEnviroment.UnRegisterObject(this);
           }
        }
    }

     
    Friday, May 4, 2007 6:48 AM
  • User808892575 posted

    Hi Svante,

     Is my mistake not calling ApplicationManager.CreateObject()?

     If so how do I identify the parameters it needs?

    TIA

    NozFx

    Friday, May 4, 2007 7:21 AM
  • User-225114762 posted

    Hi,

    Look very nice to me. Schoolbook implementation. As I said - I've not actually tried this. I think you should probably try to narrow down the issue by implementing minimal app that just registers an object, and checks when Stop is called. Then if that works, expand to async and see if it by any chance makes a difference, and continue building up to your actual app until it stops working. If it does not work in the minimal app, it's probably time for a bit of reflection...

    Friday, May 4, 2007 7:23 AM
  • User-225114762 posted

    Is my mistake not calling ApplicationManager.CreateObject()?

    Could be, although I was under the impression that you specifically should not do this in your case, since it's said to be to register things from the outside inside your app domain.

    The parameters aren't too hard though. appId is HostingEnvironment.ApplicationID, type is your type, virtualPath and physicalPath also comes from HostingEnvironment.

    Friday, May 4, 2007 7:30 AM
  • User808892575 posted

    Hi Svante,

     Thanks for checking through my code and thanks for the comments <bg>.

     Looks like I'm a prize idiot! 

     You were right to simplify the code until I worked out the problem.  It turns out the application lifetime events settings didn't kick in properly for a while.  By the time I got to implement the code at fault I actually saw why the App Domain was shutting down per-request in the log.  It turns out I forgot that I implemented some code to protect and unprotect the web.config file!!  I am doing this on each request so everytime the file changes the App Domain closes.  At least I know the my code is actually behaving correctly <g>!

     Anyway, this still leaves me with a slight problem.  I need to read this file but I want my connectionString to be protected.  Reading it shouldn't harm anything should it? (Unless it changes the file attributes!!)  Also if the file is encrypted should do I need to explicity unprotect it before reading the setting.  I thought I did but I maybe wrong.

    Thanks for your help and advice, it has been invaluable!

     NozFx

    Friday, May 4, 2007 10:41 AM
  • User-225114762 posted

    I need to read this file but I want my connectionString to be protected.  Reading it shouldn't harm anything should it? (Unless it changes the file attributes!!)  Also if the file is encrypted should do I need to explicity unprotect it before reading the setting.  I thought I did but I maybe wrong.

    If you use the built-in encrypted section API in ASP.NET 2.0, you will have the section encrypted once, and decrypted transparently. You can also move the connection strings out of the actual web.config, which may help in deployment etc - and also avoids app restarts. See http://msdn.microsoft.com/en-us/library/ms998280.aspx and http://msdn.microsoft.com/en-us/library/ms998283.aspx for how to encrypt the connection string (this may be old stuff for you) and http://msdn.microsoft.com/en-us/library/ms254494(VS.80).aspx for how to use external configuration files.

    At your service,

     

    Friday, May 4, 2007 12:57 PM