Migrated scheduled publishing task to a hosted service.
This commit is contained in:
@@ -1,110 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Sync;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Umbraco.Web.Scheduling
|
||||
{
|
||||
public class ScheduledPublishing : RecurringTaskBase
|
||||
{
|
||||
private readonly IContentService _contentService;
|
||||
private readonly ILogger<ScheduledPublishing> _logger;
|
||||
private readonly IMainDom _mainDom;
|
||||
private readonly IRuntimeState _runtime;
|
||||
private readonly IServerMessenger _serverMessenger;
|
||||
private readonly IBackofficeSecurityFactory _backofficeSecurityFactory;
|
||||
private readonly IServerRegistrar _serverRegistrar;
|
||||
private readonly IUmbracoContextFactory _umbracoContextFactory;
|
||||
|
||||
public ScheduledPublishing(IBackgroundTaskRunner<RecurringTaskBase> runner, int delayMilliseconds,
|
||||
int periodMilliseconds,
|
||||
IRuntimeState runtime, IMainDom mainDom, IServerRegistrar serverRegistrar, IContentService contentService,
|
||||
IUmbracoContextFactory umbracoContextFactory, ILogger<ScheduledPublishing> logger, IServerMessenger serverMessenger, IBackofficeSecurityFactory backofficeSecurityFactory)
|
||||
: base(runner, delayMilliseconds, periodMilliseconds)
|
||||
{
|
||||
_runtime = runtime;
|
||||
_mainDom = mainDom;
|
||||
_serverRegistrar = serverRegistrar;
|
||||
_contentService = contentService;
|
||||
_umbracoContextFactory = umbracoContextFactory;
|
||||
_logger = logger;
|
||||
_serverMessenger = serverMessenger;
|
||||
_backofficeSecurityFactory = backofficeSecurityFactory;
|
||||
}
|
||||
|
||||
public override bool IsAsync => false;
|
||||
|
||||
public override bool PerformRun()
|
||||
{
|
||||
if (Suspendable.ScheduledPublishing.CanRun == false)
|
||||
return true; // repeat, later
|
||||
|
||||
switch (_serverRegistrar.GetCurrentServerRole())
|
||||
{
|
||||
case ServerRole.Replica:
|
||||
_logger.LogDebug("Does not run on replica servers.");
|
||||
return true; // DO repeat, server role can change
|
||||
case ServerRole.Unknown:
|
||||
_logger.LogDebug("Does not run on servers with unknown role.");
|
||||
return true; // DO repeat, server role can change
|
||||
}
|
||||
|
||||
// ensure we do not run if not main domain, but do NOT lock it
|
||||
if (_mainDom.IsMainDom == false)
|
||||
{
|
||||
_logger.LogDebug("Does not run if not MainDom.");
|
||||
return false; // do NOT repeat, going down
|
||||
}
|
||||
|
||||
// do NOT run publishing if not properly running
|
||||
if (_runtime.Level != RuntimeLevel.Run)
|
||||
{
|
||||
_logger.LogDebug("Does not run if run level is not Run.");
|
||||
return true; // repeat/wait
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// We don't need an explicit scope here because PerformScheduledPublish creates it's own scope
|
||||
// so it's safe as it will create it's own ambient scope.
|
||||
// Ensure we run with an UmbracoContext, because this will run in a background task,
|
||||
// and developers may be using the UmbracoContext in the event handlers.
|
||||
|
||||
// TODO: or maybe not, CacheRefresherComponent already ensures a context when handling events
|
||||
// - UmbracoContext 'current' needs to be refactored and cleaned up
|
||||
// - batched messenger should not depend on a current HttpContext
|
||||
// but then what should be its "scope"? could we attach it to scopes?
|
||||
// - and we should definitively *not* have to flush it here (should be auto)
|
||||
//
|
||||
_backofficeSecurityFactory.EnsureBackofficeSecurity();
|
||||
using (var contextReference = _umbracoContextFactory.EnsureUmbracoContext())
|
||||
{
|
||||
try
|
||||
{
|
||||
// run
|
||||
var result = _contentService.PerformScheduledPublish(DateTime.Now);
|
||||
foreach (var grouped in result.GroupBy(x => x.Result))
|
||||
_logger.LogInformation(
|
||||
"Scheduled publishing result: '{StatusCount}' items with status {Status}",
|
||||
grouped.Count(), grouped.Key);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// if running on a temp context, we have to flush the messenger
|
||||
if (contextReference.IsRoot && _serverMessenger is IBatchedDatabaseServerMessenger m)
|
||||
m.FlushBatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// important to catch *everything* to ensure the task repeats
|
||||
_logger.LogError(ex, "Failed.");
|
||||
}
|
||||
|
||||
return true; // repeat
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Hosting;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Sync;
|
||||
using Umbraco.Web.Routing;
|
||||
|
||||
namespace Umbraco.Web.Scheduling
|
||||
{
|
||||
public sealed class SchedulerComponent : IComponent
|
||||
{
|
||||
private const int DefaultDelayMilliseconds = 180000; // 3 mins
|
||||
private const int OneMinuteMilliseconds = 60000;
|
||||
|
||||
private readonly IRuntimeState _runtime;
|
||||
private readonly IMainDom _mainDom;
|
||||
private readonly IServerRegistrar _serverRegistrar;
|
||||
private readonly IContentService _contentService;
|
||||
private readonly ILogger<SchedulerComponent> _logger;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly IApplicationShutdownRegistry _applicationShutdownRegistry;
|
||||
private readonly IUmbracoContextFactory _umbracoContextFactory;
|
||||
private readonly IServerMessenger _serverMessenger;
|
||||
private readonly IRequestAccessor _requestAccessor;
|
||||
private readonly IBackofficeSecurityFactory _backofficeSecurityFactory;
|
||||
|
||||
private BackgroundTaskRunner<IBackgroundTask> _publishingRunner;
|
||||
|
||||
private bool _started;
|
||||
private object _locker = new object();
|
||||
private IBackgroundTask[] _tasks;
|
||||
|
||||
public SchedulerComponent(IRuntimeState runtime, IMainDom mainDom, IServerRegistrar serverRegistrar,
|
||||
IContentService contentService, IUmbracoContextFactory umbracoContextFactory, ILoggerFactory loggerFactory,
|
||||
IApplicationShutdownRegistry applicationShutdownRegistry,
|
||||
IServerMessenger serverMessenger, IRequestAccessor requestAccessor,
|
||||
IBackofficeSecurityFactory backofficeSecurityFactory)
|
||||
{
|
||||
_runtime = runtime;
|
||||
_mainDom = mainDom;
|
||||
_serverRegistrar = serverRegistrar;
|
||||
_contentService = contentService;
|
||||
_loggerFactory = loggerFactory;
|
||||
_logger = loggerFactory.CreateLogger<SchedulerComponent>();
|
||||
_applicationShutdownRegistry = applicationShutdownRegistry;
|
||||
_umbracoContextFactory = umbracoContextFactory;
|
||||
_serverMessenger = serverMessenger;
|
||||
_requestAccessor = requestAccessor;
|
||||
_backofficeSecurityFactory = backofficeSecurityFactory;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
var logger = _loggerFactory.CreateLogger<BackgroundTaskRunner<IBackgroundTask>>();
|
||||
// backgrounds runners are web aware, if the app domain dies, these tasks will wind down correctly
|
||||
_publishingRunner = new BackgroundTaskRunner<IBackgroundTask>("ScheduledPublishing", logger, _applicationShutdownRegistry);
|
||||
|
||||
// we will start the whole process when a successful request is made
|
||||
_requestAccessor.RouteAttempt += RegisterBackgroundTasksOnce;
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
// the AppDomain / maindom / whatever takes care of stopping background task runners
|
||||
}
|
||||
|
||||
private void RegisterBackgroundTasksOnce(object sender, RoutableAttemptEventArgs e)
|
||||
{
|
||||
switch (e.Outcome)
|
||||
{
|
||||
case EnsureRoutableOutcome.IsRoutable:
|
||||
case EnsureRoutableOutcome.NotDocumentRequest:
|
||||
_requestAccessor.RouteAttempt -= RegisterBackgroundTasksOnce;
|
||||
RegisterBackgroundTasks();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterBackgroundTasks()
|
||||
{
|
||||
LazyInitializer.EnsureInitialized(ref _tasks, ref _started, ref _locker, () =>
|
||||
{
|
||||
_logger.LogDebug("Initializing the scheduler");
|
||||
|
||||
var tasks = new List<IBackgroundTask>();
|
||||
|
||||
tasks.Add(RegisterScheduledPublishing());
|
||||
|
||||
return tasks.ToArray();
|
||||
});
|
||||
}
|
||||
|
||||
private IBackgroundTask RegisterScheduledPublishing()
|
||||
{
|
||||
// scheduled publishing/unpublishing
|
||||
// install on all, will only run on non-replica servers
|
||||
var task = new ScheduledPublishing(_publishingRunner, DefaultDelayMilliseconds, OneMinuteMilliseconds, _runtime, _mainDom, _serverRegistrar, _contentService, _umbracoContextFactory, _loggerFactory.CreateLogger<ScheduledPublishing>(), _serverMessenger, _backofficeSecurityFactory);
|
||||
_publishingRunner.TryAdd(task);
|
||||
return task;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using System;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Composing;
|
||||
|
||||
namespace Umbraco.Web.Scheduling
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to do the scheduling for tasks, publishing, etc...
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// All tasks are run in a background task runner which is web aware and will wind down
|
||||
/// the task correctly instead of killing it completely when the app domain shuts down.
|
||||
/// </remarks>
|
||||
[RuntimeLevel(MinLevel = RuntimeLevel.Run)]
|
||||
internal sealed class SchedulerComposer : ComponentComposer<SchedulerComponent>, ICoreComposer
|
||||
{ }
|
||||
}
|
||||
Reference in New Issue
Block a user