using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NPoco;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Hosting;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Sync
{
///
/// An that works by storing messages in the database.
///
//
// this messenger writes ALL instructions to the database,
// but only processes instructions coming from remote servers,
// thus ensuring that instructions run only once
//
public class DatabaseServerMessenger : ServerMessengerBase, IDatabaseServerMessenger
{
private readonly IMainDom _mainDom;
private readonly ManualResetEvent _syncIdle;
private readonly object _locko = new object();
private readonly IProfilingLogger _profilingLogger;
private readonly IServerRegistrar _serverRegistrar;
private readonly IHostingEnvironment _hostingEnvironment;
private readonly CacheRefresherCollection _cacheRefreshers;
private readonly ISqlContext _sqlContext;
private readonly Lazy _distCacheFilePath;
private int _lastId = -1;
private DateTime _lastSync;
private DateTime _lastPruned;
private bool _initialized;
private bool _syncing;
private bool _released;
public DatabaseServerMessengerOptions Options { get; }
public DatabaseServerMessenger(
IMainDom mainDom, IScopeProvider scopeProvider, ISqlContext sqlContext, IProfilingLogger proflog, IServerRegistrar serverRegistrar,
bool distributedEnabled, DatabaseServerMessengerOptions options, IHostingEnvironment hostingEnvironment, CacheRefresherCollection cacheRefreshers)
: base(distributedEnabled)
{
ScopeProvider = scopeProvider ?? throw new ArgumentNullException(nameof(scopeProvider));
_sqlContext = sqlContext;
_mainDom = mainDom;
_profilingLogger = proflog ?? throw new ArgumentNullException(nameof(proflog));
_serverRegistrar = serverRegistrar;
_hostingEnvironment = hostingEnvironment;
_cacheRefreshers = cacheRefreshers;
Logger = proflog;
Options = options ?? throw new ArgumentNullException(nameof(options));
_lastPruned = _lastSync = DateTime.UtcNow;
_syncIdle = new ManualResetEvent(true);
_distCacheFilePath = new Lazy(() => GetDistCacheFilePath(hostingEnvironment));
// See notes on LocalIdentity
LocalIdentity = NetworkHelper.MachineName // eg DOMAIN\SERVER
+ "/" + _hostingEnvironment.ApplicationId // eg /LM/S3SVC/11/ROOT
+ " [P" + Process.GetCurrentProcess().Id // eg 1234
+ "/D" + AppDomain.CurrentDomain.Id // eg 22
+ "] " + Guid.NewGuid().ToString("N").ToUpper(); // make it truly unique
}
protected ILogger Logger { get; }
protected IScopeProvider ScopeProvider { get; }
protected Sql Sql() => _sqlContext.Sql();
private string DistCacheFilePath => _distCacheFilePath.Value;
#region Messenger
protected override bool RequiresDistributed(ICacheRefresher refresher, MessageType dispatchType)
{
// we don't care if there's servers listed or not,
// if distributed call is enabled we will make the call
return _initialized && DistributedEnabled;
}
protected override void DeliverRemote(
ICacheRefresher refresher,
MessageType messageType,
IEnumerable