Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Options: support changing lib-ver #2547

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@ Current package versions:

- Adds: RESP3 support ([#2396 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2396)) - see https://stackexchange.github.io/StackExchange.Redis/Resp3
- Fix [#2507](https://github.com/StackExchange/StackExchange.Redis/issues/2507): Pub/sub with multi-item payloads should be usable ([#2508 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2508))
- Add: connection-id tracking (internal only, no public API) ([#2508 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2508))
- Add: `ConfigurationOptions.LoggerFactory` for logging to an `ILoggerFactory` (e.g. `ILogger`) all connection and error events ([#2051 by NickCraver](https://github.com/StackExchange/StackExchange.Redis/pull/2051))
- Adds: connection-id tracking (internal only, no public API) ([#2508 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2508))
- Adds: `ConfigurationOptions.LoggerFactory` for logging to an `ILoggerFactory` (e.g. `ILogger`) all connection and error events ([#2051 by NickCraver](https://github.com/StackExchange/StackExchange.Redis/pull/2051))
- Fix [#2467](https://github.com/StackExchange/StackExchange.Redis/issues/2467): Add StreamGroupInfo EntriesRead and Lag ([#2510 by tvdias](https://github.com/StackExchange/StackExchange.Redis/pull/2510))
- Adds: `LibraryVersion` configuration option - allows the library version to be controlled at the individual options level (in addition to the existing controls in `DefaultOptionsProvider`) ([#2547 by NickCraver](https://github.com/StackExchange/StackExchange.Redis/pull/2547))

## 2.6.122

- Change: Target net6.0 instead of net5.0, since net5.0 is end of life. ([#2497 by eerhardt](https://github.com/StackExchange/StackExchange.Redis/pull/2497))
- Fix: Fix nullability annotation of IConnectionMultiplexer.RegisterProfiler ([#2494 by eerhardt](https://github.com/StackExchange/StackExchange.Redis/pull/2494))
- Add: `Timer.ActiveCount` under `POOL` in timeout messages on .NET 6+ to help diagnose timer overload affecting timeout evaluations ([#2500 by NickCraver](https://github.com/StackExchange/StackExchange.Redis/pull/2500))
- Add: `LibraryName` configuration option; allows the library name to be controlled at the individual options level (in addition to the existing controls in `DefaultOptionsProvider`) ([#2502 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2502))
- Add: `DefaultOptionsProvider.GetProvider` allows lookup of provider by endpoint ([#2502 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2502))
- Adds: `Timer.ActiveCount` under `POOL` in timeout messages on .NET 6+ to help diagnose timer overload affecting timeout evaluations ([#2500 by NickCraver](https://github.com/StackExchange/StackExchange.Redis/pull/2500))
- Adds: `LibraryName` configuration option - allows the library name to be controlled at the individual options level (in addition to the existing controls in `DefaultOptionsProvider`) ([#2502 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2502))
- Adds: `DefaultOptionsProvider.GetProvider` allows lookup of provider by endpoint ([#2502 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2502))

## 2.6.116

Expand All @@ -31,7 +32,7 @@ Current package versions:
## 2.6.111

- Fix [#2426](https://github.com/StackExchange/StackExchange.Redis/issues/2426): Don't restrict multi-slot operations on Envoy proxy; let the proxy decide ([#2428 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2428))
- Add: Support for `User`/`Password` in `DefaultOptionsProvider` to support token rotation scenarios ([#2445 by NickCraver](https://github.com/StackExchange/StackExchange.Redis/pull/2445))
- Adds: Support for `User`/`Password` in `DefaultOptionsProvider` to support token rotation scenarios ([#2445 by NickCraver](https://github.com/StackExchange/StackExchange.Redis/pull/2445))
- Fix [#2449](https://github.com/StackExchange/StackExchange.Redis/issues/2449): Resolve AOT trim warnings in `TryGetAzureRoleInstanceIdNoThrow` ([#2451 by eerhardt](https://github.com/StackExchange/StackExchange.Redis/pull/2451))
- Adds: Support for `HTTP/1.1 200 Connection established` in HTTP Tunnel ([#2448 by flobernd](https://github.com/StackExchange/StackExchange.Redis/pull/2448))
- Adds: Timeout duration to backlog timeout error messages ([#2452 by NickCraver](https://github.com/StackExchange/StackExchange.Redis/pull/2452))
Expand All @@ -40,10 +41,10 @@ Current package versions:
## 2.6.104

- Fix [#2412](https://github.com/StackExchange/StackExchange.Redis/issues/2412): Critical (but rare) GC bug that can lead to async tasks never completing if the multiplexer is not held by the consumer ([#2408 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2408))
- Add: Better error messages (over generic timeout) when commands are backlogged and unable to write to any connection ([#2408 by NickCraver](https://github.com/StackExchange/StackExchange.Redis/pull/2408))
- Adds: Better error messages (over generic timeout) when commands are backlogged and unable to write to any connection ([#2408 by NickCraver](https://github.com/StackExchange/StackExchange.Redis/pull/2408))
- Fix [#2392](https://github.com/StackExchange/StackExchange.Redis/issues/2392): Dequeue *all* timed out messages from the backlog when not connected (including Fire+Forget) ([#2397 by kornelpal](https://github.com/StackExchange/StackExchange.Redis/pull/2397))
- Fix [#2400](https://github.com/StackExchange/StackExchange.Redis/issues/2400): Expose `ChannelMessageQueue` as `IAsyncEnumerable<ChannelMessage>` ([#2402 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2402))
- Add: Support for `CLIENT SETINFO` (lib name/version) during handshake; opt-out is via `ConfigurationOptions`; also support read of `resp`, `lib-ver` and `lib-name` via `CLIENT LIST` ([#2414 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2414))
- Adds: Support for `CLIENT SETINFO` (lib name/version) during handshake; opt-out is via `ConfigurationOptions`; also support read of `resp`, `lib-ver` and `lib-name` via `CLIENT LIST` ([#2414 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2414))
- Documentation: clarify the meaning of `RedisValue.IsInteger` re [#2418](https://github.com/StackExchange/StackExchange.Redis/issues/2418) ([#2420 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/2420))

## 2.6.96
Expand Down Expand Up @@ -234,7 +235,7 @@ Current package versions:
- Fix: Server-selection strategy race condition ([#1532 by deepakverma](https://github.com/StackExchange/StackExchange.Redis/pull/1532))
- Fix: Sentinel default port ([#1525 by ejsmith](https://github.com/StackExchange/StackExchange.Redis/pull/1525))
- Fix: `Int64` parse scenario ([#1568 by arsnyder16](https://github.com/StackExchange/StackExchange.Redis/pull/1568))
- Add: Force replication check during failover ([#1563 by aravindyeduvaka & joroda](https://github.com/StackExchange/StackExchange.Redis/pull/1563))
- Adds: Force replication check during failover ([#1563 by aravindyeduvaka & joroda](https://github.com/StackExchange/StackExchange.Redis/pull/1563))
- Documentation tweaks (multiple)
- Fix: Backlog contention issue ([#1612 by mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/1612/), see also [#1574 by devbv](https://github.com/StackExchange/StackExchange.Redis/pull/1574/))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ protected virtual string GetDefaultClientName() =>
/// <summary>
/// String version of the StackExchange.Redis library, for use in any options.
/// </summary>
protected static string LibraryVersion => Utils.GetLibVersion();
public virtual string LibraryVersion => Utils.GetLibVersion();

/// <summary>
/// Name of the machine we're running on, for use in any options.
Expand Down
35 changes: 28 additions & 7 deletions src/StackExchange.Redis/ConfigurationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -249,20 +249,40 @@ public bool UseSsl
/// <summary>
/// Gets or sets whether the library should identify itself by library-name/version when possible.
/// </summary>
/// <remarks><see href="https://redis.io/commands/client-setinfo/"/></remarks>
public bool SetClientLibrary
{
get => setClientLibrary ?? Defaults.SetClientLibrary;
set => setClientLibrary = value;
}


/// <summary>
/// Gets or sets the library name to use for CLIENT SETINFO lib-name calls to Redis during handshake.
/// Defaults to "SE.Redis".
/// </summary>
/// <remarks>If the value is null, empty or whitespace, then the value from the options-provideer is used;
/// to disable the library name feature, use <see cref="SetClientLibrary"/> instead.</remarks>
public string? LibraryName { get; set; }
/// <remarks>
/// To disable the library name feature, set <see cref="SetClientLibrary"/> to <c>false</c>.
/// For allowed values, see <see href="https://redis.io/commands/client-setinfo/"/>.
/// </remarks>
public string? LibraryName
{
get => libraryName ?? Defaults.LibraryName;
set => libraryName = value;
}

/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// To disable the library name feature, set <see cref="SetClientLibrary"/> to <c>false</c>.
/// For allowed values, see <see href="https://redis.io/commands/client-setinfo/"/>.
/// </remarks>
public string? LibraryVersion
{
get => libraryVersion ?? Defaults.LibraryVersion;
set => libraryVersion = value;
}

/// <summary>
/// Automatically encodes and decodes channels.
Expand Down Expand Up @@ -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,
};

Expand Down Expand Up @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down Expand Up @@ -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!
Expand Down Expand Up @@ -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
Expand Down
19 changes: 7 additions & 12 deletions src/StackExchange.Redis/ServerEndPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
{
Expand Down Expand Up @@ -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,
Expand All @@ -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();
}
Expand Down
2 changes: 1 addition & 1 deletion tests/StackExchange.Redis.Tests/AbortOnConnectFailTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ private ConnectionMultiplexer GetWorkingBacklogConn() =>
{
AbortOnConnectFail = false,
BacklogPolicy = policy,
ConnectTimeout = 50,
ConnectTimeout = 500,
SyncTimeout = 400,
KeepAlive = 400,
AllowAdmin = true,
Expand Down
10 changes: 6 additions & 4 deletions tests/StackExchange.Redis.Tests/DefaultOptionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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 + "'");
}
}