refactor Scheduler to use new BackgroundTaskRunner capabilities
Conflicts: src/Umbraco.Web/Scheduling/Scheduler.cs
This commit is contained in:
@@ -34,12 +34,18 @@ namespace Umbraco.Web.Scheduling
|
||||
|
||||
if (_gate != null) return _gate;
|
||||
_gate = new ManualResetEvent(false);
|
||||
|
||||
// note
|
||||
// must use the single-parameter constructor on Timer to avoid it from being GC'd
|
||||
// read http://stackoverflow.com/questions/4962172/why-does-a-system-timers-timer-survive-gc-but-not-system-threading-timer
|
||||
|
||||
_timer = new Timer(_ =>
|
||||
{
|
||||
_timer.Dispose();
|
||||
_timer = null;
|
||||
_gate.Set();
|
||||
}, null, _delayMilliseconds, 0);
|
||||
});
|
||||
_timer.Change(_delayMilliseconds, 0);
|
||||
return _gate;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,17 +9,31 @@ using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Web.Scheduling
|
||||
{
|
||||
internal class LogScrubber : DisposableObject, IBackgroundTask
|
||||
internal class LogScrubber : DelayedRecurringTaskBase<LogScrubber>
|
||||
{
|
||||
private readonly ApplicationContext _appContext;
|
||||
private readonly IUmbracoSettingsSection _settings;
|
||||
|
||||
public LogScrubber(ApplicationContext appContext, IUmbracoSettingsSection settings)
|
||||
public LogScrubber(IBackgroundTaskRunner<LogScrubber> runner, int delayMilliseconds, int periodMilliseconds,
|
||||
ApplicationContext appContext, IUmbracoSettingsSection settings)
|
||||
: base(runner, delayMilliseconds, periodMilliseconds)
|
||||
{
|
||||
_appContext = appContext;
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public LogScrubber(LogScrubber source)
|
||||
: base(source)
|
||||
{
|
||||
_appContext = source._appContext;
|
||||
_settings = source._settings;
|
||||
}
|
||||
|
||||
protected override LogScrubber GetRecurring()
|
||||
{
|
||||
return new LogScrubber(this);
|
||||
}
|
||||
|
||||
private int GetLogScrubbingMaximumAge(IUmbracoSettingsSection settings)
|
||||
{
|
||||
int maximumAge = 24 * 60 * 60;
|
||||
@@ -36,14 +50,22 @@ namespace Umbraco.Web.Scheduling
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the disposal of resources. Derived from abstract class <see cref="DisposableObject"/> which handles common required locking logic.
|
||||
/// </summary>
|
||||
protected override void DisposeResources()
|
||||
public static int GetLogScrubbingInterval(IUmbracoSettingsSection settings)
|
||||
{
|
||||
int interval = 24 * 60 * 60; //24 hours
|
||||
try
|
||||
{
|
||||
if (settings.Logging.CleaningMiliseconds > -1)
|
||||
interval = settings.Logging.CleaningMiliseconds;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogHelper.Error<LogScrubber>("Unable to locate a log scrubbing interval. Defaulting to 24 horus", e);
|
||||
}
|
||||
return interval;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
public override void PerformRun()
|
||||
{
|
||||
using (DisposableTimer.DebugDuration<LogScrubber>(() => "Log scrubbing executing", () => "Log scrubbing complete"))
|
||||
{
|
||||
@@ -51,12 +73,12 @@ namespace Umbraco.Web.Scheduling
|
||||
}
|
||||
}
|
||||
|
||||
public Task RunAsync()
|
||||
public override Task PerformRunAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsAsync
|
||||
public override bool IsAsync
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
@@ -67,12 +67,17 @@ namespace Umbraco.Web.Scheduling
|
||||
var recur = GetRecurring();
|
||||
if (recur == null) return; // done
|
||||
|
||||
// note
|
||||
// must use the single-parameter constructor on Timer to avoid it from being GC'd
|
||||
// read http://stackoverflow.com/questions/4962172/why-does-a-system-timers-timer-survive-gc-but-not-system-threading-timer
|
||||
|
||||
_timer = new Timer(_ =>
|
||||
{
|
||||
_timer.Dispose();
|
||||
_timer = null;
|
||||
_runner.TryAdd(recur);
|
||||
}, null, _periodMilliseconds, 0);
|
||||
});
|
||||
_timer.Change(_periodMilliseconds, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -12,30 +12,41 @@ using Umbraco.Web.Mvc;
|
||||
|
||||
namespace Umbraco.Web.Scheduling
|
||||
{
|
||||
internal class ScheduledPublishing : DisposableObject, IBackgroundTask
|
||||
internal class ScheduledPublishing : DelayedRecurringTaskBase<ScheduledPublishing>
|
||||
{
|
||||
private readonly ApplicationContext _appContext;
|
||||
private readonly IUmbracoSettingsSection _settings;
|
||||
|
||||
private static bool _isPublishingRunning = false;
|
||||
private static bool _isPublishingRunning;
|
||||
|
||||
public ScheduledPublishing(ApplicationContext appContext, IUmbracoSettingsSection settings)
|
||||
public ScheduledPublishing(IBackgroundTaskRunner<ScheduledPublishing> runner, int delayMilliseconds, int periodMilliseconds,
|
||||
ApplicationContext appContext, IUmbracoSettingsSection settings)
|
||||
: base(runner, delayMilliseconds, periodMilliseconds)
|
||||
{
|
||||
_appContext = appContext;
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Handles the disposal of resources. Derived from abstract class <see cref="DisposableObject"/> which handles common required locking logic.
|
||||
/// </summary>
|
||||
protected override void DisposeResources()
|
||||
private ScheduledPublishing(ScheduledPublishing source)
|
||||
: base(source)
|
||||
{
|
||||
_appContext = source._appContext;
|
||||
_settings = source._settings;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
protected override ScheduledPublishing GetRecurring()
|
||||
{
|
||||
return new ScheduledPublishing(this);
|
||||
}
|
||||
|
||||
public override void PerformRun()
|
||||
{
|
||||
if (_appContext == null) return;
|
||||
if (ServerEnvironmentHelper.GetStatus(_settings) == CurrentServerEnvironmentStatus.Slave)
|
||||
{
|
||||
LogHelper.Debug<ScheduledPublishing>("Does not run on slave servers.");
|
||||
return;
|
||||
}
|
||||
|
||||
using (DisposableTimer.DebugDuration<ScheduledPublishing>(() => "Scheduled publishing executing", () => "Scheduled publishing complete"))
|
||||
{
|
||||
@@ -77,12 +88,12 @@ namespace Umbraco.Web.Scheduling
|
||||
}
|
||||
}
|
||||
|
||||
public Task RunAsync()
|
||||
public override Task PerformRunAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsAsync
|
||||
public override bool IsAsync
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
@@ -17,19 +17,33 @@ namespace Umbraco.Web.Scheduling
|
||||
// would need to be a publicly available task (URL) which isn't really very good :(
|
||||
// We should really be using the AdminTokenAuthorizeAttribute for this stuff
|
||||
|
||||
internal class ScheduledTasks : DisposableObject, IBackgroundTask
|
||||
internal class ScheduledTasks : DelayedRecurringTaskBase<ScheduledTasks>
|
||||
{
|
||||
private readonly ApplicationContext _appContext;
|
||||
private readonly IUmbracoSettingsSection _settings;
|
||||
private static readonly Hashtable ScheduledTaskTimes = new Hashtable();
|
||||
private static bool _isPublishingRunning = false;
|
||||
|
||||
public ScheduledTasks(ApplicationContext appContext, IUmbracoSettingsSection settings)
|
||||
public ScheduledTasks(IBackgroundTaskRunner<ScheduledTasks> runner, int delayMilliseconds, int periodMilliseconds,
|
||||
ApplicationContext appContext, IUmbracoSettingsSection settings)
|
||||
: base(runner, delayMilliseconds, periodMilliseconds)
|
||||
{
|
||||
_appContext = appContext;
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public ScheduledTasks(ScheduledTasks source)
|
||||
: base(source)
|
||||
{
|
||||
_appContext = source._appContext;
|
||||
_settings = source._settings;
|
||||
}
|
||||
|
||||
protected override ScheduledTasks GetRecurring()
|
||||
{
|
||||
return new ScheduledTasks(this);
|
||||
}
|
||||
|
||||
private void ProcessTasks()
|
||||
{
|
||||
var scheduledTasks = _settings.ScheduledTasks.Tasks;
|
||||
@@ -78,15 +92,14 @@ namespace Umbraco.Web.Scheduling
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the disposal of resources. Derived from abstract class <see cref="DisposableObject"/> which handles common required locking logic.
|
||||
/// </summary>
|
||||
protected override void DisposeResources()
|
||||
public override void PerformRun()
|
||||
{
|
||||
}
|
||||
if (ServerEnvironmentHelper.GetStatus(_settings) == CurrentServerEnvironmentStatus.Slave)
|
||||
{
|
||||
LogHelper.Debug<ScheduledTasks>("Does not run on slave servers.");
|
||||
return;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
using (DisposableTimer.DebugDuration<ScheduledTasks>(() => "Scheduled tasks executing", () => "Scheduled tasks complete"))
|
||||
{
|
||||
if (_isPublishingRunning) return;
|
||||
@@ -108,12 +121,12 @@ namespace Umbraco.Web.Scheduling
|
||||
}
|
||||
}
|
||||
|
||||
public Task RunAsync()
|
||||
public override Task PerformRunAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsAsync
|
||||
public override bool IsAsync
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
@@ -19,12 +19,10 @@ namespace Umbraco.Web.Scheduling
|
||||
internal sealed class Scheduler : ApplicationEventHandler
|
||||
{
|
||||
private static Timer _pingTimer;
|
||||
private static Timer _schedulingTimer;
|
||||
private static BackgroundTaskRunner<ScheduledPublishing> _publishingRunner;
|
||||
private static BackgroundTaskRunner<ScheduledTasks> _tasksRunner;
|
||||
private static BackgroundTaskRunner<LogScrubber> _scrubberRunner;
|
||||
private static Timer _logScrubberTimer;
|
||||
private static volatile bool _started = false;
|
||||
private static BackgroundTaskRunner<IBackgroundTask> _publishingRunner;
|
||||
private static BackgroundTaskRunner<IBackgroundTask> _tasksRunner;
|
||||
private static BackgroundTaskRunner<IBackgroundTask> _scrubberRunner;
|
||||
private static volatile bool _started;
|
||||
private static readonly object Locker = new object();
|
||||
|
||||
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
|
||||
@@ -49,97 +47,37 @@ namespace Umbraco.Web.Scheduling
|
||||
_started = true;
|
||||
LogHelper.Debug<Scheduler>(() => "Initializing the scheduler");
|
||||
|
||||
// time to setup the tasks
|
||||
// backgrounds runners are web aware, if the app domain dies, these tasks will wind down correctly
|
||||
_publishingRunner = new BackgroundTaskRunner<IBackgroundTask>();
|
||||
_tasksRunner = new BackgroundTaskRunner<IBackgroundTask>();
|
||||
_scrubberRunner = new BackgroundTaskRunner<IBackgroundTask>();
|
||||
|
||||
//We have 3 background runners that are web aware, if the app domain dies, these tasks will wind down correctly
|
||||
_publishingRunner = new BackgroundTaskRunner<ScheduledPublishing>();
|
||||
_tasksRunner = new BackgroundTaskRunner<ScheduledTasks>();
|
||||
_scrubberRunner = new BackgroundTaskRunner<LogScrubber>();
|
||||
var settings = UmbracoConfig.For.UmbracoSettings();
|
||||
|
||||
//NOTE: It is important to note that we need to use the ctor for a timer without the 'state' object specified, this is in order
|
||||
// to ensure that the timer itself is not GC'd since internally .net will pass itself in as the state object and that will keep it alive.
|
||||
// There's references to this here: http://stackoverflow.com/questions/4962172/why-does-a-system-timers-timer-survive-gc-but-not-system-threading-timer
|
||||
// we also make these timers static to ensure further GC safety.
|
||||
// note
|
||||
// must use the single-parameter constructor on Timer to avoid it from being GC'd
|
||||
// also make the timer static to ensure further GC safety
|
||||
// read http://stackoverflow.com/questions/4962172/why-does-a-system-timers-timer-survive-gc-but-not-system-threading-timer
|
||||
|
||||
// ping/keepalive - NOTE: we don't use a background runner for this because it does not need to be web aware, if the app domain dies, no problem
|
||||
// ping/keepalive - no need for a background runner - does not need to be web aware, ok if the app domain dies
|
||||
_pingTimer = new Timer(state => KeepAlive.Start(applicationContext, UmbracoConfig.For.UmbracoSettings()));
|
||||
_pingTimer.Change(60000, 300000);
|
||||
|
||||
// scheduled publishing/unpublishing
|
||||
_schedulingTimer = new Timer(state => PerformScheduling(applicationContext, UmbracoConfig.For.UmbracoSettings()));
|
||||
_schedulingTimer.Change(60000, 60000);
|
||||
// install on all, will only run on non-slaves servers
|
||||
// both are delayed recurring tasks
|
||||
_publishingRunner.Add(new ScheduledPublishing(_publishingRunner, 60000, 60000, applicationContext, settings));
|
||||
_tasksRunner.Add(new ScheduledTasks(_tasksRunner, 60000, 60000, applicationContext, settings));
|
||||
|
||||
//log scrubbing
|
||||
_logScrubberTimer = new Timer(state => PerformLogScrub(applicationContext, UmbracoConfig.For.UmbracoSettings()));
|
||||
_logScrubberTimer.Change(60000, GetLogScrubbingInterval(UmbracoConfig.For.UmbracoSettings()));
|
||||
// log scrubbing
|
||||
// install & run on all servers
|
||||
// LogScrubber is a delayed recurring task
|
||||
_scrubberRunner.Add(new LogScrubber(_scrubberRunner, 60000, LogScrubber.GetLogScrubbingInterval(settings), applicationContext, settings));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private int GetLogScrubbingInterval(IUmbracoSettingsSection settings)
|
||||
{
|
||||
var interval = 4 * 60 * 60 * 1000; // 4 hours, in milliseconds
|
||||
try
|
||||
{
|
||||
if (settings.Logging.CleaningMiliseconds > -1)
|
||||
interval = settings.Logging.CleaningMiliseconds;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LogHelper.Error<Scheduler>("Unable to locate a log scrubbing interval. Defaulting to 4 hours.", e);
|
||||
}
|
||||
return interval;
|
||||
}
|
||||
|
||||
private static void PerformLogScrub(ApplicationContext appContext, IUmbracoSettingsSection settings)
|
||||
{
|
||||
_scrubberRunner.Add(new LogScrubber(appContext, settings));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This performs all of the scheduling on the one timer
|
||||
/// </summary>
|
||||
/// <param name="appContext"></param>
|
||||
/// <param name="settings"></param>
|
||||
/// <remarks>
|
||||
/// No processing will be done if this server is a slave
|
||||
/// </remarks>
|
||||
private static void PerformScheduling(ApplicationContext appContext, IUmbracoSettingsSection settings)
|
||||
{
|
||||
using (DisposableTimer.DebugDuration<Scheduler>(() => "Scheduling interval executing", () => "Scheduling interval complete"))
|
||||
{
|
||||
//get the current server status to see if this server should execute the scheduled publishing
|
||||
var serverStatus = ServerEnvironmentHelper.GetStatus(settings);
|
||||
|
||||
switch (serverStatus)
|
||||
{
|
||||
case CurrentServerEnvironmentStatus.Single:
|
||||
case CurrentServerEnvironmentStatus.Master:
|
||||
case CurrentServerEnvironmentStatus.Unknown:
|
||||
//if it's a single server install, a master or it cannot be determined
|
||||
// then we will process the scheduling
|
||||
|
||||
//do the scheduled publishing
|
||||
_publishingRunner.Add(new ScheduledPublishing(appContext, settings));
|
||||
|
||||
//do the scheduled tasks
|
||||
_tasksRunner.Add(new ScheduledTasks(appContext, settings));
|
||||
|
||||
break;
|
||||
case CurrentServerEnvironmentStatus.Slave:
|
||||
//do not process
|
||||
|
||||
LogHelper.Debug<Scheduler>(
|
||||
() => string.Format("Current server ({0}) detected as a slave, no scheduled processes will execute on this server", NetworkHelper.MachineName));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user