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);
     }