mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
Added support for Windows Azure Shared Cache to Orchard.Azure.DatabaseCache.
This commit is contained in:

committed by
Sebastien Ros

parent
a690b9cc66
commit
375738df93
@@ -9,49 +9,36 @@ namespace Orchard.Azure.Services.Caching.Database {
|
|||||||
|
|
||||||
public class AzureCacheClient : ICache {
|
public class AzureCacheClient : ICache {
|
||||||
|
|
||||||
public AzureCacheClient(string cacheHostIdentifier, string cacheName, string region, bool enableCompression, TimeSpan? expirationTime) {
|
public AzureCacheClient(DataCache cache, bool isSharedCaching, string region, TimeSpan? expirationTime) {
|
||||||
|
|
||||||
var dataCacheFactoryConfiguration = new DataCacheFactoryConfiguration() {
|
|
||||||
AutoDiscoverProperty = new DataCacheAutoDiscoverProperty(true, cacheHostIdentifier),
|
|
||||||
MaxConnectionsToServer = 32,
|
|
||||||
UseLegacyProtocol = false,
|
|
||||||
IsCompressionEnabled = enableCompression
|
|
||||||
};
|
|
||||||
|
|
||||||
var dataCacheFactory = new DataCacheFactory(dataCacheFactoryConfiguration);
|
|
||||||
|
|
||||||
_logger = LoggerProvider.LoggerFor(typeof(AzureCacheClient));
|
_logger = LoggerProvider.LoggerFor(typeof(AzureCacheClient));
|
||||||
|
_cache = cache;
|
||||||
|
_isSharedCaching = isSharedCaching;
|
||||||
_region = region ?? _defaultRegion;
|
_region = region ?? _defaultRegion;
|
||||||
|
|
||||||
// Azure Cache supports only alphanumeric strings for regions and
|
// Azure Cache supports only alphanumeric strings for regions and
|
||||||
// Orchard can get a lot more creative than that. Remove all non
|
// Orchard can get a lot more creative than that. Remove all non
|
||||||
// alphanumering characters from the region, and append the hash code
|
// alphanumering characters from the region, and append the hash code
|
||||||
// of the original string to mitigate the risk of two distinct original
|
// of the original string to mitigate the risk of two distinct original
|
||||||
// region strings yielding the same transformed region string.
|
// region strings yielding the same transformed region string.
|
||||||
_regionAlphaNumeric = new String(Array.FindAll(_region.ToCharArray(), c => Char.IsLetterOrDigit(c))) + _region.GetHashCode().ToString();
|
_regionAlphaNumeric = new String(Array.FindAll(_region.ToCharArray(), c => Char.IsLetterOrDigit(c))) + _region.GetHashCode().ToString();
|
||||||
|
|
||||||
_expirationTime = expirationTime;
|
_expirationTime = expirationTime;
|
||||||
|
|
||||||
if (_logger.IsDebugEnabled)
|
if (!isSharedCaching)
|
||||||
_logger.DebugFormat("Creating cache with CacheName='{0}' and Region='{1}' (original Region='{2}').", cacheName, _regionAlphaNumeric, _region);
|
_cache.CreateRegion(_regionAlphaNumeric);
|
||||||
|
|
||||||
if (!String.IsNullOrEmpty(cacheName))
|
|
||||||
_cache = dataCacheFactory.GetCache(cacheName);
|
|
||||||
else
|
|
||||||
_cache = dataCacheFactory.GetDefaultCache();
|
|
||||||
|
|
||||||
_cache.CreateRegion(_regionAlphaNumeric);
|
|
||||||
|
|
||||||
//_lockHandleDictionary = new ConcurrentDictionary<object, DataCacheLockHandle>();
|
//_lockHandleDictionary = new ConcurrentDictionary<object, DataCacheLockHandle>();
|
||||||
//_lockTimeout = TimeSpan.FromSeconds(30);
|
//_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 const string _defaultRegion = "NHibernate";
|
||||||
private readonly IInternalLogger _logger;
|
private readonly IInternalLogger _logger;
|
||||||
|
private readonly DataCache _cache;
|
||||||
|
private readonly bool _isSharedCaching;
|
||||||
private readonly string _region;
|
private readonly string _region;
|
||||||
private readonly string _regionAlphaNumeric;
|
private readonly string _regionAlphaNumeric;
|
||||||
private readonly TimeSpan? _expirationTime;
|
private readonly TimeSpan? _expirationTime;
|
||||||
private readonly DataCache _cache;
|
|
||||||
//private readonly ConcurrentDictionary<object, DataCacheLockHandle> _lockHandleDictionary;
|
//private readonly ConcurrentDictionary<object, DataCacheLockHandle> _lockHandleDictionary;
|
||||||
//private readonly TimeSpan _lockTimeout;
|
//private readonly TimeSpan _lockTimeout;
|
||||||
|
|
||||||
@@ -64,6 +51,9 @@ namespace Orchard.Azure.Services.Caching.Database {
|
|||||||
if (_logger.IsDebugEnabled)
|
if (_logger.IsDebugEnabled)
|
||||||
_logger.DebugFormat("Get() invoked with key='{0}' in region '{1}'.", key, _regionAlphaNumeric);
|
_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);
|
return _cache.Get(key.ToString(), _regionAlphaNumeric);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +66,9 @@ namespace Orchard.Azure.Services.Caching.Database {
|
|||||||
if (_logger.IsDebugEnabled)
|
if (_logger.IsDebugEnabled)
|
||||||
_logger.DebugFormat("Put() invoked with key='{0}' and value='{1}' in region '{2}'.", key, value, _regionAlphaNumeric);
|
_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);
|
_cache.Put(key.ToString(), value, _expirationTime.Value, _regionAlphaNumeric);
|
||||||
else
|
else
|
||||||
_cache.Put(key.ToString(), value, _regionAlphaNumeric);
|
_cache.Put(key.ToString(), value, _regionAlphaNumeric);
|
||||||
@@ -89,12 +81,18 @@ namespace Orchard.Azure.Services.Caching.Database {
|
|||||||
if (_logger.IsDebugEnabled)
|
if (_logger.IsDebugEnabled)
|
||||||
_logger.DebugFormat("Remove() invoked with key='{0}' in region '{1}'.", key, _regionAlphaNumeric);
|
_logger.DebugFormat("Remove() invoked with key='{0}' in region '{1}'.", key, _regionAlphaNumeric);
|
||||||
|
|
||||||
|
if (_isSharedCaching)
|
||||||
|
_cache.Remove(key.ToString());
|
||||||
|
|
||||||
_cache.Remove(key.ToString(), _regionAlphaNumeric);
|
_cache.Remove(key.ToString(), _regionAlphaNumeric);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear() {
|
public void Clear() {
|
||||||
if (_logger.IsDebugEnabled)
|
if (_logger.IsDebugEnabled)
|
||||||
_logger.DebugFormat("Clear() invoked in region '{0}'.", _regionAlphaNumeric);
|
_logger.DebugFormat("Clear() invoked in region '{0}'.", _regionAlphaNumeric);
|
||||||
|
|
||||||
|
if (_isSharedCaching)
|
||||||
|
return; // Can't remove an individual region with Shared Caching.
|
||||||
|
|
||||||
_cache.ClearRegion(_regionAlphaNumeric);
|
_cache.ClearRegion(_regionAlphaNumeric);
|
||||||
}
|
}
|
||||||
@@ -184,5 +182,9 @@ namespace Orchard.Azure.Services.Caching.Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
private string GetSharedCachingKey(object key) {
|
||||||
|
return String.Format("{0}_{1}", _region, key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,10 +1,11 @@
|
|||||||
using NHibernate.Cfg.Loquacious;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using NHibernate.Cfg.Loquacious;
|
||||||
using Orchard;
|
using Orchard;
|
||||||
using Orchard.Data;
|
using Orchard.Data;
|
||||||
using Orchard.Environment.Configuration;
|
using Orchard.Environment.Configuration;
|
||||||
using Orchard.Environment.Extensions;
|
using Orchard.Environment.Extensions;
|
||||||
using Orchard.Logging;
|
using Orchard.Logging;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Orchard.Azure.Services.Caching.Database {
|
namespace Orchard.Azure.Services.Caching.Database {
|
||||||
|
|
||||||
@@ -14,6 +15,7 @@ namespace Orchard.Azure.Services.Caching.Database {
|
|||||||
|
|
||||||
public static string CacheHostIdentifier;
|
public static string CacheHostIdentifier;
|
||||||
public static string CacheName;
|
public static string CacheName;
|
||||||
|
public static bool IsSharedCaching;
|
||||||
|
|
||||||
public AzureCacheConfiguration(IShellSettingsManager shellSettingsManager, ShellSettings shellSettings)
|
public AzureCacheConfiguration(IShellSettingsManager shellSettingsManager, ShellSettings shellSettings)
|
||||||
: base() {
|
: base() {
|
||||||
@@ -40,6 +42,7 @@ namespace Orchard.Azure.Services.Caching.Database {
|
|||||||
|
|
||||||
CacheHostIdentifier = shellSettings[Constants.DatabaseCacheHostIdentifierSettingName];
|
CacheHostIdentifier = shellSettings[Constants.DatabaseCacheHostIdentifierSettingName];
|
||||||
CacheName = shellSettings[Constants.DatabaseCacheCacheNameSettingName];
|
CacheName = shellSettings[Constants.DatabaseCacheCacheNameSettingName];
|
||||||
|
IsSharedCaching = Boolean.Parse(shellSettings[Constants.DatabaseCacheIsSharedCachingSettingName]);
|
||||||
|
|
||||||
_shellSettings = shellSettings;
|
_shellSettings = shellSettings;
|
||||||
}
|
}
|
||||||
|
@@ -2,11 +2,54 @@ using System.Collections.Generic;
|
|||||||
using Microsoft.WindowsAzure.ServiceRuntime;
|
using Microsoft.WindowsAzure.ServiceRuntime;
|
||||||
using NHibernate.Cache;
|
using NHibernate.Cache;
|
||||||
using System;
|
using System;
|
||||||
|
using Microsoft.ApplicationServer.Caching;
|
||||||
|
using NHibernate;
|
||||||
|
|
||||||
namespace Orchard.Azure.Services.Caching.Database {
|
namespace Orchard.Azure.Services.Caching.Database {
|
||||||
|
|
||||||
public class AzureCacheProvider : ICacheProvider {
|
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
|
#region ICacheProvider Members
|
||||||
|
|
||||||
public ICache BuildCache(string regionName, IDictionary<string, string> properties) {
|
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))
|
if (properties.TryGetValue("compression_enabled", out enableCompressionString))
|
||||||
enableCompression = Boolean.Parse(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;
|
TimeSpan? expiration = null;
|
||||||
string expirationString;
|
string expirationString;
|
||||||
if (properties.TryGetValue("expiration", out expirationString) || properties.TryGetValue(global::NHibernate.Cfg.Environment.CacheDefaultExpiration, out expirationString))
|
if (properties.TryGetValue("expiration", out expirationString) || properties.TryGetValue(global::NHibernate.Cfg.Environment.CacheDefaultExpiration, out expirationString))
|
||||||
expiration = TimeSpan.FromSeconds(Int32.Parse(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() {
|
public long NextTimestamp() {
|
||||||
|
Reference in New Issue
Block a user