Fixing Redis connection creation

With the previous code, when multiple threads tried to create
a Redis connection, the ConcurrentDictionary would have
called the factory delegate multiple times and added only one
of the resulting connections. This is by design, and a solution
is to use a Lazy object to defer the creation of the connection.
Multiple Lazy objects will be created but it doesn't have any impact.
This commit is contained in:
Sebastien Ros
2015-07-31 14:52:42 -07:00
parent cc82bfcca0
commit 637cea6a02
2 changed files with 18 additions and 11 deletions

View File

@@ -8,7 +8,7 @@ using StackExchange.Redis;
namespace Orchard.Redis.Configuration {
public class RedisConnectionProvider : IRedisConnectionProvider {
private static ConcurrentDictionary<string, ConnectionMultiplexer> _connectionMultiplexers = new ConcurrentDictionary<string, ConnectionMultiplexer>();
private static ConcurrentDictionary<string, Lazy<ConnectionMultiplexer>> _connectionMultiplexers = new ConcurrentDictionary<string, Lazy<ConnectionMultiplexer>>();
private readonly ShellSettings _shellSettings;
public RedisConnectionProvider(ShellSettings shellSettings) {
@@ -37,12 +37,16 @@ namespace Orchard.Redis.Configuration {
throw new ArgumentNullException("connectionString");
}
var connectionMultiplexer = _connectionMultiplexers.GetOrAdd(connectionString, cfg => {
Logger.Debug("Creating a new cache client for: {0}", connectionString);
return ConnectionMultiplexer.Connect(connectionString);
});
// when using ConcurrentDictionary, multiple threads can create the value
// at the same time, so we need to pass a Lazy so that it's only
// the object which is added that will create a ConnectionMultiplexer,
// even when a delegate is passed
return connectionMultiplexer;
return _connectionMultiplexers.GetOrAdd(connectionString,
new Lazy<ConnectionMultiplexer>(() => {
Logger.Debug("Creating a new cache client for: {0}", connectionString);
return ConnectionMultiplexer.Connect(connectionString);
})).Value;
}
}
}

View File

@@ -15,7 +15,7 @@ namespace Orchard.Redis.OutputCache {
[OrchardFeature("Orchard.Redis.OutputCache")]
[OrchardSuppressDependency("Orchard.OutputCache.Services.DefaultCacheStorageProvider")]
public class RedisOutputCacheStorageProvider : Component, IOutputCacheStorageProvider {
public class RedisOutputCacheStorageProvider : IOutputCacheStorageProvider {
private readonly ShellSettings _shellSettings;
private readonly IRedisConnectionProvider _redisConnectionProvider;
@@ -23,18 +23,22 @@ namespace Orchard.Redis.OutputCache {
public const string ConnectionStringKey = "Orchard.Redis.OutputCache";
private readonly string _connectionString;
private readonly ConnectionMultiplexer _connectionMultiplexer;
public RedisOutputCacheStorageProvider(ShellSettings shellSettings, IRedisConnectionProvider redisConnectionProvider) {
_shellSettings = shellSettings;
_redisConnectionProvider = redisConnectionProvider;
_connectionString = _redisConnectionProvider.GetConnectionString(ConnectionStringKey);
_connectionMultiplexer = _redisConnectionProvider.GetConnection(_connectionString);
Logger = NullLogger.Instance;
}
public ILogger Logger { get; set; }
public IDatabase Database {
get {
return _redisConnectionProvider.GetConnection(_connectionString).GetDatabase();
return _connectionMultiplexer.GetDatabase();
}
}
@@ -97,9 +101,8 @@ namespace Orchard.Redis.OutputCache {
_keysCache = new HashSet<string>();
var prefix = GetLocalizedKey("");
var connection = _redisConnectionProvider.GetConnection(_connectionString);
foreach (var endPoint in connection.GetEndPoints()) {
var server = connection.GetServer(endPoint);
foreach (var endPoint in _connectionMultiplexer.GetEndPoints()) {
var server = _connectionMultiplexer.GetServer(endPoint);
foreach (var key in server.Keys(pattern: GetLocalizedKey("*"))) {
_keysCache.Add(key.ToString().Substring(prefix.Length));
}