From 509af0a3d025b19ef52bc6c8e38e6fb185a89dde Mon Sep 17 00:00:00 2001 From: slorello89 <steve.lorello@redis.com> Date: Mon, 15 Jul 2024 16:22:09 -0400 Subject: [PATCH] specifying keys in read/write commands --- src/Redis.OM/RedisCommands.cs | 39 ++++++++++--------- test/Redis.OM.Unit.Tests/CoreTests.cs | 20 ++++++++++ .../RediSearchTests/SearchTests.cs | 22 +++++------ .../VectorTests/VectorTests.cs | 5 ++- 4 files changed, 54 insertions(+), 32 deletions(-) diff --git a/src/Redis.OM/RedisCommands.cs b/src/Redis.OM/RedisCommands.cs index 8398ca93..96e9ea2a 100644 --- a/src/Redis.OM/RedisCommands.cs +++ b/src/Redis.OM/RedisCommands.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Redis.OM.Contracts; using Redis.OM.Modeling; +using StackExchange.Redis; namespace Redis.OM { @@ -84,7 +85,7 @@ public static async Task<string> SetAsync(this IRedisConnection connection, obje /// <returns>How many new fields were created.</returns> public static async Task<int> HSetAsync(this IRedisConnection connection, string key, params KeyValuePair<string, object>[] fieldValues) { - var args = new List<object> { key }; + var args = new List<object> { new RedisKey(key) }; foreach (var kvp in fieldValues) { args.Add(kvp.Key); @@ -104,7 +105,7 @@ public static async Task<int> HSetAsync(this IRedisConnection connection, string /// <returns>How many new fields were created.</returns> public static async Task<int> HSetAsync(this IRedisConnection connection, string key, TimeSpan timeSpan, params KeyValuePair<string, object>[] fieldValues) { - var args = new List<object> { key }; + var args = new List<object> { new RedisKey(key) }; foreach (var kvp in fieldValues) { args.Add(kvp.Key); @@ -124,7 +125,7 @@ public static async Task<int> HSetAsync(this IRedisConnection connection, string /// <returns>whether the operation succeeded.</returns> public static async Task<bool> JsonSetAsync(this IRedisConnection connection, string key, string path, string json) { - var result = await connection.ExecuteAsync("JSON.SET", key, path, json); + var result = await connection.ExecuteAsync("JSON.SET", new RedisKey(key), path, json); return result == "OK"; } @@ -139,7 +140,7 @@ public static async Task<bool> JsonSetAsync(this IRedisConnection connection, st public static async Task<bool> JsonSetAsync(this IRedisConnection connection, string key, string path, object obj) { var json = JsonSerializer.Serialize(obj, RedisSerializationSettings.JsonSerializerOptions); - var result = await connection.ExecuteAsync("JSON.SET", key, path, json); + var result = await connection.ExecuteAsync("JSON.SET", new RedisKey(key), path, json); return result == "OK"; } @@ -154,7 +155,7 @@ public static async Task<bool> JsonSetAsync(this IRedisConnection connection, st /// <returns>whether the operation succeeded.</returns> public static async Task<bool> JsonSetAsync(this IRedisConnection connection, string key, string path, string json, TimeSpan timeSpan) { - var args = new[] { key, path, json }; + var args = new object[] { new RedisKey(key), path, json }; return (await connection.SendCommandWithExpiryAsync("JSON.SET", args, key, timeSpan)).First() == "OK"; } @@ -227,7 +228,7 @@ public static async Task<bool> JsonSetAsync(this IRedisConnection connection, st public static int HSet(this IRedisConnection connection, string key, TimeSpan timeSpan, params KeyValuePair<string, object>[] fieldValues) { var args = new List<object>(); - args.Add(key); + args.Add(new RedisKey(key)); foreach (var kvp in fieldValues) { args.Add(kvp.Key); @@ -246,7 +247,7 @@ public static int HSet(this IRedisConnection connection, string key, TimeSpan ti /// <returns>How many new fields were created.</returns> public static int HSet(this IRedisConnection connection, string key, params KeyValuePair<string, object>[] fieldValues) { - var args = new List<object> { key }; + var args = new List<object> { new RedisKey(key) }; foreach (var kvp in fieldValues) { args.Add(kvp.Key); @@ -266,7 +267,7 @@ public static int HSet(this IRedisConnection connection, string key, params KeyV /// <returns>whether the operation succeeded.</returns> public static bool JsonSet(this IRedisConnection connection, string key, string path, string json) { - var result = connection.Execute("JSON.SET", key, path, json); + var result = connection.Execute("JSON.SET", new RedisKey(key), path, json); return result == "OK"; } @@ -281,7 +282,7 @@ public static bool JsonSet(this IRedisConnection connection, string key, string public static bool JsonSet(this IRedisConnection connection, string key, string path, object obj) { var json = JsonSerializer.Serialize(obj, RedisSerializationSettings.JsonSerializerOptions); - var result = connection.Execute("JSON.SET", key, path, json); + var result = connection.Execute("JSON.SET", new RedisKey(key), path, json); return result == "OK"; } @@ -296,7 +297,7 @@ public static bool JsonSet(this IRedisConnection connection, string key, string /// <returns>whether the operation succeeded.</returns> public static bool JsonSet(this IRedisConnection connection, string key, string path, string json, TimeSpan timeSpan) { - var arr = new[] { key, path, json }; + var arr = new object[] { new RedisKey(key), path, json }; return connection.SendCommandWithExpiry("JSON.SET", arr, key, timeSpan).First() == "OK"; } @@ -570,7 +571,7 @@ public static string Set(this IRedisConnection connection, object obj, TimeSpan /// <returns>the object pulled out of redis.</returns> public static T? JsonGet<T>(this IRedisConnection connection, string key, params string[] paths) { - var args = new List<string> { key }; + var args = new List<object> { new RedisKey(key) }; args.AddRange(paths); var res = (string)connection.Execute("JSON.GET", args.ToArray()); return !string.IsNullOrEmpty(res) ? JsonSerializer.Deserialize<T>(res, RedisSerializationSettings.JsonSerializerOptions) : default; @@ -586,7 +587,7 @@ public static string Set(this IRedisConnection connection, object obj, TimeSpan /// <returns>the object pulled out of redis.</returns> public static async Task<T?> JsonGetAsync<T>(this IRedisConnection connection, string key, params string[] paths) { - var args = new List<string> { key }; + var args = new List<object> { new RedisKey(key) }; args.AddRange(paths); var res = (string)await connection.ExecuteAsync("JSON.GET", args.ToArray()); return !string.IsNullOrEmpty(res) ? JsonSerializer.Deserialize<T>(res, RedisSerializationSettings.JsonSerializerOptions) : default; @@ -601,7 +602,7 @@ public static string Set(this IRedisConnection connection, object obj, TimeSpan public static IDictionary<string, RedisReply> HGetAll(this IRedisConnection connection, string keyName) { var ret = new Dictionary<string, RedisReply>(); - var res = connection.Execute("HGETALL", keyName).ToArray(); + var res = connection.Execute("HGETALL", new RedisKey(keyName)).ToArray(); for (var i = 0; i < res.Length; i += 2) { ret.Add(res[i], res[i + 1]); @@ -619,7 +620,7 @@ public static IDictionary<string, RedisReply> HGetAll(this IRedisConnection conn public static async Task<IDictionary<string, RedisReply>> HGetAllAsync(this IRedisConnection connection, string keyName) { var ret = new Dictionary<string, RedisReply>(); - var res = (await connection.ExecuteAsync("HGETALL", keyName)).ToArray(); + var res = (await connection.ExecuteAsync("HGETALL", new RedisKey(keyName))).ToArray(); for (var i = 0; i < res.Length; i += 2) { ret.Add(res[i], res[i + 1]); @@ -664,7 +665,7 @@ public static async Task<IDictionary<string, RedisReply>> HGetAllAsync(this IRed sha, keys.Count().ToString(), }; - args.AddRange(keys); + args.AddRange(keys.Select(x => new RedisKey(x)).Cast<object>()); args.AddRange(argv); try { @@ -756,7 +757,7 @@ public static async Task<IDictionary<string, RedisReply>> HGetAllAsync(this IRed /// <param name="connection">the connection.</param> /// <param name="key">the key to unlink.</param> /// <returns>the status.</returns> - public static async Task<long> UnlinkAsync(this IRedisConnection connection, string key) => await connection.ExecuteAsync("UNLINK", key); + public static async Task<long> UnlinkAsync(this IRedisConnection connection, string key) => await connection.ExecuteAsync("UNLINK", new RedisKey(key)); /// <summary> /// Unlinks array of keys. @@ -764,7 +765,7 @@ public static async Task<IDictionary<string, RedisReply>> HGetAllAsync(this IRed /// <param name="connection">the connection.</param> /// <param name="keys">the keys to unlink.</param> /// <returns>the status.</returns> - public static async Task<long> UnlinkAsync(this IRedisConnection connection, string[] keys) => await connection.ExecuteAsync("UNLINK", keys); + public static async Task<long> UnlinkAsync(this IRedisConnection connection, string[] keys) => await connection.ExecuteAsync("UNLINK", keys.Select(x => new RedisKey(x)).Cast<object>().ToArray()); /// <summary> /// Unlinks the key and then adds an updated value of it. @@ -835,7 +836,7 @@ private static RedisReply[] SendCommandWithExpiry( TimeSpan ts) { var commandTuple = Tuple.Create(command, args); - var expireTuple = Tuple.Create("PEXPIRE", new object[] { keyToExpire, ((long)ts.TotalMilliseconds).ToString(CultureInfo.InvariantCulture) }); + var expireTuple = Tuple.Create("PEXPIRE", new object[] { new RedisKey(keyToExpire), ((long)ts.TotalMilliseconds).ToString(CultureInfo.InvariantCulture) }); return connection.ExecuteInTransaction(new[] { commandTuple, expireTuple }); } @@ -847,7 +848,7 @@ private static Task<RedisReply[]> SendCommandWithExpiryAsync( TimeSpan ts) { var commandTuple = Tuple.Create(command, args); - var expireTuple = Tuple.Create("PEXPIRE", new object[] { keyToExpire, ((long)ts.TotalMilliseconds).ToString(CultureInfo.InvariantCulture) }); + var expireTuple = Tuple.Create("PEXPIRE", new object[] { new RedisKey(keyToExpire), ((long)ts.TotalMilliseconds).ToString(CultureInfo.InvariantCulture) }); return connection.ExecuteInTransactionAsync(new[] { commandTuple, expireTuple }); } } diff --git a/test/Redis.OM.Unit.Tests/CoreTests.cs b/test/Redis.OM.Unit.Tests/CoreTests.cs index df927388..12fec419 100644 --- a/test/Redis.OM.Unit.Tests/CoreTests.cs +++ b/test/Redis.OM.Unit.Tests/CoreTests.cs @@ -493,5 +493,25 @@ public void TestUnlink() connection.Execute("SET", key1, "bar"); Assert.Equal(1,connection.Unlink(new []{key1, key2})); } + + [SkipIfMissingEnvVar("CLUSTER_HOST_PORT")] + public async Task TestClusterOperations() + { + var hostInfo = System.Environment.GetEnvironmentVariable("CLUSTER_HOST_PORT"); + Console.WriteLine($"Current host info: {hostInfo}"); + var connectionString = $"redis://{hostInfo}"; + var provider = new RedisConnectionProvider(connectionString); + var connection = provider.Connection; + + var tasks = new List<Task>(); + for(var i = 0; i < 10_000; i++) + { + var person = new Person(); + tasks.Add(connection.SetAsync(person)); + } + + await Task.WhenAll(tasks); + } + } } \ No newline at end of file diff --git a/test/Redis.OM.Unit.Tests/RediSearchTests/SearchTests.cs b/test/Redis.OM.Unit.Tests/RediSearchTests/SearchTests.cs index ca203819..4449584f 100644 --- a/test/Redis.OM.Unit.Tests/RediSearchTests/SearchTests.cs +++ b/test/Redis.OM.Unit.Tests/RediSearchTests/SearchTests.cs @@ -992,7 +992,7 @@ public async Task TestUpdateJson() var steve = await collection.FirstAsync(x => x.Name == "Steve"); steve.Age = 33; await collection.UpdateAsync(steve); - await _substitute.Received().ExecuteAsync("EVALSHA", Arg.Any<string>(), "1", "Redis.OM.Unit.Tests.RediSearchTests.Person:01FVN836BNQGYMT80V7RCVY73N", "SET", "$.Age", "33"); + await _substitute.Received().ExecuteAsync("EVALSHA", Arg.Any<string>(), "1", new RedisKey("Redis.OM.Unit.Tests.RediSearchTests.Person:01FVN836BNQGYMT80V7RCVY73N"), "SET", "$.Age", "33"); Scripts.ShaCollection.Clear(); } @@ -1010,8 +1010,8 @@ public async Task TestUpdateJsonUnloadedScriptAsync() var steve = await collection.FirstAsync(x => x.Name == "Steve"); steve.Age = 33; await collection.UpdateAsync(steve); - await _substitute.Received().ExecuteAsync("EVALSHA", Arg.Any<string>(), "1", "Redis.OM.Unit.Tests.RediSearchTests.Person:01FVN836BNQGYMT80V7RCVY73N", "SET", "$.Age", "33"); - await _substitute.Received().ExecuteAsync("EVAL", Scripts.JsonDiffResolution, "1", "Redis.OM.Unit.Tests.RediSearchTests.Person:01FVN836BNQGYMT80V7RCVY73N", "SET", "$.Age", "33"); + await _substitute.Received().ExecuteAsync("EVALSHA", Arg.Any<string>(), "1", new RedisKey("Redis.OM.Unit.Tests.RediSearchTests.Person:01FVN836BNQGYMT80V7RCVY73N"), "SET", "$.Age", "33"); + await _substitute.Received().ExecuteAsync("EVAL", Scripts.JsonDiffResolution, "1", new RedisKey("Redis.OM.Unit.Tests.RediSearchTests.Person:01FVN836BNQGYMT80V7RCVY73N"), "SET", "$.Age", "33"); Scripts.ShaCollection.Clear(); } @@ -1028,8 +1028,8 @@ public void TestUpdateJsonUnloadedScript() var steve = collection.First(x => x.Name == "Steve"); steve.Age = 33; collection.Update(steve); - _substitute.Received().Execute("EVALSHA", Arg.Any<string>(), "1", "Redis.OM.Unit.Tests.RediSearchTests.Person:01FVN836BNQGYMT80V7RCVY73N", "SET", "$.Age", "33"); - _substitute.Received().Execute("EVAL", Scripts.JsonDiffResolution, "1", "Redis.OM.Unit.Tests.RediSearchTests.Person:01FVN836BNQGYMT80V7RCVY73N", "SET", "$.Age", "33"); + _substitute.Received().Execute("EVALSHA", Arg.Any<string>(), "1", new RedisKey("Redis.OM.Unit.Tests.RediSearchTests.Person:01FVN836BNQGYMT80V7RCVY73N"), "SET", "$.Age", "33"); + _substitute.Received().Execute("EVAL", Scripts.JsonDiffResolution, "1", new RedisKey("Redis.OM.Unit.Tests.RediSearchTests.Person:01FVN836BNQGYMT80V7RCVY73N"), "SET", "$.Age", "33"); Scripts.ShaCollection.Clear(); } @@ -1043,7 +1043,7 @@ public async Task TestUpdateJsonName() var steve = await collection.FirstAsync(x => x.Name == "Steve"); steve.Name = "Bob"; await collection.UpdateAsync(steve); - await _substitute.Received().ExecuteAsync("EVALSHA", Arg.Any<string>(), "1", "Redis.OM.Unit.Tests.RediSearchTests.Person:01FVN836BNQGYMT80V7RCVY73N", "SET", "$.Name", "\"Bob\""); + await _substitute.Received().ExecuteAsync("EVALSHA", Arg.Any<string>(), "1", new RedisKey("Redis.OM.Unit.Tests.RediSearchTests.Person:01FVN836BNQGYMT80V7RCVY73N"), "SET", "$.Name", "\"Bob\""); Scripts.ShaCollection.Clear(); } @@ -1058,12 +1058,12 @@ public async Task TestUpdateJsonNestedObject() steve.Address = new Address { State = "Florida" }; await collection.UpdateAsync(steve); var expected = $"{{{Environment.NewLine} \"State\": \"Florida\"{Environment.NewLine}}}"; - await _substitute.Received().ExecuteAsync("EVALSHA", Arg.Any<string>(), "1", "Redis.OM.Unit.Tests.RediSearchTests.Person:01FVN836BNQGYMT80V7RCVY73N", "SET", "$.Address", expected); + await _substitute.Received().ExecuteAsync("EVALSHA", Arg.Any<string>(), "1", new RedisKey("Redis.OM.Unit.Tests.RediSearchTests.Person:01FVN836BNQGYMT80V7RCVY73N"), "SET", "$.Address", expected); steve.Address.City = "Satellite Beach"; await collection.UpdateAsync(steve); expected = "\"Satellite Beach\""; - await _substitute.Received().ExecuteAsync("EVALSHA", Arg.Any<string>(), "1", "Redis.OM.Unit.Tests.RediSearchTests.Person:01FVN836BNQGYMT80V7RCVY73N", "SET", "$.Address.City", expected); + await _substitute.Received().ExecuteAsync("EVALSHA", Arg.Any<string>(), "1", new RedisKey("Redis.OM.Unit.Tests.RediSearchTests.Person:01FVN836BNQGYMT80V7RCVY73N"), "SET", "$.Address.City", expected); Scripts.ShaCollection.Clear(); } @@ -1079,7 +1079,7 @@ public async Task TestUpdateJsonWithDouble() steve.Age = 33; steve.Height = 71.5; await collection.UpdateAsync(steve); - await _substitute.Received().ExecuteAsync("EVALSHA", Arg.Any<string>(), "1", "Redis.OM.Unit.Tests.RediSearchTests.Person:01FVN836BNQGYMT80V7RCVY73N", "SET", "$.Age", "33", "SET", "$.Height", "71.5"); + await _substitute.Received().ExecuteAsync("EVALSHA", Arg.Any<string>(), "1", new RedisKey("Redis.OM.Unit.Tests.RediSearchTests.Person:01FVN836BNQGYMT80V7RCVY73N"), "SET", "$.Age", "33", "SET", "$.Height", "71.5"); Scripts.ShaCollection.Clear(); } @@ -1103,7 +1103,7 @@ public async Task TestDeleteAsync() Assert.True(collection.StateManager.Data.ContainsKey(key)); Assert.True(collection.StateManager.Snapshot.ContainsKey(key)); await collection.DeleteAsync(steve); - await _substitute.Received().ExecuteAsync("UNLINK", key); + await _substitute.Received().ExecuteAsync("UNLINK", new RedisKey(key)); Assert.False(collection.StateManager.Data.ContainsKey(key)); Assert.False(collection.StateManager.Snapshot.ContainsKey(key)); } @@ -1119,7 +1119,7 @@ public void TestDelete() Assert.True(collection.StateManager.Data.ContainsKey(key)); Assert.True(collection.StateManager.Snapshot.ContainsKey(key)); collection.Delete(steve); - _substitute.Received().Execute("UNLINK", key); + _substitute.Received().Execute("UNLINK", new RedisKey(key)); Assert.False(collection.StateManager.Data.ContainsKey(key)); Assert.False(collection.StateManager.Snapshot.ContainsKey(key)); } diff --git a/test/Redis.OM.Unit.Tests/RediSearchTests/VectorTests/VectorTests.cs b/test/Redis.OM.Unit.Tests/RediSearchTests/VectorTests/VectorTests.cs index 66016e60..5c033463 100644 --- a/test/Redis.OM.Unit.Tests/RediSearchTests/VectorTests/VectorTests.cs +++ b/test/Redis.OM.Unit.Tests/RediSearchTests/VectorTests/VectorTests.cs @@ -8,6 +8,7 @@ using Redis.OM.Modeling; using Redis.OM.Modeling.Vectors; using Redis.OM.Searching; +using StackExchange.Redis; using Xunit; namespace Redis.OM.Unit.Tests; @@ -189,9 +190,9 @@ public void InsertVectors() _substitute.Execute("JSON.SET", Arg.Any<object[]>()).Returns(new RedisReply("OK")); _substitute.Set(hashObj); _substitute.Set(jsonObj); - _substitute.Received().Execute("HSET", "Redis.OM.Unit.Tests.ObjectWithVectorHash:foo", "Id", "foo", "Num", "0", "SimpleHnswVector", + _substitute.Received().Execute("HSET", new RedisKey("Redis.OM.Unit.Tests.ObjectWithVectorHash:foo"), "Id", "foo", "Num", "0", "SimpleHnswVector", Arg.Is<byte[]>(x=>x.SequenceEqual(simpleHnswBytes)), "SimpleVectorizedVector.Vector", Arg.Is<byte[]>(x=>x.SequenceEqual(flatVectorizedBytes)), "SimpleVectorizedVector.Value", "\"foobar\""); - _substitute.Received().Execute("JSON.SET", "Redis.OM.Unit.Tests.ObjectWithVector:foo", ".", json); + _substitute.Received().Execute("JSON.SET", new RedisKey("Redis.OM.Unit.Tests.ObjectWithVector:foo"), ".", json); var deseralized = JsonSerializer.Deserialize<ObjectWithVector>(json); Assert.Equal("foobar", deseralized.SimpleVectorizedVector.Value); }