Merge branch 'v8/bugfix/8893-examine-startup' into v8/feature/nucache-perf
# Conflicts: # src/Umbraco.Core/Sync/DatabaseServerMessenger.cs # src/Umbraco.Core/Sync/ISyncBootStateAccessor.cs # src/Umbraco.Core/Sync/NonRuntimeLevelBootStateAccessor.cs # src/Umbraco.Core/Sync/SyncBootState.cs # src/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs # src/Umbraco.Tests/PublishedContent/NuCacheTests.cs # src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs # src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs # src/Umbraco.Tests/TestHelpers/TestSyncBootStateAccessor.cs # src/Umbraco.Web/Compose/DatabaseServerRegistrarAndMessengerComponent.cs # src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs # src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs
This commit is contained in:
@@ -39,9 +39,9 @@ namespace Umbraco.Core.Sync
|
||||
private int _lastId = -1;
|
||||
private DateTime _lastSync;
|
||||
private DateTime _lastPruned;
|
||||
private bool _initialized;
|
||||
private bool _syncing;
|
||||
private bool _released;
|
||||
private readonly Lazy<SyncBootState> _getSyncBootState;
|
||||
|
||||
public DatabaseServerMessengerOptions Options { get; }
|
||||
|
||||
@@ -59,6 +59,7 @@ namespace Umbraco.Core.Sync
|
||||
_lastPruned = _lastSync = DateTime.UtcNow;
|
||||
_syncIdle = new ManualResetEvent(true);
|
||||
_distCacheFilePath = new Lazy<string>(() => GetDistCacheFilePath(globalSettings));
|
||||
_getSyncBootState = new Lazy<SyncBootState>(BootInternal);
|
||||
}
|
||||
|
||||
protected ILogger Logger { get; }
|
||||
@@ -75,7 +76,7 @@ namespace Umbraco.Core.Sync
|
||||
{
|
||||
// we don't care if there's servers listed or not,
|
||||
// if distributed call is enabled we will make the call
|
||||
return _initialized && DistributedEnabled;
|
||||
return _getSyncBootState.IsValueCreated && DistributedEnabled;
|
||||
}
|
||||
|
||||
protected override void DeliverRemote(
|
||||
@@ -110,14 +111,14 @@ namespace Umbraco.Core.Sync
|
||||
|
||||
#region Sync
|
||||
|
||||
/// <summary>
|
||||
/// Boots the messenger.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Thread safety: this is NOT thread safe. Because it is NOT meant to run multi-threaded.
|
||||
/// Callers MUST ensure thread-safety.
|
||||
/// </remarks>
|
||||
[Obsolete("This is no longer used and will be removed in future versions")]
|
||||
protected void Boot()
|
||||
{
|
||||
// if called, just forces the boot logic
|
||||
_ = GetSyncBootState();
|
||||
}
|
||||
|
||||
private SyncBootState BootInternal()
|
||||
{
|
||||
// weight:10, must release *before* the published snapshot service, because once released
|
||||
// the service will *not* be able to properly handle our notifications anymore
|
||||
@@ -139,7 +140,7 @@ namespace Umbraco.Core.Sync
|
||||
// properly releasing MainDom - a timeout here means that one refresher
|
||||
// is taking too much time processing, however when it's done we will
|
||||
// not update lastId and stop everything
|
||||
var idle =_syncIdle.WaitOne(5000);
|
||||
var idle = _syncIdle.WaitOne(5000);
|
||||
if (idle == false)
|
||||
{
|
||||
Logger.Warn<DatabaseServerMessenger>("The wait lock timed out, application is shutting down. The current instruction batch will be re-processed.");
|
||||
@@ -147,17 +148,23 @@ namespace Umbraco.Core.Sync
|
||||
},
|
||||
weight);
|
||||
|
||||
SyncBootState bootState = SyncBootState.Unknown;
|
||||
|
||||
if (registered == false)
|
||||
return;
|
||||
{
|
||||
return bootState;
|
||||
}
|
||||
|
||||
ReadLastSynced(); // get _lastId
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
EnsureInstructions(scope.Database); // reset _lastId if instructions are missing
|
||||
Initialize(scope.Database); // boot
|
||||
bootState = Initialize(scope.Database); // boot
|
||||
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
return bootState;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -167,36 +174,11 @@ namespace Umbraco.Core.Sync
|
||||
/// Thread safety: this is NOT thread safe. Because it is NOT meant to run multi-threaded.
|
||||
/// Callers MUST ensure thread-safety.
|
||||
/// </remarks>
|
||||
private void Initialize(IUmbracoDatabase database)
|
||||
private SyncBootState Initialize(IUmbracoDatabase database)
|
||||
{
|
||||
lock (_locko)
|
||||
{
|
||||
if (_released) return;
|
||||
var coldboot = IsColdBoot(database);
|
||||
// could occur if shutting down immediately once starting up and before we've initialized
|
||||
if (_released) return SyncBootState.Unknown;
|
||||
|
||||
if (coldboot)
|
||||
{
|
||||
// go get the last id in the db and store it
|
||||
// note: do it BEFORE initializing otherwise some instructions might get lost
|
||||
// when doing it before, some instructions might run twice - not an issue
|
||||
var maxId = database.ExecuteScalar<int>("SELECT MAX(id) FROM umbracoCacheInstruction");
|
||||
|
||||
//if there is a max currently, or if we've never synced
|
||||
if (maxId > 0 || _lastId < 0)
|
||||
SaveLastSynced(maxId);
|
||||
|
||||
// execute initializing callbacks
|
||||
if (Options.InitializingCallbacks != null)
|
||||
foreach (var callback in Options.InitializingCallbacks)
|
||||
callback();
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsColdBoot(IUmbracoDatabase database)
|
||||
{
|
||||
var coldboot = false;
|
||||
if (_lastId < 0) // never synced before
|
||||
{
|
||||
@@ -206,27 +188,48 @@ namespace Umbraco.Core.Sync
|
||||
+ " The server will build its caches and indexes, and then adjust its last synced Id to the latest found in"
|
||||
+ " the database and maintain cache updates based on that Id.");
|
||||
|
||||
coldboot = true;
|
||||
}
|
||||
else
|
||||
coldboot = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//check for how many instructions there are to process, each row contains a count of the number of instructions contained in each
|
||||
//row so we will sum these numbers to get the actual count.
|
||||
var count = database.ExecuteScalar<int>("SELECT SUM(instructionCount) FROM umbracoCacheInstruction WHERE id > @lastId", new { lastId = _lastId });
|
||||
if (count > Options.MaxProcessingInstructionCount)
|
||||
{
|
||||
//check for how many instructions there are to process, each row contains a count of the number of instructions contained in each
|
||||
//row so we will sum these numbers to get the actual count.
|
||||
var count = database.ExecuteScalar<int>("SELECT SUM(instructionCount) FROM umbracoCacheInstruction WHERE id > @lastId", new {lastId = _lastId});
|
||||
if (count > Options.MaxProcessingInstructionCount)
|
||||
{
|
||||
//too many instructions, proceed to cold boot
|
||||
Logger.Warn<DatabaseServerMessenger,int,int>(
|
||||
"The instruction count ({InstructionCount}) exceeds the specified MaxProcessingInstructionCount ({MaxProcessingInstructionCount})."
|
||||
+ " The server will skip existing instructions, rebuild its caches and indexes entirely, adjust its last synced Id"
|
||||
+ " to the latest found in the database and maintain cache updates based on that Id.",
|
||||
count, Options.MaxProcessingInstructionCount);
|
||||
//too many instructions, proceed to cold boot
|
||||
Logger.Warn<DatabaseServerMessenger, int, int>(
|
||||
"The instruction count ({InstructionCount}) exceeds the specified MaxProcessingInstructionCount ({MaxProcessingInstructionCount})."
|
||||
+ " The server will skip existing instructions, rebuild its caches and indexes entirely, adjust its last synced Id"
|
||||
+ " to the latest found in the database and maintain cache updates based on that Id.",
|
||||
count, Options.MaxProcessingInstructionCount);
|
||||
|
||||
coldboot = true;
|
||||
}
|
||||
}
|
||||
|
||||
return coldboot;
|
||||
if (coldboot)
|
||||
{
|
||||
// go get the last id in the db and store it
|
||||
// note: do it BEFORE initializing otherwise some instructions might get lost
|
||||
// when doing it before, some instructions might run twice - not an issue
|
||||
var maxId = database.ExecuteScalar<int>("SELECT MAX(id) FROM umbracoCacheInstruction");
|
||||
|
||||
//if there is a max currently, or if we've never synced
|
||||
if (maxId > 0 || _lastId < 0)
|
||||
SaveLastSynced(maxId);
|
||||
|
||||
// execute initializing callbacks
|
||||
if (Options.InitializingCallbacks != null)
|
||||
{
|
||||
foreach (var callback in Options.InitializingCallbacks)
|
||||
{
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return coldboot ? SyncBootState.ColdBoot : SyncBootState.WarmBoot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -358,7 +361,7 @@ namespace Umbraco.Core.Sync
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
Logger.Error<DatabaseServerMessenger,int, string>(ex, "Failed to deserialize instructions ({DtoId}: '{DtoInstructions}').",
|
||||
Logger.Error<DatabaseServerMessenger, int, string>(ex, "Failed to deserialize instructions ({DtoId}: '{DtoInstructions}').",
|
||||
dto.Id,
|
||||
dto.Instructions);
|
||||
|
||||
@@ -416,11 +419,11 @@ namespace Umbraco.Core.Sync
|
||||
//}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error<DatabaseServerMessenger,int, string> (
|
||||
ex,
|
||||
"DISTRIBUTED CACHE IS NOT UPDATED. Failed to execute instructions ({DtoId}: '{DtoInstructions}'). Instruction is being skipped/ignored",
|
||||
dto.Id,
|
||||
dto.Instructions);
|
||||
Logger.Error<DatabaseServerMessenger, int, string>(
|
||||
ex,
|
||||
"DISTRIBUTED CACHE IS NOT UPDATED. Failed to execute instructions ({DtoId}: '{DtoInstructions}'). Instruction is being skipped/ignored",
|
||||
dto.Id,
|
||||
dto.Instructions);
|
||||
|
||||
//we cannot throw here because this invalid instruction will just keep getting processed over and over and errors
|
||||
// will be thrown over and over. The only thing we can do is ignore and move on.
|
||||
@@ -554,29 +557,7 @@ namespace Umbraco.Core.Sync
|
||||
|
||||
#endregion
|
||||
|
||||
public SyncBootState GetSyncBootState()
|
||||
{
|
||||
try
|
||||
{
|
||||
ReadLastSynced(); // get _lastId
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
EnsureInstructions(scope.Database);
|
||||
bool isColdBoot = IsColdBoot(scope.Database);
|
||||
|
||||
if (isColdBoot)
|
||||
{
|
||||
return SyncBootState.ColdBoot;
|
||||
}
|
||||
return SyncBootState.HasSyncState;
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Logger.Warn<DatabaseServerMessenger>("Error determining Sync Boot State", ex);
|
||||
return SyncBootState.Unknown;
|
||||
}
|
||||
}
|
||||
public virtual SyncBootState GetSyncBootState() => _getSyncBootState.Value;
|
||||
|
||||
#region Notify refreshers
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
@@ -24,13 +25,8 @@ namespace Umbraco.Core.Sync
|
||||
/// </summary>
|
||||
public int MaxProcessingInstructionCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of callbacks that will be invoked if the lastsynced.txt file does not exist.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// These callbacks will typically be for eg rebuilding the xml cache file, or examine indexes, based on
|
||||
/// the data in the database to get this particular server node up to date.
|
||||
/// </remarks>
|
||||
[Obsolete("This should not be used. If initialization calls need to be invoked on a cold boot, use the ISyncBootStateAccessor.Booting event.")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public IEnumerable<Action> InitializingCallbacks { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace Umbraco.Core.Sync
|
||||
/// </summary>
|
||||
public class NonRuntimeLevelBootStateAccessor : ISyncBootStateAccessor
|
||||
{
|
||||
public event EventHandler<SyncBootState> Booting;
|
||||
|
||||
public SyncBootState GetSyncBootState()
|
||||
{
|
||||
return SyncBootState.Unknown;
|
||||
|
||||
@@ -1,24 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
public enum SyncBootState
|
||||
{
|
||||
/// <summary>
|
||||
/// Unknown state. Treat as HasSyncState
|
||||
/// Unknown state. Treat as WarmBoot
|
||||
/// </summary>
|
||||
Unknown = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Cold boot. No Sync state
|
||||
/// </summary>
|
||||
ColdBoot = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Warm boot. Sync state present
|
||||
/// </summary>
|
||||
HasSyncState = 2
|
||||
WarmBoot = 2
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user