diff --git a/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs b/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs index 3e4c4f1ba9..9a3534cfc4 100644 --- a/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs +++ b/src/Umbraco.Tests/Routing/UmbracoModuleTests.cs @@ -46,7 +46,11 @@ namespace Umbraco.Tests.Routing logger, null, // FIXME: PublishedRouter complexities... Mock.Of(), - Mock.Of() + Mock.Of(), + new Umbraco.Web.Cache.BackgroundPublishedSnapshotServiceNotifier( + Factory.GetInstance(), + Factory.GetInstance(), + Logger) ); runtime.Level = RuntimeLevel.Run; diff --git a/src/Umbraco.Web/Cache/BackgroundSafeLiveFactory.cs b/src/Umbraco.Web/Cache/BackgroundPublishedSnapshotServiceNotifier.cs similarity index 57% rename from src/Umbraco.Web/Cache/BackgroundSafeLiveFactory.cs rename to src/Umbraco.Web/Cache/BackgroundPublishedSnapshotServiceNotifier.cs index d45c71d04d..725b1389ce 100644 --- a/src/Umbraco.Web/Cache/BackgroundSafeLiveFactory.cs +++ b/src/Umbraco.Web/Cache/BackgroundPublishedSnapshotServiceNotifier.cs @@ -8,29 +8,62 @@ using Umbraco.Web.Scheduling; namespace Umbraco.Web.Cache { - public sealed class BackgroundSafeLiveFactory + /// + /// Used to notify the of changes using a background thread + /// + /// + /// When in Pure Live mode, the models need to be rebuilt before the IPublishedSnapshotService is notified which can result in performance penalties so + /// this performs these actions on a background thread so the user isn't waiting for the rebuilding to occur on a UI thread. + /// + public sealed class BackgroundPublishedSnapshotServiceNotifier { private readonly IPublishedModelFactory _publishedModelFactory; private readonly IPublishedSnapshotService _publishedSnapshotService; - private BackgroundTaskRunner _runner; + private readonly BackgroundTaskRunner _runner; - public BackgroundSafeLiveFactory(IPublishedModelFactory publishedModelFactory, IPublishedSnapshotService publishedSnapshotService, ILogger logger) + /// + /// Constructor + /// + /// + /// + /// + public BackgroundPublishedSnapshotServiceNotifier(IPublishedModelFactory publishedModelFactory, IPublishedSnapshotService publishedSnapshotService, ILogger logger) { _publishedModelFactory = publishedModelFactory; _publishedSnapshotService = publishedSnapshotService; + + // TODO: We have the option to check if we are in live mode and only run on a background thread if that is the case, else run normally? + // IMO I think we should just always run on a background thread, then no matter what state the app is in, it's always doing a consistent operation. + _runner = new BackgroundTaskRunner("RebuildModelsAndCache", logger); } - public void Execute(ContentTypeCacheRefresher.JsonPayload[] payloads) + /// + /// Blocks until the background operation is completed + /// + public void Wait() => _runner.StoppedAwaitable.GetAwaiter().GetResult(); //TODO: do we need a try/catch? + + /// + /// Notify the of content type changes + /// + /// + public void NotifyWithSafeLiveFactory(ContentTypeCacheRefresher.JsonPayload[] payloads) { _runner.TryAdd(new RebuildModelsAndCacheTask(payloads, _publishedModelFactory, _publishedSnapshotService)); } - public void Execute(DataTypeCacheRefresher.JsonPayload[] payloads) + /// + /// Notify the of data type changes + /// + /// + public void NotifyWithSafeLiveFactory(DataTypeCacheRefresher.JsonPayload[] payloads) { _runner.TryAdd(new RebuildModelsAndCacheTask(payloads, _publishedModelFactory, _publishedSnapshotService)); } + /// + /// A simple background task that notifies the of changes + /// private class RebuildModelsAndCacheTask : IBackgroundTask { private readonly DataTypeCacheRefresher.JsonPayload[] _dataTypePayloads; @@ -71,10 +104,7 @@ namespace Umbraco.Web.Cache }); } - public Task RunAsync(CancellationToken token) - { - throw new System.NotImplementedException(); - } + public Task RunAsync(CancellationToken token) => throw new System.NotImplementedException(); public bool IsAsync => false; diff --git a/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs index 490adeb1d9..8ff82b2644 100644 --- a/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/ContentTypeCacheRefresher.cs @@ -10,11 +10,11 @@ namespace Umbraco.Web.Cache { public sealed class ContentTypeCacheRefresher : PayloadCacheRefresherBase { - private readonly BackgroundSafeLiveFactory _backgroundModelFactory; + private readonly BackgroundPublishedSnapshotServiceNotifier _backgroundModelFactory; private readonly IContentTypeCommonRepository _contentTypeCommonRepository; private readonly IdkMap _idkMap; - public ContentTypeCacheRefresher(AppCaches appCaches, BackgroundSafeLiveFactory backgroundModelFactory, IdkMap idkMap, IContentTypeCommonRepository contentTypeCommonRepository) + public ContentTypeCacheRefresher(AppCaches appCaches, BackgroundPublishedSnapshotServiceNotifier backgroundModelFactory, IdkMap idkMap, IContentTypeCommonRepository contentTypeCommonRepository) : base(appCaches) { _backgroundModelFactory = backgroundModelFactory; @@ -80,7 +80,7 @@ namespace Umbraco.Web.Cache MemberCacheRefresher.RefreshMemberTypes(AppCaches); // refresh the models and cache - _backgroundModelFactory.Execute(payloads); + _backgroundModelFactory.NotifyWithSafeLiveFactory(payloads); // now we can trigger the event base.Refresh(payloads); diff --git a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs index c777da1971..5363182a08 100644 --- a/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/DataTypeCacheRefresher.cs @@ -9,10 +9,10 @@ namespace Umbraco.Web.Cache public sealed class DataTypeCacheRefresher : PayloadCacheRefresherBase { - private readonly BackgroundSafeLiveFactory _backgroundModelFactory; + private readonly BackgroundPublishedSnapshotServiceNotifier _backgroundModelFactory; private readonly IdkMap _idkMap; - public DataTypeCacheRefresher(AppCaches appCaches, BackgroundSafeLiveFactory backgroundModelFactory, IdkMap idkMap) + public DataTypeCacheRefresher(AppCaches appCaches, BackgroundPublishedSnapshotServiceNotifier backgroundModelFactory, IdkMap idkMap) : base(appCaches) { _backgroundModelFactory = backgroundModelFactory; @@ -58,7 +58,7 @@ namespace Umbraco.Web.Cache SliderValueConverter.ClearCaches(); // refresh the models and cache - _backgroundModelFactory.Execute(payloads); + _backgroundModelFactory.NotifyWithSafeLiveFactory(payloads); base.Refresh(payloads); } diff --git a/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs b/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs index a68e137665..7850125970 100644 --- a/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs +++ b/src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs @@ -1,12 +1,8 @@ using System; using System.Threading; using Umbraco.Core; -using Umbraco.Core.Compose; using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; using Umbraco.Core.Logging; -using Umbraco.Core.Persistence; -using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; using Umbraco.Core.Sync; diff --git a/src/Umbraco.Web/Runtime/WebInitialComposer.cs b/src/Umbraco.Web/Runtime/WebInitialComposer.cs index 13d2a6b74c..d59caff63c 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs @@ -125,7 +125,7 @@ namespace Umbraco.Web.Runtime // register distributed cache composition.RegisterUnique(f => new DistributedCache()); - composition.RegisterUnique(); + composition.RegisterUnique(); // replace some services composition.RegisterUnique(); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index c7a576c94f..b5641e8aae 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -115,7 +115,7 @@ - + diff --git a/src/Umbraco.Web/UmbracoInjectedModule.cs b/src/Umbraco.Web/UmbracoInjectedModule.cs index db5372efa2..b16d86a8ba 100644 --- a/src/Umbraco.Web/UmbracoInjectedModule.cs +++ b/src/Umbraco.Web/UmbracoInjectedModule.cs @@ -18,6 +18,7 @@ using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Web.Composing; using Umbraco.Web.PublishedCache; +using Umbraco.Web.Cache; namespace Umbraco.Web { @@ -48,6 +49,7 @@ namespace Umbraco.Web private readonly IPublishedRouter _publishedRouter; private readonly IVariationContextAccessor _variationContextAccessor; private readonly IUmbracoContextFactory _umbracoContextFactory; + private readonly BackgroundPublishedSnapshotServiceNotifier _backgroundNotifier; public UmbracoInjectedModule( IGlobalSettings globalSettings, @@ -59,7 +61,8 @@ namespace Umbraco.Web ILogger logger, IPublishedRouter publishedRouter, IVariationContextAccessor variationContextAccessor, - IUmbracoContextFactory umbracoContextFactory) + IUmbracoContextFactory umbracoContextFactory, + BackgroundPublishedSnapshotServiceNotifier backgroundNotifier) { _combinedRouteCollection = new Lazy(CreateRouteCollection); @@ -73,6 +76,7 @@ namespace Umbraco.Web _publishedRouter = publishedRouter; _variationContextAccessor = variationContextAccessor; _umbracoContextFactory = umbracoContextFactory; + _backgroundNotifier = backgroundNotifier; } #region HttpModule event handlers @@ -136,6 +140,10 @@ namespace Umbraco.Web // do not process if this request is not a front-end routable page var isRoutableAttempt = EnsureUmbracoRoutablePage(umbracoContext, httpContext); + // If this page is probably front-end routable, block here until the backround notifier isn't busy + if (isRoutableAttempt) + _backgroundNotifier.Wait(); + // raise event here UmbracoModule.OnRouteAttempt(this, new RoutableAttemptEventArgs(isRoutableAttempt.Result, umbracoContext, httpContext)); if (isRoutableAttempt.Success == false) return;