Search code examples
quartz.netshutdowniis-10asp.net-core-2.2

How to gracefully end a Quartz 3.0.x with ASP.NET Core 2.2 job in IIS?


I am trying to gracefully end a Quartz job which run in IIS. My code is as follows:

the job

    [DisallowConcurrentExecution]
    public class TestJob : IJob
    {
        private ILoggingService Logger { get; }
        private IApplicationLifetime ApplicationLifetime { get; }

        private static object lockHandle = new object();
        private static bool shouldExit = false;

        public TestJob(ILoggingService loggingService, IApplicationLifetime applicationLifetime)
        {
            Logger = loggingService;
            ApplicationLifetime = applicationLifetime;
        }

        public Task Execute(IJobExecutionContext context)
        {
            //TODO: does not seem work
            //context.CancellationToken.Register(() =>
            //{
            //    lock (lockHandle)
            //    {
            //        shouldExit = true;
            //    }
            //});

            return Task.Run(() =>
            {
                //TODO: check
                ApplicationLifetime.ApplicationStopping.Register(() =>
                {
                    lock (lockHandle)
                    {
                        shouldExit = true;
                    }
                });

                try
                {
                    for (int i = 0; i < 10; i ++)
                    {
                        lock (lockHandle)
                        {
                            if (shouldExit)
                            {
                                Logger.LogDebug($"TestJob detected that application is shutting down - exiting");
                                break;
                            }
                        }

                        Logger.LogDebug($"TestJob ran step {i+1}");
                        Thread.Sleep(3000);
                    }
                }
                catch (Exception exc)
                {
                    Logger.LogError(exc, "An error occurred during execution of scheduled job");
                }
            });
        }
    }

Startup.cs

protected void StartJobs(IApplicationBuilder app, IApplicationLifetime lifetime)
{
    var scheduler = app.ApplicationServices.GetService<IScheduler>();
    //TODO: use some config
    QuartzServicesUtilities.StartJob<TestJob>(scheduler, TimeSpan.FromSeconds(60));

    lifetime.ApplicationStarted.Register(() => scheduler.Start());
    lifetime.ApplicationStopping.Register(() => scheduler.Shutdown());
}

private static void ConfigureApplicationLifetime(ILoggingService logger, IApplicationLifetime lifetime)
{
    lifetime.ApplicationStopping.Register(() =>
    {
        logger.LogInfo(null, "Application is stopping...");
    });

    lifetime.ApplicationStopped.Register(() =>
    {
        logger.LogInfo(null, "Application stopped");
    });
}

So, I have hooked to ApplicationStopping to be able to shutdown Quartz jobs in a graceful way. However, when IIS application pool is about to be ended the jobs are ended abruptly:

  • change something in web.config to trigger application pool stopping
  • application pool is configured to allow up to 90s before ending. Also overlapping is allowed
  • logging records Application is stopping and immediately Application stopped, thus the job is finished abruptly

I remember that a similar implementation worked in ASP.NET with Quartz 2.x: Quartz jobs were allowed to finalise their work provided they managed to do so within application pool shutdown period.

Question: How to gracefully end a Quartz 3.0.x with ASP.NET Core 2.2 job in IIS?


Solution

  • You can give a true to 'scheduler.Shutdown()' to wait for the jobs to be finished. This should delay the pool shutdown till the jobs run to an end.

    lifetime.ApplicationStopping.Register(() => scheduler.Shutdown(true));
    

    You can read more about it here.