Added support for Windows Azure Shared Cache to Orchard.Azure.DatabaseCache.

This commit is contained in:
Daniel Stolt 2013-08-28 05:29:37 +02:00 committed by Sebastien Ros
parent a690b9cc66
commit 375738df93
3 changed files with 82 additions and 29 deletions

View File

@ -9,49 +9,36 @@ namespace Orchard.Azure.Services.Caching.Database {
public class AzureCacheClient : ICache {
public AzureCacheClient(string cacheHostIdentifier, string cacheName, string region, bool enableCompression, TimeSpan? expirationTime) {
var dataCacheFactoryConfiguration = new DataCacheFactoryConfiguration() {
AutoDiscoverProperty = new DataCacheAutoDiscoverProperty(true, cacheHostIdentifier),
MaxConnectionsToServer = 32,
UseLegacyProtocol = false,
IsCompressionEnabled = enableCompression
};
var dataCacheFactory = new DataCacheFactory(dataCacheFactoryConfiguration);
public AzureCacheClient(DataCache cache, bool isSharedCaching, string region, TimeSpan? expirationTime) {
_logger = LoggerProvider.LoggerFor(typeof(AzureCacheClient));
_cache = cache;
_isSharedCaching = isSharedCaching;
_region = region ?? _defaultRegion;
// Azure Cache supports only alphanumeric strings for regions and
// Orchard can get a lot more creative than that. Remove all non
// alphanumering characters from the region, and append the hash code
// of the original string to mitigate the risk of two distinct original
// region strings yielding the same transformed region string.
_regionAlphaNumeric = new String(Array.FindAll(_region.ToCharArray(), c => Char.IsLetterOrDigit(c))) + _region.GetHashCode().ToString();
_expirationTime = expirationTime;
if (_logger.IsDebugEnabled)
_logger.DebugFormat("Creating cache with CacheName='{0}' and Region='{1}' (original Region='{2}').", cacheName, _regionAlphaNumeric, _region);
if (!String.IsNullOrEmpty(cacheName))
_cache = dataCacheFactory.GetCache(cacheName);
else
_cache = dataCacheFactory.GetDefaultCache();
_cache.CreateRegion(_regionAlphaNumeric);
if (!isSharedCaching)
_cache.CreateRegion(_regionAlphaNumeric);
//_lockHandleDictionary = new ConcurrentDictionary<object, DataCacheLockHandle>();
//_lockTimeout = TimeSpan.FromSeconds(30);
if (_logger.IsDebugEnabled)
_logger.DebugFormat("Created an AzureCacheClient for region '{0}' (original region '{1}').", _regionAlphaNumeric, _region);
}
private const string _defaultRegion = "NHibernate";
private readonly IInternalLogger _logger;
private readonly DataCache _cache;
private readonly bool _isSharedCaching;
private readonly string _region;
private readonly string _regionAlphaNumeric;
private readonly TimeSpan? _expirationTime;
private readonly DataCache _cache;
//private readonly ConcurrentDictionary<object, DataCacheLockHandle> _lockHandleDictionary;
//private readonly TimeSpan _lockTimeout;
@ -64,6 +51,9 @@ namespace Orchard.Azure.Services.Caching.Database {
if (_logger.IsDebugEnabled)
_logger.DebugFormat("Get() invoked with key='{0}' in region '{1}'.", key, _regionAlphaNumeric);
if (_isSharedCaching)
return _cache.Get(GetSharedCachingKey(key));
return _cache.Get(key.ToString(), _regionAlphaNumeric);
}
@ -76,7 +66,9 @@ namespace Orchard.Azure.Services.Caching.Database {
if (_logger.IsDebugEnabled)
_logger.DebugFormat("Put() invoked with key='{0}' and value='{1}' in region '{2}'.", key, value, _regionAlphaNumeric);
if (_expirationTime.HasValue)
if (_isSharedCaching)
_cache.Put(GetSharedCachingKey(key), value);
else if (_expirationTime.HasValue)
_cache.Put(key.ToString(), value, _expirationTime.Value, _regionAlphaNumeric);
else
_cache.Put(key.ToString(), value, _regionAlphaNumeric);
@ -89,12 +81,18 @@ namespace Orchard.Azure.Services.Caching.Database {
if (_logger.IsDebugEnabled)
_logger.DebugFormat("Remove() invoked with key='{0}' in region '{1}'.", key, _regionAlphaNumeric);
if (_isSharedCaching)
_cache.Remove(key.ToString());
_cache.Remove(key.ToString(), _regionAlphaNumeric);
}
public void Clear() {
if (_logger.IsDebugEnabled)
_logger.DebugFormat("Clear() invoked in region '{0}'.", _regionAlphaNumeric);
if (_isSharedCaching)
return; // Can't remove an individual region with Shared Caching.
_cache.ClearRegion(_regionAlphaNumeric);
}
@ -184,5 +182,9 @@ namespace Orchard.Azure.Services.Caching.Database {
}
#endregion
private string GetSharedCachingKey(object key) {
return String.Format("{0}_{1}", _region, key);
}
}
}

View File

@ -1,10 +1,11 @@
using NHibernate.Cfg.Loquacious;
using System;
using System.Linq;
using NHibernate.Cfg.Loquacious;
using Orchard;
using Orchard.Data;
using Orchard.Environment.Configuration;
using Orchard.Environment.Extensions;
using Orchard.Logging;
using System.Linq;
namespace Orchard.Azure.Services.Caching.Database {
@ -14,6 +15,7 @@ namespace Orchard.Azure.Services.Caching.Database {
public static string CacheHostIdentifier;
public static string CacheName;
public static bool IsSharedCaching;
public AzureCacheConfiguration(IShellSettingsManager shellSettingsManager, ShellSettings shellSettings)
: base() {
@ -40,6 +42,7 @@ namespace Orchard.Azure.Services.Caching.Database {
CacheHostIdentifier = shellSettings[Constants.DatabaseCacheHostIdentifierSettingName];
CacheName = shellSettings[Constants.DatabaseCacheCacheNameSettingName];
IsSharedCaching = Boolean.Parse(shellSettings[Constants.DatabaseCacheIsSharedCachingSettingName]);
_shellSettings = shellSettings;
}

View File

@ -2,11 +2,54 @@ using System.Collections.Generic;
using Microsoft.WindowsAzure.ServiceRuntime;
using NHibernate.Cache;
using System;
using Microsoft.ApplicationServer.Caching;
using NHibernate;
namespace Orchard.Azure.Services.Caching.Database {
public class AzureCacheProvider : ICacheProvider {
#region DataCache repository
private static IDictionary<string, DataCache> _cacheDictionary = new Dictionary<string, DataCache>();
private static DataCache GetCache(IInternalLogger logger, string cacheHostIdentifier, string cacheName, bool enableCompression) {
string key = String.Format("{0}_{1}_{2}", cacheHostIdentifier, cacheName, enableCompression);
if (!_cacheDictionary.ContainsKey(key)) {
var dataCacheFactoryConfiguration = new DataCacheFactoryConfiguration() {
AutoDiscoverProperty = new DataCacheAutoDiscoverProperty(true, AzureCacheConfiguration.CacheHostIdentifier),
MaxConnectionsToServer = 32,
UseLegacyProtocol = false
};
var dataCacheFactory = new DataCacheFactory(dataCacheFactoryConfiguration);
if (logger.IsDebugEnabled)
logger.DebugFormat("Creating DataCache with CacheHostIdentifier='{0}' and CacheName='{1}'.", cacheHostIdentifier, cacheName);
DataCache newCache;
if (!String.IsNullOrEmpty(cacheName))
newCache = dataCacheFactory.GetCache(cacheName);
else
newCache = dataCacheFactory.GetDefaultCache();
_cacheDictionary[key] = newCache;
}
else {
if (logger.IsDebugEnabled)
logger.DebugFormat("Reusing existing DataCache with CacheHostIdentifier='{0}' and CacheName='{1}'.", cacheHostIdentifier, cacheName);
}
return _cacheDictionary[key];
}
#endregion
public AzureCacheProvider() {
_logger = LoggerProvider.LoggerFor(typeof(AzureCacheProvider));
}
private readonly IInternalLogger _logger;
#region ICacheProvider Members
public ICache BuildCache(string regionName, IDictionary<string, string> properties) {
@ -15,12 +58,17 @@ namespace Orchard.Azure.Services.Caching.Database {
if (properties.TryGetValue("compression_enabled", out enableCompressionString))
enableCompression = Boolean.Parse(enableCompressionString);
// Using static fields to communicate host identifier and cache name from AzureCacheConfiguration to
// this class might cause problems in multi-tenancy scenarios when tenants have different settings
// for these in shell settings. We should think of something more robust.
var cache = GetCache(_logger, AzureCacheConfiguration.CacheHostIdentifier, AzureCacheConfiguration.CacheName, enableCompression);
TimeSpan? expiration = null;
string expirationString;
if (properties.TryGetValue("expiration", out expirationString) || properties.TryGetValue(global::NHibernate.Cfg.Environment.CacheDefaultExpiration, out expirationString))
expiration = TimeSpan.FromSeconds(Int32.Parse(expirationString));
return new AzureCacheClient(AzureCacheConfiguration.CacheHostIdentifier, AzureCacheConfiguration.CacheName, regionName, enableCompression, expiration);
return new AzureCacheClient(cache, AzureCacheConfiguration.IsSharedCaching, regionName, expiration);
}
public long NextTimestamp() {