Commit 9c0adcbd authored by Marc Gravell's avatar Marc Gravell

Automatic retry during connect (configurable)

parent ef618429
...@@ -6,6 +6,7 @@ namespace StackExchange.Redis.Tests ...@@ -6,6 +6,7 @@ namespace StackExchange.Redis.Tests
[TestFixture] [TestFixture]
public class ConnectFailTimeout : TestBase public class ConnectFailTimeout : TestBase
{ {
#if DEBUG
[TestCase] [TestCase]
public void NoticesConnectFail() public void NoticesConnectFail()
{ {
...@@ -34,6 +35,6 @@ public void NoticesConnectFail() ...@@ -34,6 +35,6 @@ public void NoticesConnectFail()
System.Console.WriteLine(time); System.Console.WriteLine(time);
} }
} }
#endif
} }
} }
...@@ -99,6 +99,7 @@ ...@@ -99,6 +99,7 @@
<Compile Include="TaskTests.cs" /> <Compile Include="TaskTests.cs" />
<Compile Include="TestBase.cs" /> <Compile Include="TestBase.cs" />
<Compile Include="Transactions.cs" /> <Compile Include="Transactions.cs" />
<Compile Include="VPNTest.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <None Include="packages.config" />
......
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StackExchange.Redis.Tests
{
[TestFixture]
public class VPNTest : TestBase
{
[Test]
[MaxTime(100000)]
[TestCase("or-devredis01.ds.stackexchange.com:6379")]
public void Execute(string config)
{
for (int i = 0; i < 50; i++)
{
var log = new StringWriter();
try
{
var options = ConfigurationOptions.Parse(config);
options.SyncTimeout = 3000;
options.ConnectRetry = 5;
using (var conn = ConnectionMultiplexer.Connect(options, log))
{
var ttl = conn.GetDatabase().Ping();
Console.WriteLine(ttl);
}
}
catch
{
Console.WriteLine(log);
Assert.Fail();
}
Console.WriteLine();
Console.WriteLine("===");
Console.WriteLine();
}
}
}
}
...@@ -72,7 +72,7 @@ internal static void Unknown(string key) ...@@ -72,7 +72,7 @@ internal static void Unknown(string key)
Version = "version", ConnectTimeout = "connectTimeout", Password = "password", Version = "version", ConnectTimeout = "connectTimeout", Password = "password",
TieBreaker = "tiebreaker", WriteBuffer = "writeBuffer", Ssl = "ssl", SslHost = "sslHost", TieBreaker = "tiebreaker", WriteBuffer = "writeBuffer", Ssl = "ssl", SslHost = "sslHost",
ConfigChannel = "configChannel", AbortOnConnectFail = "abortConnect", ResolveDns = "resolveDns", ConfigChannel = "configChannel", AbortOnConnectFail = "abortConnect", ResolveDns = "resolveDns",
ChannelPrefix = "channelPrefix", Proxy = "proxy"; ChannelPrefix = "channelPrefix", Proxy = "proxy", ConnectRetry = "connectRetry";
private static readonly Dictionary<string, string> normalizedOptions = new[] private static readonly Dictionary<string, string> normalizedOptions = new[]
{ {
AllowAdmin, SyncTimeout, AllowAdmin, SyncTimeout,
...@@ -80,7 +80,7 @@ internal static void Unknown(string key) ...@@ -80,7 +80,7 @@ internal static void Unknown(string key)
Version, ConnectTimeout, Password, Version, ConnectTimeout, Password,
TieBreaker, WriteBuffer, Ssl, SslHost, TieBreaker, WriteBuffer, Ssl, SslHost,
ConfigChannel, AbortOnConnectFail, ResolveDns, ConfigChannel, AbortOnConnectFail, ResolveDns,
ChannelPrefix, Proxy ChannelPrefix, Proxy, ConnectRetry
}.ToDictionary(x => x, StringComparer.InvariantCultureIgnoreCase); }.ToDictionary(x => x, StringComparer.InvariantCultureIgnoreCase);
public static string TryNormalize(string value) public static string TryNormalize(string value)
...@@ -105,7 +105,7 @@ public static string TryNormalize(string value) ...@@ -105,7 +105,7 @@ public static string TryNormalize(string value)
private Version defaultVersion; private Version defaultVersion;
private int? keepAlive, syncTimeout, connectTimeout, writeBuffer; private int? keepAlive, syncTimeout, connectTimeout, writeBuffer, connectRetry;
private Proxy? proxy; private Proxy? proxy;
...@@ -153,6 +153,11 @@ public static string TryNormalize(string value) ...@@ -153,6 +153,11 @@ public static string TryNormalize(string value)
/// </summary> /// </summary>
public string ClientName { get { return clientName; } set { clientName = value; } } public string ClientName { get { return clientName; } set { clientName = value; } }
/// <summary>
/// The number of times to repeat the initial connect cycle if no servers respond promptly
/// </summary>
public int ConnectRetry { get { return connectRetry ?? 3; } set { connectRetry = value; } }
/// <summary> /// <summary>
/// The command-map associated with this configuration /// The command-map associated with this configuration
/// </summary> /// </summary>
...@@ -302,6 +307,7 @@ public ConfigurationOptions Clone() ...@@ -302,6 +307,7 @@ public ConfigurationOptions Clone()
CertificateSelectionCallback = CertificateSelectionCallback, CertificateSelectionCallback = CertificateSelectionCallback,
ChannelPrefix = ChannelPrefix.Clone(), ChannelPrefix = ChannelPrefix.Clone(),
SocketManager = SocketManager, SocketManager = SocketManager,
connectRetry = connectRetry
}; };
foreach (var item in endpoints) foreach (var item in endpoints)
options.endpoints.Add(item); options.endpoints.Add(item);
...@@ -343,6 +349,7 @@ public override string ToString() ...@@ -343,6 +349,7 @@ public override string ToString()
Append(sb, OptionKeys.AbortOnConnectFail, abortOnConnectFail); Append(sb, OptionKeys.AbortOnConnectFail, abortOnConnectFail);
Append(sb, OptionKeys.ResolveDns, resolveDns); Append(sb, OptionKeys.ResolveDns, resolveDns);
Append(sb, OptionKeys.ChannelPrefix, (string)ChannelPrefix); Append(sb, OptionKeys.ChannelPrefix, (string)ChannelPrefix);
Append(sb, OptionKeys.ConnectRetry, connectRetry);
Append(sb, OptionKeys.Proxy, proxy); Append(sb, OptionKeys.Proxy, proxy);
if(commandMap != null) commandMap.AppendDeltas(sb); if(commandMap != null) commandMap.AppendDeltas(sb);
return sb.ToString(); return sb.ToString();
...@@ -433,7 +440,7 @@ static bool IsOption(string option, string prefix) ...@@ -433,7 +440,7 @@ static bool IsOption(string option, string prefix)
void Clear() void Clear()
{ {
clientName = serviceName = password = tieBreaker = sslHost = configChannel = null; clientName = serviceName = password = tieBreaker = sslHost = configChannel = null;
keepAlive = syncTimeout = connectTimeout = writeBuffer = null; keepAlive = syncTimeout = connectTimeout = writeBuffer = connectRetry = null;
allowAdmin = abortOnConnectFail = resolveDns = ssl = null; allowAdmin = abortOnConnectFail = resolveDns = ssl = null;
defaultVersion = null; defaultVersion = null;
endpoints.Clear(); endpoints.Clear();
...@@ -500,6 +507,9 @@ private void DoParse(string configuration, bool ignoreUnknown) ...@@ -500,6 +507,9 @@ private void DoParse(string configuration, bool ignoreUnknown)
case OptionKeys.ConnectTimeout: case OptionKeys.ConnectTimeout:
ConnectTimeout = OptionKeys.ParseInt32(key, value); ConnectTimeout = OptionKeys.ParseInt32(key, value);
break; break;
case OptionKeys.ConnectRetry:
ConnectRetry = OptionKeys.ParseInt32(key, value);
break;
case OptionKeys.Version: case OptionKeys.Version:
DefaultVersion = OptionKeys.ParseVersion(key, value); DefaultVersion = OptionKeys.ParseVersion(key, value);
break; break;
......
...@@ -301,6 +301,17 @@ internal void OnConnected(PhysicalConnection connection) ...@@ -301,6 +301,17 @@ internal void OnConnected(PhysicalConnection connection)
} }
} }
internal void ResetNonConnected()
{
var tmp = physical;
if (tmp != null && state != (int)State.ConnectedEstablished)
{
tmp.RecordConnectionFailed(ConnectionFailureType.UnableToConnect);
}
GetConnection();
}
internal void OnConnectionFailed(PhysicalConnection connection, ConnectionFailureType failureType, Exception innerException) internal void OnConnectionFailed(PhysicalConnection connection, ConnectionFailureType failureType, Exception innerException)
{ {
if (reportNextFailure) if (reportNextFailure)
......
...@@ -46,6 +46,14 @@ internal sealed partial class ServerEndPoint : IDisposable ...@@ -46,6 +46,14 @@ internal sealed partial class ServerEndPoint : IDisposable
private Version version; private Version version;
internal void ResetNonConnected()
{
var tmp = interactive;
if (tmp != null) tmp.ResetNonConnected();
tmp = subscription;
if (tmp != null) tmp.ResetNonConnected();
}
public ServerEndPoint(ConnectionMultiplexer multiplexer, EndPoint endpoint) public ServerEndPoint(ConnectionMultiplexer multiplexer, EndPoint endpoint)
{ {
this.multiplexer = multiplexer; this.multiplexer = multiplexer;
......
...@@ -138,7 +138,8 @@ internal SocketToken BeginConnect(EndPoint endpoint, ISocketCallback callback) ...@@ -138,7 +138,8 @@ internal SocketToken BeginConnect(EndPoint endpoint, ISocketCallback callback)
} }
throw; throw;
} }
return new SocketToken(socket); var token = new SocketToken(socket);
return token;
} }
internal void SetFastLoopbackOption(Socket socket) internal void SetFastLoopbackOption(Socket socket)
{ {
...@@ -228,6 +229,19 @@ private void EndConnectImpl(IAsyncResult ar) ...@@ -228,6 +229,19 @@ private void EndConnectImpl(IAsyncResult ar)
break; break;
} }
} }
catch(ObjectDisposedException)
{
ConnectionMultiplexer.TraceWithoutContext("(socket shutdown)");
if (tuple != null)
{
try
{ tuple.Item2.Error(); }
catch (Exception inner)
{
ConnectionMultiplexer.TraceWithoutContext(inner.Message);
}
}
}
catch(Exception outer) catch(Exception outer)
{ {
ConnectionMultiplexer.TraceWithoutContext(outer.Message); ConnectionMultiplexer.TraceWithoutContext(outer.Message);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment