diff --git a/src/StackExchange.Redis/ConnectionMultiplexer.Sentinel.cs b/src/StackExchange.Redis/ConnectionMultiplexer.Sentinel.cs index 61b36b014..593adc2ff 100644 --- a/src/StackExchange.Redis/ConnectionMultiplexer.Sentinel.cs +++ b/src/StackExchange.Redis/ConnectionMultiplexer.Sentinel.cs @@ -166,7 +166,8 @@ private static async Task SentinelPrimaryConnectAsync(Con /// The writer to log to, if any. public ConnectionMultiplexer GetSentinelMasterConnection(ConfigurationOptions config, TextWriter? log = null) { - if (ServerSelectionStrategy.ServerType != ServerType.Sentinel) + if (ServerSelectionStrategy.ServerType != ServerType.Sentinel + && (RawConfig.AbortOnConnectFail || IsConnected)) { throw new RedisConnectionException( ConnectionFailureType.UnableToConnect, @@ -203,6 +204,18 @@ public ConnectionMultiplexer GetSentinelMasterConnection(ConfigurationOptions co if (newPrimaryEndPoint is null) { + if (!config.AbortOnConnectFail) + { + connection = ConnectImpl(config, log, endpoints: config.EndPoints.Clone()); + connection.ConnectionRestored += OnManagedConnectionRestored; + connection.ConnectionFailed += OnManagedConnectionFailed; + lock (sentinelConnectionChildren) + { + sentinelConnectionChildren[serviceName] = connection; + } + return connection; + } + throw new RedisConnectionException( ConnectionFailureType.UnableToConnect, $"Sentinel: Failed connecting to configured primary for service: {config.ServiceName}"); diff --git a/tests/StackExchange.Redis.Tests/SentinelAbortOnConnectTests.cs b/tests/StackExchange.Redis.Tests/SentinelAbortOnConnectTests.cs new file mode 100644 index 000000000..e6ff1b7e1 --- /dev/null +++ b/tests/StackExchange.Redis.Tests/SentinelAbortOnConnectTests.cs @@ -0,0 +1,41 @@ +using System.Threading.Tasks; +using Xunit; + +namespace StackExchange.Redis.Tests; + +public class SentinelAbortOnConnectTests(ITestOutputHelper output) : TestBase(output) +{ + [Fact] + public void ConnectToUnreachableSentinelReturnsDisconnectedMultiplexer() + { + var config = ConfigurationOptions.Parse("nonexistent:26379,serviceName=mymaster"); + config.AbortOnConnectFail = false; + config.ConnectTimeout = 1000; + + using var mux = ConnectionMultiplexer.Connect(config); + Assert.NotNull(mux); + Assert.False(mux.IsConnected); + } + + [Fact] + public async Task ConnectAsyncToUnreachableSentinelReturnsDisconnectedMultiplexer() + { + var config = ConfigurationOptions.Parse("nonexistent:26379,serviceName=mymaster"); + config.AbortOnConnectFail = false; + config.ConnectTimeout = 1000; + + await using var mux = await ConnectionMultiplexer.ConnectAsync(config); + Assert.NotNull(mux); + Assert.False(mux.IsConnected); + } + + [Fact] + public void ConnectToUnreachableSentinelThrowsWhenAbortOnConnectFailIsTrue() + { + var config = ConfigurationOptions.Parse("nonexistent:26379,serviceName=mymaster"); + config.AbortOnConnectFail = true; + config.ConnectTimeout = 1000; + + Assert.Throws(() => ConnectionMultiplexer.Connect(config)); + } +}