Refactored logic for calculating the first run time for a recurring hosted service. (#12828)

* Refactors the logic for calculating the first run time for a recurring task for easier re-use by similar hostservices.

* Renamed method to match wider usage.
This commit is contained in:
Andy Butland
2022-08-12 10:14:15 +01:00
committed by GitHub
parent c7501bd47b
commit 98c9124735
6 changed files with 126 additions and 45 deletions

View File

@@ -3,6 +3,9 @@ using Umbraco.Cms.Core.Configuration.Models;
namespace Umbraco.Extensions;
// TODO (V12): Remove this class that's no longer used.
[Obsolete("Please use RecurringHostedServiceBase.GetDelay(). This class is no longer used within Umbraco and will be removed in V12.")]
public static class HealthCheckSettingsExtensions
{
public static TimeSpan GetNotificationDelay(this HealthChecksSettings settings, ICronTabParser cronTabParser, DateTime now, TimeSpan defaultDelay)

View File

@@ -2,8 +2,12 @@ using NCrontab;
namespace Umbraco.Cms.Core.Configuration;
/// <summary>
/// Implements <see cref="ICronTabParser"/> using the NCrontab library
/// </summary>
public class NCronTabParser : ICronTabParser
{
/// <inheritdoc/>
public bool IsValidCronTab(string cronTab)
{
var result = CrontabSchedule.TryParse(cronTab);
@@ -11,6 +15,7 @@ public class NCronTabParser : ICronTabParser
return !(result is null);
}
/// <inheritdoc/>
public DateTime GetNextOccurrence(string cronTab, DateTime time)
{
var result = CrontabSchedule.Parse(cronTab);

View File

@@ -59,7 +59,7 @@ public class HealthCheckNotifier : RecurringHostedServiceBase
: base(
logger,
healthChecksSettings.CurrentValue.Notification.Period,
healthChecksSettings.CurrentValue.GetNotificationDelay(cronTabParser, DateTime.Now, DefaultDelay))
GetDelay(healthChecksSettings.CurrentValue.Notification.FirstRunTime, cronTabParser, logger, DefaultDelay))
{
_healthChecksSettings = healthChecksSettings.CurrentValue;
_healthChecks = healthChecks;

View File

@@ -4,6 +4,7 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration;
namespace Umbraco.Cms.Infrastructure.HostedServices;
@@ -58,6 +59,60 @@ public abstract class RecurringHostedServiceBase : IHostedService, IDisposable
GC.SuppressFinalize(this);
}
/// <summary>
/// Determines the delay before the first run of a recurring task implemented as a hosted service when an optonal
/// configuration for the first run time is available.
/// </summary>
/// <param name="firstRunTime">The configured time to first run the task in crontab format.</param>
/// <param name="cronTabParser">An instance of <see cref="ICronTabParser"/></param>
/// <param name="logger">The logger.</param>
/// <param name="defaultDelay">The default delay to use when a first run time is not configured.</param>
/// <returns>The delay before first running the recurring task.</returns>
protected static TimeSpan GetDelay(
string firstRunTime,
ICronTabParser cronTabParser,
ILogger logger,
TimeSpan defaultDelay) => GetDelay(firstRunTime, cronTabParser, logger, DateTime.Now, defaultDelay);
/// <summary>
/// Determines the delay before the first run of a recurring task implemented as a hosted service when an optonal
/// configuration for the first run time is available.
/// </summary>
/// <param name="firstRunTime">The configured time to first run the task in crontab format.</param>
/// <param name="cronTabParser">An instance of <see cref="ICronTabParser"/></param>
/// <param name="logger">The logger.</param>
/// <param name="now">The current datetime.</param>
/// <param name="defaultDelay">The default delay to use when a first run time is not configured.</param>
/// <returns>The delay before first running the recurring task.</returns>
/// <remarks>Internal to expose for unit tests.</remarks>
internal static TimeSpan GetDelay(
string firstRunTime,
ICronTabParser cronTabParser,
ILogger logger,
DateTime now,
TimeSpan defaultDelay)
{
// If first run time not set, start with just small delay after application start.
if (string.IsNullOrEmpty(firstRunTime))
{
return defaultDelay;
}
// If first run time not a valid cron tab, log, and revert to small delay after application start.
if (!cronTabParser.IsValidCronTab(firstRunTime))
{
logger.LogWarning("Could not parse {FirstRunTime} as a crontab expression. Defaulting to default delay for hosted service start.", firstRunTime);
return defaultDelay;
}
// Otherwise start at scheduled time according to cron expression, unless within the default delay period.
DateTime firstRunOccurance = cronTabParser.GetNextOccurrence(firstRunTime, now);
TimeSpan delay = firstRunOccurance - now;
return delay < defaultDelay
? defaultDelay
: delay;
}
/// <inheritdoc />
public Task StartAsync(CancellationToken cancellationToken)
{