diff --git a/src/StackExchange.Redis/CommandMap.cs b/src/StackExchange.Redis/CommandMap.cs index a12828033..b22fd5ae9 100644 --- a/src/StackExchange.Redis/CommandMap.cs +++ b/src/StackExchange.Redis/CommandMap.cs @@ -7,7 +7,7 @@ namespace StackExchange.Redis /// /// Represents the commands mapped on a particular configuration. /// - public sealed class CommandMap + public sealed class CommandMap // TODO: (RESP3) add HELLO command here? { private readonly CommandBytes[] map; diff --git a/src/StackExchange.Redis/Configuration/DefaultOptionsProvider.cs b/src/StackExchange.Redis/Configuration/DefaultOptionsProvider.cs index 72b1d3997..744e48bbf 100644 --- a/src/StackExchange.Redis/Configuration/DefaultOptionsProvider.cs +++ b/src/StackExchange.Redis/Configuration/DefaultOptionsProvider.cs @@ -141,6 +141,11 @@ public static void AddProvider(DefaultOptionsProvider provider) /// public virtual TimeSpan KeepAliveInterval => TimeSpan.FromSeconds(60); + /// + /// Which Resp protocol to use (default is RESP 2). + /// + public virtual string Protocol => "2"; + /// /// Type of proxy to use (if any); for example . /// diff --git a/src/StackExchange.Redis/ConfigurationOptions.cs b/src/StackExchange.Redis/ConfigurationOptions.cs index e773944d1..91702fa0e 100644 --- a/src/StackExchange.Redis/ConfigurationOptions.cs +++ b/src/StackExchange.Redis/ConfigurationOptions.cs @@ -85,6 +85,7 @@ internal const string User = "user", Password = "password", PreserveAsyncOrder = "preserveAsyncOrder", + Protocol = "protocol", Proxy = "proxy", ResolveDns = "resolveDns", ResponseTimeout = "responseTimeout", @@ -116,6 +117,7 @@ internal const string User, Password, PreserveAsyncOrder, + Protocol, Proxy, ResolveDns, ServiceName, @@ -144,7 +146,7 @@ public static string TryNormalize(string value) private bool? allowAdmin, abortOnConnectFail, resolveDns, ssl, checkCertificateRevocation, includeDetailInExceptions, includePerformanceCountersInExceptions; - private string? tieBreaker, sslHost, configChannel; + private string? tieBreaker, sslHost, configChannel, protocol; private TimeSpan? heartbeatInterval; @@ -449,6 +451,15 @@ public bool PreserveAsyncOrder set { } } + /// + /// Which Resp protocol to use (default is RESP 2). + /// + public string Protocol + { + get => protocol ?? Defaults.Protocol; + set => protocol = value; + } + /// /// Type of proxy to use (if any); for example . /// @@ -632,6 +643,7 @@ public static ConfigurationOptions Parse(string configuration, bool ignoreUnknow configChannel = configChannel, abortOnConnectFail = abortOnConnectFail, resolveDns = resolveDns, + protocol = protocol, proxy = proxy, commandMap = commandMap, CertificateValidationCallback = CertificateValidationCallback, @@ -727,6 +739,7 @@ public string ToString(bool includePassword) Append(sb, OptionKeys.ResolveDns, resolveDns); Append(sb, OptionKeys.ChannelPrefix, (string?)ChannelPrefix); Append(sb, OptionKeys.ConnectRetry, connectRetry); + Append(sb, OptionKeys.Protocol, protocol); Append(sb, OptionKeys.Proxy, proxy); Append(sb, OptionKeys.ConfigCheckSeconds, configCheckSeconds); Append(sb, OptionKeys.ResponseTimeout, responseTimeout); @@ -874,6 +887,9 @@ private ConfigurationOptions DoParse(string configuration, bool ignoreUnknown) case OptionKeys.SslHost: SslHost = value; break; + case OptionKeys.Protocol: + Protocol = value; + break; case OptionKeys.Proxy: Proxy = OptionKeys.ParseProxy(key, value); break; diff --git a/src/StackExchange.Redis/ConnectionMultiplexer.cs b/src/StackExchange.Redis/ConnectionMultiplexer.cs index b94e4650b..30037867e 100644 --- a/src/StackExchange.Redis/ConnectionMultiplexer.cs +++ b/src/StackExchange.Redis/ConnectionMultiplexer.cs @@ -110,6 +110,11 @@ public bool IncludePerformanceCountersInExceptions /// public string Configuration => RawConfig.ToString(); + /// + /// Gets the RESP protocol in use. + /// + public string Protocol => RawConfig.Protocol; + /// /// Indicates whether any servers are connected. /// diff --git a/src/StackExchange.Redis/Enums/RedisCommand.cs b/src/StackExchange.Redis/Enums/RedisCommand.cs index 40cb5c708..b2ba34727 100644 --- a/src/StackExchange.Redis/Enums/RedisCommand.cs +++ b/src/StackExchange.Redis/Enums/RedisCommand.cs @@ -64,6 +64,7 @@ internal enum RedisCommand GETSET, HDEL, + HELLO, HEXISTS, HGET, HGETALL, @@ -279,6 +280,7 @@ internal static bool IsPrimaryOnly(this RedisCommand command) case RedisCommand.GETEX: case RedisCommand.GETSET: case RedisCommand.HDEL: + case RedisCommand.HELLO: case RedisCommand.HINCRBY: case RedisCommand.HINCRBYFLOAT: case RedisCommand.HMSET: diff --git a/src/StackExchange.Redis/Interfaces/IConnectionMultiplexer.cs b/src/StackExchange.Redis/Interfaces/IConnectionMultiplexer.cs index 583d621eb..88aae4cf6 100644 --- a/src/StackExchange.Redis/Interfaces/IConnectionMultiplexer.cs +++ b/src/StackExchange.Redis/Interfaces/IConnectionMultiplexer.cs @@ -34,6 +34,11 @@ public interface IConnectionMultiplexer : IDisposable, IAsyncDisposable /// string Configuration { get; } + /// + /// Gets the RESP protocol in use. + /// + public string Protocol { get; } + /// /// Gets the timeout associated with the connections. /// diff --git a/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt b/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt index 72ae963c1..224fa7df2 100644 --- a/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt +++ b/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt @@ -242,6 +242,8 @@ StackExchange.Redis.ConfigurationOptions.Password.get -> string? StackExchange.Redis.ConfigurationOptions.Password.set -> void StackExchange.Redis.ConfigurationOptions.PreserveAsyncOrder.get -> bool StackExchange.Redis.ConfigurationOptions.PreserveAsyncOrder.set -> void +StackExchange.Redis.ConfigurationOptions.Protocol.get -> string! +StackExchange.Redis.ConfigurationOptions.Protocol.set -> void StackExchange.Redis.ConfigurationOptions.Proxy.get -> StackExchange.Redis.Proxy StackExchange.Redis.ConfigurationOptions.Proxy.set -> void StackExchange.Redis.ConfigurationOptions.ReconnectRetryPolicy.get -> StackExchange.Redis.IReconnectRetryPolicy! @@ -349,6 +351,7 @@ StackExchange.Redis.ConnectionMultiplexer.IsConnecting.get -> bool StackExchange.Redis.ConnectionMultiplexer.OperationCount.get -> long StackExchange.Redis.ConnectionMultiplexer.PreserveAsyncOrder.get -> bool StackExchange.Redis.ConnectionMultiplexer.PreserveAsyncOrder.set -> void +StackExchange.Redis.ConnectionMultiplexer.Protocol.get -> string! StackExchange.Redis.ConnectionMultiplexer.PublishReconfigure(StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.ConnectionMultiplexer.PublishReconfigureAsync(StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.ConnectionMultiplexer.ReconfigureAsync(string! reason) -> System.Threading.Tasks.Task! @@ -488,6 +491,7 @@ StackExchange.Redis.IConnectionMultiplexer.IsConnecting.get -> bool StackExchange.Redis.IConnectionMultiplexer.OperationCount.get -> long StackExchange.Redis.IConnectionMultiplexer.PreserveAsyncOrder.get -> bool StackExchange.Redis.IConnectionMultiplexer.PreserveAsyncOrder.set -> void +StackExchange.Redis.IConnectionMultiplexer.Protocol.get -> string! StackExchange.Redis.IConnectionMultiplexer.PublishReconfigure(StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IConnectionMultiplexer.PublishReconfigureAsync(StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IConnectionMultiplexer.RegisterProfiler(System.Func! profilingSessionProvider) -> void @@ -1781,6 +1785,7 @@ virtual StackExchange.Redis.Configuration.DefaultOptionsProvider.IncludeDetailIn virtual StackExchange.Redis.Configuration.DefaultOptionsProvider.IncludePerformanceCountersInExceptions.get -> bool virtual StackExchange.Redis.Configuration.DefaultOptionsProvider.IsMatch(System.Net.EndPoint! endpoint) -> bool virtual StackExchange.Redis.Configuration.DefaultOptionsProvider.KeepAliveInterval.get -> System.TimeSpan +virtual StackExchange.Redis.Configuration.DefaultOptionsProvider.Protocol.get -> string! virtual StackExchange.Redis.Configuration.DefaultOptionsProvider.Proxy.get -> StackExchange.Redis.Proxy virtual StackExchange.Redis.Configuration.DefaultOptionsProvider.ReconnectRetryPolicy.get -> StackExchange.Redis.IReconnectRetryPolicy? virtual StackExchange.Redis.Configuration.DefaultOptionsProvider.ResolveDns.get -> bool diff --git a/src/StackExchange.Redis/ServerEndPoint.cs b/src/StackExchange.Redis/ServerEndPoint.cs index 1e192d85e..484da5c0a 100644 --- a/src/StackExchange.Redis/ServerEndPoint.cs +++ b/src/StackExchange.Redis/ServerEndPoint.cs @@ -931,6 +931,14 @@ private async Task HandshakeAsync(PhysicalConnection connection, LogProxy? log) } } + if (Multiplexer.Protocol == "3") + { + log?.WriteLine($"{Format.ToString(this)}: Setting RESP protocol to RESP {Multiplexer.Protocol}"); + msg = Message.Create(-1, CommandFlags.FireAndForget, RedisCommand.HELLO, (RedisValue)Multiplexer.Protocol); + msg.SetInternalCall(); + await WriteDirectOrQueueFireAndForgetAsync(connection, msg, ResultProcessor.DemandOK).ForAwait(); + } + var bridge = connection.BridgeCouldBeNull; if (bridge == null) { diff --git a/tests/StackExchange.Redis.Tests/ConfigTests.cs b/tests/StackExchange.Redis.Tests/ConfigTests.cs index 668abe607..23f2c19ce 100644 --- a/tests/StackExchange.Redis.Tests/ConfigTests.cs +++ b/tests/StackExchange.Redis.Tests/ConfigTests.cs @@ -458,6 +458,20 @@ public void ThreadPoolManagerIsDetected() Assert.Same(PipeScheduler.ThreadPool, conn.SocketManager?.Scheduler); } + [Fact] + public void Resp3Test() + { + var config = new ConfigurationOptions + { + Protocol = "3" + }; + + var conn = ConnectionMultiplexer.Connect(config); + var db = conn.GetDatabase(); + db.Ping(); + db.Ping(); + } + [Fact] public void DefaultThreadPoolManagerIsDetected() { diff --git a/tests/StackExchange.Redis.Tests/Helpers/SharedConnectionFixture.cs b/tests/StackExchange.Redis.Tests/Helpers/SharedConnectionFixture.cs index aec2e0a83..5f6e7cbb3 100644 --- a/tests/StackExchange.Redis.Tests/Helpers/SharedConnectionFixture.cs +++ b/tests/StackExchange.Redis.Tests/Helpers/SharedConnectionFixture.cs @@ -59,6 +59,8 @@ public bool IgnoreConnect public string Configuration => _inner.Configuration; + public string Protocol => _inner.Protocol; + public int TimeoutMilliseconds => _inner.TimeoutMilliseconds; public long OperationCount => _inner.OperationCount;