From b07f726734b07297c390aad8b642f83c4b10cbf4 Mon Sep 17 00:00:00 2001 From: Nick Craver Date: Sun, 10 Sep 2023 08:55:18 -0400 Subject: [PATCH] Options: support changing lib-ver This adds similar support from LibraryName to LibraryVersion so that consumers can override both if they so choose - this works in all the same places, DefaultOptionsProvider, etc. --- .../Configuration/DefaultOptionsProvider.cs | 2 +- .../ConfigurationOptions.cs | 35 +++++++++++++++---- .../PublicAPI/PublicAPI.Shipped.txt | 4 ++- src/StackExchange.Redis/ServerEndPoint.cs | 19 ++++------ .../DefaultOptionsTests.cs | 10 +++--- 5 files changed, 45 insertions(+), 25 deletions(-) diff --git a/src/StackExchange.Redis/Configuration/DefaultOptionsProvider.cs b/src/StackExchange.Redis/Configuration/DefaultOptionsProvider.cs index 67c74089b..9f106c5c2 100644 --- a/src/StackExchange.Redis/Configuration/DefaultOptionsProvider.cs +++ b/src/StackExchange.Redis/Configuration/DefaultOptionsProvider.cs @@ -228,7 +228,7 @@ protected virtual string GetDefaultClientName() => /// /// String version of the StackExchange.Redis library, for use in any options. /// - protected static string LibraryVersion => Utils.GetLibVersion(); + public virtual string LibraryVersion => Utils.GetLibVersion(); /// /// Name of the machine we're running on, for use in any options. diff --git a/src/StackExchange.Redis/ConfigurationOptions.cs b/src/StackExchange.Redis/ConfigurationOptions.cs index a85232172..3549a8b66 100644 --- a/src/StackExchange.Redis/ConfigurationOptions.cs +++ b/src/StackExchange.Redis/ConfigurationOptions.cs @@ -157,7 +157,7 @@ public static string TryNormalize(string value) private bool? allowAdmin, abortOnConnectFail, resolveDns, ssl, checkCertificateRevocation, includeDetailInExceptions, includePerformanceCountersInExceptions, setClientLibrary; - private string? tieBreaker, sslHost, configChannel, user, password; + private string? tieBreaker, sslHost, configChannel, user, password, libraryName, libraryVersion; private TimeSpan? heartbeatInterval; @@ -249,20 +249,40 @@ public bool UseSsl /// /// Gets or sets whether the library should identify itself by library-name/version when possible. /// + /// public bool SetClientLibrary { get => setClientLibrary ?? Defaults.SetClientLibrary; set => setClientLibrary = value; } - /// /// Gets or sets the library name to use for CLIENT SETINFO lib-name calls to Redis during handshake. /// Defaults to "SE.Redis". /// - /// If the value is null, empty or whitespace, then the value from the options-provideer is used; - /// to disable the library name feature, use instead. - public string? LibraryName { get; set; } + /// + /// To disable the library name feature, set to false. + /// For allowed values, see . + /// + public string? LibraryName + { + get => libraryName ?? Defaults.LibraryName; + set => libraryName = value; + } + + /// + /// Gets or sets the library version to use for CLIENT SETINFO lib-ver calls to Redis during handshake. + /// Defaults to the build version of StackExchange.Redis. + /// + /// + /// To disable the library name feature, set to false. + /// For allowed values, see . + /// + public string? LibraryVersion + { + get => libraryVersion ?? Defaults.LibraryVersion; + set => libraryVersion = value; + } /// /// Automatically encodes and decodes channels. @@ -709,7 +729,8 @@ public static ConfigurationOptions Parse(string configuration, bool ignoreUnknow #endif Tunnel = Tunnel, setClientLibrary = setClientLibrary, - LibraryName = LibraryName, + libraryName = libraryName, + libraryVersion = libraryVersion, Protocol = Protocol, }; @@ -834,7 +855,7 @@ private static void Append(StringBuilder sb, string prefix, object? value) private void Clear() { - ClientName = ServiceName = user = password = tieBreaker = sslHost = configChannel = null; + ClientName = ServiceName = user = password = tieBreaker = sslHost = configChannel = libraryName = libraryVersion = null; keepAlive = syncTimeout = asyncTimeout = connectTimeout = connectRetry = configCheckSeconds = DefaultDatabase = null; allowAdmin = abortOnConnectFail = resolveDns = ssl = setClientLibrary = null; SslProtocols = null; diff --git a/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt b/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt index 459f66cf8..f12fa8f4a 100644 --- a/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt +++ b/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt @@ -244,6 +244,8 @@ StackExchange.Redis.ConfigurationOptions.KeepAlive.get -> int StackExchange.Redis.ConfigurationOptions.KeepAlive.set -> void StackExchange.Redis.ConfigurationOptions.LibraryName.get -> string? StackExchange.Redis.ConfigurationOptions.LibraryName.set -> void +StackExchange.Redis.ConfigurationOptions.LibraryVersion.get -> string? +StackExchange.Redis.ConfigurationOptions.LibraryVersion.set -> void StackExchange.Redis.ConfigurationOptions.LoggerFactory.get -> Microsoft.Extensions.Logging.ILoggerFactory? StackExchange.Redis.ConfigurationOptions.LoggerFactory.set -> void StackExchange.Redis.ConfigurationOptions.Password.get -> string? @@ -1611,7 +1613,6 @@ static StackExchange.Redis.Configuration.DefaultOptionsProvider.AddProvider(Stac static StackExchange.Redis.Configuration.DefaultOptionsProvider.ComputerName.get -> string! static StackExchange.Redis.Configuration.DefaultOptionsProvider.GetProvider(StackExchange.Redis.EndPointCollection! endpoints) -> StackExchange.Redis.Configuration.DefaultOptionsProvider! static StackExchange.Redis.Configuration.DefaultOptionsProvider.GetProvider(System.Net.EndPoint! endpoint) -> StackExchange.Redis.Configuration.DefaultOptionsProvider! -static StackExchange.Redis.Configuration.DefaultOptionsProvider.LibraryVersion.get -> string! static StackExchange.Redis.ConfigurationOptions.Parse(string! configuration) -> StackExchange.Redis.ConfigurationOptions! static StackExchange.Redis.ConfigurationOptions.Parse(string! configuration, bool ignoreUnknown) -> StackExchange.Redis.ConfigurationOptions! static StackExchange.Redis.ConnectionMultiplexer.Connect(StackExchange.Redis.ConfigurationOptions! configuration, System.IO.TextWriter? log = null) -> StackExchange.Redis.ConnectionMultiplexer! @@ -1803,6 +1804,7 @@ virtual StackExchange.Redis.Configuration.DefaultOptionsProvider.IncludePerforma 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.LibraryName.get -> string! +virtual StackExchange.Redis.Configuration.DefaultOptionsProvider.LibraryVersion.get -> string! virtual StackExchange.Redis.Configuration.DefaultOptionsProvider.LoggerFactory.get -> Microsoft.Extensions.Logging.ILoggerFactory? virtual StackExchange.Redis.Configuration.DefaultOptionsProvider.Password.get -> string? virtual StackExchange.Redis.Configuration.DefaultOptionsProvider.Proxy.get -> StackExchange.Redis.Proxy diff --git a/src/StackExchange.Redis/ServerEndPoint.cs b/src/StackExchange.Redis/ServerEndPoint.cs index ebb66ec2a..56ad81286 100644 --- a/src/StackExchange.Redis/ServerEndPoint.cs +++ b/src/StackExchange.Redis/ServerEndPoint.cs @@ -926,7 +926,7 @@ private async Task HandshakeAsync(PhysicalConnection connection, ILogger? log) var config = Multiplexer.RawConfig; string? user = config.User; string password = config.Password ?? ""; - + string clientName = Multiplexer.ClientName; if (!string.IsNullOrWhiteSpace(clientName)) { @@ -1009,17 +1009,13 @@ private async Task HandshakeAsync(PhysicalConnection connection, ILogger? log) if (config.SetClientLibrary) { + var libName = config.LibraryName; + var libVersion = config.LibraryVersion; + // note that this is a relatively new feature, but usually we won't know the // server version, so we will use this speculatively and hope for the best - log?.LogInformation($"{Format.ToString(this)}: Setting client lib/ver"); + log?.LogInformation($"{Format.ToString(this)}: Setting client lib/ver to {libName}/{libVersion}"); - var libName = config.LibraryName; - if (string.IsNullOrWhiteSpace(libName)) - { - // defer to provider if missing (note re null vs blank; if caller wants to disable - // it, they should set SetClientLibrary to false, not set the name to empty string) - libName = config.Defaults.LibraryName; - } if (!string.IsNullOrWhiteSpace(libName)) { msg = Message.Create(-1, CommandFlags.FireAndForget, RedisCommand.CLIENT, @@ -1028,11 +1024,10 @@ private async Task HandshakeAsync(PhysicalConnection connection, ILogger? log) await WriteDirectOrQueueFireAndForgetAsync(connection, msg, ResultProcessor.DemandOK).ForAwait(); } - var version = Utils.GetLibVersion(); - if (!string.IsNullOrWhiteSpace(version)) + if (!string.IsNullOrWhiteSpace(libVersion)) { msg = Message.Create(-1, CommandFlags.FireAndForget, RedisCommand.CLIENT, - RedisLiterals.SETINFO, RedisLiterals.lib_ver, version); + RedisLiterals.SETINFO, RedisLiterals.lib_ver, libVersion); msg.SetInternalCall(); await WriteDirectOrQueueFireAndForgetAsync(connection, msg, ResultProcessor.DemandOK).ForAwait(); } diff --git a/tests/StackExchange.Redis.Tests/DefaultOptionsTests.cs b/tests/StackExchange.Redis.Tests/DefaultOptionsTests.cs index e3941f749..70badab25 100644 --- a/tests/StackExchange.Redis.Tests/DefaultOptionsTests.cs +++ b/tests/StackExchange.Redis.Tests/DefaultOptionsTests.cs @@ -167,11 +167,12 @@ public async Task ClientNameExplicitWins() public class TestLibraryNameOptionsProvider : DefaultOptionsProvider { public string Id { get; } = Guid.NewGuid().ToString(); - public override string LibraryName => Id; + public override string LibraryName => Id + "Name"; + public override string LibraryVersion => Id + "Version"; } [Fact] - public async Task LibraryNameOverride() + public async Task LibraryNameAndVersionOverride() { var options = ConfigurationOptions.Parse(GetConfiguration()); var defaults = new TestLibraryNameOptionsProvider(); @@ -185,10 +186,11 @@ public async Task LibraryNameOverride() var clients = await GetServer(conn).ClientListAsync(); foreach (var client in clients) { - Log("Library name: " + client.LibraryName); + Log("Library name: " + client.LibraryName + ", version:" + client.LibraryVersion); } Assert.True(conn.IsConnected); - Assert.True(clients.Any(c => c.LibraryName == defaults.LibraryName), "Did not find client with name: " + defaults.Id); + Assert.True(clients.Any(c => c.LibraryName == defaults.LibraryName && c.LibraryVersion == defaults.LibraryVersion), + "Did not find client with name: '" + defaults.LibraryName + "' and version: '" + defaults.LibraryVersion + "'"); } }