Skip to content

Commit aa6f6a4

Browse files
authored
Supporting CLIENT identification via SETINFO (#180)
* deprecated redisGraph * supporting client SetInfo command * add tests * check somthing * check2 * Async * try ConnectionMultiplexer.Connect("localhost"); * new format * fix core tests * try change to IDatabase instead of IDatabaseAsync * add bool _setinfo to Auxiliary * comment CoreTests * fix Aux * change test * fix tests * not using pipeline * move _setInfo = false; * delete unused key * if redis version less than 7.1.242 dont sent SETINFO * add sleep * no sleep * try setinfo to true in each test * reset info defaults in each test * skip cluster + send commands in pipeline * delete comments * add null test * compare and of strings in null test * fixes
1 parent da200b6 commit aa6f6a4

File tree

9 files changed

+311
-3
lines changed

9 files changed

+311
-3
lines changed

src/NRedisStack/Auxiliary.cs

+69-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
1+
using System.Xml.Linq;
2+
using NRedisStack.Core;
13
using NRedisStack.RedisStackCommands;
24
using StackExchange.Redis;
35

46
namespace NRedisStack
57
{
68
public static class Auxiliary
79
{
10+
private static string? _libraryName = $"NRedisStack;.NET-{Environment.Version}";
11+
private static bool _setInfo = true;
12+
public static void ResetInfoDefaults()
13+
{
14+
_setInfo = true;
15+
_libraryName = $"NRedisStack;.NET-{Environment.Version}";
16+
}
817
public static List<object> MergeArgs(RedisKey key, params RedisValue[] items)
918
{
1019
var args = new List<object>(items.Length + 1) { key };
@@ -26,20 +35,57 @@ public static object[] AssembleNonNullArguments(params object?[] arguments)
2635
return args.ToArray();
2736
}
2837

38+
// public static IDatabase GetDatabase(this ConnectionMultiplexer redis) => redis.GetDatabase("", "");
39+
40+
// TODO: add all the signatures of GetDatabase
41+
public static IDatabase GetDatabase(this ConnectionMultiplexer redis,
42+
string? LibraryName)
43+
{
44+
var _db = redis.GetDatabase();
45+
if (LibraryName == null) // the user wants to disable the library name and version sending
46+
_setInfo = false;
47+
48+
else // the user set his own the library name
49+
_libraryName = $"NRedisStack({LibraryName});.NET-{Environment.Version})";
50+
51+
return _db;
52+
}
53+
54+
private static void SetInfoInPipeline(this IDatabase db)
55+
{
56+
if (_libraryName == null) return;
57+
Pipeline pipeline = new Pipeline(db);
58+
_ = pipeline.Db.ClientSetInfoAsync(SetInfoAttr.LibraryName, _libraryName!);
59+
_ = pipeline.Db.ClientSetInfoAsync(SetInfoAttr.LibraryVersion, GetNRedisStackVersion());
60+
pipeline.Execute();
61+
}
62+
2963
public static RedisResult Execute(this IDatabase db, SerializedCommand command)
3064
{
65+
var compareVersions = db.Multiplexer.GetServer(db.Multiplexer.GetEndPoints()[0]).Version.CompareTo(new Version(7, 1, 242));
66+
if (_setInfo && compareVersions >= 0)
67+
{
68+
_setInfo = false;
69+
db.SetInfoInPipeline();
70+
}
3171
return db.Execute(command.Command, command.Args);
3272
}
3373

3474
public async static Task<RedisResult> ExecuteAsync(this IDatabaseAsync db, SerializedCommand command)
3575
{
76+
var compareVersions = db.Multiplexer.GetServer(db.Multiplexer.GetEndPoints()[0]).Version.CompareTo(new Version(7, 1, 242));
77+
if (_setInfo && compareVersions >= 0)
78+
{
79+
_setInfo = false;
80+
((IDatabase)db).SetInfoInPipeline();
81+
}
3682
return await db.ExecuteAsync(command.Command, command.Args);
3783
}
3884

39-
public static List<RedisResult> ExecuteBroadcast(this IDatabaseAsync db, string command)
85+
public static List<RedisResult> ExecuteBroadcast(this IDatabase db, string command)
4086
=> db.ExecuteBroadcast(new SerializedCommand(command));
4187

42-
public static List<RedisResult> ExecuteBroadcast(this IDatabaseAsync db, SerializedCommand command)
88+
public static List<RedisResult> ExecuteBroadcast(this IDatabase db, SerializedCommand command)
4389
{
4490
var redis = db.Multiplexer;
4591
var endpoints = redis.GetEndPoints();
@@ -83,5 +129,26 @@ public async static Task<List<RedisResult>> ExecuteBroadcastAsync(this IDatabase
83129
}
84130
return results;
85131
}
132+
133+
public static string GetNRedisStackVersion()
134+
{
135+
XDocument csprojDocument = GetCsprojDocument();
136+
137+
// Find the Version element and get its value.
138+
var versionElement = csprojDocument.Root!
139+
.Descendants("Version")
140+
.FirstOrDefault();
141+
142+
return versionElement!.Value;
143+
}
144+
145+
private static XDocument GetCsprojDocument()
146+
{
147+
string csprojFilePath = Path.Combine(Directory.GetCurrentDirectory(), "..", "..", "..", "..", "..", "src", "NRedisStack", "NRedisStack.csproj");
148+
149+
// Load the .csproj file.
150+
var csprojDocument = XDocument.Load(csprojFilePath);
151+
return csprojDocument;
152+
}
86153
}
87154
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using NRedisStack.RedisStackCommands;
2+
using NRedisStack.Core.Literals;
3+
using NRedisStack.Core;
4+
5+
namespace NRedisStack
6+
{
7+
8+
public static class CoreCommandBuilder
9+
{
10+
public static SerializedCommand ClientSetInfo(SetInfoAttr attr, string value)
11+
{
12+
string attrValue = attr switch
13+
{
14+
SetInfoAttr.LibraryName => CoreArgs.lib_name,
15+
SetInfoAttr.LibraryVersion => CoreArgs.lib_ver,
16+
_ => throw new System.NotImplementedException(),
17+
};
18+
19+
return new SerializedCommand(RedisCoreCommands.CLIENT, RedisCoreCommands.SETINFO, attrValue, value);
20+
}
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using NRedisStack.Core;
2+
using StackExchange.Redis;
3+
namespace NRedisStack
4+
{
5+
6+
public static class CoreCommands
7+
{
8+
/// <summary>
9+
/// Sets information specific to the client or connection.
10+
/// </summary>
11+
/// <param name="attr">which attribute to set</param>
12+
/// <param name="value">the attribute value</param>
13+
/// <returns><see langword="true"/> if the attribute name was successfully set, Error otherwise.</returns>
14+
/// <remarks><seealso href="https://redis.io/commands/client-setinfo/"/></remarks>
15+
public static bool ClientSetInfo(this IDatabase db, SetInfoAttr attr, string value)
16+
{
17+
return db.Execute(CoreCommandBuilder.ClientSetInfo(attr, value)).OKtoBoolean();
18+
}
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using NRedisStack.Core;
2+
using StackExchange.Redis;
3+
namespace NRedisStack
4+
{
5+
6+
public static class CoreCommandsAsync //: ICoreCommandsAsync
7+
{
8+
/// <summary>
9+
/// Sets information specific to the client or connection.
10+
/// </summary>
11+
/// <param name="attr">which attribute to set</param>
12+
/// <param name="value">the attribute value</param>
13+
/// <returns><see langword="true"/> if the attribute name was successfully set, Error otherwise.</returns>
14+
/// <remarks><seealso href="https://redis.io/commands/client-setinfo/"/></remarks>
15+
public static async Task<bool> ClientSetInfoAsync(this IDatabaseAsync db, SetInfoAttr attr, string value)
16+
{
17+
return (await db.ExecuteAsync(CoreCommandBuilder.ClientSetInfo(attr, value))).OKtoBoolean();
18+
}
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace NRedisStack.Core;
2+
public enum SetInfoAttr
3+
{
4+
/// <summary>
5+
/// meant to hold the name of the client library that's in use.
6+
/// </summary>
7+
LibraryName,
8+
/// <summary>
9+
/// meant to hold the client library's version.
10+
/// </summary>
11+
LibraryVersion
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace NRedisStack.Core.Literals
2+
{
3+
internal class CoreArgs
4+
{
5+
public const string lib_name = "LIB-NAME";
6+
public const string lib_ver = "LIB-VER";
7+
}
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace NRedisStack.Core.Literals
2+
{
3+
/// <summary>
4+
/// Redis Core command literals
5+
/// </summary>
6+
internal class RedisCoreCommands
7+
{
8+
public const string CLIENT = "CLIENT";
9+
public const string SETINFO = "SETINFO";
10+
}
11+
}

src/NRedisStack/Graph/IGraphCommands.cs

-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
namespace NRedisStack
55
{
6-
76
[Obsolete("RedisGraph support is deprecated as of Redis Stack 7.2 (https://redis.com/blog/redisgraph-eol/)")]
87
public interface IGraphCommands
98
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
using Xunit;
2+
using NRedisStack.Core;
3+
using NRedisStack;
4+
using static NRedisStack.Auxiliary;
5+
using StackExchange.Redis;
6+
using System.Xml.Linq;
7+
using System.Reflection;
8+
using NRedisStack.RedisStackCommands;
9+
10+
11+
namespace NRedisStack.Tests.Core;
12+
13+
public class CoreTests : AbstractNRedisStackTest, IDisposable
14+
{
15+
public CoreTests(RedisFixture redisFixture) : base(redisFixture) { }
16+
17+
18+
[SkipIfRedis(Is.OSSCluster, Comparison.LessThan, "7.1.242")]
19+
public void TestSimpleSetInfo()
20+
{
21+
var redis = ConnectionMultiplexer.Connect("localhost");
22+
var db = redis.GetDatabase();
23+
db.Execute("FLUSHALL");
24+
25+
db.ClientSetInfo(SetInfoAttr.LibraryName, "TestLibraryName");
26+
db.ClientSetInfo(SetInfoAttr.LibraryVersion, "1.2.3");
27+
28+
var info = db.Execute("CLIENT", "INFO").ToString();
29+
Assert.EndsWith($"lib-name=TestLibraryName lib-ver=1.2.3\n", info);
30+
}
31+
32+
[SkipIfRedis(Is.OSSCluster, Comparison.LessThan, "7.1.242")]
33+
public async Task TestSimpleSetInfoAsync()
34+
{
35+
var redis = ConnectionMultiplexer.Connect("localhost");
36+
var db = redis.GetDatabase();
37+
db.Execute("FLUSHALL");
38+
39+
await db.ClientSetInfoAsync(SetInfoAttr.LibraryName, "TestLibraryName");
40+
await db.ClientSetInfoAsync(SetInfoAttr.LibraryVersion, "1.2.3");
41+
42+
var info = db.Execute("CLIENT", "INFO").ToString();
43+
Assert.EndsWith($"lib-name=TestLibraryName lib-ver=1.2.3\n", info);
44+
}
45+
46+
[SkipIfRedis(Is.OSSCluster, Comparison.LessThan, "7.1.242")]
47+
public void TestSetInfoDefaultValue()
48+
{
49+
ResetInfoDefaults(); // demonstrate first connection
50+
var redis = ConnectionMultiplexer.Connect("localhost");
51+
var db = redis.GetDatabase();
52+
db.Execute("FLUSHALL");
53+
54+
db.Execute(new SerializedCommand("PING")); // only the extension method of Execute (which is used for all the commands of Redis Stack) will set the library name and version.
55+
56+
var info = db.Execute("CLIENT", "INFO").ToString();
57+
Assert.EndsWith($"lib-name=NRedisStack;.NET-{Environment.Version} lib-ver={GetNRedisStackVersion()}\n", info);
58+
}
59+
60+
[SkipIfRedis(Is.OSSCluster, Comparison.LessThan, "7.1.242")]
61+
public async Task TestSetInfoDefaultValueAsync()
62+
{
63+
ResetInfoDefaults(); // demonstrate first connection
64+
var redis = ConnectionMultiplexer.Connect("localhost");
65+
var db = redis.GetDatabase();
66+
db.Execute("FLUSHALL");
67+
68+
await db.ExecuteAsync(new SerializedCommand("PING")); // only the extension method of Execute (which is used for all the commands of Redis Stack) will set the library name and version.
69+
70+
var info = (await db.ExecuteAsync("CLIENT", "INFO")).ToString();
71+
Assert.EndsWith($"lib-name=NRedisStack;.NET-{Environment.Version} lib-ver={GetNRedisStackVersion()}\n", info);
72+
}
73+
74+
[SkipIfRedis(Is.OSSCluster, Comparison.LessThan, "7.1.242")]
75+
public void TestSetInfoWithValue()
76+
{
77+
ResetInfoDefaults(); // demonstrate first connection
78+
var redis = ConnectionMultiplexer.Connect("localhost");
79+
var db = redis.GetDatabase("MyLibraryName;v1.0.0");
80+
db.Execute("FLUSHALL");
81+
82+
db.Execute(new SerializedCommand("PING")); // only the extension method of Execute (which is used for all the commands of Redis Stack) will set the library name and version.
83+
84+
var info = db.Execute("CLIENT", "INFO").ToString();
85+
Assert.EndsWith($"NRedisStack(MyLibraryName;v1.0.0);.NET-{Environment.Version}) lib-ver={GetNRedisStackVersion()}\n", info);
86+
}
87+
88+
[SkipIfRedis(Is.OSSCluster, Comparison.LessThan, "7.1.242")]
89+
public async Task TestSetInfoWithValueAsync()
90+
{
91+
ResetInfoDefaults(); // demonstrate first connection
92+
var redis = ConnectionMultiplexer.Connect("localhost");
93+
var db = redis.GetDatabase("MyLibraryName;v1.0.0");
94+
db.Execute("FLUSHALL");
95+
96+
await db.ExecuteAsync(new SerializedCommand("PING")); // only the extension method of Execute (which is used for all the commands of Redis Stack) will set the library name and version.
97+
98+
var info = (await db.ExecuteAsync("CLIENT", "INFO")).ToString();
99+
Assert.EndsWith($"NRedisStack(MyLibraryName;v1.0.0);.NET-{Environment.Version}) lib-ver={GetNRedisStackVersion()}\n", info);
100+
}
101+
102+
[SkipIfRedis(Is.OSSCluster, Comparison.LessThan, "7.1.242")]
103+
public void TestSetInfoNull()
104+
{
105+
ResetInfoDefaults(); // demonstrate first connection
106+
var redis = ConnectionMultiplexer.Connect("localhost");
107+
var db = redis.GetDatabase(null);
108+
109+
db.Execute("FLUSHALL");
110+
var infoBefore = db.Execute("CLIENT", "INFO").ToString();
111+
db.Execute(new SerializedCommand("PING")); // only the extension method of Execute (which is used for all the commands of Redis Stack) will set the library name and version.
112+
113+
var infoAfter = db.Execute("CLIENT", "INFO").ToString();
114+
// Find the indices of "lib-name=" in the strings
115+
int infoAfterLibNameIndex = infoAfter!.IndexOf("lib-name=");
116+
int infoBeforeLibNameIndex = infoBefore!.IndexOf("lib-name=");
117+
118+
// Extract the sub-strings starting from "lib-name="
119+
string infoAfterLibNameToEnd = infoAfter.Substring(infoAfterLibNameIndex);
120+
string infoBeforeLibNameToEnd = infoBefore.Substring(infoBeforeLibNameIndex);
121+
122+
// Assert that the extracted sub-strings are equal
123+
Assert.Equal(infoAfterLibNameToEnd, infoBeforeLibNameToEnd);
124+
}
125+
126+
[SkipIfRedis(Is.OSSCluster, Comparison.LessThan, "7.1.242")]
127+
public async Task TestSetInfoNullAsync()
128+
{
129+
ResetInfoDefaults(); // demonstrate first connection
130+
var redis = ConnectionMultiplexer.Connect("localhost");
131+
var db = redis.GetDatabase(null);
132+
133+
db.Execute("FLUSHALL");
134+
var infoBefore = (await db.ExecuteAsync("CLIENT", "INFO")).ToString();
135+
await db.ExecuteAsync(new SerializedCommand("PING")); // only the extension method of Execute (which is used for all the commands of Redis Stack) will set the library name and version.
136+
137+
var infoAfter = (await db.ExecuteAsync("CLIENT", "INFO")).ToString();
138+
// Find the indices of "lib-name=" in the strings
139+
int infoAfterLibNameIndex = infoAfter!.IndexOf("lib-name=");
140+
int infoBeforeLibNameIndex = infoBefore!.IndexOf("lib-name=");
141+
142+
// Extract the sub-strings starting from "lib-name="
143+
string infoAfterLibNameToEnd = infoAfter.Substring(infoAfterLibNameIndex);
144+
string infoBeforeLibNameToEnd = infoBefore.Substring(infoBeforeLibNameIndex);
145+
146+
// Assert that the extracted sub-strings are equal
147+
Assert.Equal(infoAfterLibNameToEnd, infoBeforeLibNameToEnd);
148+
}
149+
}

0 commit comments

Comments
 (0)