diff --git a/src/Orchard.Web/Core/Settings/Descriptor/ShellDescriptorManager.cs b/src/Orchard.Web/Core/Settings/Descriptor/ShellDescriptorManager.cs index faf80b3ec..4ffd7846e 100644 --- a/src/Orchard.Web/Core/Settings/Descriptor/ShellDescriptorManager.cs +++ b/src/Orchard.Web/Core/Settings/Descriptor/ShellDescriptorManager.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; using System.Linq; +using Orchard.Caching; using Orchard.Core.Settings.Descriptor.Records; using Orchard.Data; using Orchard.Environment.Configuration; using Orchard.Environment.Descriptor; using Orchard.Environment.Descriptor.Models; +using Orchard.Locking; using Orchard.Logging; namespace Orchard.Core.Settings.Descriptor { @@ -13,14 +15,28 @@ namespace Orchard.Core.Settings.Descriptor { private readonly IRepository _shellDescriptorRepository; private readonly IShellDescriptorManagerEventHandler _events; private readonly ShellSettings _shellSettings; - + private readonly ILockingProvider _lockingProvider; + private readonly ICacheManager _cacheManager; + private readonly ISignals _signals; + public ShellDescriptorManager( IRepository shellDescriptorRepository, IShellDescriptorManagerEventHandler events, - ShellSettings shellSettings) { + ShellSettings shellSettings, + ILockingProvider lockingProvider, + ICacheManager cacheManager, + ISignals signals) { + _shellDescriptorRepository = shellDescriptorRepository; _events = events; _shellSettings = shellSettings; + _lockingProvider = lockingProvider; + _cacheManager = cacheManager; + _signals = signals; + + _lockString = string.Join(".", + _shellSettings["Name"] ?? "", + "ShellDescriptorManager"); } public ShellDescriptor GetShellDescriptor() { @@ -50,47 +66,72 @@ namespace Orchard.Core.Settings.Descriptor { return descriptor; } + private const string EvictSignalName = + "ShellDescriptorRecord_EvictCache"; + private const string DescriptorCacheName = + "Orchard.Core.Settings.Descriptor.ShellDescriptorManager.ShellDescriptorRecord"; private ShellDescriptorRecord GetDescriptorRecord() { - return _shellDescriptorRepository.Get(x => x != null); + // fetching the ShellDescriptorRecord also causes NHibernate to launch + // SELECT queries to fetch the ShellFeatureRecords and the ShellParameterRecords. + // If we cache all that, we save those three select queries on every request. + // We should be careful in the eviction policy when the ShellDescriptorRecord + // gets updated. + return _cacheManager.Get(DescriptorCacheName, true, ctx => { + ctx.Monitor(_signals.When(EvictSignalName)); + return _shellDescriptorRepository.Get(x => x != null); + }); } - public void UpdateShellDescriptor(int priorSerialNumber, IEnumerable enabledFeatures, IEnumerable parameters) { - ShellDescriptorRecord shellDescriptorRecord = GetDescriptorRecord(); - var serialNumber = shellDescriptorRecord == null ? 0 : shellDescriptorRecord.SerialNumber; - if (priorSerialNumber != serialNumber) - throw new InvalidOperationException(T("Invalid serial number for shell descriptor").ToString()); + private string _lockString; - Logger.Information("Updating shell descriptor for shell '{0}'...", _shellSettings.Name); + public void UpdateShellDescriptor( + int priorSerialNumber, IEnumerable enabledFeatures, IEnumerable parameters) { + // This is where the shell descriptor will be updated. + // Since we plan to cache it, this method will have to update the + // actual records in the database, and then evict the cache. + // We are going to put an application lock around this to prevent + // issues when for some weird reason several updates are being attempted + // concurrently. + _lockingProvider.Lock(_lockString, () => { + ShellDescriptorRecord shellDescriptorRecord = _shellDescriptorRepository.Get(x => x != null); + var serialNumber = shellDescriptorRecord == null ? 0 : shellDescriptorRecord.SerialNumber; + if (priorSerialNumber != serialNumber) + throw new InvalidOperationException(T("Invalid serial number for shell descriptor").ToString()); - if (shellDescriptorRecord == null) { - shellDescriptorRecord = new ShellDescriptorRecord { SerialNumber = 1 }; - _shellDescriptorRepository.Create(shellDescriptorRecord); - } - else { - shellDescriptorRecord.SerialNumber++; - } + Logger.Information("Updating shell descriptor for shell '{0}'...", _shellSettings.Name); - shellDescriptorRecord.Features.Clear(); - foreach (var feature in enabledFeatures) { - shellDescriptorRecord.Features.Add(new ShellFeatureRecord { Name = feature.Name, ShellDescriptorRecord = shellDescriptorRecord }); - } - Logger.Debug("Enabled features for shell '{0}' set: {1}.", _shellSettings.Name, String.Join(", ", enabledFeatures.Select(feature => feature.Name))); + if (shellDescriptorRecord == null) { + shellDescriptorRecord = new ShellDescriptorRecord { SerialNumber = 1 }; + _shellDescriptorRepository.Create(shellDescriptorRecord); + } else { + shellDescriptorRecord.SerialNumber++; + } + + shellDescriptorRecord.Features.Clear(); + foreach (var feature in enabledFeatures) { + shellDescriptorRecord.Features.Add(new ShellFeatureRecord { Name = feature.Name, ShellDescriptorRecord = shellDescriptorRecord }); + } + Logger.Debug("Enabled features for shell '{0}' set: {1}.", _shellSettings.Name, String.Join(", ", enabledFeatures.Select(feature => feature.Name))); - shellDescriptorRecord.Parameters.Clear(); - foreach (var parameter in parameters) { - shellDescriptorRecord.Parameters.Add(new ShellParameterRecord { - Component = parameter.Component, - Name = parameter.Name, - Value = parameter.Value, - ShellDescriptorRecord = shellDescriptorRecord - }); - } - Logger.Debug("Parameters for shell '{0}' set: {1}.", _shellSettings.Name, String.Join(", ", parameters.Select(parameter => parameter.Name + "-" + parameter.Value))); + shellDescriptorRecord.Parameters.Clear(); + foreach (var parameter in parameters) { + shellDescriptorRecord.Parameters.Add(new ShellParameterRecord { + Component = parameter.Component, + Name = parameter.Name, + Value = parameter.Value, + ShellDescriptorRecord = shellDescriptorRecord + }); + } - Logger.Information("Shell descriptor updated for shell '{0}'.", _shellSettings.Name); + _signals.Trigger(EvictSignalName); - _events.Changed(GetShellDescriptorFromRecord(shellDescriptorRecord), _shellSettings.Name); + Logger.Debug("Parameters for shell '{0}' set: {1}.", _shellSettings.Name, String.Join(", ", parameters.Select(parameter => parameter.Name + "-" + parameter.Value))); + + Logger.Information("Shell descriptor updated for shell '{0}'.", _shellSettings.Name); + + _events.Changed(GetShellDescriptorFromRecord(GetDescriptorRecord()), _shellSettings.Name); + }); } } }