From ea607994b85612bc09bc6551ba2988bf0c8c264b Mon Sep 17 00:00:00 2001 From: Ivan Wennberg Date: Thu, 23 Apr 2026 12:13:38 +0200 Subject: [PATCH 1/2] Fix #2173: Honor AbortOnConnectFail on sentinel path --- .../ConnectionMultiplexer.Sentinel.cs | 15 ++++++- .../SentinelAbortOnConnectTests.cs | 41 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 tests/StackExchange.Redis.Tests/SentinelAbortOnConnectTests.cs diff --git a/src/StackExchange.Redis/ConnectionMultiplexer.Sentinel.cs b/src/StackExchange.Redis/ConnectionMultiplexer.Sentinel.cs index 61b36b014..8a30ae8c4 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); + 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)); + } +} From 7ac12a8ebd693baed3fd5a207ac6e347890938b3 Mon Sep 17 00:00:00 2001 From: Ivan Wennberg Date: Thu, 23 Apr 2026 13:15:18 +0200 Subject: [PATCH 2/2] Clone endpoints to avoid mutating caller's ConfigurationOptions --- src/StackExchange.Redis/ConnectionMultiplexer.Sentinel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/StackExchange.Redis/ConnectionMultiplexer.Sentinel.cs b/src/StackExchange.Redis/ConnectionMultiplexer.Sentinel.cs index 8a30ae8c4..593adc2ff 100644 --- a/src/StackExchange.Redis/ConnectionMultiplexer.Sentinel.cs +++ b/src/StackExchange.Redis/ConnectionMultiplexer.Sentinel.cs @@ -206,7 +206,7 @@ public ConnectionMultiplexer GetSentinelMasterConnection(ConfigurationOptions co { if (!config.AbortOnConnectFail) { - connection = ConnectImpl(config, log, endpoints: config.EndPoints); + connection = ConnectImpl(config, log, endpoints: config.EndPoints.Clone()); connection.ConnectionRestored += OnManagedConnectionRestored; connection.ConnectionFailed += OnManagedConnectionFailed; lock (sentinelConnectionChildren)