diff --git a/src/Shared/ProviderConfiguration.cs b/src/Shared/ProviderConfiguration.cs index c3423252..d5feab2c 100644 --- a/src/Shared/ProviderConfiguration.cs +++ b/src/Shared/ProviderConfiguration.cs @@ -29,6 +29,7 @@ internal class ProviderConfiguration public int OperationTimeoutInMilliSec { get; set; } public string ConnectionString { get; set; } public string RedisSerializerType { get; set; } + public int ConnectionPoolSize { get; set; } /* Empty constructor required for testing */ internal ProviderConfiguration() @@ -41,7 +42,6 @@ internal static ProviderConfiguration ProviderConfigurationForSessionState(NameV configuration.ThrowOnError = GetBoolSettings(config, "throwOnError", true); int retryTimeoutInMilliSec = GetIntSettings(config, "retryTimeoutInMilliseconds", 5000); configuration.RetryTimeout = new TimeSpan(0, 0, 0, 0, retryTimeoutInMilliSec); - // Get request timeout from config HttpRuntimeSection httpRuntimeSection = ConfigurationManager.GetSection("system.web/httpRuntime") as HttpRuntimeSection; configuration.RequestTimeout = httpRuntimeSection.ExecutionTimeout; @@ -61,7 +61,6 @@ internal static ProviderConfiguration ProviderConfigurationForOutputCache(NameVa // No retry login for output cache provider configuration.RetryTimeout = TimeSpan.Zero; - // Session state specific attribute which are not applicable to output cache configuration.ThrowOnError = true; configuration.RequestTimeout = TimeSpan.Zero; @@ -77,6 +76,7 @@ private ProviderConfiguration(NameValueCollection config) EnableLoggingIfParametersAvailable(config); // Get connection host, port and password. // host, port, accessKey and ssl are firest fetched from appSettings if not found there than taken from web.config + ConnectionPoolSize = GetIntSettings(config, "ConnectionPoolSize", 1); ConnectionString = GetConnectionString(config); Host = GetStringSettings(config, "host", "127.0.0.1"); Port = GetIntSettings(config, "port", 0); diff --git a/src/Shared/RedisSharedConnection.cs b/src/Shared/RedisSharedConnection.cs index 87c285fc..2df37123 100644 --- a/src/Shared/RedisSharedConnection.cs +++ b/src/Shared/RedisSharedConnection.cs @@ -4,6 +4,7 @@ // using System; +using System.Collections.Concurrent; using StackExchange.Redis; namespace Microsoft.Web.Redis @@ -12,7 +13,7 @@ internal class RedisSharedConnection { private ProviderConfiguration _configuration; private ConfigurationOptions _configOption; - private Lazy _redisMultiplexer; + private ConcurrentQueue> _redisMultiplexer; internal static DateTimeOffset lastReconnectTime = DateTimeOffset.MinValue; internal static DateTimeOffset firstErrorTime = DateTimeOffset.MinValue; @@ -20,6 +21,7 @@ internal class RedisSharedConnection static object reconnectLock = new object(); internal static TimeSpan ReconnectFrequency = TimeSpan.FromSeconds(60); internal static TimeSpan ReconnectErrorThreshold = TimeSpan.FromSeconds(30); + private int _poolSize; // Used for mocking in testing internal RedisSharedConnection() @@ -28,7 +30,8 @@ internal RedisSharedConnection() public RedisSharedConnection(ProviderConfiguration configuration) { _configuration = configuration; - + _redisMultiplexer = new ConcurrentQueue>(); + _poolSize = configuration.ConnectionPoolSize; // If connection string is given then use it otherwise use individual options if (!string.IsNullOrEmpty(configuration.ConnectionString)) { @@ -68,7 +71,14 @@ public RedisSharedConnection(ProviderConfiguration configuration) public IDatabase Connection { - get { return _redisMultiplexer.Value.GetDatabase(_configOption.DefaultDatabase ?? _configuration.DatabaseId); } + get + { + //return _redisMultiplexer.Value.GetDatabase(_configOption.DefaultDatabase ?? _configuration.DatabaseId); + Lazy multiPlexorLazy = null; + while (!_redisMultiplexer.TryDequeue(out multiPlexorLazy)) continue; + _redisMultiplexer.Enqueue(multiPlexorLazy); + return multiPlexorLazy.Value.GetDatabase(_configOption.DefaultDatabase ?? _configuration.DatabaseId); + } } public void ForceReconnect() @@ -110,8 +120,7 @@ public void ForceReconnect() firstErrorTime = DateTimeOffset.MinValue; previousErrorTime = DateTimeOffset.MinValue; - var oldMultiplexer = _redisMultiplexer; - CloseMultiplexer(oldMultiplexer); + CloseMultiplexer(); CreateMultiplexer(); } } @@ -122,26 +131,37 @@ private void CreateMultiplexer() { if (LogUtility.logger == null) { - _redisMultiplexer = new Lazy(() => ConnectionMultiplexer.Connect(_configOption)); + for (int i = 0; i < _poolSize; i++) + { + _redisMultiplexer.Enqueue(new Lazy(() => ConnectionMultiplexer.Connect(_configOption))); + } } else { - _redisMultiplexer = new Lazy(() => ConnectionMultiplexer.Connect(_configOption, LogUtility.logger)); + for (int i = 0; i < _poolSize; i++) + { + _redisMultiplexer.Enqueue(new Lazy(() => ConnectionMultiplexer.Connect(_configOption,LogUtility.logger))); + } } lastReconnectTime = DateTimeOffset.UtcNow; } - private void CloseMultiplexer(Lazy oldMultiplexer) + + private void CloseMultiplexer() { - if (oldMultiplexer.Value != null) + Lazy oldMultiplexer = null; + while (!_redisMultiplexer.TryDequeue(out oldMultiplexer)) { - try + if (oldMultiplexer.Value != null) { - oldMultiplexer.Value.Close(); - } - catch (Exception) - { - // Example error condition: if accessing old.Value causes a connection attempt and that fails. + try + { + oldMultiplexer.Value.Close(); + } + catch (Exception) + { + // Example error condition: if accessing old.Value causes a connection attempt and that fails. + } } } } diff --git a/test/Shared/Utility.cs b/test/Shared/Utility.cs index 89c220c2..bbde926d 100644 --- a/test/Shared/Utility.cs +++ b/test/Shared/Utility.cs @@ -33,6 +33,7 @@ internal static ProviderConfiguration GetDefaultConfigUtility() configuration.RequestTimeout = new TimeSpan(0, 1, 30); //1.5 min configuration.Host = "127.0.0.1"; configuration.Port = 0; + configuration.ConnectionPoolSize = 1; configuration.AccessKey = null; configuration.UseSsl = false; configuration.DatabaseId = 0;