diff --git a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs index afa5eb8132..adb9c6c0dd 100644 --- a/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs +++ b/src/Umbraco.Web/Scheduling/BackgroundTaskRunner.cs @@ -217,7 +217,11 @@ namespace Umbraco.Web.Scheduling { lock (_locker) { - if (_isCompleted) return false; + if (_isCompleted) + { + _logger.Debug(_logPrefix + "Task cannot be added {0}, the task runner is already shutdown", task.GetType); + return false; + } // add task _logger.Debug(_logPrefix + "Task added {0}", task.GetType); diff --git a/src/Umbraco.Web/Scheduling/Scheduler.cs b/src/Umbraco.Web/Scheduling/Scheduler.cs index f1f48f141a..82dd32b870 100644 --- a/src/Umbraco.Web/Scheduling/Scheduler.cs +++ b/src/Umbraco.Web/Scheduling/Scheduler.cs @@ -1,7 +1,10 @@ -using System.Web; +using System.Collections.Generic; +using System.Threading; +using System.Web; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; +using Umbraco.Web.Routing; namespace Umbraco.Web.Scheduling { @@ -14,60 +17,74 @@ namespace Umbraco.Web.Scheduling /// internal sealed class Scheduler : ApplicationEventHandler { - private static BackgroundTaskRunner _keepAliveRunner; - private static BackgroundTaskRunner _publishingRunner; - private static BackgroundTaskRunner _tasksRunner; - private static BackgroundTaskRunner _scrubberRunner; - private static volatile bool _started; - private static readonly object Locker = new object(); + private BackgroundTaskRunner _keepAliveRunner; + private BackgroundTaskRunner _publishingRunner; + private BackgroundTaskRunner _tasksRunner; + private BackgroundTaskRunner _scrubberRunner; + private bool _started = false; + private object _locker = new object(); + private IBackgroundTask[] _tasks; protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) { if (umbracoApplication.Context == null) return; - //subscribe to app init so we can subsribe to the application events - UmbracoApplicationBase.ApplicationInit += (sender, args) => + // backgrounds runners are web aware, if the app domain dies, these tasks will wind down correctly + _keepAliveRunner = new BackgroundTaskRunner("KeepAlive", applicationContext.ProfilingLogger.Logger); + _publishingRunner = new BackgroundTaskRunner("ScheduledPublishing", applicationContext.ProfilingLogger.Logger); + _tasksRunner = new BackgroundTaskRunner("ScheduledTasks", applicationContext.ProfilingLogger.Logger); + _scrubberRunner = new BackgroundTaskRunner("LogScrubber", applicationContext.ProfilingLogger.Logger); + + //We will start the whole process when a successful request is made + UmbracoModule.RouteAttempt += UmbracoModuleRouteAttempt; + } + + private void UmbracoModuleRouteAttempt(object sender, RoutableAttemptEventArgs e) + { + switch (e.Outcome) { - var app = (HttpApplication)sender; + case EnsureRoutableOutcome.IsRoutable: + case EnsureRoutableOutcome.NotDocumentRequest: + RegisterBackgroundTasks(e); + break; + } + } - //subscribe to the end of a successful request (a handler actually executed) - app.PostRequestHandlerExecute += (o, eventArgs) => + private void RegisterBackgroundTasks(UmbracoRequestEventArgs e) + { + //remove handler, we're done + UmbracoModule.RouteAttempt -= UmbracoModuleRouteAttempt; + + LazyInitializer.EnsureInitialized(ref _tasks, ref _started, ref _locker, () => + { + LogHelper.Debug(() => "Initializing the scheduler"); + var settings = UmbracoConfig.For.UmbracoSettings(); + + var tasks = new List { - if (_started == false) - { - lock (Locker) - { - if (_started == false) - { - _started = true; - LogHelper.Debug(() => "Initializing the scheduler"); - - // backgrounds runners are web aware, if the app domain dies, these tasks will wind down correctly - _keepAliveRunner = new BackgroundTaskRunner("KeepAlive", applicationContext.ProfilingLogger.Logger); - _publishingRunner = new BackgroundTaskRunner("ScheduledPublishing", applicationContext.ProfilingLogger.Logger); - _tasksRunner = new BackgroundTaskRunner("ScheduledTasks", applicationContext.ProfilingLogger.Logger); - _scrubberRunner = new BackgroundTaskRunner("LogScrubber", applicationContext.ProfilingLogger.Logger); - - var settings = UmbracoConfig.For.UmbracoSettings(); - - // ping/keepalive - // on all servers - _keepAliveRunner.Add(new KeepAlive(_keepAliveRunner, 60000, 300000, applicationContext)); - - // scheduled publishing/unpublishing - // install on all, will only run on non-slaves servers - _publishingRunner.Add(new ScheduledPublishing(_publishingRunner, 60000, 60000, applicationContext, settings)); - _tasksRunner.Add(new ScheduledTasks(_tasksRunner, 60000, 60000, applicationContext, settings)); - - // log scrubbing - // install on all, will only run on non-slaves servers - _scrubberRunner.Add(new LogScrubber(_scrubberRunner, 60000, LogScrubber.GetLogScrubbingInterval(settings), applicationContext, settings)); - } - } - } + new KeepAlive(_keepAliveRunner, 60000, 300000, e.UmbracoContext.Application), + new ScheduledPublishing(_publishingRunner, 60000, 60000, e.UmbracoContext.Application, settings), + new ScheduledTasks(_tasksRunner, 60000, 60000, e.UmbracoContext.Application, settings), + new LogScrubber(_scrubberRunner, 60000, LogScrubber.GetLogScrubbingInterval(settings), e.UmbracoContext.Application, settings) }; - }; + + // ping/keepalive + // on all servers + _keepAliveRunner.TryAdd(tasks[0]); + + // scheduled publishing/unpublishing + // install on all, will only run on non-slaves servers + _publishingRunner.TryAdd(tasks[1]); + + _tasksRunner.TryAdd(tasks[2]); + + // log scrubbing + // install on all, will only run on non-slaves servers + _scrubberRunner.TryAdd(tasks[3]); + + return tasks.ToArray(); + }); } } } diff --git a/src/Umbraco.Web/Strategies/ServerRegistrationEventHandler.cs b/src/Umbraco.Web/Strategies/ServerRegistrationEventHandler.cs index 6c15814b92..a6dfa07be1 100644 --- a/src/Umbraco.Web/Strategies/ServerRegistrationEventHandler.cs +++ b/src/Umbraco.Web/Strategies/ServerRegistrationEventHandler.cs @@ -28,19 +28,21 @@ namespace Umbraco.Web.Strategies private DatabaseServerRegistrar _registrar; private BackgroundTaskRunner _backgroundTaskRunner; private bool _started = false; + private TouchServerTask _task; + private object _lock = new object(); // bind to events protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) { _registrar = ServerRegistrarResolver.Current.Registrar as DatabaseServerRegistrar; + + // only for the DatabaseServerRegistrar + if (_registrar == null) return; _backgroundTaskRunner = new BackgroundTaskRunner( new BackgroundTaskRunnerOptions { AutoStart = true }, applicationContext.ProfilingLogger.Logger); - // only for the DatabaseServerRegistrar - if (_registrar == null) return; - //We will start the whole process when a successful request is made UmbracoModule.RouteAttempt += UmbracoModuleRouteAttempt; } @@ -61,40 +63,34 @@ namespace Umbraco.Web.Strategies switch (e.Outcome) { case EnsureRoutableOutcome.IsRoutable: - // front-end request - RegisterServer(e); - //remove handler, we're done - UmbracoModule.RouteAttempt -= UmbracoModuleRouteAttempt; - break; case EnsureRoutableOutcome.NotDocumentRequest: - // anything else (back-end request, service...) - //so it's not a document request, we'll check if it's a back office request - if (e.HttpContext.Request.Url.IsBackOfficeRequest(HttpRuntime.AppDomainAppVirtualPath)) - { - RegisterServer(e); - //remove handler, we're done - UmbracoModule.RouteAttempt -= UmbracoModuleRouteAttempt; - } - break; + RegisterBackgroundTasks(e); + break; } } - private void RegisterServer(UmbracoRequestEventArgs e) + private void RegisterBackgroundTasks(UmbracoRequestEventArgs e) { - //only process once - if (_started) return; + //remove handler, we're done + UmbracoModule.RouteAttempt -= UmbracoModuleRouteAttempt; - _started = true; + //only perform this one time ever + LazyInitializer.EnsureInitialized(ref _task, ref _started, ref _lock, () => + { + var serverAddress = e.UmbracoContext.Application.UmbracoApplicationUrl; + var svc = e.UmbracoContext.Application.Services.ServerRegistrationService; - var serverAddress = e.UmbracoContext.Application.UmbracoApplicationUrl; - var svc = e.UmbracoContext.Application.Services.ServerRegistrationService; + var task = new TouchServerTask(_backgroundTaskRunner, + 15000, //delay before first execution + _registrar.Options.RecurringSeconds*1000, //amount of ms between executions + svc, _registrar, serverAddress); + + //Perform the rest async, we don't want to block the startup sequence + // this will just reoccur on a background thread + _backgroundTaskRunner.TryAdd(task); - //Perform the rest async, we don't want to block the startup sequence - // this will just reoccur on a background thread - _backgroundTaskRunner.Add(new TouchServerTask(_backgroundTaskRunner, - 15000, //delay before first execution - _registrar.Options.RecurringSeconds * 1000, //amount of ms between executions - svc, _registrar, serverAddress)); + return task; + }); } private class TouchServerTask : RecurringTaskBase