From 667c00ccd4d3373650b28af9c072f4335b3c924a Mon Sep 17 00:00:00 2001 From: Paul Johnson Date: Wed, 25 Nov 2020 10:42:09 +0000 Subject: [PATCH] Hotfix for PublishedSnapshotService initialization --- .../PublishedSnapshotService.cs | 57 ++++++++++++------- .../Services/EntityServiceTests.cs | 5 +- .../PublishedContent/NuCacheChildrenTests.cs | 4 ++ .../PublishedContent/NuCacheTests.cs | 5 ++ .../Scoping/ScopedNuCacheTests.cs | 10 +++- 5 files changed, 58 insertions(+), 23 deletions(-) diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs index 688515ad33..2b80b32b71 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs @@ -3,19 +3,14 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; -using System.Threading; -using System.Threading.Tasks; using CSharpTest.Net.Collections; using Microsoft.Extensions.Options; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.Models; using Umbraco.Core.Hosting; -using Umbraco.Core.Install; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; @@ -30,8 +25,8 @@ using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; using Umbraco.Core.Services.Implement; using Umbraco.Core.Strings; +using Umbraco.Net; using Umbraco.Web.Cache; -using Umbraco.Web.Install; using Umbraco.Web.PublishedCache.NuCache.DataSource; using Umbraco.Web.Routing; using File = System.IO.File; @@ -40,6 +35,10 @@ namespace Umbraco.Web.PublishedCache.NuCache { internal class PublishedSnapshotService : PublishedSnapshotServiceBase { + private readonly PublishedSnapshotServiceOptions _options; + private readonly IMainDom _mainDom; + private readonly IUmbracoApplicationLifetime _lifeTime; + private readonly IRuntimeState _runtime; private readonly ServiceContext _serviceContext; private readonly IPublishedContentTypeFactory _publishedContentTypeFactory; private readonly IProfilingLogger _profilingLogger; @@ -63,9 +62,9 @@ namespace Umbraco.Web.PublishedCache.NuCache // volatile because we read it with no lock private volatile bool _isReady; - private readonly ContentStore _contentStore; - private readonly ContentStore _mediaStore; - private readonly SnapDictionary _domainStore; + private ContentStore _contentStore; + private ContentStore _mediaStore; + private SnapDictionary _domainStore; private readonly object _storesLock = new object(); private readonly object _elementsLock = new object(); @@ -84,13 +83,21 @@ namespace Umbraco.Web.PublishedCache.NuCache //private static int _singletonCheck; - public PublishedSnapshotService(PublishedSnapshotServiceOptions options, IMainDom mainDom, IRuntimeState runtime, - ServiceContext serviceContext, IPublishedContentTypeFactory publishedContentTypeFactory, - IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, + public PublishedSnapshotService( + PublishedSnapshotServiceOptions options, + IMainDom mainDom, + IUmbracoApplicationLifetime lifeTime, + IRuntimeState runtime, + ServiceContext serviceContext, + IPublishedContentTypeFactory publishedContentTypeFactory, + IPublishedSnapshotAccessor publishedSnapshotAccessor, + IVariationContextAccessor variationContextAccessor, IProfilingLogger profilingLogger, ILoggerFactory loggerFactory, IScopeProvider scopeProvider, - IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, + IDocumentRepository documentRepository, + IMediaRepository mediaRepository, + IMemberRepository memberRepository, IDefaultCultureAccessor defaultCultureAccessor, IDataSource dataSource, IOptions globalSettings, @@ -106,6 +113,10 @@ namespace Umbraco.Web.PublishedCache.NuCache //if (Interlocked.Increment(ref _singletonCheck) > 1) // throw new Exception("Singleton must be instantiated only once!"); + _options = options; + _mainDom = mainDom; + _lifeTime = lifeTime; + _runtime = runtime; _serviceContext = serviceContext; _publishedContentTypeFactory = publishedContentTypeFactory; _profilingLogger = profilingLogger; @@ -134,12 +145,17 @@ namespace Umbraco.Web.PublishedCache.NuCache // (ideally we'd have Upgrading.App vs Upgrading.Data application states...) InitializeRepositoryEvents(); + _lifeTime.ApplicationInit += OnApplicationInit; + } + + internal void OnApplicationInit(object sender, EventArgs e) + { // however, the cache is NOT available until we are configured, because loading // content (and content types) from database cannot be consistent (see notes in "Handle // Notifications" region), so // - notifications will be ignored // - trying to obtain a published snapshot from the service will throw - if (runtime.Level != RuntimeLevel.Run) + if (_runtime.Level != RuntimeLevel.Run) return; // lock this entire call, we only want a single thread to be accessing the stores at once and within @@ -148,25 +164,25 @@ namespace Umbraco.Web.PublishedCache.NuCache // it will not be able to close the stores until we are done populating (if the store is empty) lock (_storesLock) { - if (options.IgnoreLocalDb == false) + if (!_options.IgnoreLocalDb) { - var registered = mainDom.Register(MainDomRegister, MainDomRelease); + _mainDom.Register(MainDomRegister, MainDomRelease); // stores are created with a db so they can write to it, but they do not read from it, // stores need to be populated, happens in OnResolutionFrozen which uses _localDbExists to // figure out whether it can read the databases or it should populate them from sql _logger.LogInformation("Creating the content store, localContentDbExists? {LocalContentDbExists}", _localContentDbExists); - _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, publishedModelFactory, _localContentDb); + _contentStore = new ContentStore(PublishedSnapshotAccessor, VariationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, _publishedModelFactory, _localContentDb); _logger.LogInformation("Creating the media store, localMediaDbExists? {LocalMediaDbExists}", _localMediaDbExists); - _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, publishedModelFactory, _localMediaDb); + _mediaStore = new ContentStore(PublishedSnapshotAccessor, VariationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, _publishedModelFactory, _localMediaDb); } else { _logger.LogInformation("Creating the content store (local db ignored)"); - _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, publishedModelFactory); + _contentStore = new ContentStore(PublishedSnapshotAccessor, VariationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, _publishedModelFactory); _logger.LogInformation("Creating the media store (local db ignored)"); - _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, publishedModelFactory); + _mediaStore = new ContentStore(PublishedSnapshotAccessor, VariationContextAccessor, _loggerFactory.CreateLogger("ContentStore"), _loggerFactory, _publishedModelFactory); } _domainStore = new SnapDictionary(); @@ -330,6 +346,7 @@ namespace Umbraco.Web.PublishedCache.NuCache public override void Dispose() { TearDownRepositoryEvents(); + _lifeTime.ApplicationInit -= OnApplicationInit; base.Dispose(); } diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/EntityServiceTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/EntityServiceTests.cs index ecb3292727..b7ab3ceea1 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/EntityServiceTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/EntityServiceTests.cs @@ -9,10 +9,12 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Core.Persistence; using Umbraco.Core.Services; +using Umbraco.Net; using Umbraco.Tests.Common.Builders; using Umbraco.Tests.Integration.Testing; using Umbraco.Tests.Testing; using Umbraco.Web.PublishedCache; +using Umbraco.Web.PublishedCache.NuCache; namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services { @@ -42,7 +44,8 @@ namespace Umbraco.Tests.Integration.Umbraco.Infrastructure.Services { //This is super nasty, but this lets us initialize the cache while it is empty. - _ = GetRequiredService(); + var publishedSnapshotService = GetRequiredService() as PublishedSnapshotService; + publishedSnapshotService?.OnApplicationInit(null, EventArgs.Empty); if (_langFr == null && _langEs == null) { diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs index 78cae13b13..a17eb71ab0 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs @@ -32,6 +32,7 @@ using Umbraco.Web.PublishedCache.NuCache; using Umbraco.Web.PublishedCache.NuCache.DataSource; using Current = Umbraco.Web.Composing.Current; using Umbraco.Core.Serialization; +using Umbraco.Net; namespace Umbraco.Tests.PublishedContent { @@ -147,8 +148,10 @@ namespace Umbraco.Tests.PublishedContent // at last, create the complete NuCache snapshot service! var options = new PublishedSnapshotServiceOptions { IgnoreLocalDb = true }; + var lifetime = new Mock(); _snapshotService = new PublishedSnapshotService(options, null, + lifetime.Object, runtime, serviceContext, contentTypeFactory, @@ -173,6 +176,7 @@ namespace Umbraco.Tests.PublishedContent // invariant is the current default _variationAccesor.VariationContext = new VariationContext(); + lifetime.Raise(e => e.ApplicationInit += null, EventArgs.Empty); Mock.Get(factory).Setup(x => x.GetService(typeof(IVariationContextAccessor))).Returns(_variationAccesor); } diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index af2f738cf7..d689215081 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -21,6 +21,7 @@ using Umbraco.Core.Serialization; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; using Umbraco.Core.Strings; +using Umbraco.Net; using Umbraco.Tests.Common; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing.Objects; @@ -188,8 +189,10 @@ namespace Umbraco.Tests.PublishedContent // at last, create the complete NuCache snapshot service! var options = new PublishedSnapshotServiceOptions { IgnoreLocalDb = true }; + var lifetime = new Mock(); _snapshotService = new PublishedSnapshotService(options, null, + lifetime.Object, runtime, serviceContext, contentTypeFactory, @@ -212,6 +215,8 @@ namespace Umbraco.Tests.PublishedContent TestHelper.IOHelper, Microsoft.Extensions.Options.Options.Create(nuCacheSettings)); + lifetime.Raise(e => e.ApplicationInit += null, EventArgs.Empty); + // invariant is the current default _variationAccesor.VariationContext = new VariationContext(); diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index f32ce9d9e1..1a8e485634 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -17,6 +17,7 @@ using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; using Umbraco.Core.Strings; using Umbraco.Core.Sync; +using Umbraco.Net; using Umbraco.Tests.Common; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; @@ -83,10 +84,11 @@ namespace Umbraco.Tests.Scoping var typeFinder = TestHelper.GetTypeFinder(); var nuCacheSettings = new NuCacheSettings(); - - return new PublishedSnapshotService( + var lifetime = new Mock(); + var snapshotService = new PublishedSnapshotService( options, null, + lifetime.Object, runtimeStateMock.Object, ServiceContext, contentTypeFactory, @@ -106,6 +108,10 @@ namespace Umbraco.Tests.Scoping Mock.Of(), IOHelper, Microsoft.Extensions.Options.Options.Create(nuCacheSettings)); + + lifetime.Raise(e => e.ApplicationInit += null, EventArgs.Empty); + + return snapshotService; } protected IUmbracoContext GetUmbracoContextNu(string url, RouteData routeData = null, bool setSingleton = false)