Skip to content
Open
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
3 changes: 2 additions & 1 deletion docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ Current package versions:

## Unreleased

- (none)
- Add `IServer.GetProductVariant` to detect the product variant and version of the connected server, and use that internally
to enable multi-DB operations on Valkey clusters ([#3040 by @mgravell](https://github.com/StackExchange/StackExchange.Redis/pull/3040))

## 2.12.4

Expand Down
49 changes: 49 additions & 0 deletions src/StackExchange.Redis/AutoConfigureInfoField.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using RESPite;

namespace StackExchange.Redis;

/// <summary>
/// Fields that can appear in INFO output during auto-configuration.
/// </summary>
internal enum AutoConfigureInfoField
{
/// <summary>
/// Unknown or unrecognized field.
/// </summary>
[AsciiHash("")]
Unknown = 0,

[AsciiHash("role")]
Role,

[AsciiHash("master_host")]
MasterHost,

[AsciiHash("master_port")]
MasterPort,

[AsciiHash("redis_version")]
RedisVersion,

[AsciiHash("redis_mode")]
RedisMode,

[AsciiHash("run_id")]
RunId,

[AsciiHash("garnet_version")]
GarnetVersion,

[AsciiHash("valkey_version")]
ValkeyVersion,
}

/// <summary>
/// Metadata and parsing methods for <see cref="AutoConfigureInfoField"/>.
/// </summary>
internal static partial class AutoConfigureInfoFieldMetadata
{
[AsciiHash]
internal static partial bool TryParse(ReadOnlySpan<char> value, out AutoConfigureInfoField field);
}
28 changes: 28 additions & 0 deletions src/StackExchange.Redis/Enums/ProductVariant.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// ReSharper disable once CheckNamespace
namespace StackExchange.Redis;

/// <summary>
/// Indicates the flavor of RESP-server being used, if known. Unknown variants will be reported as <see cref="Redis"/>.
/// Inclusion (or omission) in this list does not imply support for any given variant; nor does it indicate any specific
/// relationship with the vendor or rights to use the name. It is provided solely for informational purposes. Identification
/// is not guaranteed, and is based on the server's self-reporting (typically via `INFO`), which may be incomplete or misleading.
/// </summary>
public enum ProductVariant
{
/// <summary>
/// The original Redis server. This is also the default value if the variant is unknown.
/// </summary>
Redis,

/// <summary>
/// <a href="https://valkey.io/">Valkey</a> is a fork of open-source Redis associated with AWS.
/// </summary>
Valkey,

/// <summary>
/// <a href="https://microsoft.github.io/garnet/">Garnet</a> is a Redis-compatible server from Microsoft.
/// </summary>
Garnet,

// if you want to add another variant here, please open an issue with the details (variant name, INFO output, etc.)
}
26 changes: 25 additions & 1 deletion src/StackExchange.Redis/Enums/ServerType.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
namespace StackExchange.Redis
using System;
using RESPite;

namespace StackExchange.Redis
{
/// <summary>
/// Indicates the flavor of a particular redis server.
Expand All @@ -8,29 +11,50 @@ public enum ServerType
/// <summary>
/// Classic redis-server server.
/// </summary>
[AsciiHash("standalone")]
Standalone,

/// <summary>
/// Monitoring/configuration redis-sentinel server.
/// </summary>
[AsciiHash("sentinel")]
Sentinel,

/// <summary>
/// Distributed redis-cluster server.
/// </summary>
[AsciiHash("cluster")]
Cluster,

/// <summary>
/// Distributed redis installation via <a href="https://github.com/twitter/twemproxy">twemproxy</a>.
/// </summary>
[AsciiHash("")]
Twemproxy,

/// <summary>
/// Redis cluster via <a href="https://github.com/envoyproxy/envoy">envoyproxy</a>.
/// </summary>
[AsciiHash("")]
Envoyproxy,
}

/// <summary>
/// Metadata and parsing methods for <see cref="ServerType"/>.
/// </summary>
internal static partial class ServerTypeMetadata
{
[AsciiHash]
internal static partial bool TryParse(ReadOnlySpan<char> value, out ServerType serverType);

internal static bool TryParse(string? val, out ServerType serverType)
{
if (val is not null) return TryParse(val.AsSpan().Trim(), out serverType);
serverType = default;
return false;
}
}

internal static class ServerTypeExtensions
{
/// <summary>
Expand Down
10 changes: 2 additions & 8 deletions src/StackExchange.Redis/Format.cs
Original file line number Diff line number Diff line change
Expand Up @@ -583,14 +583,8 @@ internal static bool TryParseVersion(ReadOnlySpan<char> input, [NotNullWhen(true
version = null;
return false;
}
unsafe
{
fixed (char* ptr = input)
{
string s = new(ptr, 0, input.Length);
return TryParseVersion(s, out version);
}
}
string s = input.ToString();
return TryParseVersion(s, out version);
#endif
}

Expand Down
6 changes: 6 additions & 0 deletions src/StackExchange.Redis/Interfaces/IServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ public partial interface IServer : IRedis
/// </summary>
Version Version { get; }

/// <summary>
/// Attempt to identify the specific Redis product variant and version.
/// </summary>
/// <remarks>Note that it is explicitly not assumed that the version will conform to the <see cref="Version"/> format.</remarks>
ProductVariant GetProductVariant(out string version);

/// <summary>
/// The number of databases supported on this server.
/// </summary>
Expand Down
55 changes: 55 additions & 0 deletions src/StackExchange.Redis/KnownRole.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System;
using RESPite;

namespace StackExchange.Redis;

/// <summary>
/// Known role values used during auto-configuration parsing.
/// </summary>
internal enum KnownRole
{
/// <summary>
/// Unknown or unrecognized role.
/// </summary>
[AsciiHash("")]
None = 0,

[AsciiHash("primary")]
Primary,

[AsciiHash("master")]
Master,

[AsciiHash("replica")]
Replica,

[AsciiHash("slave")]
Slave,
}

/// <summary>
/// Metadata and parsing methods for <see cref="KnownRole"/>.
/// </summary>
internal static partial class KnownRoleMetadata
{
[AsciiHash]
private static partial bool TryParseCore(ReadOnlySpan<char> value, out KnownRole role);

internal static bool TryParse(ReadOnlySpan<char> value, out bool isReplica)
{
if (!TryParseCore(value.Trim(), out var role))
{
isReplica = false;
return false;
}

isReplica = role is KnownRole.Replica or KnownRole.Slave;
return true;
}
internal static bool TryParse(string? val, out bool isReplica)
{
if (val is not null) return TryParse(val.AsSpan(), out isReplica);
isReplica = false;
return false;
}
}
5 changes: 5 additions & 0 deletions src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
#nullable enable
StackExchange.Redis.IServer.GetProductVariant(out string! version) -> StackExchange.Redis.ProductVariant
StackExchange.Redis.ProductVariant
StackExchange.Redis.ProductVariant.Garnet = 2 -> StackExchange.Redis.ProductVariant
StackExchange.Redis.ProductVariant.Redis = 0 -> StackExchange.Redis.ProductVariant
StackExchange.Redis.ProductVariant.Valkey = 1 -> StackExchange.Redis.ProductVariant
2 changes: 2 additions & 0 deletions src/StackExchange.Redis/RedisServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public bool AllowReplicaWrites

public ServerType ServerType => server.ServerType;

public ProductVariant GetProductVariant(out string version) => server.GetProductVariant(out version);

public Version Version => server.Version;

public void ClientKill(EndPoint endpoint, CommandFlags flags = CommandFlags.None)
Expand Down
Loading
Loading