diff --git a/src/Orchard.Web/Modules/Orchard.Azure/Orchard.Azure.csproj b/src/Orchard.Web/Modules/Orchard.Azure/Orchard.Azure.csproj index a37eca615..b92f1bb98 100644 --- a/src/Orchard.Web/Modules/Orchard.Azure/Orchard.Azure.csproj +++ b/src/Orchard.Web/Modules/Orchard.Azure/Orchard.Azure.csproj @@ -130,7 +130,6 @@ - diff --git a/src/Orchard.Web/Modules/Orchard.Azure/Services/Caching/Output/AzureOutputCacheStorageProvider.cs b/src/Orchard.Web/Modules/Orchard.Azure/Services/Caching/Output/AzureOutputCacheStorageProvider.cs index bf994e216..27adc02d9 100644 --- a/src/Orchard.Web/Modules/Orchard.Azure/Services/Caching/Output/AzureOutputCacheStorageProvider.cs +++ b/src/Orchard.Web/Modules/Orchard.Azure/Services/Caching/Output/AzureOutputCacheStorageProvider.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.Linq; using Microsoft.ApplicationServer.Caching; using Orchard.Azure.Services.Environment.Configuration; +using Orchard.Caching; using Orchard.Environment.Configuration; using Orchard.Environment.Extensions; using Orchard.Logging; @@ -15,11 +16,25 @@ namespace Orchard.Azure.Services.Caching.Output { [OrchardFeature(Constants.OutputCacheFeatureName)] [OrchardSuppressDependency("Orchard.OutputCache.Services.DefaultCacheStorageProvider")] public class AzureOutputCacheStorageProvider : Component, IOutputCacheStorageProvider { + public const string DataCacheKey = "DataCache"; + public const string ClientConfigurationKey = "CacheClientConfiguration"; + public const int Retries = 2; - private readonly DataCache _cache; private readonly string _regionAlphaNumeric; + private readonly ICacheManager _cacheManager; + private readonly ShellSettings _shellSettings; + private readonly IPlatformConfigurationAccessor _pca; + private readonly ISignals _signals; - public AzureOutputCacheStorageProvider(ShellSettings shellSettings, IAzureOutputCacheHolder cacheHolder, IPlatformConfigurationAccessor pca) { + public AzureOutputCacheStorageProvider( + ShellSettings shellSettings, + IPlatformConfigurationAccessor pca, + ICacheManager cacheManager, + ISignals signals) { + _cacheManager = cacheManager; + _shellSettings = shellSettings; + _pca = pca; + _signals = signals; var region = shellSettings.Name; @@ -29,64 +44,117 @@ namespace Orchard.Azure.Services.Caching.Output { // of two distinct original region strings yielding the same transformed region string. _regionAlphaNumeric = new String(Array.FindAll(region.ToCharArray(), Char.IsLetterOrDigit)) + region.GetHashCode().ToString(CultureInfo.InvariantCulture); - _cache = cacheHolder.TryGetDataCache(() => { - CacheClientConfiguration cacheConfig; - - try { - cacheConfig = CacheClientConfiguration.FromPlatformConfiguration(shellSettings.Name, Constants.OutputCacheSettingNamePrefix, pca); - cacheConfig.Validate(); - } - catch (Exception ex) { - throw new Exception(String.Format("The {0} configuration settings are missing or invalid.", Constants.OutputCacheFeatureName), ex); - } - - var cache = cacheConfig.CreateCache(); - cache.CreateRegion(_regionAlphaNumeric); - - return cache; - }); + Logger = NullLogger.Instance; } + public ILogger Logger { get; set; } + + public CacheClientConfiguration CacheConfiguration { + get { + return _cacheManager.Get(ClientConfigurationKey, ctx => { + CacheClientConfiguration cacheConfig ; + try { + cacheConfig = CacheClientConfiguration.FromPlatformConfiguration( + _shellSettings.Name, + Constants.OutputCacheSettingNamePrefix, + _pca); + + cacheConfig.Validate(); + return cacheConfig; + } + catch (Exception ex) { + throw new Exception(String.Format("The {0} configuration settings are missing or invalid.", Constants.OutputCacheFeatureName), ex); + } + }); + } + } + + public DataCache Cache { + get { + return _cacheManager.Get(DataCacheKey, ctx => { + ctx.Monitor(_signals.When(DataCacheKey)); + var cache = CacheConfiguration.CreateCache(); + cache.CreateRegion(_regionAlphaNumeric); + + return cache; + }); + } + } + + public T SafeCall(Func function) { + return Retry(function, Retries); + } + + public void SafeCall(Action function) { + Retry(() => { + function(); + return null; + }, Retries); + } + + public T Retry(Func function, int times) { + if (times == 0) { + Logger.Error("Too many retries in cache resolution."); + return default(T); + } + + try { + return function.Invoke(); + } + catch (DataCacheException) { + _signals.Trigger(DataCacheKey); + return Retry(function, times--); + } + } + public void Set(string key, CacheItem cacheItem) { if (cacheItem.ValidFor <= 0) { return; } Logger.Debug("Set() invoked with key='{0}' in region '{1}'.", key, _regionAlphaNumeric); - _cache.Put(key, cacheItem, TimeSpan.FromSeconds(cacheItem.ValidFor), _regionAlphaNumeric); + SafeCall(() => Cache.Put(key, cacheItem, TimeSpan.FromSeconds(cacheItem.ValidFor), _regionAlphaNumeric)); } public void Remove(string key) { Logger.Debug("Remove() invoked with key='{0}' in region '{1}'.", key, _regionAlphaNumeric); - _cache.Remove(key, _regionAlphaNumeric); + SafeCall(() => Cache.Remove(key, _regionAlphaNumeric)); } public void RemoveAll() { Logger.Debug("RemoveAll() invoked in region '{0}'.", _regionAlphaNumeric); - _cache.ClearRegion(_regionAlphaNumeric); + SafeCall(() => { + Cache.ClearRegion(_regionAlphaNumeric); + return null; + }); } public CacheItem GetCacheItem(string key) { Logger.Debug("GetCacheItem() invoked with key='{0}' in region '{1}'.", key, _regionAlphaNumeric); - return _cache.Get(key, _regionAlphaNumeric) as CacheItem; + return SafeCall(() => Cache.Get(key, _regionAlphaNumeric)) as CacheItem; } public IEnumerable GetCacheItems(int skip, int count) { Logger.Debug("GetCacheItems() invoked in region '{0}'.", _regionAlphaNumeric); - return _cache.GetObjectsInRegion(_regionAlphaNumeric).AsParallel() + return SafeCall(() => + Cache.GetObjectsInRegion(_regionAlphaNumeric) + .AsParallel() .Select(x => x.Value) .OfType() .Skip(skip) .Take(count) - .ToArray(); + .ToArray() + ); } public int GetCacheItemsCount() { Logger.Debug("GetCacheItemsCount() invoked in region '{0}'.", _regionAlphaNumeric); - return _cache.GetObjectsInRegion(_regionAlphaNumeric).AsParallel() + return SafeCall(() => + Cache.GetObjectsInRegion(_regionAlphaNumeric).AsParallel() .Select(x => x.Value) .OfType() - .Count(); + .Count() + ); } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Azure/Services/Caching/Output/IAzureOutputCacheHolder.cs b/src/Orchard.Web/Modules/Orchard.Azure/Services/Caching/Output/IAzureOutputCacheHolder.cs deleted file mode 100644 index 12bdddbd4..000000000 --- a/src/Orchard.Web/Modules/Orchard.Azure/Services/Caching/Output/IAzureOutputCacheHolder.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using Microsoft.ApplicationServer.Caching; - -namespace Orchard.Azure.Services.Caching.Output { - public interface IAzureOutputCacheHolder : ISingletonDependency { - DataCache TryGetDataCache(Func builder); - } - - public class AzureOutputCacheHolder : IAzureOutputCacheHolder { - private readonly object _synLock = new object(); - private DataCache _dataCache; - - public DataCache TryGetDataCache(Func builder) { - lock (_synLock) { - if (_dataCache != null) { - return _dataCache; - } - - return _dataCache = builder(); - } - } - } -} \ No newline at end of file