diff --git a/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs b/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs
index 249b038cae..8d4cda093c 100644
--- a/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs
+++ b/src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs
@@ -12,10 +12,5 @@ namespace Umbraco.Core.Sync
///
///
SyncBootState GetSyncBootState();
-
- ///
- /// Raised when the boot state is known
- ///
- event EventHandler Booting; // TODO: This should be removed in netcore
}
}
diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs
index b35a3d0edb..f70160cde8 100644
--- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs
+++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs
@@ -36,6 +36,8 @@ namespace Umbraco.Web.PublishedCache.NuCache
internal class PublishedSnapshotService : PublishedSnapshotServiceBase
{
+ private readonly PublishedSnapshotServiceOptions _options;
+ private readonly IMainDom _mainDom;
private readonly ServiceContext _serviceContext;
private readonly IPublishedContentTypeFactory _publishedContentTypeFactory;
private readonly IScopeProvider _scopeProvider;
@@ -50,12 +52,13 @@ namespace Umbraco.Web.PublishedCache.NuCache
private readonly IDefaultCultureAccessor _defaultCultureAccessor;
private readonly UrlSegmentProviderCollection _urlSegmentProviders;
- // volatile because we read it with no lock
- private volatile bool _isReady;
+ private bool _isReady;
+ private bool _isReadSet;
+ private object _isReadyLock;
- 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();
@@ -88,9 +91,12 @@ namespace Umbraco.Web.PublishedCache.NuCache
ISyncBootStateAccessor syncBootStateAccessor)
: base(publishedSnapshotAccessor, variationContextAccessor)
{
+
//if (Interlocked.Increment(ref _singletonCheck) > 1)
// throw new Exception("Singleton must be instantiated only once!");
+ _options = options;
+ _mainDom = mainDom;
_serviceContext = serviceContext;
_publishedContentTypeFactory = publishedContentTypeFactory;
_dataSource = dataSource;
@@ -123,44 +129,6 @@ namespace Umbraco.Web.PublishedCache.NuCache
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
- // the call below to mainDom.Register, a callback may occur on a threadpool thread to MainDomRelease
- // at the same time as we are trying to write to the stores. MainDomRelease also locks on _storesLock so
- // 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)
- {
- var registered = 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.Info("Creating the content store, localContentDbExists? {LocalContentDbExists}", _localContentDbExists);
- _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, _localContentDb);
- _logger.Info("Creating the media store, localMediaDbExists? {LocalMediaDbExists}", _localMediaDbExists);
- _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, _localMediaDb);
- }
- else
- {
- _logger.Info("Creating the content store (local db ignored)");
- _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger);
- _logger.Info("Creating the media store (local db ignored)");
- _mediaStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger);
- }
-
- _domainStore = new SnapDictionary();
-
- _syncBootStateAccessor.Booting += (sender, args) =>
- {
- LoadCachesOnStartup(args);
- };
- }
-
- Guid GetUid(ContentStore store, int id) => store.LiveSnapshot.Get(id)?.Uid ?? default;
- int GetId(ContentStore store, Guid uid) => store.LiveSnapshot.Get(uid)?.Id ?? default;
-
if (idkMap != null)
{
idkMap.SetMapper(UmbracoObjectTypes.Document, id => GetUid(_contentStore, id), uid => GetId(_contentStore, uid));
@@ -168,6 +136,18 @@ namespace Umbraco.Web.PublishedCache.NuCache
}
}
+ private int GetId(ContentStore store, Guid uid)
+ {
+ EnsureCaches();
+ return store.LiveSnapshot.Get(uid)?.Id ?? default;
+ }
+
+ private Guid GetUid(ContentStore store, int id)
+ {
+ EnsureCaches();
+ return store.LiveSnapshot.Get(id)?.Uid ?? default;
+ }
+
///
/// Install phase of
///
@@ -219,51 +199,82 @@ namespace Umbraco.Web.PublishedCache.NuCache
}
///
- /// Populates the stores
+ /// Lazily populates the stores only when they are first requested
///
- /// This is called inside of a lock for _storesLock
- private void LoadCachesOnStartup(SyncBootState bootState)
- {
- // TODO: This is super ugly that this does this as part of the ctor.
- // In netcore this will be different, the initialization will happen
- // outside of the ctor.
-
- var okContent = false;
- var okMedia = false;
-
- try
+ internal void EnsureCaches() => LazyInitializer.EnsureInitialized(
+ ref _isReady,
+ ref _isReadSet,
+ ref _isReadyLock,
+ () =>
{
- if (bootState != SyncBootState.ColdBoot && _localContentDbExists)
+ // lock this entire call, we only want a single thread to be accessing the stores at once and within
+ // the call below to mainDom.Register, a callback may occur on a threadpool thread to MainDomRelease
+ // at the same time as we are trying to write to the stores. MainDomRelease also locks on _storesLock so
+ // it will not be able to close the stores until we are done populating (if the store is empty)
+ lock (_storesLock)
{
- okContent = LockAndLoadContent(scope => LoadContentFromLocalDbLocked(true));
- if (!okContent)
- _logger.Warn("Loading content from local db raised warnings, will reload from database.");
+ if (!_options.IgnoreLocalDb)
+ {
+ var registered = _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.Info("Creating the content store, localContentDbExists? {LocalContentDbExists}", _localContentDbExists);
+ _contentStore = new ContentStore(PublishedSnapshotAccessor, VariationContextAccessor, _logger, _localContentDb);
+ _logger.Info("Creating the media store, localMediaDbExists? {LocalMediaDbExists}", _localMediaDbExists);
+ _mediaStore = new ContentStore(PublishedSnapshotAccessor, VariationContextAccessor, _logger, _localMediaDb);
+ }
+ else
+ {
+ _logger.Info("Creating the content store (local db ignored)");
+ _contentStore = new ContentStore(PublishedSnapshotAccessor, VariationContextAccessor, _logger);
+ _logger.Info("Creating the media store (local db ignored)");
+ _mediaStore = new ContentStore(PublishedSnapshotAccessor, VariationContextAccessor, _logger);
+ }
+
+ _domainStore = new SnapDictionary();
+
+ SyncBootState bootState = _syncBootStateAccessor.GetSyncBootState();
+
+ var okContent = false;
+ var okMedia = false;
+
+ try
+ {
+ if (bootState != SyncBootState.ColdBoot && _localContentDbExists)
+ {
+ okContent = LockAndLoadContent(scope => LoadContentFromLocalDbLocked(true));
+ if (!okContent)
+ _logger.Warn("Loading content from local db raised warnings, will reload from database.");
+ }
+
+ if (bootState != SyncBootState.ColdBoot && _localMediaDbExists)
+ {
+ okMedia = LockAndLoadMedia(scope => LoadMediaFromLocalDbLocked(true));
+ if (!okMedia)
+ _logger.Warn("Loading media from local db raised warnings, will reload from database.");
+ }
+
+ if (!okContent)
+ LockAndLoadContent(scope => LoadContentFromDatabaseLocked(scope, true));
+
+ if (!okMedia)
+ LockAndLoadMedia(scope => LoadMediaFromDatabaseLocked(scope, true));
+
+ LockAndLoadDomains();
+ }
+ catch (Exception ex)
+ {
+ _logger.Fatal(ex, "Panic, exception while loading cache data.");
+ throw;
+ }
+
+ // finally, cache is ready!
+ return true;
}
-
- if (bootState != SyncBootState.ColdBoot && _localMediaDbExists)
- {
- okMedia = LockAndLoadMedia(scope => LoadMediaFromLocalDbLocked(true));
- if (!okMedia)
- _logger.Warn("Loading media from local db raised warnings, will reload from database.");
- }
-
- if (!okContent)
- LockAndLoadContent(scope => LoadContentFromDatabaseLocked(scope, true));
-
- if (!okMedia)
- LockAndLoadMedia(scope => LoadMediaFromDatabaseLocked(scope, true));
-
- LockAndLoadDomains();
- }
- catch (Exception ex)
- {
- _logger.Fatal(ex, "Panic, exception while loading cache data.");
- throw;
- }
-
- // finally, cache is ready!
- _isReady = true;
- }
+ });
private void InitializeRepositoryEvents()
{
@@ -1146,9 +1157,13 @@ namespace Umbraco.Web.PublishedCache.NuCache
public override IPublishedSnapshot CreatePublishedSnapshot(string previewToken)
{
+ EnsureCaches();
+
// no cache, no joy
- if (_isReady == false)
+ if (Volatile.Read(ref _isReady) == false)
+ {
throw new InvalidOperationException("The published snapshot service has not properly initialized.");
+ }
var preview = previewToken.IsNullOrWhiteSpace() == false;
return new PublishedSnapshot(this, preview);
@@ -1159,6 +1174,8 @@ namespace Umbraco.Web.PublishedCache.NuCache
// even though the underlying elements may not change (store snapshots)
public PublishedSnapshot.PublishedSnapshotElements GetElements(bool previewDefault)
{
+ EnsureCaches();
+
// note: using ObjectCacheAppCache for elements and snapshot caches
// is not recommended because it creates an inner MemoryCache which is a heavy
// thing - better use a dictionary-based cache which "just" creates a concurrent
@@ -1805,6 +1822,8 @@ AND cmsContentNu.nodeId IS NULL
public void Collect()
{
+ EnsureCaches();
+
var contentCollect = _contentStore.CollectAsync();
var mediaCollect = _mediaStore.CollectAsync();
System.Threading.Tasks.Task.WaitAll(contentCollect, mediaCollect);
@@ -1814,8 +1833,17 @@ AND cmsContentNu.nodeId IS NULL
#region Internals/Testing
- internal ContentStore GetContentStore() => _contentStore;
- internal ContentStore GetMediaStore() => _mediaStore;
+ internal ContentStore GetContentStore()
+ {
+ EnsureCaches();
+ return _contentStore;
+ }
+
+ internal ContentStore GetMediaStore()
+ {
+ EnsureCaches();
+ return _mediaStore;
+ }
#endregion
}
diff --git a/src/Umbraco.Web/Search/ExamineFinalComponent.cs b/src/Umbraco.Web/Search/ExamineFinalComponent.cs
index 335d19baf0..6dd775ab64 100644
--- a/src/Umbraco.Web/Search/ExamineFinalComponent.cs
+++ b/src/Umbraco.Web/Search/ExamineFinalComponent.cs
@@ -29,26 +29,10 @@ namespace Umbraco.Web.Search
_indexRebuilder = indexRebuilder;
_mainDom = mainDom;
_syncBootStateAccessor = syncBootStateAccessor;
-
- // must add the handler in the ctor because it will be too late in Initialize
- // TODO: All of this boot synchronization for cold boot logic needs should be fixed in netcore
- _syncBootStateAccessor.Booting += _syncBootStateAccessor_Booting;
- }
-
- ///
- /// Once the boot state is known we can see if we require rebuilds
- ///
- ///
- ///
- private void _syncBootStateAccessor_Booting(object sender, SyncBootState e)
- {
- UmbracoModule.RouteAttempt += UmbracoModule_RouteAttempt;
}
private void UmbracoModule_RouteAttempt(object sender, RoutableAttemptEventArgs e)
{
- UmbracoModule.RouteAttempt -= UmbracoModule_RouteAttempt;
-
if (!_initialized)
{
lock (_locker)
@@ -58,6 +42,8 @@ namespace Umbraco.Web.Search
{
_initialized = true;
+ UmbracoModule.RouteAttempt -= UmbracoModule_RouteAttempt;
+
if (!_mainDom.IsMainDom) return;
var bootState = _syncBootStateAccessor.GetSyncBootState();
@@ -74,12 +60,12 @@ namespace Umbraco.Web.Search
}
public void Initialize()
- {
+ {
+ UmbracoModule.RouteAttempt += UmbracoModule_RouteAttempt;
}
public void Terminate()
{
- _syncBootStateAccessor.Booting -= _syncBootStateAccessor_Booting;
}
}
}