From 83000361f162ddff163eb100944e9c900521295b Mon Sep 17 00:00:00 2001 From: Stabzs Date: Thu, 18 Aug 2022 16:05:36 -0600 Subject: [PATCH] Initial POC commit for adding ReadOnlyMemory overloads to StringSet operations. --- .../Interfaces/IDatabase.cs | 14 +++++++++++ .../Interfaces/IDatabaseAsync.cs | 14 +++++++++++ .../KeyspaceIsolation/DatabaseWrapper.cs | 3 +++ .../KeyspaceIsolation/WrapperBase.cs | 22 +++++++++++++++++ src/StackExchange.Redis/PublicAPI.Shipped.txt | 2 ++ src/StackExchange.Redis/RedisDatabase.cs | 24 ++++++++++++++----- 6 files changed, 73 insertions(+), 6 deletions(-) diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs index 1c9b8b9ce..cb8f22817 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabase.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs @@ -2864,6 +2864,20 @@ IEnumerable SortedSetScan(RedisKey key, /// bool StringSet(KeyValuePair[] values, When when = When.Always, CommandFlags flags = CommandFlags.None); + /// + /// Sets the given keys to their respective values. + /// If is specified, this will not perform any operation at all even if just a single key already exists. + /// + /// The keys and values to set. + /// Which condition to set the value under (defaults to always). + /// The flags to use for this operation. + /// if the keys were set, otherwise. + /// + /// , + /// + /// + bool StringSet(ReadOnlyMemory> values, When when = When.Always, CommandFlags flags = CommandFlags.None); + /// /// Atomically sets key to value and returns the previous value (if any) stored at . /// diff --git a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs index b875e2887..11ce681ab 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs @@ -2817,6 +2817,20 @@ IAsyncEnumerable SortedSetScanAsync(RedisKey key, /// Task StringSetAsync(KeyValuePair[] values, When when = When.Always, CommandFlags flags = CommandFlags.None); + /// + /// Sets the given keys to their respective values. + /// If is specified, this will not perform any operation at all even if just a single key already exists. + /// + /// The keys and values to set. + /// Which condition to set the value under (defaults to always). + /// The flags to use for this operation. + /// if the keys were set, otherwise. + /// + /// , + /// + /// + Task StringSetAsync(ReadOnlyMemory> values, When when = When.Always, CommandFlags flags = CommandFlags.None); + /// /// Atomically sets key to value and returns the previous value (if any) stored at . /// diff --git a/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs b/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs index d04ca60f6..eb2ddecdb 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs @@ -670,6 +670,9 @@ public long StringLength(RedisKey key, CommandFlags flags = CommandFlags.None) = public bool StringSet(KeyValuePair[] values, When when = When.Always, CommandFlags flags = CommandFlags.None) => Inner.StringSet(ToInner(values), when, flags); + public bool StringSet(ReadOnlyMemory> values, When when = When.Always, CommandFlags flags = CommandFlags.None) => + Inner.StringSet(ToInner(values), when, flags); + public bool StringSet(RedisKey key, RedisValue value, TimeSpan? expiry, When when) => Inner.StringSet(ToInner(key), value, expiry, when); public bool StringSet(RedisKey key, RedisValue value, TimeSpan? expiry, When when, CommandFlags flags) => diff --git a/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs b/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs index 702939f51..19ea9ea3f 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs @@ -686,6 +686,9 @@ public Task StringLengthAsync(RedisKey key, CommandFlags flags = CommandFl public Task StringSetAsync(KeyValuePair[] values, When when = When.Always, CommandFlags flags = CommandFlags.None) => Inner.StringSetAsync(ToInner(values), when, flags); + public Task StringSetAsync(ReadOnlyMemory> values, When when = When.Always, CommandFlags flags = CommandFlags.None) => + Inner.StringSetAsync(ToInner(values), when, flags); + public Task StringSetAsync(RedisKey key, RedisValue value, TimeSpan? expiry, When when) => Inner.StringSetAsync(ToInner(key), value, expiry, when); public Task StringSetAsync(RedisKey key, RedisValue value, TimeSpan? expiry, When when, CommandFlags flags) => @@ -804,6 +807,25 @@ protected KeyValuePair ToInner(KeyValuePair> ToInner(ReadOnlyMemory> outer) + { + if (outer.Length == 0) + { + return outer; + } + else + { + KeyValuePair[] inner = new KeyValuePair[outer.Length]; + + for (int i = 0; i < outer.Length; ++i) + { + inner[i] = ToInner(outer.Span[i]); + } + + return inner; + } + } + protected RedisValue ToInner(RedisValue outer) => RedisKey.ConcatenateBytes(Prefix, null, (byte[]?)outer); diff --git a/src/StackExchange.Redis/PublicAPI.Shipped.txt b/src/StackExchange.Redis/PublicAPI.Shipped.txt index 8ffbccb7b..6c426888f 100644 --- a/src/StackExchange.Redis/PublicAPI.Shipped.txt +++ b/src/StackExchange.Redis/PublicAPI.Shipped.txt @@ -715,6 +715,7 @@ StackExchange.Redis.IDatabase.StringSet(StackExchange.Redis.RedisKey key, StackE StackExchange.Redis.IDatabase.StringSet(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, System.TimeSpan? expiry, StackExchange.Redis.When when) -> bool StackExchange.Redis.IDatabase.StringSet(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, System.TimeSpan? expiry, StackExchange.Redis.When when, StackExchange.Redis.CommandFlags flags) -> bool StackExchange.Redis.IDatabase.StringSet(System.Collections.Generic.KeyValuePair[]! values, StackExchange.Redis.When when = StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> bool +StackExchange.Redis.IDatabase.StringSet(System.ReadOnlyMemory> values, StackExchange.Redis.When when = StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> bool StackExchange.Redis.IDatabase.StringSetAndGet(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, System.TimeSpan? expiry = null, bool keepTtl = false, StackExchange.Redis.When when = StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue StackExchange.Redis.IDatabase.StringSetAndGet(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, System.TimeSpan? expiry, StackExchange.Redis.When when, StackExchange.Redis.CommandFlags flags) -> StackExchange.Redis.RedisValue StackExchange.Redis.IDatabase.StringSetBit(StackExchange.Redis.RedisKey key, long offset, bool bit, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> bool @@ -944,6 +945,7 @@ StackExchange.Redis.IDatabaseAsync.StringSetAsync(StackExchange.Redis.RedisKey k StackExchange.Redis.IDatabaseAsync.StringSetAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, System.TimeSpan? expiry, StackExchange.Redis.When when) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringSetAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, System.TimeSpan? expiry, StackExchange.Redis.When when, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringSetAsync(System.Collections.Generic.KeyValuePair[]! values, StackExchange.Redis.When when = StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StringSetAsync(System.ReadOnlyMemory> values, StackExchange.Redis.When when = StackExchange.Redis.When.Always, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringSetBitAsync(StackExchange.Redis.RedisKey key, long offset, bool bit, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringSetRangeAsync(StackExchange.Redis.RedisKey key, long offset, StackExchange.Redis.RedisValue value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.InternalErrorEventArgs diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs index bb4f15a2c..6171be587 100644 --- a/src/StackExchange.Redis/RedisDatabase.cs +++ b/src/StackExchange.Redis/RedisDatabase.cs @@ -3125,6 +3125,12 @@ public bool StringSet(KeyValuePair[] values, When when = W return ExecuteSync(msg, ResultProcessor.Boolean); } + public bool StringSet(ReadOnlyMemory> values, When when = When.Always, CommandFlags flags = CommandFlags.None) + { + var msg = GetStringSetMessage(values, when, flags); + return ExecuteSync(msg, ResultProcessor.Boolean); + } + // Backwards compatibility overloads: public Task StringSetAsync(RedisKey key, RedisValue value, TimeSpan? expiry, When when) => StringSetAsync(key, value, expiry, false, when); @@ -3143,6 +3149,12 @@ public Task StringSetAsync(KeyValuePair[] values, Wh return ExecuteAsync(msg, ResultProcessor.Boolean); } + public Task StringSetAsync(ReadOnlyMemory> values, When when = When.Always, CommandFlags flags = CommandFlags.None) + { + var msg = GetStringSetMessage(values, when, flags); + return ExecuteAsync(msg, ResultProcessor.Boolean); + } + public RedisValue StringSetAndGet(RedisKey key, RedisValue value, TimeSpan? expiry, When when, CommandFlags flags) => StringSetAndGet(key, value, expiry, false, when, flags); @@ -4356,13 +4368,13 @@ private Message GetStringGetWithExpiryMessage(RedisKey key, CommandFlags flags, return new StringGetWithExpiryMessage(Database, flags, RedisCommand.TTL, key); } - private Message? GetStringSetMessage(KeyValuePair[] values, When when = When.Always, CommandFlags flags = CommandFlags.None) + private Message? GetStringSetMessage(ReadOnlyMemory> values, When when = When.Always, CommandFlags flags = CommandFlags.None) { - if (values == null) throw new ArgumentNullException(nameof(values)); + if (values.Span == null) throw new ArgumentNullException(nameof(values)); switch (values.Length) { case 0: return null; - case 1: return GetStringSetMessage(values[0].Key, values[0].Value, null, false, when, flags); + case 1: return GetStringSetMessage(values.Span[0].Key, values.Span[0].Value, null, false, when, flags); default: WhenAlwaysOrNotExists(when); int slot = ServerSelectionStrategy.NoSlot, offset = 0; @@ -4370,9 +4382,9 @@ private Message GetStringGetWithExpiryMessage(RedisKey key, CommandFlags flags, var serverSelectionStrategy = multiplexer.ServerSelectionStrategy; for (int i = 0; i < values.Length; i++) { - args[offset++] = values[i].Key.AsRedisValue(); - args[offset++] = values[i].Value; - slot = serverSelectionStrategy.CombineSlot(slot, values[i].Key); + args[offset++] = values.Span[i].Key.AsRedisValue(); + args[offset++] = values.Span[i].Value; + slot = serverSelectionStrategy.CombineSlot(slot, values.Span[i].Key); } return Message.CreateInSlot(Database, slot, flags, when == When.NotExists ? RedisCommand.MSETNX : RedisCommand.MSET, args); }