Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d6191198ff | |||
| ad17f7d69a | |||
| 8d3ae307dc | |||
| 2584771505 | |||
| a10e0ae275 | |||
| 3db10e176b | |||
| 1a142c0127 |
@@ -5,7 +5,8 @@ using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Migrations.Upgrade.V_7_12_0;
|
||||
using Umbraco.Core.Migrations.Upgrade.V_7_14_0;
|
||||
using Umbraco.Core.Migrations.Upgrade.V_8_0_0;
|
||||
using Umbraco.Core.Migrations.Upgrade.V_8_0_1;
|
||||
using Umbraco.Core.Migrations.Upgrade.V_8_1_0;
|
||||
using ChangeNuCacheJsonFormat = Umbraco.Core.Migrations.Upgrade.V_8_0_1.ChangeNuCacheJsonFormat;
|
||||
|
||||
namespace Umbraco.Core.Migrations.Upgrade
|
||||
{
|
||||
@@ -139,6 +140,7 @@ namespace Umbraco.Core.Migrations.Upgrade
|
||||
To<RenameLabelAndRichTextPropertyEditorAliases>("{E0CBE54D-A84F-4A8F-9B13-900945FD7ED9}");
|
||||
To<MergeDateAndDateTimePropertyEditor>("{78BAF571-90D0-4D28-8175-EF96316DA789}");
|
||||
To<ChangeNuCacheJsonFormat>("{80C0A0CB-0DD5-4573-B000-C4B7C313C70D}");
|
||||
To<AddLastCacheInstructionColumn>("{4D0DF167-66CC-4D7B-826A-FFF8ABFCC45A}");
|
||||
|
||||
//FINAL
|
||||
|
||||
@@ -170,7 +172,7 @@ namespace Umbraco.Core.Migrations.Upgrade
|
||||
From("{init-7.12.4}").To("{init-7.10.0}"); // same as 7.12.0
|
||||
From("{init-7.13.0}").To("{init-7.10.0}"); // same as 7.12.0
|
||||
From("{init-7.13.1}").To("{init-7.10.0}"); // same as 7.12.0
|
||||
|
||||
|
||||
// 7.14.0 has migrations, handle it...
|
||||
// clone going from 7.10 to 1350617A (the last one before we started to merge 7.12 migrations), then
|
||||
// clone going from CF51B39B (after 7.12 migrations) to 0009109C (the last one before we started to merge 7.12 migrations),
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
|
||||
namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0
|
||||
{
|
||||
public class AddLastCacheInstructionColumn : MigrationBase
|
||||
{
|
||||
public AddLastCacheInstructionColumn(IMigrationContext context)
|
||||
: base(context)
|
||||
{ }
|
||||
|
||||
public override void Migrate()
|
||||
{
|
||||
var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToList();
|
||||
|
||||
AddColumnIfNotExists<ServerRegistrationDto>(columns, "lastCacheInstructionId");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,5 +32,10 @@ namespace Umbraco.Core.Models
|
||||
/// Gets the date and time the registration was last accessed.
|
||||
/// </summary>
|
||||
DateTime Accessed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value of the last executed cache instruction.
|
||||
/// </summary>
|
||||
int LastCacheInstructionId { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Umbraco.Core.Models
|
||||
private string _serverIdentity;
|
||||
private bool _isActive;
|
||||
private bool _isMaster;
|
||||
private int _lastCacheInstructionId;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ServerRegistration"/> class.
|
||||
@@ -30,7 +31,8 @@ namespace Umbraco.Core.Models
|
||||
/// <param name="accessed">The date and time the registration was last accessed.</param>
|
||||
/// <param name="isActive">A value indicating whether the registration is active.</param>
|
||||
/// <param name="isMaster">A value indicating whether the registration is master.</param>
|
||||
public ServerRegistration(int id, string serverAddress, string serverIdentity, DateTime registered, DateTime accessed, bool isActive, bool isMaster)
|
||||
/// <param name="lastCacheInstructionId">A value indicating the id of the last executed cache instruction.</param>
|
||||
public ServerRegistration(int id, string serverAddress, string serverIdentity, DateTime registered, DateTime accessed, bool isActive, bool isMaster, int lastCacheInstructionId)
|
||||
{
|
||||
UpdateDate = accessed;
|
||||
CreateDate = registered;
|
||||
@@ -40,6 +42,7 @@ namespace Umbraco.Core.Models
|
||||
ServerIdentity = serverIdentity;
|
||||
IsActive = isActive;
|
||||
IsMaster = isMaster;
|
||||
LastCacheInstructionId = lastCacheInstructionId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -55,6 +58,7 @@ namespace Umbraco.Core.Models
|
||||
Key = 0.ToString(CultureInfo.InvariantCulture).EncodeAsGuid();
|
||||
ServerAddress = serverAddress;
|
||||
ServerIdentity = serverIdentity;
|
||||
LastCacheInstructionId = -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -111,13 +115,20 @@ namespace Umbraco.Core.Models
|
||||
set => UpdateDate = value;
|
||||
}
|
||||
|
||||
public int LastCacheInstructionId
|
||||
{
|
||||
get => _lastCacheInstructionId;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _lastCacheInstructionId, nameof(_lastCacheInstructionId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the value of this instance to its equivalent string representation.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{{\"{0}\", \"{1}\", {2}active, {3}master}}", ServerAddress, ServerIdentity, IsActive ? "" : "!", IsMaster ? "" : "!");
|
||||
return
|
||||
$"{{\"{ServerAddress}\", \"{ServerIdentity}\", {(IsActive ? "" : "!")}active, {(IsMaster ? "" : "!")}master, {LastCacheInstructionId}}}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,5 +36,9 @@ namespace Umbraco.Core.Persistence.Dtos
|
||||
|
||||
[Column("isMaster")]
|
||||
public bool IsMaster { get; set; }
|
||||
|
||||
[Constraint(Default = -1)]
|
||||
[Column("lastCacheInstructionId")]
|
||||
public int LastCacheInstructionId { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
public static ServerRegistration BuildEntity(ServerRegistrationDto dto)
|
||||
{
|
||||
var model = new ServerRegistration(dto.Id, dto.ServerAddress, dto.ServerIdentity, dto.DateRegistered, dto.DateAccessed, dto.IsActive, dto.IsMaster);
|
||||
var model = new ServerRegistration(dto.Id, dto.ServerAddress, dto.ServerIdentity, dto.DateRegistered, dto.DateAccessed, dto.IsActive, dto.IsMaster, dto.LastCacheInstructionId);
|
||||
// reset dirty initial properties (U4-1946)
|
||||
model.ResetDirtyProperties(false);
|
||||
return model;
|
||||
@@ -22,7 +22,8 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
IsActive = entity.IsActive,
|
||||
IsMaster = ((ServerRegistration) entity).IsMaster,
|
||||
DateAccessed = entity.UpdateDate,
|
||||
ServerIdentity = entity.ServerIdentity
|
||||
ServerIdentity = entity.ServerIdentity,
|
||||
LastCacheInstructionId = entity.LastCacheInstructionId
|
||||
};
|
||||
|
||||
if (entity.HasIdentity)
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
DefineMap<ServerRegistration, ServerRegistrationDto>(nameof(ServerRegistration.CreateDate), nameof(ServerRegistrationDto.DateRegistered));
|
||||
DefineMap<ServerRegistration, ServerRegistrationDto>(nameof(ServerRegistration.UpdateDate), nameof(ServerRegistrationDto.DateAccessed));
|
||||
DefineMap<ServerRegistration, ServerRegistrationDto>(nameof(ServerRegistration.ServerIdentity), nameof(ServerRegistrationDto.ServerIdentity));
|
||||
DefineMap<ServerRegistration, ServerRegistrationDto>(nameof(ServerRegistration.LastCacheInstructionId), nameof(ServerRegistrationDto.LastCacheInstructionId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,13 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
protected override IEnumerable<IServerRegistration> PerformGetByQuery(IQuery<IServerRegistration> query)
|
||||
{
|
||||
throw new NotSupportedException("This repository does not support this method.");
|
||||
var sqlClause = GetBaseQuery(false);
|
||||
var translator = new SqlTranslator<IServerRegistration>(sqlClause, query);
|
||||
var sql = translator.Translate();
|
||||
|
||||
var dtos = Database.Fetch<ServerRegistrationDto>(sql);
|
||||
|
||||
return dtos.Select(x => ServerRegistrationFactory.BuildEntity(x));
|
||||
}
|
||||
|
||||
protected override Sql<ISqlContext> GetBaseQuery(bool isCount)
|
||||
@@ -109,7 +115,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
protected override void PersistUpdatedItem(IServerRegistration entity)
|
||||
{
|
||||
((ServerRegistration)entity).UpdatingEntity();
|
||||
|
||||
|
||||
var dto = ServerRegistrationFactory.BuildDto(entity);
|
||||
|
||||
Database.Update(dto);
|
||||
|
||||
@@ -86,6 +86,7 @@ namespace Umbraco.Core.Runtime
|
||||
new DatabaseServerRegistrarOptions());
|
||||
});
|
||||
|
||||
composition.RegisterUnique<IServerMessengerSyncRepository, DatabaseServerMessengerSyncRepository>();
|
||||
// by default we'll use the database server messenger with default options (no callbacks),
|
||||
// this will be overridden by the db thing in the corresponding components in the web
|
||||
// project
|
||||
@@ -95,7 +96,7 @@ namespace Umbraco.Core.Runtime
|
||||
factory.GetInstance<IScopeProvider>(),
|
||||
factory.GetInstance<ISqlContext>(),
|
||||
factory.GetInstance<IProfilingLogger>(),
|
||||
factory.GetInstance<IGlobalSettings>(),
|
||||
factory.GetInstance<IServerMessengerSyncRepository>(),
|
||||
true, new DatabaseServerMessengerOptions()));
|
||||
|
||||
composition.WithCollectionBuilder<CacheRefresherCollectionBuilder>()
|
||||
|
||||
@@ -34,9 +34,9 @@ namespace Umbraco.Core.Sync
|
||||
private readonly ManualResetEvent _syncIdle;
|
||||
private readonly object _locko = new object();
|
||||
private readonly IProfilingLogger _profilingLogger;
|
||||
private readonly IServerMessengerSyncRepository _serverMessengerSyncRepository;
|
||||
private readonly ISqlContext _sqlContext;
|
||||
private readonly Lazy<string> _distCacheFilePath;
|
||||
private int _lastId = -1;
|
||||
|
||||
private DateTime _lastSync;
|
||||
private DateTime _lastPruned;
|
||||
private bool _initialized;
|
||||
@@ -46,7 +46,7 @@ namespace Umbraco.Core.Sync
|
||||
public DatabaseServerMessengerOptions Options { get; }
|
||||
|
||||
public DatabaseServerMessenger(
|
||||
IRuntimeState runtime, IScopeProvider scopeProvider, ISqlContext sqlContext, IProfilingLogger proflog, IGlobalSettings globalSettings,
|
||||
IRuntimeState runtime, IScopeProvider scopeProvider, ISqlContext sqlContext, IProfilingLogger proflog, IServerMessengerSyncRepository serverMessengerSyncRepository,
|
||||
bool distributedEnabled, DatabaseServerMessengerOptions options)
|
||||
: base(distributedEnabled)
|
||||
{
|
||||
@@ -54,11 +54,11 @@ namespace Umbraco.Core.Sync
|
||||
_sqlContext = sqlContext;
|
||||
_runtime = runtime;
|
||||
_profilingLogger = proflog ?? throw new ArgumentNullException(nameof(proflog));
|
||||
_serverMessengerSyncRepository = serverMessengerSyncRepository;
|
||||
Logger = proflog;
|
||||
Options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
_lastPruned = _lastSync = DateTime.UtcNow;
|
||||
_syncIdle = new ManualResetEvent(true);
|
||||
_distCacheFilePath = new Lazy<string>(() => GetDistCacheFilePath(globalSettings));
|
||||
}
|
||||
|
||||
protected ILogger Logger { get; }
|
||||
@@ -67,7 +67,7 @@ namespace Umbraco.Core.Sync
|
||||
|
||||
protected Sql<ISqlContext> Sql() => _sqlContext.Sql();
|
||||
|
||||
private string DistCacheFilePath => _distCacheFilePath.Value;
|
||||
|
||||
|
||||
#region Messenger
|
||||
|
||||
@@ -150,7 +150,7 @@ namespace Umbraco.Core.Sync
|
||||
if (registered == false)
|
||||
return;
|
||||
|
||||
ReadLastSynced(); // get _lastId
|
||||
_serverMessengerSyncRepository.Read(); // get _lastId
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
EnsureInstructions(scope.Database); // reset _lastId if instructions are missing
|
||||
@@ -174,7 +174,7 @@ namespace Umbraco.Core.Sync
|
||||
if (_released) return;
|
||||
|
||||
var coldboot = false;
|
||||
if (_lastId < 0) // never synced before
|
||||
if (_serverMessengerSyncRepository.Value < 0) // never synced before
|
||||
{
|
||||
// we haven't synced - in this case we aren't going to sync the whole thing, we will assume this is a new
|
||||
// server and it will need to rebuild it's own caches, eg Lucene or the xml cache file.
|
||||
@@ -188,7 +188,7 @@ namespace Umbraco.Core.Sync
|
||||
{
|
||||
//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});
|
||||
var count = database.ExecuteScalar<int>("SELECT SUM(instructionCount) FROM umbracoCacheInstruction WHERE id > @lastId", new {lastId = _serverMessengerSyncRepository.Value});
|
||||
if (count > Options.MaxProcessingInstructionCount)
|
||||
{
|
||||
//too many instructions, proceed to cold boot
|
||||
@@ -210,8 +210,11 @@ namespace Umbraco.Core.Sync
|
||||
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);
|
||||
if (maxId > 0 || _serverMessengerSyncRepository.Value < 0)
|
||||
{
|
||||
_serverMessengerSyncRepository.Save(maxId);
|
||||
}
|
||||
|
||||
|
||||
// execute initializing callbacks
|
||||
if (Options.InitializingCallbacks != null)
|
||||
@@ -306,7 +309,7 @@ namespace Umbraco.Core.Sync
|
||||
|
||||
var sql = Sql().SelectAll()
|
||||
.From<CacheInstructionDto>()
|
||||
.Where<CacheInstructionDto>(dto => dto.Id > _lastId)
|
||||
.Where<CacheInstructionDto>(dto => dto.Id > _serverMessengerSyncRepository.Value)
|
||||
.OrderBy<CacheInstructionDto>(dto => dto.Id);
|
||||
|
||||
//only retrieve the top 100 (just in case there's tons)
|
||||
@@ -375,7 +378,7 @@ namespace Umbraco.Core.Sync
|
||||
}
|
||||
|
||||
if (lastId > 0)
|
||||
SaveLastSynced(lastId);
|
||||
_serverMessengerSyncRepository.Save(lastId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -461,7 +464,7 @@ namespace Umbraco.Core.Sync
|
||||
/// </remarks>
|
||||
private void EnsureInstructions(IUmbracoDatabase database)
|
||||
{
|
||||
if (_lastId == 0)
|
||||
if (_serverMessengerSyncRepository.Value == 0)
|
||||
{
|
||||
var sql = Sql().Select("COUNT(*)")
|
||||
.From<CacheInstructionDto>();
|
||||
@@ -470,50 +473,22 @@ namespace Umbraco.Core.Sync
|
||||
|
||||
//if there are instructions but we haven't synced, then a cold boot is necessary
|
||||
if (count > 0)
|
||||
_lastId = -1;
|
||||
_serverMessengerSyncRepository.Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
var sql = Sql().SelectAll()
|
||||
.From<CacheInstructionDto>()
|
||||
.Where<CacheInstructionDto>(dto => dto.Id == _lastId);
|
||||
.Where<CacheInstructionDto>(dto => dto.Id == _serverMessengerSyncRepository.Value);
|
||||
|
||||
var dtos = database.Fetch<CacheInstructionDto>(sql);
|
||||
|
||||
//if the last synced instruction is not found in the db, then a cold boot is necessary
|
||||
if (dtos.Count == 0)
|
||||
_lastId = -1;
|
||||
_serverMessengerSyncRepository.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the last-synced id from file into memory.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Thread safety: this is NOT thread safe. Because it is NOT meant to run multi-threaded.
|
||||
/// </remarks>
|
||||
private void ReadLastSynced()
|
||||
{
|
||||
if (File.Exists(DistCacheFilePath) == false) return;
|
||||
|
||||
var content = File.ReadAllText(DistCacheFilePath);
|
||||
if (int.TryParse(content, out var last))
|
||||
_lastId = last;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the in-memory last-synced id and persists it to file.
|
||||
/// </summary>
|
||||
/// <param name="id">The id.</param>
|
||||
/// <remarks>
|
||||
/// Thread safety: this is NOT thread safe. Because it is NOT meant to run multi-threaded.
|
||||
/// </remarks>
|
||||
private void SaveLastSynced(int id)
|
||||
{
|
||||
File.WriteAllText(DistCacheFilePath, id.ToString(CultureInfo.InvariantCulture));
|
||||
_lastId = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique local identity of the executing AppDomain.
|
||||
/// </summary>
|
||||
@@ -530,21 +505,7 @@ namespace Umbraco.Core.Sync
|
||||
+ "/D" + AppDomain.CurrentDomain.Id // eg 22
|
||||
+ "] " + Guid.NewGuid().ToString("N").ToUpper(); // make it truly unique
|
||||
|
||||
private string GetDistCacheFilePath(IGlobalSettings globalSettings)
|
||||
{
|
||||
var fileName = HttpRuntime.AppDomainAppId.ReplaceNonAlphanumericChars(string.Empty) + "-lastsynced.txt";
|
||||
|
||||
var distCacheFilePath = Path.Combine(globalSettings.LocalTempPath, "DistCache", fileName);
|
||||
|
||||
//ensure the folder exists
|
||||
var folder = Path.GetDirectoryName(distCacheFilePath);
|
||||
if (folder == null)
|
||||
throw new InvalidOperationException("The folder could not be determined for the file " + distCacheFilePath);
|
||||
if (Directory.Exists(folder) == false)
|
||||
Directory.CreateDirectory(folder);
|
||||
|
||||
return distCacheFilePath;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
/// <summary>
|
||||
/// A version of <see cref="IServerMessengerSyncRepository" /> that uses the umbracoServer table to persist the
|
||||
/// information in the database.
|
||||
/// </summary>
|
||||
public class DatabaseServerMessengerSyncRepository : IServerMessengerSyncRepository
|
||||
{
|
||||
private static readonly string ServerIdentity = NetworkHelper.MachineName // eg DOMAIN\SERVER
|
||||
+ "/" + HttpRuntime.AppDomainAppId; // eg /LM/S3SVC/11/ROOT
|
||||
|
||||
private readonly IScopeProvider _scopeProvider;
|
||||
private readonly IServerRegistrationRepository _serverRegistrationRepository;
|
||||
|
||||
public DatabaseServerMessengerSyncRepository(IServerRegistrationRepository serverRegistrationRepository,
|
||||
IScopeProvider scopeProvider)
|
||||
{
|
||||
_serverRegistrationRepository = serverRegistrationRepository;
|
||||
_scopeProvider = scopeProvider;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Value { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Reset()
|
||||
{
|
||||
Value = -1;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Save(int value)
|
||||
{
|
||||
using (var scope = _scopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
var server = GetServer(scope);
|
||||
|
||||
if (server != null)
|
||||
{
|
||||
server.LastCacheInstructionId = value;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
_serverRegistrationRepository.Save(server);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Read()
|
||||
{
|
||||
using (var scope = _scopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
var server = GetServer(scope);
|
||||
|
||||
if (server != null) Value = server.LastCacheInstructionId;
|
||||
}
|
||||
}
|
||||
|
||||
private IServerRegistration GetServer(IScope scope)
|
||||
{
|
||||
var serverRegistration = _serverRegistrationRepository
|
||||
.Get(scope.SqlContext.Query<IServerRegistration>().Where(x => x.ServerIdentity == ServerIdentity))
|
||||
.FirstOrDefault();
|
||||
|
||||
return serverRegistration;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
/// <summary>
|
||||
/// Persistence of the information about the last read value from the cache instructions
|
||||
/// </summary>
|
||||
public interface IServerMessengerSyncRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the incremental value.
|
||||
/// </summary>
|
||||
int Value { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates the in-memory last-synced id and persists it to file.
|
||||
/// </summary>
|
||||
/// <param name="value">The incremental value.</param>
|
||||
void Save(int value);
|
||||
|
||||
/// <summary>
|
||||
/// DisableElectionForSingleServer
|
||||
/// Reads the last-synced id from file into memory.
|
||||
/// </summary>
|
||||
void Read();
|
||||
|
||||
/// <summary>
|
||||
/// Resets the value to its initial value.
|
||||
/// </summary>
|
||||
void Reset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Web;
|
||||
using Umbraco.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Sync
|
||||
{
|
||||
/// <summary>
|
||||
/// A version of <see cref="IServerMessengerSyncRepository"/> that uses a file in the local temp folder to persist the information.
|
||||
/// </summary>
|
||||
public class LocalTempFileServerMessengerSyncRepository : IServerMessengerSyncRepository
|
||||
{
|
||||
private readonly Lazy<string> _distCacheFilePath;
|
||||
|
||||
public LocalTempFileServerMessengerSyncRepository(IGlobalSettings globalSettings)
|
||||
{
|
||||
_distCacheFilePath = new Lazy<string>(() => GetDistCacheFilePath(globalSettings));
|
||||
}
|
||||
|
||||
private string DistCacheFilePath => _distCacheFilePath.Value;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Value { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Reset()
|
||||
{
|
||||
Value = -1;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// Thread safety: this is NOT thread safe. Because it is NOT meant to run multi-threaded.
|
||||
/// </remarks>
|
||||
public void Read()
|
||||
{
|
||||
if (File.Exists(DistCacheFilePath) == false) return;
|
||||
|
||||
var content = File.ReadAllText(DistCacheFilePath);
|
||||
if (int.TryParse(content, out var last)) Value = last;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// Thread safety: this is NOT thread safe. Because it is NOT meant to run multi-threaded.
|
||||
/// </remarks>
|
||||
public void Save(int value)
|
||||
{
|
||||
File.WriteAllText(DistCacheFilePath, value.ToString(CultureInfo.InvariantCulture));
|
||||
Value = value;
|
||||
}
|
||||
|
||||
private string GetDistCacheFilePath(IGlobalSettings globalSettings)
|
||||
{
|
||||
var fileName = HttpRuntime.AppDomainAppId.ReplaceNonAlphanumericChars(string.Empty) + "-lastsynced.txt";
|
||||
|
||||
var distCacheFilePath = Path.Combine(globalSettings.LocalTempPath, "DistCache", fileName);
|
||||
|
||||
//ensure the folder exists
|
||||
var folder = Path.GetDirectoryName(distCacheFilePath);
|
||||
if (folder == null)
|
||||
throw new InvalidOperationException("The folder could not be determined for the file " +
|
||||
distCacheFilePath);
|
||||
if (Directory.Exists(folder) == false)
|
||||
Directory.CreateDirectory(folder);
|
||||
|
||||
return distCacheFilePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -219,6 +219,8 @@
|
||||
<Compile Include="Mapping\MapDefinitionCollectionBuilder.cs" />
|
||||
<Compile Include="Mapping\IMapDefinition.cs" />
|
||||
<Compile Include="Mapping\UmbracoMapper.cs" />
|
||||
<Compile Include="Migrations\Upgrade\V_8_1_0\AddLastCacheInstructionColumn.cs" />
|
||||
<Compile Include="Migrations\Upgrade\V_8_1_0\ChangeNuCacheJsonFormat.cs" />
|
||||
<Compile Include="Models\CultureImpact.cs" />
|
||||
<Compile Include="Models\PublishedContent\ILivePublishedModelFactory.cs" />
|
||||
<Compile Include="Persistence\Dtos\PropertyTypeCommonDto.cs" />
|
||||
@@ -230,6 +232,9 @@
|
||||
<Compile Include="PublishedModelFactoryExtensions.cs" />
|
||||
<Compile Include="Services\PropertyValidationService.cs" />
|
||||
<Compile Include="Composing\TypeCollectionBuilderBase.cs" />
|
||||
<Compile Include="Sync\DatabaseServerMessengerSyncRepository.cs" />
|
||||
<Compile Include="Sync\IServerMessengerSyncRepository.cs" />
|
||||
<Compile Include="Sync\LocalTempFileServerMessengerSyncRepository.cs" />
|
||||
<Compile Include="TypeLoaderExtensions.cs" />
|
||||
<Compile Include="Composing\WeightAttribute.cs" />
|
||||
<Compile Include="Composing\WeightedCollectionBuilderBase.cs" />
|
||||
|
||||
@@ -225,7 +225,7 @@ namespace Umbraco.Tests.Persistence.NPocoTests
|
||||
|
||||
// Assert
|
||||
Assert.That(commands[0].CommandText,
|
||||
Is.EqualTo("INSERT INTO [umbracoServer] ([umbracoServer].[address], [umbracoServer].[computerName], [umbracoServer].[registeredDate], [umbracoServer].[lastNotifiedDate], [umbracoServer].[isActive], [umbracoServer].[isMaster]) VALUES (@0,@1,@2,@3,@4,@5), (@6,@7,@8,@9,@10,@11)"));
|
||||
Is.EqualTo("INSERT INTO [umbracoServer] ([umbracoServer].[address], [umbracoServer].[computerName], [umbracoServer].[registeredDate], [umbracoServer].[lastNotifiedDate], [umbracoServer].[isActive], [umbracoServer].[isMaster], [umbracoServer].[lastCacheInstructionId]) VALUES (@0,@1,@2,@3,@4,@5,@6), (@7,@8,@9,@10,@11,@12,@13)"));
|
||||
}
|
||||
|
||||
|
||||
@@ -242,7 +242,8 @@ namespace Umbraco.Tests.Persistence.NPocoTests
|
||||
DateRegistered = DateTime.Now,
|
||||
IsActive = true,
|
||||
DateAccessed = DateTime.Now,
|
||||
IsMaster = true
|
||||
IsMaster = true,
|
||||
LastCacheInstructionId = -1
|
||||
});
|
||||
}
|
||||
|
||||
@@ -254,7 +255,7 @@ namespace Umbraco.Tests.Persistence.NPocoTests
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.That(commands.Length, Is.EqualTo(5));
|
||||
Assert.That(commands.Length, Is.EqualTo(6));
|
||||
foreach (var s in commands.Select(x => x.CommandText))
|
||||
{
|
||||
Assert.LessOrEqual(Regex.Matches(s, "@\\d+").Count, 2000);
|
||||
|
||||
@@ -26,9 +26,10 @@ namespace Umbraco.Web
|
||||
{
|
||||
private readonly IUmbracoDatabaseFactory _databaseFactory;
|
||||
|
||||
public BatchedDatabaseServerMessenger(
|
||||
IRuntimeState runtime, IUmbracoDatabaseFactory databaseFactory, IScopeProvider scopeProvider, ISqlContext sqlContext, IProfilingLogger proflog, IGlobalSettings globalSettings, DatabaseServerMessengerOptions options)
|
||||
: base(runtime, scopeProvider, sqlContext, proflog, globalSettings, true, options)
|
||||
public BatchedDatabaseServerMessenger(IRuntimeState runtime, IUmbracoDatabaseFactory databaseFactory,
|
||||
IScopeProvider scopeProvider, ISqlContext sqlContext, IProfilingLogger proflog, DatabaseServerMessengerOptions options,
|
||||
IServerMessengerSyncRepository serverMessengerSyncRepository)
|
||||
: base(runtime, scopeProvider, sqlContext, proflog, serverMessengerSyncRepository, true, options)
|
||||
{
|
||||
_databaseFactory = databaseFactory;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user