Load Balancing: Implement distributed background jobs (#20397)
* Start work * Introduce dto * Start making repository * Add migrations * Implement fetchable first job * Fix up to also finish tasks * Refactor jobs to distributed background jobs * Filter jobs correctly on LastRun * Hardcode delay * Add settings to configure delay and period * Fix formatting * Add default data * Add update on startup, which will update periods on startup * Refactor service to return job directly * Update src/Umbraco.Infrastructure/Services/Implement/DistributedJobService.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/Umbraco.Infrastructure/BackgroundJobs/DistributedBackgroundJobHostedService.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/Umbraco.Infrastructure/BackgroundJobs/DistributedBackgroundJobHostedService.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Remove unused * Move jobs and make internal * make OpenIddictCleanupJob.cs public, as it is used elsewhere * Minor docstring changes * Update src/Umbraco.Core/Persistence/Constants-Locks.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * ´Throw correct exceptions * Update xml doc * Remove business logic from repository * Remove more business logic from repository into service * Remove adding jobs from migration * fix creation * Rename to ExecuteAsync --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: mole <nikolajlauridsen@protonmail.ch>
This commit is contained in:
@@ -9,8 +9,8 @@ using Umbraco.Cms.Api.Common.Security;
|
|||||||
using Umbraco.Cms.Core;
|
using Umbraco.Cms.Core;
|
||||||
using Umbraco.Cms.Core.Configuration.Models;
|
using Umbraco.Cms.Core.Configuration.Models;
|
||||||
using Umbraco.Cms.Core.DependencyInjection;
|
using Umbraco.Cms.Core.DependencyInjection;
|
||||||
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
using Umbraco.Cms.Infrastructure.BackgroundJobs;
|
||||||
using Umbraco.Extensions;
|
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.DistributedJobs;
|
||||||
|
|
||||||
namespace Umbraco.Cms.Api.Common.DependencyInjection;
|
namespace Umbraco.Cms.Api.Common.DependencyInjection;
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ public static class UmbracoBuilderAuthExtensions
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddRecurringBackgroundJob<OpenIddictCleanupJob>();
|
builder.Services.AddSingleton<IDistributedBackgroundJob, OpenIddictCleanupJob>();
|
||||||
builder.Services.ConfigureOptions<ConfigureOpenIddict>();
|
builder.Services.ConfigureOptions<ConfigureOpenIddict>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public static partial class UmbracoBuilderExtensions
|
|||||||
.AddMembersIdentity()
|
.AddMembersIdentity()
|
||||||
.AddUmbracoProfiler()
|
.AddUmbracoProfiler()
|
||||||
.AddMvcAndRazor(configureMvc)
|
.AddMvcAndRazor(configureMvc)
|
||||||
.AddRecurringBackgroundJobs()
|
.AddBackgroundJobs()
|
||||||
.AddUmbracoHybridCache()
|
.AddUmbracoHybridCache()
|
||||||
.AddDistributedCache()
|
.AddDistributedCache()
|
||||||
.AddCoreNotifications();
|
.AddCoreNotifications();
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace Umbraco.Cms.Core.Configuration.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Settings for distributed jobs.
|
||||||
|
/// </summary>
|
||||||
|
[UmbracoOptions(Constants.Configuration.ConfigDistributedJobs)]
|
||||||
|
public class DistributedJobSettings
|
||||||
|
{
|
||||||
|
internal const string StaticPeriod = "00:00:10";
|
||||||
|
internal const string StaticDelay = "00:01:00";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value for the period of checking if there are any runnable distributed jobs.
|
||||||
|
/// </summary>
|
||||||
|
[DefaultValue(StaticPeriod)]
|
||||||
|
public TimeSpan Period { get; set; } = TimeSpan.Parse(StaticPeriod);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value for the delay of when to start checking for distributed jobs.
|
||||||
|
/// </summary>
|
||||||
|
[DefaultValue(StaticDelay)]
|
||||||
|
public TimeSpan Delay { get; set; } = TimeSpan.Parse(StaticDelay);
|
||||||
|
}
|
||||||
@@ -65,6 +65,7 @@ public static partial class Constants
|
|||||||
public const string ConfigWebhook = ConfigPrefix + "Webhook";
|
public const string ConfigWebhook = ConfigPrefix + "Webhook";
|
||||||
public const string ConfigWebhookPayloadType = ConfigWebhook + ":PayloadType";
|
public const string ConfigWebhookPayloadType = ConfigWebhook + ":PayloadType";
|
||||||
public const string ConfigCache = ConfigPrefix + "Cache";
|
public const string ConfigCache = ConfigPrefix + "Cache";
|
||||||
|
public const string ConfigDistributedJobs = ConfigPrefix + "DistributedJobs";
|
||||||
|
|
||||||
public static class NamedOptions
|
public static class NamedOptions
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -87,7 +87,8 @@ public static partial class UmbracoBuilderExtensions
|
|||||||
.AddUmbracoOptions<DataTypesSettings>()
|
.AddUmbracoOptions<DataTypesSettings>()
|
||||||
.AddUmbracoOptions<WebhookSettings>()
|
.AddUmbracoOptions<WebhookSettings>()
|
||||||
.AddUmbracoOptions<CacheSettings>()
|
.AddUmbracoOptions<CacheSettings>()
|
||||||
.AddUmbracoOptions<SystemDateMigrationSettings>();
|
.AddUmbracoOptions<SystemDateMigrationSettings>()
|
||||||
|
.AddUmbracoOptions<DistributedJobSettings>();
|
||||||
|
|
||||||
// Configure connection string and ensure it's updated when the configuration changes
|
// Configure connection string and ensure it's updated when the configuration changes
|
||||||
builder.Services.AddSingleton<IConfigureOptions<ConnectionStrings>, ConfigureConnectionStrings>();
|
builder.Services.AddSingleton<IConfigureOptions<ConnectionStrings>, ConfigureConnectionStrings>();
|
||||||
|
|||||||
@@ -103,8 +103,8 @@ public static partial class Constants
|
|||||||
public const string Webhook2Headers = Webhook + "2Headers";
|
public const string Webhook2Headers = Webhook + "2Headers";
|
||||||
public const string WebhookLog = Webhook + "Log";
|
public const string WebhookLog = Webhook + "Log";
|
||||||
public const string WebhookRequest = Webhook + "Request";
|
public const string WebhookRequest = Webhook + "Request";
|
||||||
|
|
||||||
public const string LongRunningOperation = TableNamePrefix + "LongRunningOperation";
|
public const string LongRunningOperation = TableNamePrefix + "LongRunningOperation";
|
||||||
|
public const string DistributedJob = TableNamePrefix + "DistributedJob";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,5 +90,10 @@ public static partial class Constants
|
|||||||
/// All document URLs.
|
/// All document URLs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int DocumentUrls = -345;
|
public const int DocumentUrls = -345;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All distributed jobs.
|
||||||
|
/// </summary>
|
||||||
|
public const int DistributedJobs = -347;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Umbraco.Cms.Core;
|
||||||
|
using Umbraco.Cms.Core.Configuration.Models;
|
||||||
|
using Umbraco.Cms.Core.Services;
|
||||||
|
using Umbraco.Cms.Infrastructure.Services;
|
||||||
|
|
||||||
|
namespace Umbraco.Cms.Infrastructure.BackgroundJobs;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A hosted service that checks for any runnable distributed background jobs on a timer.
|
||||||
|
/// </summary>
|
||||||
|
public class DistributedBackgroundJobHostedService : BackgroundService
|
||||||
|
{
|
||||||
|
private readonly ILogger<DistributedBackgroundJobHostedService> _logger;
|
||||||
|
private readonly IRuntimeState _runtimeState;
|
||||||
|
private readonly IDistributedJobService _distributedJobService;
|
||||||
|
private DistributedJobSettings _distributedJobSettings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DistributedBackgroundJobHostedService"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger"></param>
|
||||||
|
/// <param name="runtimeState"></param>
|
||||||
|
/// <param name="distributedJobService"></param>
|
||||||
|
/// <param name="distributedJobSettings"></param>
|
||||||
|
public DistributedBackgroundJobHostedService(
|
||||||
|
ILogger<DistributedBackgroundJobHostedService> logger,
|
||||||
|
IRuntimeState runtimeState,
|
||||||
|
IDistributedJobService distributedJobService,
|
||||||
|
IOptionsMonitor<DistributedJobSettings> distributedJobSettings)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_runtimeState = runtimeState;
|
||||||
|
_distributedJobService = distributedJobService;
|
||||||
|
_distributedJobSettings = distributedJobSettings.CurrentValue;
|
||||||
|
distributedJobSettings.OnChange(options =>
|
||||||
|
{
|
||||||
|
_distributedJobSettings = options;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
await Task.Delay(_distributedJobSettings.Delay, stoppingToken);
|
||||||
|
|
||||||
|
while (_runtimeState.Level != RuntimeLevel.Run)
|
||||||
|
{
|
||||||
|
await Task.Delay(_distributedJobSettings.Delay, stoppingToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update all jobs, periods might have changed when restarting.
|
||||||
|
await _distributedJobService.EnsureJobsAsync();
|
||||||
|
|
||||||
|
using PeriodicTimer timer = new(_distributedJobSettings.Period);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (await timer.WaitForNextTickAsync(stoppingToken))
|
||||||
|
{
|
||||||
|
await RunRunnableJob();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Timed Hosted Service is stopping.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RunRunnableJob()
|
||||||
|
{
|
||||||
|
IDistributedBackgroundJob? job = await _distributedJobService.TryTakeRunnableAsync();
|
||||||
|
|
||||||
|
if (job is null)
|
||||||
|
{
|
||||||
|
// No runnable jobs for now, return
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await job.ExecuteAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "An exception occurred while running distributed background job '{JobName}'.", job.Name);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _distributedJobService.FinishAsync(job.Name);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "An exception occurred while finishing distributed background job '{JobName}'.", job.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
namespace Umbraco.Cms.Infrastructure.BackgroundJobs;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A background job that will be executed by an available server. With a single server setup this will always be the same.
|
||||||
|
/// With a load balanced setup, the executing server might change every time this needs to be executed.
|
||||||
|
/// </summary>
|
||||||
|
public interface IDistributedBackgroundJob
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Name of the job.
|
||||||
|
/// </summary>
|
||||||
|
string Name { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Timespan representing how often the task should recur.
|
||||||
|
/// </summary>
|
||||||
|
TimeSpan Period { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Run the job.
|
||||||
|
/// </summary>
|
||||||
|
Task ExecuteAsync();
|
||||||
|
}
|
||||||
@@ -2,14 +2,13 @@ using Microsoft.Extensions.Options;
|
|||||||
using Umbraco.Cms.Core.Configuration.Models;
|
using Umbraco.Cms.Core.Configuration.Models;
|
||||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||||
using Umbraco.Cms.Core.Scoping;
|
using Umbraco.Cms.Core.Scoping;
|
||||||
using Umbraco.Cms.Infrastructure.Scoping;
|
|
||||||
|
|
||||||
namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.DistributedJobs;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A background job that prunes cache instructions from the database.
|
/// A background job that prunes cache instructions from the database.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class CacheInstructionsPruningJob : IRecurringBackgroundJob
|
internal class CacheInstructionsPruningJob : IDistributedBackgroundJob
|
||||||
{
|
{
|
||||||
private readonly IOptions<GlobalSettings> _globalSettings;
|
private readonly IOptions<GlobalSettings> _globalSettings;
|
||||||
private readonly ICacheInstructionRepository _cacheInstructionRepository;
|
private readonly ICacheInstructionRepository _cacheInstructionRepository;
|
||||||
@@ -36,18 +35,13 @@ public class CacheInstructionsPruningJob : IRecurringBackgroundJob
|
|||||||
Period = globalSettings.Value.DatabaseServerMessenger.TimeBetweenPruneOperations;
|
Period = globalSettings.Value.DatabaseServerMessenger.TimeBetweenPruneOperations;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
public string Name => "CacheInstructionsPruningJob";
|
||||||
public event EventHandler PeriodChanged
|
|
||||||
{
|
|
||||||
add { }
|
|
||||||
remove { }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public TimeSpan Period { get; }
|
public TimeSpan Period { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task RunJobAsync()
|
public Task ExecuteAsync()
|
||||||
{
|
{
|
||||||
DateTimeOffset pruneDate = _timeProvider.GetUtcNow() - _globalSettings.Value.DatabaseServerMessenger.TimeToRetainInstructions;
|
DateTimeOffset pruneDate = _timeProvider.GetUtcNow() - _globalSettings.Value.DatabaseServerMessenger.TimeToRetainInstructions;
|
||||||
using (ICoreScope scope = _scopeProvider.CreateCoreScope())
|
using (ICoreScope scope = _scopeProvider.CreateCoreScope())
|
||||||
@@ -1,24 +1,21 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Umbraco.Cms.Core;
|
|
||||||
using Umbraco.Cms.Core.Configuration.Models;
|
using Umbraco.Cms.Core.Configuration.Models;
|
||||||
using Umbraco.Cms.Core.Runtime;
|
|
||||||
using Umbraco.Cms.Core.Services;
|
using Umbraco.Cms.Core.Services;
|
||||||
using Umbraco.Cms.Core.Sync;
|
|
||||||
using Umbraco.Cms.Infrastructure.BackgroundJobs;
|
|
||||||
|
|
||||||
namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.DistributedJobs;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Recurring hosted service that executes the content history cleanup.
|
/// Recurring hosted service that executes the content history cleanup.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ContentVersionCleanupJob : IRecurringBackgroundJob
|
internal class ContentVersionCleanupJob : IDistributedBackgroundJob
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Name => "ContentVersionCleanupJob";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public TimeSpan Period { get => TimeSpan.FromHours(1); }
|
public TimeSpan Period { get => TimeSpan.FromHours(1); }
|
||||||
|
|
||||||
// No-op event as the period never changes on this job
|
|
||||||
public event EventHandler PeriodChanged { add { } remove { } }
|
|
||||||
|
|
||||||
private readonly ILogger<ContentVersionCleanupJob> _logger;
|
private readonly ILogger<ContentVersionCleanupJob> _logger;
|
||||||
private readonly IContentVersionService _service;
|
private readonly IContentVersionService _service;
|
||||||
@@ -39,7 +36,7 @@ public class ContentVersionCleanupJob : IRecurringBackgroundJob
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Task RunJobAsync()
|
public Task ExecuteAsync()
|
||||||
{
|
{
|
||||||
// Globally disabled by feature flag
|
// Globally disabled by feature flag
|
||||||
if (!_settingsMonitor.CurrentValue.ContentVersionCleanupPolicy.EnableCleanup)
|
if (!_settingsMonitor.CurrentValue.ContentVersionCleanupPolicy.EnableCleanup)
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
// Copyright (c) Umbraco.
|
// Copyright (c) Umbraco.
|
||||||
// See LICENSE for more details.
|
// See LICENSE for more details.
|
||||||
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Umbraco.Cms.Core.Configuration;
|
|
||||||
using Umbraco.Cms.Core.Configuration.Models;
|
using Umbraco.Cms.Core.Configuration.Models;
|
||||||
using Umbraco.Cms.Core.Events;
|
using Umbraco.Cms.Core.Events;
|
||||||
using Umbraco.Cms.Core.HealthChecks;
|
using Umbraco.Cms.Core.HealthChecks;
|
||||||
@@ -12,25 +10,20 @@ using Umbraco.Cms.Core.Logging;
|
|||||||
using Umbraco.Cms.Core.Notifications;
|
using Umbraco.Cms.Core.Notifications;
|
||||||
using Umbraco.Cms.Core.Scoping;
|
using Umbraco.Cms.Core.Scoping;
|
||||||
|
|
||||||
namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.DistributedJobs;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Hosted service implementation for recurring health check notifications.
|
/// Hosted service implementation for recurring health check notifications.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class HealthCheckNotifierJob : IRecurringBackgroundJob
|
internal class HealthCheckNotifierJob : IDistributedBackgroundJob
|
||||||
{
|
{
|
||||||
public TimeSpan Period { get; private set; }
|
/// <inheritdoc />
|
||||||
public TimeSpan Delay { get; private set; }
|
public string Name => "HealthCheckNotifierJob";
|
||||||
|
|
||||||
private event EventHandler? _periodChanged;
|
/// <inheritdoc/>
|
||||||
public event EventHandler PeriodChanged
|
public TimeSpan Period { get; private set; }
|
||||||
{
|
|
||||||
add { _periodChanged += value; }
|
|
||||||
remove { _periodChanged -= value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly HealthCheckCollection _healthChecks;
|
private readonly HealthCheckCollection _healthChecks;
|
||||||
private readonly ILogger<HealthCheckNotifierJob> _logger;
|
|
||||||
private readonly HealthCheckNotificationMethodCollection _notifications;
|
private readonly HealthCheckNotificationMethodCollection _notifications;
|
||||||
private readonly IProfilingLogger _profilingLogger;
|
private readonly IProfilingLogger _profilingLogger;
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
@@ -53,32 +46,28 @@ public class HealthCheckNotifierJob : IRecurringBackgroundJob
|
|||||||
HealthCheckCollection healthChecks,
|
HealthCheckCollection healthChecks,
|
||||||
HealthCheckNotificationMethodCollection notifications,
|
HealthCheckNotificationMethodCollection notifications,
|
||||||
ICoreScopeProvider scopeProvider,
|
ICoreScopeProvider scopeProvider,
|
||||||
ILogger<HealthCheckNotifierJob> logger,
|
|
||||||
IProfilingLogger profilingLogger,
|
IProfilingLogger profilingLogger,
|
||||||
ICronTabParser cronTabParser,
|
|
||||||
IEventAggregator eventAggregator)
|
IEventAggregator eventAggregator)
|
||||||
{
|
{
|
||||||
_healthChecksSettings = healthChecksSettings.CurrentValue;
|
_healthChecksSettings = healthChecksSettings.CurrentValue;
|
||||||
_healthChecks = healthChecks;
|
_healthChecks = healthChecks;
|
||||||
_notifications = notifications;
|
_notifications = notifications;
|
||||||
_scopeProvider = scopeProvider;
|
_scopeProvider = scopeProvider;
|
||||||
_logger = logger;
|
|
||||||
_profilingLogger = profilingLogger;
|
_profilingLogger = profilingLogger;
|
||||||
_eventAggregator = eventAggregator;
|
_eventAggregator = eventAggregator;
|
||||||
|
|
||||||
Period = healthChecksSettings.CurrentValue.Notification.Period;
|
Period = healthChecksSettings.CurrentValue.Notification.Period;
|
||||||
Delay = DelayCalculator.GetDelay(healthChecksSettings.CurrentValue.Notification.FirstRunTime, cronTabParser, logger, TimeSpan.FromMinutes(3));
|
|
||||||
|
|
||||||
|
|
||||||
healthChecksSettings.OnChange(x =>
|
healthChecksSettings.OnChange(x =>
|
||||||
{
|
{
|
||||||
_healthChecksSettings = x;
|
_healthChecksSettings = x;
|
||||||
Period = x.Notification.Period;
|
Period = x.Notification.Period;
|
||||||
_periodChanged?.Invoke(this, EventArgs.Empty);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RunJobAsync()
|
/// <inheritdoc/>
|
||||||
|
public async Task ExecuteAsync()
|
||||||
{
|
{
|
||||||
if (_healthChecksSettings.Notification.Enabled == false)
|
if (_healthChecksSettings.Notification.Enabled == false)
|
||||||
{
|
{
|
||||||
@@ -1,17 +1,13 @@
|
|||||||
// Copyright (c) Umbraco.
|
// Copyright (c) Umbraco.
|
||||||
// See LICENSE for more details.
|
// See LICENSE for more details.
|
||||||
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Umbraco.Cms.Core;
|
|
||||||
using Umbraco.Cms.Core.Configuration.Models;
|
using Umbraco.Cms.Core.Configuration.Models;
|
||||||
using Umbraco.Cms.Core.Logging;
|
using Umbraco.Cms.Core.Logging;
|
||||||
using Umbraco.Cms.Core.Runtime;
|
|
||||||
using Umbraco.Cms.Core.Scoping;
|
using Umbraco.Cms.Core.Scoping;
|
||||||
using Umbraco.Cms.Core.Services;
|
using Umbraco.Cms.Core.Services;
|
||||||
using Umbraco.Cms.Core.Sync;
|
|
||||||
|
|
||||||
namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.DistributedJobs;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Log scrubbing hosted service.
|
/// Log scrubbing hosted service.
|
||||||
@@ -19,18 +15,18 @@ namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Will only run on non-replica servers.
|
/// Will only run on non-replica servers.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class LogScrubberJob : IRecurringBackgroundJob
|
internal class LogScrubberJob : IDistributedBackgroundJob
|
||||||
{
|
{
|
||||||
private readonly IAuditService _auditService;
|
private readonly IAuditService _auditService;
|
||||||
private readonly ILogger<LogScrubberJob> _logger;
|
|
||||||
private readonly IProfilingLogger _profilingLogger;
|
private readonly IProfilingLogger _profilingLogger;
|
||||||
private readonly ICoreScopeProvider _scopeProvider;
|
private readonly ICoreScopeProvider _scopeProvider;
|
||||||
private LoggingSettings _settings;
|
private LoggingSettings _settings;
|
||||||
|
|
||||||
public TimeSpan Period => TimeSpan.FromHours(4);
|
/// <inheritdoc />
|
||||||
|
public string Name => "LogScrubberJob";
|
||||||
|
|
||||||
// No-op event as the period never changes on this job
|
/// <inheritdoc />
|
||||||
public event EventHandler PeriodChanged { add { } remove { } }
|
public TimeSpan Period => TimeSpan.FromHours(4);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="LogScrubberJob" /> class.
|
/// Initializes a new instance of the <see cref="LogScrubberJob" /> class.
|
||||||
@@ -44,21 +40,20 @@ public class LogScrubberJob : IRecurringBackgroundJob
|
|||||||
IAuditService auditService,
|
IAuditService auditService,
|
||||||
IOptionsMonitor<LoggingSettings> settings,
|
IOptionsMonitor<LoggingSettings> settings,
|
||||||
ICoreScopeProvider scopeProvider,
|
ICoreScopeProvider scopeProvider,
|
||||||
ILogger<LogScrubberJob> logger,
|
|
||||||
IProfilingLogger profilingLogger)
|
IProfilingLogger profilingLogger)
|
||||||
{
|
{
|
||||||
_auditService = auditService;
|
_auditService = auditService;
|
||||||
_settings = settings.CurrentValue;
|
_settings = settings.CurrentValue;
|
||||||
_scopeProvider = scopeProvider;
|
_scopeProvider = scopeProvider;
|
||||||
_logger = logger;
|
|
||||||
_profilingLogger = profilingLogger;
|
_profilingLogger = profilingLogger;
|
||||||
settings.OnChange(x => _settings = x);
|
settings.OnChange(x => _settings = x);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RunJobAsync()
|
/// <inheritdoc/>
|
||||||
|
public async Task ExecuteAsync()
|
||||||
{
|
{
|
||||||
// Ensure we use an explicit scope since we are running on a background thread.
|
// Ensure we use an explicit scope since we are running on a background thread.
|
||||||
using (ICoreScope scope = _scopeProvider.CreateCoreScope())
|
using ICoreScope scope = _scopeProvider.CreateCoreScope();
|
||||||
using (_profilingLogger.DebugDuration<LogScrubberJob>("Log scrubbing executing", "Log scrubbing complete"))
|
using (_profilingLogger.DebugDuration<LogScrubberJob>("Log scrubbing executing", "Log scrubbing complete"))
|
||||||
{
|
{
|
||||||
await _auditService.CleanLogsAsync((int)_settings.MaxLogAge.TotalMinutes);
|
await _auditService.CleanLogsAsync((int)_settings.MaxLogAge.TotalMinutes);
|
||||||
@@ -3,12 +3,12 @@ using Umbraco.Cms.Core.Configuration.Models;
|
|||||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||||
using Umbraco.Cms.Core.Scoping;
|
using Umbraco.Cms.Core.Scoping;
|
||||||
|
|
||||||
namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.DistributedJobs;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Cleans up long-running operations that have exceeded a specified age.
|
/// Cleans up long-running operations that have exceeded a specified age.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class LongRunningOperationsCleanupJob : IRecurringBackgroundJob
|
internal class LongRunningOperationsCleanupJob : IDistributedBackgroundJob
|
||||||
{
|
{
|
||||||
private readonly ICoreScopeProvider _scopeProvider;
|
private readonly ICoreScopeProvider _scopeProvider;
|
||||||
private readonly ILongRunningOperationRepository _longRunningOperationRepository;
|
private readonly ILongRunningOperationRepository _longRunningOperationRepository;
|
||||||
@@ -36,20 +36,13 @@ public class LongRunningOperationsCleanupJob : IRecurringBackgroundJob
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public event EventHandler? PeriodChanged
|
public string Name => "LongRunningOperationsCleanupJob";
|
||||||
{
|
|
||||||
add { }
|
|
||||||
remove { }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public TimeSpan Period { get; }
|
public TimeSpan Period { get; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public TimeSpan Delay { get; } = TimeSpan.FromSeconds(10);
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task RunJobAsync()
|
public async Task ExecuteAsync()
|
||||||
{
|
{
|
||||||
using ICoreScope scope = _scopeProvider.CreateCoreScope();
|
using ICoreScope scope = _scopeProvider.CreateCoreScope();
|
||||||
await _longRunningOperationRepository.CleanOperationsAsync(_timeProvider.GetUtcNow() - _maxEntryAge);
|
await _longRunningOperationRepository.CleanOperationsAsync(_timeProvider.GetUtcNow() - _maxEntryAge);
|
||||||
@@ -1,18 +1,20 @@
|
|||||||
using System.Configuration;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using OpenIddict.Abstractions;
|
using OpenIddict.Abstractions;
|
||||||
|
|
||||||
namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.DistributedJobs;
|
||||||
|
|
||||||
// port of the OpenIddict Quartz job for cleaning up - see https://github.com/openiddict/openiddict-core/tree/dev/src/OpenIddict.Quartz
|
|
||||||
public class OpenIddictCleanupJob : IRecurringBackgroundJob
|
/// <summary>
|
||||||
|
/// Port of the OpenIddict Quartz job for cleaning up - see https://github.com/openiddict/openiddict-core/tree/dev/src/OpenIddict.Quartz
|
||||||
|
/// </summary>
|
||||||
|
public class OpenIddictCleanupJob : IDistributedBackgroundJob
|
||||||
{
|
{
|
||||||
public TimeSpan Period { get => TimeSpan.FromHours(1); }
|
/// <inheritdoc />
|
||||||
public TimeSpan Delay { get => TimeSpan.FromMinutes(5); }
|
public string Name => "OpenIddictCleanupJob";
|
||||||
|
|
||||||
// No-op event as the period never changes on this job
|
/// <inheritdoc />
|
||||||
public event EventHandler PeriodChanged { add { } remove { } }
|
public TimeSpan Period => TimeSpan.FromHours(1);
|
||||||
|
|
||||||
|
|
||||||
// keep tokens and authorizations in the database for 7 days
|
// keep tokens and authorizations in the database for 7 days
|
||||||
@@ -22,13 +24,19 @@ public class OpenIddictCleanupJob : IRecurringBackgroundJob
|
|||||||
private readonly ILogger<OpenIddictCleanupJob> _logger;
|
private readonly ILogger<OpenIddictCleanupJob> _logger;
|
||||||
private readonly IServiceProvider _provider;
|
private readonly IServiceProvider _provider;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="OpenIddictCleanupJob"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger"></param>
|
||||||
|
/// <param name="provider"></param>
|
||||||
public OpenIddictCleanupJob(ILogger<OpenIddictCleanupJob> logger, IServiceProvider provider)
|
public OpenIddictCleanupJob(ILogger<OpenIddictCleanupJob> logger, IServiceProvider provider)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_provider = provider;
|
_provider = provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RunJobAsync()
|
/// <inheritdoc />
|
||||||
|
public async Task ExecuteAsync()
|
||||||
{
|
{
|
||||||
// hosted services are registered as singletons, but this particular one consumes scoped services... so
|
// hosted services are registered as singletons, but this particular one consumes scoped services... so
|
||||||
// we have to fetch the service dependencies manually using a new scope per invocation.
|
// we have to fetch the service dependencies manually using a new scope per invocation.
|
||||||
@@ -3,13 +3,12 @@
|
|||||||
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Umbraco.Cms.Core;
|
using Umbraco.Cms.Core;
|
||||||
using Umbraco.Cms.Core.Runtime;
|
|
||||||
using Umbraco.Cms.Core.Scoping;
|
using Umbraco.Cms.Core.Scoping;
|
||||||
using Umbraco.Cms.Core.Services;
|
using Umbraco.Cms.Core.Services;
|
||||||
using Umbraco.Cms.Core.Sync;
|
using Umbraco.Cms.Core.Sync;
|
||||||
using Umbraco.Cms.Core.Web;
|
using Umbraco.Cms.Core.Web;
|
||||||
|
|
||||||
namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.DistributedJobs;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Hosted service implementation for scheduled publishing feature.
|
/// Hosted service implementation for scheduled publishing feature.
|
||||||
@@ -17,11 +16,13 @@ namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Runs only on non-replica servers.
|
/// Runs only on non-replica servers.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class ScheduledPublishingJob : IRecurringBackgroundJob
|
internal class ScheduledPublishingJob : IDistributedBackgroundJob
|
||||||
{
|
{
|
||||||
public TimeSpan Period { get => TimeSpan.FromMinutes(1); }
|
/// <inheritdoc />
|
||||||
// No-op event as the period never changes on this job
|
public string Name => "ScheduledPublishingJob";
|
||||||
public event EventHandler PeriodChanged { add { } remove { } }
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public TimeSpan Period => TimeSpan.FromMinutes(1);
|
||||||
|
|
||||||
|
|
||||||
private readonly IContentService _contentService;
|
private readonly IContentService _contentService;
|
||||||
@@ -50,7 +51,8 @@ public class ScheduledPublishingJob : IRecurringBackgroundJob
|
|||||||
_timeProvider = timeProvider;
|
_timeProvider = timeProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task RunJobAsync()
|
/// <inheritdoc />
|
||||||
|
public Task ExecuteAsync()
|
||||||
{
|
{
|
||||||
if (Suspendable.ScheduledPublishing.CanRun == false)
|
if (Suspendable.ScheduledPublishing.CanRun == false)
|
||||||
{
|
{
|
||||||
@@ -1,15 +1,18 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Umbraco.Cms.Core.Services;
|
using Umbraco.Cms.Core.Services;
|
||||||
|
|
||||||
namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.DistributedJobs;
|
||||||
|
|
||||||
public class TemporaryFileCleanupJob : IRecurringBackgroundJob
|
/// <summary>
|
||||||
|
/// Cleans up temporary media files.
|
||||||
|
/// </summary>
|
||||||
|
internal class TemporaryFileCleanupJob : IDistributedBackgroundJob
|
||||||
{
|
{
|
||||||
public TimeSpan Period { get => TimeSpan.FromMinutes(5); }
|
/// <inheritdoc />
|
||||||
public TimeSpan Delay { get => TimeSpan.FromMinutes(5); }
|
public string Name => "TemporaryFileCleanupJob";
|
||||||
|
|
||||||
// No-op event as the period never changes on this job
|
/// <inheritdoc />
|
||||||
public event EventHandler PeriodChanged { add { } remove { } }
|
public TimeSpan Period => TimeSpan.FromMinutes(5);
|
||||||
|
|
||||||
private readonly ILogger<TemporaryFileCleanupJob> _logger;
|
private readonly ILogger<TemporaryFileCleanupJob> _logger;
|
||||||
private readonly ITemporaryFileService _service;
|
private readonly ITemporaryFileService _service;
|
||||||
@@ -26,11 +29,9 @@ public class TemporaryFileCleanupJob : IRecurringBackgroundJob
|
|||||||
_service = temporaryFileService;
|
_service = temporaryFileService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Runs the background task to send the anonymous ID
|
/// <inheritdoc />
|
||||||
/// to telemetry service
|
public async Task ExecuteAsync()
|
||||||
/// </summary>
|
|
||||||
public async Task RunJobAsync()
|
|
||||||
{
|
{
|
||||||
var count = (await _service.CleanUpOldTempFiles()).Count();
|
var count = (await _service.CleanUpOldTempFiles()).Count();
|
||||||
|
|
||||||
@@ -8,9 +8,12 @@ using Umbraco.Cms.Core.Models;
|
|||||||
using Umbraco.Cms.Core.Scoping;
|
using Umbraco.Cms.Core.Scoping;
|
||||||
using Umbraco.Cms.Core.Services;
|
using Umbraco.Cms.Core.Services;
|
||||||
|
|
||||||
namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.DistributedJobs;
|
||||||
|
|
||||||
public class WebhookFiring : IRecurringBackgroundJob
|
/// <summary>
|
||||||
|
/// Fires pending webhooks.
|
||||||
|
/// </summary>
|
||||||
|
internal class WebhookFiring : IDistributedBackgroundJob
|
||||||
{
|
{
|
||||||
private readonly ILogger<WebhookFiring> _logger;
|
private readonly ILogger<WebhookFiring> _logger;
|
||||||
private readonly IWebhookRequestService _webhookRequestService;
|
private readonly IWebhookRequestService _webhookRequestService;
|
||||||
@@ -21,13 +24,23 @@ public class WebhookFiring : IRecurringBackgroundJob
|
|||||||
private readonly IHttpClientFactory _httpClientFactory;
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
private WebhookSettings _webhookSettings;
|
private WebhookSettings _webhookSettings;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Name => "WebhookFiring";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public TimeSpan Period => _webhookSettings.Period;
|
public TimeSpan Period => _webhookSettings.Period;
|
||||||
|
|
||||||
public TimeSpan Delay { get; } = TimeSpan.FromSeconds(20);
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="WebhookFiring"/> class.
|
||||||
// No-op event as the period never changes on this job
|
/// </summary>
|
||||||
public event EventHandler PeriodChanged { add { } remove { } }
|
/// <param name="logger"></param>
|
||||||
|
/// <param name="webhookRequestService"></param>
|
||||||
|
/// <param name="webhookLogFactory"></param>
|
||||||
|
/// <param name="webhookLogService"></param>
|
||||||
|
/// <param name="webHookService"></param>
|
||||||
|
/// <param name="webhookSettings"></param>
|
||||||
|
/// <param name="coreScopeProvider"></param>
|
||||||
|
/// <param name="httpClientFactory"></param>
|
||||||
public WebhookFiring(
|
public WebhookFiring(
|
||||||
ILogger<WebhookFiring> logger,
|
ILogger<WebhookFiring> logger,
|
||||||
IWebhookRequestService webhookRequestService,
|
IWebhookRequestService webhookRequestService,
|
||||||
@@ -49,7 +62,8 @@ public class WebhookFiring : IRecurringBackgroundJob
|
|||||||
webhookSettings.OnChange(x => _webhookSettings = x);
|
webhookSettings.OnChange(x => _webhookSettings = x);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RunJobAsync()
|
/// <inheritdoc />
|
||||||
|
public async Task ExecuteAsync()
|
||||||
{
|
{
|
||||||
if (_webhookSettings.Enabled is false)
|
if (_webhookSettings.Enabled is false)
|
||||||
{
|
{
|
||||||
@@ -7,18 +7,25 @@ using Umbraco.Cms.Core.Persistence.Repositories;
|
|||||||
using Umbraco.Cms.Core.Scoping;
|
using Umbraco.Cms.Core.Scoping;
|
||||||
using Umbraco.Extensions;
|
using Umbraco.Extensions;
|
||||||
|
|
||||||
namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
namespace Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.DistributedJobs;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Daily background job that removes all webhook log data older than x days as defined by <see cref="WebhookSettings.KeepLogsForDays"/>
|
/// Daily background job that removes all webhook log data older than x days as defined by <see cref="WebhookSettings.KeepLogsForDays"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class WebhookLoggingCleanup : IRecurringBackgroundJob
|
internal class WebhookLoggingCleanup : IDistributedBackgroundJob
|
||||||
{
|
{
|
||||||
private readonly ILogger<WebhookLoggingCleanup> _logger;
|
private readonly ILogger<WebhookLoggingCleanup> _logger;
|
||||||
private readonly WebhookSettings _webhookSettings;
|
private readonly WebhookSettings _webhookSettings;
|
||||||
private readonly IWebhookLogRepository _webhookLogRepository;
|
private readonly IWebhookLogRepository _webhookLogRepository;
|
||||||
private readonly ICoreScopeProvider _coreScopeProvider;
|
private readonly ICoreScopeProvider _coreScopeProvider;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="WebhookLoggingCleanup"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger"></param>
|
||||||
|
/// <param name="webhookSettings"></param>
|
||||||
|
/// <param name="webhookLogRepository"></param>
|
||||||
|
/// <param name="coreScopeProvider"></param>
|
||||||
public WebhookLoggingCleanup(ILogger<WebhookLoggingCleanup> logger, IOptionsMonitor<WebhookSettings> webhookSettings, IWebhookLogRepository webhookLogRepository, ICoreScopeProvider coreScopeProvider)
|
public WebhookLoggingCleanup(ILogger<WebhookLoggingCleanup> logger, IOptionsMonitor<WebhookSettings> webhookSettings, IWebhookLogRepository webhookLogRepository, ICoreScopeProvider coreScopeProvider)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -28,20 +35,13 @@ public class WebhookLoggingCleanup : IRecurringBackgroundJob
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
// No-op event as the period never changes on this job
|
public string Name => "WebhookLoggingCleanup";
|
||||||
public event EventHandler PeriodChanged
|
|
||||||
{
|
|
||||||
add { } remove { }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public TimeSpan Period => TimeSpan.FromDays(1);
|
public TimeSpan Period => TimeSpan.FromDays(1);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public TimeSpan Delay { get; } = TimeSpan.FromSeconds(20);
|
public async Task ExecuteAsync()
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task RunJobAsync()
|
|
||||||
{
|
{
|
||||||
if (_webhookSettings.EnableLoggingCleanup is false)
|
if (_webhookSettings.EnableLoggingCleanup is false)
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Umbraco.Cms.Core.DependencyInjection;
|
||||||
|
using Umbraco.Cms.Core.Notifications;
|
||||||
|
using Umbraco.Cms.Core.Services.Navigation;
|
||||||
|
using Umbraco.Cms.Infrastructure.BackgroundJobs;
|
||||||
|
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
||||||
|
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.DistributedJobs;
|
||||||
|
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.ServerRegistration;
|
||||||
|
using Umbraco.Cms.Infrastructure.HostedServices;
|
||||||
|
using Umbraco.Extensions;
|
||||||
|
|
||||||
|
namespace Umbraco.Cms.Infrastructure.DependencyInjection;
|
||||||
|
|
||||||
|
public static partial class UmbracoBuilderExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Add Umbraco background jobs
|
||||||
|
/// </summary>
|
||||||
|
public static IUmbracoBuilder AddBackgroundJobs(this IUmbracoBuilder builder)
|
||||||
|
{
|
||||||
|
// Add background jobs
|
||||||
|
builder.Services.AddRecurringBackgroundJob<TempFileCleanupJob>();
|
||||||
|
builder.Services.AddRecurringBackgroundJob<InstructionProcessJob>();
|
||||||
|
builder.Services.AddRecurringBackgroundJob<TouchServerJob>();
|
||||||
|
builder.Services.AddRecurringBackgroundJob<ReportSiteJob>();
|
||||||
|
|
||||||
|
builder.Services.AddSingleton<IDistributedBackgroundJob, WebhookFiring>();
|
||||||
|
builder.Services.AddSingleton<IDistributedBackgroundJob, ContentVersionCleanupJob>();
|
||||||
|
builder.Services.AddSingleton<IDistributedBackgroundJob, HealthCheckNotifierJob>();
|
||||||
|
builder.Services.AddSingleton<IDistributedBackgroundJob, LogScrubberJob>();
|
||||||
|
builder.Services.AddSingleton<IDistributedBackgroundJob, ScheduledPublishingJob>();
|
||||||
|
builder.Services.AddSingleton<IDistributedBackgroundJob, TemporaryFileCleanupJob>();
|
||||||
|
builder.Services.AddSingleton<IDistributedBackgroundJob, WebhookLoggingCleanup>();
|
||||||
|
builder.Services.AddSingleton<IDistributedBackgroundJob, CacheInstructionsPruningJob>();
|
||||||
|
builder.Services.AddSingleton<IDistributedBackgroundJob, LongRunningOperationsCleanupJob>();
|
||||||
|
builder.Services.AddHostedService<DistributedBackgroundJobHostedService>();
|
||||||
|
|
||||||
|
builder.Services.AddSingleton(RecurringBackgroundJobHostedService.CreateHostedServiceFactory);
|
||||||
|
builder.Services.AddHostedService<RecurringBackgroundJobHostedServiceRunner>();
|
||||||
|
builder.Services.AddHostedService<QueuedHostedService>();
|
||||||
|
builder.AddNotificationAsyncHandler<PostRuntimePremigrationsUpgradeNotification, NavigationInitializationNotificationHandler>();
|
||||||
|
builder.AddNotificationAsyncHandler<PostRuntimePremigrationsUpgradeNotification, PublishStatusInitializationNotificationHandler>();
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -83,6 +83,7 @@ public static partial class UmbracoBuilderExtensions
|
|||||||
builder.Services.AddUnique<INavigationRepository, ContentNavigationRepository>();
|
builder.Services.AddUnique<INavigationRepository, ContentNavigationRepository>();
|
||||||
builder.Services.AddUnique<IPublishStatusRepository, PublishStatusRepository>();
|
builder.Services.AddUnique<IPublishStatusRepository, PublishStatusRepository>();
|
||||||
builder.Services.AddUnique<ILongRunningOperationRepository, LongRunningOperationRepository>();
|
builder.Services.AddUnique<ILongRunningOperationRepository, LongRunningOperationRepository>();
|
||||||
|
builder.Services.AddUnique<IDistributedJobRepository, DistributedJobRepository>();
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ public static partial class UmbracoBuilderExtensions
|
|||||||
builder.Services.TryAddTransient<IReservedFieldNamesService, ReservedFieldNamesService>();
|
builder.Services.TryAddTransient<IReservedFieldNamesService, ReservedFieldNamesService>();
|
||||||
builder.Services.AddUnique<IContentSearchService, ContentSearchService>();
|
builder.Services.AddUnique<IContentSearchService, ContentSearchService>();
|
||||||
builder.Services.AddUnique<IMediaSearchService, MediaSearchService>();
|
builder.Services.AddUnique<IMediaSearchService, MediaSearchService>();
|
||||||
|
builder.Services.AddUnique<IDistributedJobService, DistributedJobService>();
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using Umbraco.Cms.Core.Actions;
|
|||||||
using Umbraco.Cms.Core.Configuration;
|
using Umbraco.Cms.Core.Configuration;
|
||||||
using Umbraco.Cms.Core.Configuration.Models;
|
using Umbraco.Cms.Core.Configuration.Models;
|
||||||
using Umbraco.Cms.Core.Models;
|
using Umbraco.Cms.Core.Models;
|
||||||
|
using Umbraco.Cms.Infrastructure.BackgroundJobs;
|
||||||
using Umbraco.Cms.Infrastructure.Migrations.Upgrade;
|
using Umbraco.Cms.Infrastructure.Migrations.Upgrade;
|
||||||
using Umbraco.Cms.Infrastructure.Persistence;
|
using Umbraco.Cms.Infrastructure.Persistence;
|
||||||
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
|
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
|
||||||
@@ -1077,6 +1078,8 @@ internal sealed class DatabaseDataCreator
|
|||||||
_database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.WebhookLogs, Name = "WebhookLogs" });
|
_database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.WebhookLogs, Name = "WebhookLogs" });
|
||||||
_database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.LongRunningOperations, Name = "LongRunningOperations" });
|
_database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.LongRunningOperations, Name = "LongRunningOperations" });
|
||||||
_database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.DocumentUrls, Name = "DocumentUrls" });
|
_database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.DocumentUrls, Name = "DocumentUrls" });
|
||||||
|
_database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.DistributedJobs, Name = "DistributedJobs" });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateContentTypeData()
|
private void CreateContentTypeData()
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ public class DatabaseSchemaCreator
|
|||||||
typeof(WebhookRequestDto),
|
typeof(WebhookRequestDto),
|
||||||
typeof(UserDataDto),
|
typeof(UserDataDto),
|
||||||
typeof(LongRunningOperationDto),
|
typeof(LongRunningOperationDto),
|
||||||
|
typeof(DistributedJobDto),
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly IUmbracoDatabase _database;
|
private readonly IUmbracoDatabase _database;
|
||||||
|
|||||||
@@ -136,5 +136,6 @@ public class UmbracoPlan : MigrationPlan
|
|||||||
To<V_17_0_0.MigrateCheckboxListDataTypesAndPropertyData>("{EB1E50B7-CD5E-4B6B-B307-36237DD2C506}");
|
To<V_17_0_0.MigrateCheckboxListDataTypesAndPropertyData>("{EB1E50B7-CD5E-4B6B-B307-36237DD2C506}");
|
||||||
To<V_17_0_0.SetDateDefaultsToUtcNow>("{1847C7FF-B021-44EB-BEB0-A77A4376A6F2}");
|
To<V_17_0_0.SetDateDefaultsToUtcNow>("{1847C7FF-B021-44EB-BEB0-A77A4376A6F2}");
|
||||||
To<V_17_0_0.MigrateSystemDatesToUtc>("{7208B20D-6BFC-472E-9374-85EEA817B27D}");
|
To<V_17_0_0.MigrateSystemDatesToUtc>("{7208B20D-6BFC-472E-9374-85EEA817B27D}");
|
||||||
|
To<V_17_0_0.AddDistributedJobLock>("{263075BF-F18A-480D-92B4-4947D2EAB772}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
using NPoco;
|
||||||
|
using Umbraco.Cms.Core;
|
||||||
|
using Umbraco.Cms.Infrastructure.BackgroundJobs;
|
||||||
|
using Umbraco.Cms.Infrastructure.Persistence;
|
||||||
|
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
|
||||||
|
using Umbraco.Extensions;
|
||||||
|
|
||||||
|
namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_17_0_0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds all the distributed jobs to the database.
|
||||||
|
/// </summary>
|
||||||
|
public class AddDistributedJobLock : AsyncMigrationBase
|
||||||
|
{
|
||||||
|
private readonly IEnumerable<IDistributedBackgroundJob> _distributedBackgroundJobs;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="V_17_0_0.AddDistributedJobLock"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="distributedBackgroundJobs"></param>
|
||||||
|
public AddDistributedJobLock(IMigrationContext context, IEnumerable<IDistributedBackgroundJob> distributedBackgroundJobs)
|
||||||
|
: base(context) => _distributedBackgroundJobs = distributedBackgroundJobs;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override Task MigrateAsync()
|
||||||
|
{
|
||||||
|
if (!TableExists(Constants.DatabaseSchema.Tables.DistributedJob))
|
||||||
|
{
|
||||||
|
Create.Table<DistributedJobDto>().Do();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TableExists(Constants.DatabaseSchema.Tables.Lock))
|
||||||
|
{
|
||||||
|
Create.Table<LockDto>().Do();
|
||||||
|
}
|
||||||
|
|
||||||
|
Sql<ISqlContext> sql = Database.SqlContext.Sql()
|
||||||
|
.Select<LockDto>()
|
||||||
|
.From<LockDto>()
|
||||||
|
.Where<LockDto>(x => x.Id == Constants.Locks.DistributedJobs);
|
||||||
|
|
||||||
|
LockDto? existingLockDto = Database.FirstOrDefault<LockDto>(sql);
|
||||||
|
if (existingLockDto is null)
|
||||||
|
{
|
||||||
|
Database.Insert(Constants.DatabaseSchema.Tables.Lock, "id", false, new LockDto { Id = Constants.Locks.DistributedJobs, Name = "DistributedJobs" });
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
namespace Umbraco.Cms.Infrastructure.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Model for distributed background jobs.
|
||||||
|
/// </summary>
|
||||||
|
public class DistributedBackgroundJobModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Name of job.
|
||||||
|
/// </summary>
|
||||||
|
public required string Name { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Period of job.
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan Period { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Time of last run.
|
||||||
|
/// </summary>
|
||||||
|
public DateTime LastRun { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If the job is running.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRunning { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Time of last attempted run.
|
||||||
|
/// </summary>
|
||||||
|
public DateTime LastAttemptedRun { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using NPoco;
|
||||||
|
using Umbraco.Cms.Core;
|
||||||
|
using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations;
|
||||||
|
using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions;
|
||||||
|
|
||||||
|
namespace Umbraco.Cms.Infrastructure.Persistence.Dtos;
|
||||||
|
|
||||||
|
[TableName(TableName)]
|
||||||
|
[PrimaryKey("id", AutoIncrement = true)]
|
||||||
|
[ExplicitColumns]
|
||||||
|
internal sealed class DistributedJobDto
|
||||||
|
{
|
||||||
|
public const string TableName = Constants.DatabaseSchema.Tables.DistributedJob;
|
||||||
|
|
||||||
|
[Column("id")]
|
||||||
|
[PrimaryKeyColumn(AutoIncrement = true)]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[Column("Name")]
|
||||||
|
[NullSetting(NullSetting = NullSettings.NotNull)]
|
||||||
|
public required string Name { get; set; }
|
||||||
|
|
||||||
|
[Column("lastRun")]
|
||||||
|
[Constraint(Default = SystemMethods.CurrentUTCDateTime)]
|
||||||
|
public DateTime LastRun { get; set; }
|
||||||
|
|
||||||
|
[Column("period")]
|
||||||
|
public long Period { get; set; }
|
||||||
|
|
||||||
|
[Column("IsRunning")]
|
||||||
|
public bool IsRunning { get; set; }
|
||||||
|
|
||||||
|
[Column("lastAttemptedRun")]
|
||||||
|
[Constraint(Default = SystemMethods.CurrentUTCDateTime)]
|
||||||
|
public DateTime LastAttemptedRun { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using Umbraco.Cms.Infrastructure.Models;
|
||||||
|
|
||||||
|
namespace Umbraco.Cms.Infrastructure.Persistence.Repositories;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines a repository for managing distributed jobs.
|
||||||
|
/// </summary>
|
||||||
|
public interface IDistributedJobRepository
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a job by name.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
DistributedBackgroundJobModel? GetByName(string jobName);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all jobs.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
IEnumerable<DistributedBackgroundJobModel> GetAll();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates a job.
|
||||||
|
/// </summary>
|
||||||
|
void Update(DistributedBackgroundJobModel distributedBackgroundJob);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a job.
|
||||||
|
/// </summary>
|
||||||
|
void Add(DistributedBackgroundJobModel distributedBackgroundJob);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes a job.
|
||||||
|
/// </summary>
|
||||||
|
void Delete(DistributedBackgroundJobModel distributedBackgroundJob);
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
using NPoco;
|
||||||
|
using Umbraco.Cms.Core.Exceptions;
|
||||||
|
using Umbraco.Cms.Infrastructure.Models;
|
||||||
|
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
|
||||||
|
using Umbraco.Cms.Infrastructure.Scoping;
|
||||||
|
using Umbraco.Extensions;
|
||||||
|
|
||||||
|
namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
internal class DistributedJobRepository(IScopeAccessor scopeAccessor) : IDistributedJobRepository
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public DistributedBackgroundJobModel? GetByName(string jobName)
|
||||||
|
{
|
||||||
|
if (scopeAccessor.AmbientScope is null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("No scope, could not get distributed jobs");
|
||||||
|
}
|
||||||
|
|
||||||
|
Sql<ISqlContext> sql = scopeAccessor.AmbientScope.SqlContext.Sql()
|
||||||
|
.Select<DistributedJobDto>()
|
||||||
|
.From<DistributedJobDto>()
|
||||||
|
.Where<DistributedJobDto>(x => x.Name == jobName);
|
||||||
|
|
||||||
|
DistributedJobDto? dto = scopeAccessor.AmbientScope.Database.FirstOrDefault<DistributedJobDto>(sql);
|
||||||
|
return dto is null ? null : MapFromDto(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public IEnumerable<DistributedBackgroundJobModel> GetAll()
|
||||||
|
{
|
||||||
|
if (scopeAccessor.AmbientScope is null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("No scope, could not get distributed jobs");
|
||||||
|
}
|
||||||
|
|
||||||
|
Sql<ISqlContext> sql = scopeAccessor.AmbientScope.SqlContext.Sql()
|
||||||
|
.Select<DistributedJobDto>()
|
||||||
|
.From<DistributedJobDto>();
|
||||||
|
|
||||||
|
IUmbracoDatabase database = scopeAccessor.AmbientScope.Database;
|
||||||
|
List<DistributedJobDto> jobs = database.Fetch<DistributedJobDto>(sql);
|
||||||
|
return jobs.Select(MapFromDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Update(DistributedBackgroundJobModel distributedBackgroundJob)
|
||||||
|
{
|
||||||
|
if (scopeAccessor.AmbientScope is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DistributedJobDto dto = MapToDto(distributedBackgroundJob);
|
||||||
|
|
||||||
|
scopeAccessor.AmbientScope.Database.Update(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Add(DistributedBackgroundJobModel distributedBackgroundJob)
|
||||||
|
{
|
||||||
|
if (scopeAccessor.AmbientScope is null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("No scope, could not add distributed job");
|
||||||
|
}
|
||||||
|
|
||||||
|
DistributedJobDto dto = MapToDto(distributedBackgroundJob);
|
||||||
|
|
||||||
|
scopeAccessor.AmbientScope.Database.Insert(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Delete(DistributedBackgroundJobModel distributedBackgroundJob)
|
||||||
|
{
|
||||||
|
if (scopeAccessor.AmbientScope is null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("No scope, could not delete distributed job");
|
||||||
|
}
|
||||||
|
|
||||||
|
DistributedJobDto dto = MapToDto(distributedBackgroundJob);
|
||||||
|
|
||||||
|
int rowsAffected = scopeAccessor.AmbientScope.Database.Delete(dto);
|
||||||
|
if (rowsAffected == 0)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Could not delete distributed job, it may have already been deleted");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private DistributedJobDto MapToDto(DistributedBackgroundJobModel model) =>
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Name = model.Name,
|
||||||
|
Period = model.Period.Ticks,
|
||||||
|
LastRun = model.LastRun,
|
||||||
|
IsRunning = model.IsRunning,
|
||||||
|
LastAttemptedRun = model.LastAttemptedRun,
|
||||||
|
};
|
||||||
|
|
||||||
|
private DistributedBackgroundJobModel MapFromDto(DistributedJobDto jobDto) =>
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Name = jobDto.Name,
|
||||||
|
Period = TimeSpan.FromTicks(jobDto.Period),
|
||||||
|
LastRun = jobDto.LastRun,
|
||||||
|
IsRunning = jobDto.IsRunning,
|
||||||
|
LastAttemptedRun = jobDto.LastAttemptedRun,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using Umbraco.Cms.Infrastructure.BackgroundJobs;
|
||||||
|
|
||||||
|
namespace Umbraco.Cms.Infrastructure.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Service for managing distributed jobs.
|
||||||
|
/// </summary>
|
||||||
|
public interface IDistributedJobService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to claim a runnable distributed job for execution.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// The claimed <see cref="IDistributedBackgroundJob"/> if available, or <see langword="null"/> if no jobs are ready to run.
|
||||||
|
/// </returns>
|
||||||
|
Task<IDistributedBackgroundJob?> TryTakeRunnableAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finishes a job.
|
||||||
|
/// </summary>
|
||||||
|
Task FinishAsync(string jobName);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures all distributed jobs are registered in the database on startup.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method handles two scenarios:
|
||||||
|
/// <list type="bullet">
|
||||||
|
/// <item><description>Fresh install: Adds all registered jobs to the database</description></item>
|
||||||
|
/// <item><description>Restart: Updates existing jobs where periods have changed and adds any new jobs</description></item>
|
||||||
|
/// </list>
|
||||||
|
/// Jobs that exist in the database but are no longer registered in code will be removed.
|
||||||
|
/// </remarks>
|
||||||
|
Task EnsureJobsAsync();
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Umbraco.Cms.Core;
|
||||||
|
using Umbraco.Cms.Core.Scoping;
|
||||||
|
using Umbraco.Cms.Infrastructure.BackgroundJobs;
|
||||||
|
using Umbraco.Cms.Infrastructure.Models;
|
||||||
|
using Umbraco.Cms.Infrastructure.Persistence.Repositories;
|
||||||
|
|
||||||
|
namespace Umbraco.Cms.Infrastructure.Services.Implement;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public class DistributedJobService : IDistributedJobService
|
||||||
|
{
|
||||||
|
private readonly ICoreScopeProvider _coreScopeProvider;
|
||||||
|
private readonly IDistributedJobRepository _distributedJobRepository;
|
||||||
|
private readonly IEnumerable<IDistributedBackgroundJob> _distributedBackgroundJobs;
|
||||||
|
private readonly ILogger<DistributedJobService> _logger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DistributedJobService"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="coreScopeProvider"></param>
|
||||||
|
/// <param name="distributedJobRepository"></param>
|
||||||
|
/// <param name="distributedBackgroundJobs"></param>
|
||||||
|
/// <param name="logger"></param>
|
||||||
|
public DistributedJobService(
|
||||||
|
ICoreScopeProvider coreScopeProvider,
|
||||||
|
IDistributedJobRepository distributedJobRepository,
|
||||||
|
IEnumerable<IDistributedBackgroundJob> distributedBackgroundJobs,
|
||||||
|
ILogger<DistributedJobService> logger)
|
||||||
|
{
|
||||||
|
_coreScopeProvider = coreScopeProvider;
|
||||||
|
_distributedJobRepository = distributedJobRepository;
|
||||||
|
_distributedBackgroundJobs = distributedBackgroundJobs;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<IDistributedBackgroundJob?> TryTakeRunnableAsync()
|
||||||
|
{
|
||||||
|
using ICoreScope scope = _coreScopeProvider.CreateCoreScope();
|
||||||
|
|
||||||
|
scope.EagerWriteLock(Constants.Locks.DistributedJobs);
|
||||||
|
|
||||||
|
IEnumerable<DistributedBackgroundJobModel> jobs = _distributedJobRepository.GetAll();
|
||||||
|
DistributedBackgroundJobModel? job = jobs.FirstOrDefault(x => x.LastRun < DateTime.UtcNow - x.Period);
|
||||||
|
|
||||||
|
if (job is null)
|
||||||
|
{
|
||||||
|
// No runnable jobs for now.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
job.LastAttemptedRun = DateTime.UtcNow;
|
||||||
|
job.IsRunning = true;
|
||||||
|
_distributedJobRepository.Update(job);
|
||||||
|
|
||||||
|
IDistributedBackgroundJob? distributedJob = _distributedBackgroundJobs.FirstOrDefault(x => x.Name == job.Name);
|
||||||
|
|
||||||
|
if (distributedJob is null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Could not find a distributed job with the name '{JobName}'", job.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.Complete();
|
||||||
|
|
||||||
|
return distributedJob;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task FinishAsync(string jobName)
|
||||||
|
{
|
||||||
|
using ICoreScope scope = _coreScopeProvider.CreateCoreScope();
|
||||||
|
|
||||||
|
scope.EagerWriteLock(Constants.Locks.DistributedJobs);
|
||||||
|
DistributedBackgroundJobModel? job = _distributedJobRepository.GetByName(jobName);
|
||||||
|
|
||||||
|
if (job is null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Could not finish a distributed job with the name '{JobName}'", jobName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime currentDateTime = DateTime.UtcNow;
|
||||||
|
job.LastAttemptedRun = currentDateTime;
|
||||||
|
job.LastRun = currentDateTime;
|
||||||
|
job.IsRunning = false;
|
||||||
|
_distributedJobRepository.Update(job);
|
||||||
|
|
||||||
|
scope.Complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task EnsureJobsAsync()
|
||||||
|
{
|
||||||
|
using ICoreScope scope = _coreScopeProvider.CreateCoreScope();
|
||||||
|
scope.WriteLock(Constants.Locks.DistributedJobs);
|
||||||
|
|
||||||
|
DistributedBackgroundJobModel[] existingJobs = _distributedJobRepository.GetAll().ToArray();
|
||||||
|
var existingJobsByName = existingJobs.ToDictionary(x => x.Name);
|
||||||
|
|
||||||
|
foreach (IDistributedBackgroundJob registeredJob in _distributedBackgroundJobs)
|
||||||
|
{
|
||||||
|
if (existingJobsByName.TryGetValue(registeredJob.Name, out DistributedBackgroundJobModel? existingJob))
|
||||||
|
{
|
||||||
|
// Update if period has changed
|
||||||
|
if (existingJob.Period != registeredJob.Period)
|
||||||
|
{
|
||||||
|
existingJob.Period = registeredJob.Period;
|
||||||
|
_distributedJobRepository.Update(existingJob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Add new job (fresh install or newly registered job)
|
||||||
|
var newJob = new DistributedBackgroundJobModel
|
||||||
|
{
|
||||||
|
Name = registeredJob.Name,
|
||||||
|
Period = registeredJob.Period,
|
||||||
|
LastRun = DateTime.UtcNow,
|
||||||
|
IsRunning = false,
|
||||||
|
LastAttemptedRun = DateTime.UtcNow,
|
||||||
|
};
|
||||||
|
_distributedJobRepository.Add(newJob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove jobs that are no longer registered in code
|
||||||
|
var registeredJobNames = _distributedBackgroundJobs.Select(x => x.Name).ToHashSet();
|
||||||
|
IEnumerable<DistributedBackgroundJobModel> jobsToRemove = existingJobs.Where(x => registeredJobNames.Contains(x.Name) is false);
|
||||||
|
|
||||||
|
foreach (DistributedBackgroundJobModel jobToRemove in jobsToRemove)
|
||||||
|
{
|
||||||
|
_distributedJobRepository.Delete(jobToRemove);
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.Complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,10 @@
|
|||||||
using System.Data.Common;
|
using System.Data.Common;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.DataProtection.Infrastructure;
|
using Microsoft.AspNetCore.DataProtection.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
@@ -36,6 +33,7 @@ using Umbraco.Cms.Core.Templates;
|
|||||||
using Umbraco.Cms.Core.Web;
|
using Umbraco.Cms.Core.Web;
|
||||||
using Umbraco.Cms.Infrastructure.BackgroundJobs;
|
using Umbraco.Cms.Infrastructure.BackgroundJobs;
|
||||||
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
||||||
|
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.DistributedJobs;
|
||||||
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.ServerRegistration;
|
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.ServerRegistration;
|
||||||
using Umbraco.Cms.Infrastructure.DependencyInjection;
|
using Umbraco.Cms.Infrastructure.DependencyInjection;
|
||||||
using Umbraco.Cms.Infrastructure.HostedServices;
|
using Umbraco.Cms.Infrastructure.HostedServices;
|
||||||
@@ -172,35 +170,6 @@ public static partial class UmbracoBuilderExtensions
|
|||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Add Umbraco recurring background jobs
|
|
||||||
/// </summary>
|
|
||||||
public static IUmbracoBuilder AddRecurringBackgroundJobs(this IUmbracoBuilder builder)
|
|
||||||
{
|
|
||||||
// Add background jobs
|
|
||||||
builder.Services.AddRecurringBackgroundJob<HealthCheckNotifierJob>();
|
|
||||||
builder.Services.AddRecurringBackgroundJob<LogScrubberJob>();
|
|
||||||
builder.Services.AddRecurringBackgroundJob<ContentVersionCleanupJob>();
|
|
||||||
builder.Services.AddRecurringBackgroundJob<ScheduledPublishingJob>();
|
|
||||||
builder.Services.AddRecurringBackgroundJob<TempFileCleanupJob>();
|
|
||||||
builder.Services.AddRecurringBackgroundJob<TemporaryFileCleanupJob>();
|
|
||||||
builder.Services.AddRecurringBackgroundJob<InstructionProcessJob>();
|
|
||||||
builder.Services.AddRecurringBackgroundJob<TouchServerJob>();
|
|
||||||
builder.Services.AddRecurringBackgroundJob<WebhookFiring>();
|
|
||||||
builder.Services.AddRecurringBackgroundJob<WebhookLoggingCleanup>();
|
|
||||||
builder.Services.AddRecurringBackgroundJob<ReportSiteJob>();
|
|
||||||
builder.Services.AddRecurringBackgroundJob<CacheInstructionsPruningJob>();
|
|
||||||
builder.Services.AddRecurringBackgroundJob<LongRunningOperationsCleanupJob>();
|
|
||||||
|
|
||||||
builder.Services.AddSingleton(RecurringBackgroundJobHostedService.CreateHostedServiceFactory);
|
|
||||||
builder.Services.AddHostedService<RecurringBackgroundJobHostedServiceRunner>();
|
|
||||||
builder.Services.AddHostedService<QueuedHostedService>();
|
|
||||||
builder.AddNotificationAsyncHandler<PostRuntimePremigrationsUpgradeNotification, NavigationInitializationNotificationHandler>();
|
|
||||||
builder.AddNotificationAsyncHandler<PostRuntimePremigrationsUpgradeNotification, PublishStatusInitializationNotificationHandler>();
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the Umbraco request profiler
|
/// Adds the Umbraco request profiler
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using Umbraco.Cms.Core.Runtime;
|
|||||||
using Umbraco.Cms.Core.Services;
|
using Umbraco.Cms.Core.Services;
|
||||||
using Umbraco.Cms.Core.Sync;
|
using Umbraco.Cms.Core.Sync;
|
||||||
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
||||||
|
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.DistributedJobs;
|
||||||
using Umbraco.Cms.Infrastructure.HostedServices;
|
using Umbraco.Cms.Infrastructure.HostedServices;
|
||||||
using Umbraco.Cms.Tests.UnitTests.AutoFixture;
|
using Umbraco.Cms.Tests.UnitTests.AutoFixture;
|
||||||
|
|
||||||
@@ -35,7 +36,7 @@ internal class ContentVersionCleanupTest
|
|||||||
mainDom.Setup(x => x.IsMainDom).Returns(true);
|
mainDom.Setup(x => x.IsMainDom).Returns(true);
|
||||||
serverRoleAccessor.Setup(x => x.CurrentServerRole).Returns(ServerRole.SchedulingPublisher);
|
serverRoleAccessor.Setup(x => x.CurrentServerRole).Returns(ServerRole.SchedulingPublisher);
|
||||||
|
|
||||||
await sut.RunJobAsync();
|
await sut.ExecuteAsync();
|
||||||
|
|
||||||
cleanupService.Verify(x => x.PerformContentVersionCleanup(It.IsAny<DateTime>()), Times.Never);
|
cleanupService.Verify(x => x.PerformContentVersionCleanup(It.IsAny<DateTime>()), Times.Never);
|
||||||
}
|
}
|
||||||
@@ -59,7 +60,7 @@ internal class ContentVersionCleanupTest
|
|||||||
mainDom.Setup(x => x.IsMainDom).Returns(true);
|
mainDom.Setup(x => x.IsMainDom).Returns(true);
|
||||||
serverRoleAccessor.Setup(x => x.CurrentServerRole).Returns(ServerRole.SchedulingPublisher);
|
serverRoleAccessor.Setup(x => x.CurrentServerRole).Returns(ServerRole.SchedulingPublisher);
|
||||||
|
|
||||||
await sut.RunJobAsync();
|
await sut.ExecuteAsync();
|
||||||
|
|
||||||
cleanupService.Verify(x => x.PerformContentVersionCleanup(It.IsAny<DateTime>()), Times.Once);
|
cleanupService.Verify(x => x.PerformContentVersionCleanup(It.IsAny<DateTime>()), Times.Once);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using Umbraco.Cms.Core.Events;
|
|||||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||||
using Umbraco.Cms.Core.Scoping;
|
using Umbraco.Cms.Core.Scoping;
|
||||||
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
||||||
|
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.DistributedJobs;
|
||||||
|
|
||||||
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.BackgroundJobs.Jobs;
|
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.BackgroundJobs.Jobs;
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ public class CacheInstructionsPruningJobTests
|
|||||||
|
|
||||||
var job = CreateCacheInstructionsPruningJob(timeToRetainInstructions: timeToRetainInstructions);
|
var job = CreateCacheInstructionsPruningJob(timeToRetainInstructions: timeToRetainInstructions);
|
||||||
|
|
||||||
await job.RunJobAsync();
|
await job.ExecuteAsync();
|
||||||
|
|
||||||
_cacheInstructionRepositoryMock.Verify(repo => repo.DeleteInstructionsOlderThan(expectedPruneDate), Times.Once);
|
_cacheInstructionRepositoryMock.Verify(repo => repo.DeleteInstructionsOlderThan(expectedPruneDate), Times.Once);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,16 @@
|
|||||||
// Copyright (c) Umbraco.
|
// Copyright (c) Umbraco.
|
||||||
// See LICENSE for more details.
|
// See LICENSE for more details.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Moq;
|
using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Umbraco.Cms.Core;
|
|
||||||
using Umbraco.Cms.Core.Configuration;
|
using Umbraco.Cms.Core.Configuration;
|
||||||
using Umbraco.Cms.Core.Configuration.Models;
|
using Umbraco.Cms.Core.Configuration.Models;
|
||||||
using Umbraco.Cms.Core.Events;
|
using Umbraco.Cms.Core.Events;
|
||||||
using Umbraco.Cms.Core.HealthChecks;
|
using Umbraco.Cms.Core.HealthChecks;
|
||||||
using Umbraco.Cms.Core.HealthChecks.NotificationMethods;
|
using Umbraco.Cms.Core.HealthChecks.NotificationMethods;
|
||||||
using Umbraco.Cms.Core.Logging;
|
using Umbraco.Cms.Core.Logging;
|
||||||
using Umbraco.Cms.Core.Runtime;
|
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.DistributedJobs;
|
||||||
using Umbraco.Cms.Core.Services;
|
|
||||||
using Umbraco.Cms.Core.Sync;
|
|
||||||
using Umbraco.Cms.Infrastructure.BackgroundJobs;
|
|
||||||
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
|
||||||
using Umbraco.Cms.Infrastructure.Scoping;
|
using Umbraco.Cms.Infrastructure.Scoping;
|
||||||
using Umbraco.Cms.Tests.Common;
|
using Umbraco.Cms.Tests.Common;
|
||||||
|
|
||||||
@@ -37,7 +29,7 @@ public class HealthCheckNotifierJobTests
|
|||||||
public async Task Does_Not_Execute_When_Not_Enabled()
|
public async Task Does_Not_Execute_When_Not_Enabled()
|
||||||
{
|
{
|
||||||
var sut = CreateHealthCheckNotifier(false);
|
var sut = CreateHealthCheckNotifier(false);
|
||||||
await sut.RunJobAsync();
|
await sut.ExecuteAsync();
|
||||||
VerifyNotificationsNotSent();
|
VerifyNotificationsNotSent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +37,7 @@ public class HealthCheckNotifierJobTests
|
|||||||
public async Task Does_Not_Execute_With_No_Enabled_Notification_Methods()
|
public async Task Does_Not_Execute_With_No_Enabled_Notification_Methods()
|
||||||
{
|
{
|
||||||
var sut = CreateHealthCheckNotifier(notificationEnabled: false);
|
var sut = CreateHealthCheckNotifier(notificationEnabled: false);
|
||||||
await sut.RunJobAsync();
|
await sut.ExecuteAsync();
|
||||||
VerifyNotificationsNotSent();
|
VerifyNotificationsNotSent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +45,7 @@ public class HealthCheckNotifierJobTests
|
|||||||
public async Task Executes_With_Enabled_Notification_Methods()
|
public async Task Executes_With_Enabled_Notification_Methods()
|
||||||
{
|
{
|
||||||
var sut = CreateHealthCheckNotifier();
|
var sut = CreateHealthCheckNotifier();
|
||||||
await sut.RunJobAsync();
|
await sut.ExecuteAsync();
|
||||||
VerifyNotificationsSent();
|
VerifyNotificationsSent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +53,7 @@ public class HealthCheckNotifierJobTests
|
|||||||
public async Task Executes_Only_Enabled_Checks()
|
public async Task Executes_Only_Enabled_Checks()
|
||||||
{
|
{
|
||||||
var sut = CreateHealthCheckNotifier();
|
var sut = CreateHealthCheckNotifier();
|
||||||
await sut.RunJobAsync();
|
await sut.ExecuteAsync();
|
||||||
_mockNotificationMethod.Verify(
|
_mockNotificationMethod.Verify(
|
||||||
x => x.SendAsync(
|
x => x.SendAsync(
|
||||||
It.Is<HealthCheckResults>(y =>
|
It.Is<HealthCheckResults>(y =>
|
||||||
@@ -96,7 +88,6 @@ public class HealthCheckNotifierJobTests
|
|||||||
|
|
||||||
|
|
||||||
var mockScopeProvider = new Mock<IScopeProvider>();
|
var mockScopeProvider = new Mock<IScopeProvider>();
|
||||||
var mockLogger = new Mock<ILogger<HealthCheckNotifierJob>>();
|
|
||||||
var mockProfilingLogger = new Mock<IProfilingLogger>();
|
var mockProfilingLogger = new Mock<IProfilingLogger>();
|
||||||
|
|
||||||
return new HealthCheckNotifierJob(
|
return new HealthCheckNotifierJob(
|
||||||
@@ -104,9 +95,7 @@ public class HealthCheckNotifierJobTests
|
|||||||
checks,
|
checks,
|
||||||
notifications,
|
notifications,
|
||||||
mockScopeProvider.Object,
|
mockScopeProvider.Object,
|
||||||
mockLogger.Object,
|
|
||||||
mockProfilingLogger.Object,
|
mockProfilingLogger.Object,
|
||||||
Mock.Of<ICronTabParser>(),
|
|
||||||
Mock.Of<IEventAggregator>());
|
Mock.Of<IEventAggregator>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,19 +2,15 @@
|
|||||||
// See LICENSE for more details.
|
// See LICENSE for more details.
|
||||||
|
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Moq;
|
using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Umbraco.Cms.Core.Configuration.Models;
|
using Umbraco.Cms.Core.Configuration.Models;
|
||||||
using Umbraco.Cms.Core.Events;
|
using Umbraco.Cms.Core.Events;
|
||||||
using Umbraco.Cms.Core.Logging;
|
using Umbraco.Cms.Core.Logging;
|
||||||
using Umbraco.Cms.Core.Runtime;
|
|
||||||
using Umbraco.Cms.Core.Scoping;
|
using Umbraco.Cms.Core.Scoping;
|
||||||
using Umbraco.Cms.Core.Services;
|
using Umbraco.Cms.Core.Services;
|
||||||
using Umbraco.Cms.Core.Sync;
|
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.DistributedJobs;
|
||||||
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
|
||||||
using Umbraco.Cms.Infrastructure.HostedServices;
|
|
||||||
using Umbraco.Cms.Tests.Common;
|
using Umbraco.Cms.Tests.Common;
|
||||||
|
|
||||||
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.BackgroundJobs.Jobs;
|
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.BackgroundJobs.Jobs;
|
||||||
@@ -30,7 +26,7 @@ public class LogScrubberJobTests
|
|||||||
public async Task Executes_And_Scrubs_Logs()
|
public async Task Executes_And_Scrubs_Logs()
|
||||||
{
|
{
|
||||||
var sut = CreateLogScrubber();
|
var sut = CreateLogScrubber();
|
||||||
await sut.RunJobAsync();
|
await sut.ExecuteAsync();
|
||||||
VerifyLogsScrubbed();
|
VerifyLogsScrubbed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +46,6 @@ public class LogScrubberJobTests
|
|||||||
It.IsAny<bool>(),
|
It.IsAny<bool>(),
|
||||||
It.IsAny<bool>()))
|
It.IsAny<bool>()))
|
||||||
.Returns(mockScope.Object);
|
.Returns(mockScope.Object);
|
||||||
var mockLogger = new Mock<ILogger<LogScrubberJob>>();
|
|
||||||
var mockProfilingLogger = new Mock<IProfilingLogger>();
|
var mockProfilingLogger = new Mock<IProfilingLogger>();
|
||||||
|
|
||||||
_mockAuditService = new Mock<IAuditService>();
|
_mockAuditService = new Mock<IAuditService>();
|
||||||
@@ -59,7 +54,6 @@ public class LogScrubberJobTests
|
|||||||
_mockAuditService.Object,
|
_mockAuditService.Object,
|
||||||
new TestOptionsMonitor<LoggingSettings>(settings),
|
new TestOptionsMonitor<LoggingSettings>(settings),
|
||||||
mockScopeProvider.Object,
|
mockScopeProvider.Object,
|
||||||
mockLogger.Object,
|
|
||||||
mockProfilingLogger.Object);
|
mockProfilingLogger.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ using Umbraco.Cms.Core.Sync;
|
|||||||
using Umbraco.Cms.Core.Web;
|
using Umbraco.Cms.Core.Web;
|
||||||
using Umbraco.Cms.Infrastructure;
|
using Umbraco.Cms.Infrastructure;
|
||||||
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs;
|
||||||
|
using Umbraco.Cms.Infrastructure.BackgroundJobs.Jobs.DistributedJobs;
|
||||||
using Umbraco.Cms.Infrastructure.HostedServices;
|
using Umbraco.Cms.Infrastructure.HostedServices;
|
||||||
|
|
||||||
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.BackgroundJobs.Jobs;
|
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.BackgroundJobs.Jobs;
|
||||||
@@ -29,7 +30,7 @@ public class ScheduledPublishingJobTests
|
|||||||
public async Task Does_Not_Execute_When_Not_Enabled()
|
public async Task Does_Not_Execute_When_Not_Enabled()
|
||||||
{
|
{
|
||||||
var sut = CreateScheduledPublishing(enabled: false);
|
var sut = CreateScheduledPublishing(enabled: false);
|
||||||
await sut.RunJobAsync();
|
await sut.ExecuteAsync();
|
||||||
VerifyScheduledPublishingNotPerformed();
|
VerifyScheduledPublishingNotPerformed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +38,7 @@ public class ScheduledPublishingJobTests
|
|||||||
public async Task Executes_And_Performs_Scheduled_Publishing()
|
public async Task Executes_And_Performs_Scheduled_Publishing()
|
||||||
{
|
{
|
||||||
var sut = CreateScheduledPublishing();
|
var sut = CreateScheduledPublishing();
|
||||||
await sut.RunJobAsync();
|
await sut.ExecuteAsync();
|
||||||
VerifyScheduledPublishingPerformed();
|
VerifyScheduledPublishingPerformed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,6 +75,8 @@ internal sealed class UmbracoCmsSchema
|
|||||||
public required WebhookSettings Webhook { get; set; }
|
public required WebhookSettings Webhook { get; set; }
|
||||||
|
|
||||||
public required CacheSettings Cache { get; set; }
|
public required CacheSettings Cache { get; set; }
|
||||||
|
|
||||||
|
public required DistributedJobSettings DistributedJobSettings { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class InstallDefaultDataNamedOptions
|
public class InstallDefaultDataNamedOptions
|
||||||
|
|||||||
Reference in New Issue
Block a user