diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 9522e76622..d7480baf67 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -128,10 +128,14 @@ namespace Umbraco.Web.PublishedCache.NuCache var path = GetLocalFilesPath(); var localContentDbPath = Path.Combine(path, "NuCache.Content.db"); var localMediaDbPath = Path.Combine(path, "NuCache.Media.db"); - _localDbExists = File.Exists(localContentDbPath) && File.Exists(localMediaDbPath); + var localContentDbExists = File.Exists(localContentDbPath); + var localMediaDbExists = File.Exists(localMediaDbPath); + _localDbExists = localContentDbExists && localMediaDbExists; // if both local databases exist then GetTree will open them, else new databases will be created _localContentDb = BTree.GetTree(localContentDbPath, _localDbExists); _localMediaDb = BTree.GetTree(localMediaDbPath, _localDbExists); + + _logger.Info("Registered with MainDom, localContentDbExists? {LocalContentDbExists}, localMediaDbExists? {LocalMediaDbExists}", localContentDbExists, localMediaDbExists); }, () => { @@ -143,18 +147,25 @@ namespace Umbraco.Web.PublishedCache.NuCache _localContentDb = null; _mediaStore?.ReleaseLocalDb(); //null check because we could shut down before being assigned _localMediaDb = null; + + _logger.Info("Released from MainDom"); } }); // 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}", _localContentDb != null); _contentStore = new ContentStore(publishedSnapshotAccessor, variationContextAccessor, logger, _localContentDb); + _logger.Info("Creating the media store, localMediaDbExists? {LocalMediaDbExists}", _localMediaDb != null); _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); } @@ -371,7 +382,27 @@ namespace Umbraco.Web.PublishedCache.NuCache var kits = _localContentDb.Select(x => x.Value) .OrderBy(x => x.Node.Level) .ThenBy(x => x.Node.ParentContentId) - .ThenBy(x => x.Node.SortOrder); // IMPORTANT sort by level + parentId + sortOrder + .ThenBy(x => x.Node.SortOrder) // IMPORTANT sort by level + parentId + sortOrder + .ToList(); + + if (kits.Count == 0) + { + // If there's nothing in the local cache file, we should return false? YES even though the site legitately might be empty. + // Is it possible that the cache file is empty but the database is not? YES... + // * A new file is created when one doesn't exist, this will only be done when MainDom is acquired + // * The new file will be populated as soon as LoadCachesOnStartup is called + // * If the appdomain is going down the moment after MainDom was acquired and we've created an empty cache file, + // then the MainDom release callback is triggered from on a different thread, which will close the file and + // set the cache file reference to null. At this moment, it is possible that the file is closed and the + // reference is set to null BEFORE LoadCachesOnStartup which would mean that the current appdomain would load + // in the in-mem cache via DB calls, BUT this now means that there is an empty cache file which will be + // loaded by the next appdomain and it won't check if it's empty, it just assumes that since the cache + // file is there, that is correct. + + _logger.Info("Tried to load content from the local cache file but it was empty."); + return false; + } + return onStartup ? _contentStore.SetAllFastSorted(kits) : _contentStore.SetAll(kits); } } diff --git a/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs b/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs index 4f66af11bd..746bc61e34 100644 --- a/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs +++ b/src/Umbraco.Web/Scheduling/HealthCheckNotifier.cs @@ -51,7 +51,7 @@ namespace Umbraco.Web.Scheduling return false; // do NOT repeat, going down } - using (_logger.DebugDuration("Health checks executing", "Health checks complete")) + using (_logger.DebugDuration("Health checks executing", "Health checks complete")) { var healthCheckConfig = Current.Configs.HealthChecks(); diff --git a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs b/src/Umbraco.Web/Scheduling/SchedulerComponent.cs index c334bcee1a..1416393f46 100644 --- a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs +++ b/src/Umbraco.Web/Scheduling/SchedulerComponent.cs @@ -18,6 +18,11 @@ namespace Umbraco.Web.Scheduling { internal sealed class SchedulerComponent : IComponent { + private const int DefaultDelayMilliseconds = 180000; // 3 mins + private const int OneMinuteMilliseconds = 60000; + private const int FiveMinuteMilliseconds = 300000; + private const int OneHourMilliseconds = 3600000; + private readonly IRuntimeState _runtime; private readonly IContentService _contentService; private readonly IAuditService _auditService; @@ -111,7 +116,7 @@ namespace Umbraco.Web.Scheduling { // ping/keepalive // on all servers - var task = new KeepAlive(_keepAliveRunner, 60000, 300000, _runtime, _logger); + var task = new KeepAlive(_keepAliveRunner, DefaultDelayMilliseconds, FiveMinuteMilliseconds, _runtime, _logger); _keepAliveRunner.TryAdd(task); return task; } @@ -120,7 +125,7 @@ namespace Umbraco.Web.Scheduling { // scheduled publishing/unpublishing // install on all, will only run on non-replica servers - var task = new ScheduledPublishing(_publishingRunner, 60000, 60000, _runtime, _contentService, _umbracoContextFactory, _logger); + var task = new ScheduledPublishing(_publishingRunner, DefaultDelayMilliseconds, OneMinuteMilliseconds, _runtime, _contentService, _umbracoContextFactory, _logger); _publishingRunner.TryAdd(task); return task; } @@ -133,15 +138,15 @@ namespace Umbraco.Web.Scheduling int delayInMilliseconds; if (string.IsNullOrEmpty(healthCheckConfig.NotificationSettings.FirstRunTime)) { - delayInMilliseconds = 60000; + delayInMilliseconds = DefaultDelayMilliseconds; } else { // Otherwise start at scheduled time delayInMilliseconds = DateTime.Now.PeriodicMinutesFrom(healthCheckConfig.NotificationSettings.FirstRunTime) * 60 * 1000; - if (delayInMilliseconds < 60000) + if (delayInMilliseconds < DefaultDelayMilliseconds) { - delayInMilliseconds = 60000; + delayInMilliseconds = DefaultDelayMilliseconds; } } @@ -155,7 +160,7 @@ namespace Umbraco.Web.Scheduling { // log scrubbing // install on all, will only run on non-replica servers - var task = new LogScrubber(_scrubberRunner, 60000, LogScrubber.GetLogScrubbingInterval(settings, _logger), _runtime, _auditService, settings, _scopeProvider, _logger); + var task = new LogScrubber(_scrubberRunner, DefaultDelayMilliseconds, LogScrubber.GetLogScrubbingInterval(settings, _logger), _runtime, _auditService, settings, _scopeProvider, _logger); _scrubberRunner.TryAdd(task); return task; } @@ -164,7 +169,7 @@ namespace Umbraco.Web.Scheduling { // temp file cleanup, will run on all servers - even though file upload should only be handled on the master, this will // ensure that in the case it happes on replicas that they are cleaned up. - var task = new TempFileCleanup(_fileCleanupRunner, 60000, 3600000 /* 1 hr */, + var task = new TempFileCleanup(_fileCleanupRunner, DefaultDelayMilliseconds, OneHourMilliseconds, new[] { new DirectoryInfo(IOHelper.MapPath(SystemDirectories.TempFileUploads)) }, TimeSpan.FromDays(1), //files that are over a day old _runtime, _logger);