diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs
index d0d01a201..12a76e74e 100644
--- a/src/StackExchange.Redis/Interfaces/IDatabase.cs
+++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs
@@ -1264,6 +1264,36 @@ public interface IDatabase : IRedis, IDatabaseAsync
///
RedisResult ScriptEvaluate(string script, RedisKey[]? keys = null, RedisValue[]? values = null, CommandFlags flags = CommandFlags.None);
+ ///
+ /// Execute a Lua script against the server.
+ ///
+ /// The script to execute.
+ /// The key to execute against.
+ /// The values to execute against.
+ /// The flags to use for this operation.
+ /// A dynamic representation of the script's result.
+ ///
+ /// ,
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Not ambiguous due to type delta")]
+ RedisResult ScriptEvaluate(string script, RedisKey key, ReadOnlyMemory values = default, CommandFlags flags = CommandFlags.None);
+
+ ///
+ /// Execute a Lua script against the server.
+ ///
+ /// The script to execute.
+ /// The keys to execute against.
+ /// The values to execute against.
+ /// The flags to use for this operation.
+ /// A dynamic representation of the script's result.
+ ///
+ /// ,
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Not ambiguous due to type delta")]
+ RedisResult ScriptEvaluate(string script, ReadOnlyMemory keys, ReadOnlyMemory values = default, CommandFlags flags = CommandFlags.None);
+
///
/// Execute a Lua script against the server using just the SHA1 hash.
///
@@ -1316,6 +1346,36 @@ public interface IDatabase : IRedis, IDatabaseAsync
///
RedisResult ScriptEvaluateReadOnly(string script, RedisKey[]? keys = null, RedisValue[]? values = null, CommandFlags flags = CommandFlags.None);
+ ///
+ /// Read-only variant of the EVAL command that cannot execute commands that modify data, Execute a Lua script against the server.
+ ///
+ /// The script to execute.
+ /// The key to execute against.
+ /// The values to execute against.
+ /// The flags to use for this operation.
+ /// A dynamic representation of the script's result.
+ ///
+ /// ,
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Not ambiguous due to type delta")]
+ RedisResult ScriptEvaluateReadOnly(string script, RedisKey key, ReadOnlyMemory values = default, CommandFlags flags = CommandFlags.None);
+
+ ///
+ /// Read-only variant of the EVAL command that cannot execute commands that modify data, Execute a Lua script against the server.
+ ///
+ /// The script to execute.
+ /// The keys to execute against.
+ /// The values to execute against.
+ /// The flags to use for this operation.
+ /// A dynamic representation of the script's result.
+ ///
+ /// ,
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Not ambiguous due to type delta")]
+ RedisResult ScriptEvaluateReadOnly(string script, ReadOnlyMemory keys, ReadOnlyMemory values = default, CommandFlags flags = CommandFlags.None);
+
///
/// Read-only variant of the EVALSHA command that cannot execute commands that modify data, Execute a Lua script against the server using just the SHA1 hash.
///
diff --git a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs
index 7f90cf1a5..1e4d4de91 100644
--- a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs
+++ b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs
@@ -1240,6 +1240,36 @@ public interface IDatabaseAsync : IRedisAsync
///
Task ScriptEvaluateAsync(string script, RedisKey[]? keys = null, RedisValue[]? values = null, CommandFlags flags = CommandFlags.None);
+ ///
+ /// Execute a Lua script against the server.
+ ///
+ /// The script to execute.
+ /// The key to execute against.
+ /// The values to execute against.
+ /// The flags to use for this operation.
+ /// A dynamic representation of the script's result.
+ ///
+ /// ,
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Not ambiguous due to type delta")]
+ Task ScriptEvaluateAsync(string script, RedisKey key, ReadOnlyMemory values = default, CommandFlags flags = CommandFlags.None);
+
+ ///
+ /// Execute a Lua script against the server.
+ ///
+ /// The script to execute.
+ /// The keys to execute against.
+ /// The values to execute against.
+ /// The flags to use for this operation.
+ /// A dynamic representation of the script's result.
+ ///
+ /// ,
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Not ambiguous due to type delta")]
+ Task ScriptEvaluateAsync(string script, ReadOnlyMemory keys, ReadOnlyMemory values = default, CommandFlags flags = CommandFlags.None);
+
///
/// Execute a Lua script against the server using just the SHA1 hash.
///
@@ -1303,6 +1333,36 @@ public interface IDatabaseAsync : IRedisAsync
///
Task ScriptEvaluateReadOnlyAsync(byte[] hash, RedisKey[]? keys = null, RedisValue[]? values = null, CommandFlags flags = CommandFlags.None);
+ ///
+ /// Read-only variant of the EVAL command that cannot execute commands that modify data, Execute a Lua script against the server.
+ ///
+ /// The script to execute.
+ /// The key to execute against.
+ /// The values to execute against.
+ /// The flags to use for this operation.
+ /// A dynamic representation of the script's result.
+ ///
+ /// ,
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Not ambiguous due to type delta")]
+ Task ScriptEvaluateReadOnlyAsync(string script, RedisKey key, ReadOnlyMemory values = default, CommandFlags flags = CommandFlags.None);
+
+ ///
+ /// Read-only variant of the EVAL command that cannot execute commands that modify data, Execute a Lua script against the server.
+ ///
+ /// The script to execute.
+ /// The keys to execute against.
+ /// The values to execute against.
+ /// The flags to use for this operation.
+ /// A dynamic representation of the script's result.
+ ///
+ /// ,
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Not ambiguous due to type delta")]
+ Task ScriptEvaluateReadOnlyAsync(string script, ReadOnlyMemory keys, ReadOnlyMemory values = default, CommandFlags flags = CommandFlags.None);
+
///
/// Add the specified member to the set stored at key.
/// Specified members that are already a member of this set are ignored.
diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs
index 290fbed59..79f336e92 100644
--- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs
+++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs
@@ -1,4 +1,5 @@
using System;
+using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
@@ -332,9 +333,31 @@ public Task ScriptEvaluateAsync(byte[] hash, RedisKey[]? keys = nul
// TODO: The return value could contain prefixed keys. It might make sense to 'unprefix' those?
Inner.ScriptEvaluateAsync(hash, ToInner(keys), values, flags);
- public Task ScriptEvaluateAsync(string script, RedisKey[]? keys = null, RedisValue[]? values = null, CommandFlags flags = CommandFlags.None) =>
- // TODO: The return value could contain prefixed keys. It might make sense to 'unprefix' those?
- Inner.ScriptEvaluateAsync(script, ToInner(keys), values, flags);
+ public Task ScriptEvaluateAsync(string script, RedisKey[]? keys = null, RedisValue[]? values = null, CommandFlags flags = CommandFlags.None)
+ { // note we may end up using the memory overloads to enable better pooling etc usage
+ if (keys is null || keys.Length == 0) return Inner.ScriptEvaluateAsync(script, keys, values, flags);
+ if (keys.Length == 1) return Inner.ScriptEvaluateAsync(script, ToInner(keys[0]), values, flags);
+ if ((flags & CommandFlags.FireAndForget) != 0) return Inner.ScriptEvaluateAsync(script, ToInnerCopy(keys), values, flags);
+ return Lease_ScriptEvaluateAsync(script, keys, values, flags);
+ }
+
+ public Task ScriptEvaluateAsync(string script, RedisKey key, ReadOnlyMemory values = default, CommandFlags flags = CommandFlags.None) =>
+ Inner.ScriptEvaluateAsync(script, ToInner(key), values, flags);
+
+ public Task ScriptEvaluateAsync(string script, ReadOnlyMemory keys, ReadOnlyMemory values = default, CommandFlags flags = CommandFlags.None)
+ {
+ if (keys.Length == 0) return Inner.ScriptEvaluateAsync(script, keys, values, flags);
+ if (keys.Length == 1) return Inner.ScriptEvaluateAsync(script, ToInner(keys.Span[0]), values, flags);
+ if ((flags & CommandFlags.FireAndForget) != 0) return Inner.ScriptEvaluateAsync(script, ToInnerCopy(keys), values, flags);
+ return Lease_ScriptEvaluateAsync(script, keys, values, flags);
+ }
+
+ private async Task Lease_ScriptEvaluateAsync(string script, ReadOnlyMemory keys, ReadOnlyMemory values, CommandFlags flags = CommandFlags.None)
+ {
+ var result = await Inner.ScriptEvaluateAsync(script, ToInnerLease(keys, out var lease), values, flags);
+ ArrayPool.Shared.Return(lease); // happy to only recycle on success
+ return result;
+ }
public Task ScriptEvaluateAsync(LuaScript script, object? parameters = null, CommandFlags flags = CommandFlags.None) =>
// TODO: The return value could contain prefixed keys. It might make sense to 'unprefix' those?
@@ -346,11 +369,61 @@ public Task ScriptEvaluateAsync(LoadedLuaScript script, object? par
public Task ScriptEvaluateReadOnlyAsync(byte[] hash, RedisKey[]? keys = null, RedisValue[]? values = null, CommandFlags flags = CommandFlags.None) =>
// TODO: The return value could contain prefixed keys. It might make sense to 'unprefix' those?
- Inner.ScriptEvaluateAsync(hash, ToInner(keys), values, flags);
+ Inner.ScriptEvaluateReadOnlyAsync(hash, ToInner(keys), values, flags);
+
+ public Task ScriptEvaluateReadOnlyAsync(string script, RedisKey[]? keys = null, RedisValue[]? values = null, CommandFlags flags = CommandFlags.None)
+ { // note we may end up using the memory overloads to enable better pooling etc usage
+ if (keys is null || keys.Length == 0) return Inner.ScriptEvaluateReadOnlyAsync(script, keys, values, flags);
+ if (keys.Length == 1) return Inner.ScriptEvaluateReadOnlyAsync(script, ToInner(keys[0]), values, flags);
+ if ((flags & CommandFlags.FireAndForget) != 0) return Inner.ScriptEvaluateReadOnlyAsync(script, ToInnerCopy(keys), values, flags);
+ return Lease_ScriptEvaluateReadOnlyAsync(script, keys, values, flags);
+ }
- public Task ScriptEvaluateReadOnlyAsync(string script, RedisKey[]? keys = null, RedisValue[]? values = null, CommandFlags flags = CommandFlags.None) =>
- // TODO: The return value could contain prefixed keys. It might make sense to 'unprefix' those?
- Inner.ScriptEvaluateAsync(script, ToInner(keys), values, flags);
+ public Task ScriptEvaluateReadOnlyAsync(string script, RedisKey key, ReadOnlyMemory values = default, CommandFlags flags = CommandFlags.None) =>
+ Inner.ScriptEvaluateReadOnlyAsync(script, ToInner(key), values, flags);
+
+ public Task ScriptEvaluateReadOnlyAsync(string script, ReadOnlyMemory keys, ReadOnlyMemory values = default, CommandFlags flags = CommandFlags.None)
+ {
+ if (keys.Length == 0) return Inner.ScriptEvaluateReadOnlyAsync(script, keys, values, flags);
+ if (keys.Length == 1) return Inner.ScriptEvaluateReadOnlyAsync(script, ToInner(keys.Span[0]), values, flags);
+ if ((flags & CommandFlags.FireAndForget) != 0) return Inner.ScriptEvaluateReadOnlyAsync(script, ToInnerCopy(keys), values, flags);
+ return Lease_ScriptEvaluateReadOnlyAsync(script, keys, values, flags);
+ }
+
+ private async Task Lease_ScriptEvaluateReadOnlyAsync(string script, ReadOnlyMemory keys, ReadOnlyMemory values, CommandFlags flags)
+ {
+ var result = await Inner.ScriptEvaluateReadOnlyAsync(script, ToInnerLease(keys, out var lease), values, flags);
+ ArrayPool.Shared.Return(lease); // happy to only recycle on success
+ return result;
+ }
+
+ protected ReadOnlyMemory ToInnerCopy(ReadOnlyMemory keys)
+ {
+ if (keys.Length == 0) return default;
+ var arr = new RedisKey[keys.Length];
+ ToInner(keys, arr);
+ return arr;
+ }
+
+ protected ReadOnlyMemory ToInnerLease(ReadOnlyMemory keys, out RedisKey[] lease)
+ {
+ if (keys.Length == 0)
+ {
+ lease = Array.Empty();
+ return default;
+ }
+ lease = ArrayPool.Shared.Rent(keys.Length);
+ return new ReadOnlyMemory(lease, 0, ToInner(keys, lease));
+ }
+ private int ToInner(ReadOnlyMemory from, RedisKey[] arr)
+ {
+ int i = 0;
+ foreach (ref readonly var key in from.Span)
+ {
+ arr[i++] = ToInner(key);
+ }
+ return i;
+ }
public Task SetAddAsync(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None) =>
Inner.SetAddAsync(ToInner(key), values, flags);
@@ -734,10 +807,10 @@ public void Wait(Task task) =>
public void WaitAll(params Task[] tasks) =>
Inner.WaitAll(tasks);
- protected internal RedisKey ToInner(RedisKey outer) =>
+ protected internal RedisKey ToInner(in RedisKey outer) =>
RedisKey.WithPrefix(Prefix, outer);
- protected RedisKey ToInnerOrDefault(RedisKey outer) =>
+ protected RedisKey ToInnerOrDefault(in RedisKey outer) =>
(outer == default(RedisKey)) ? outer : ToInner(outer);
[return: NotNullIfNotNull("args")]
@@ -847,6 +920,9 @@ protected RedisChannel ToInner(RedisChannel outer) =>
private Func? mapFunction;
protected Func GetMapFunction() =>
// create as a delegate when first required, then re-use
- mapFunction ??= new Func(ToInner);
+ mapFunction ??= CreateMapFunction(); // avoid inlining this because of capture scopes etc
+
+ private Func CreateMapFunction() => ToInnerNoRef;
+ private RedisKey ToInnerNoRef(RedisKey value) => ToInner(value);
}
}
diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs
index d1c47aeab..3ecfbaa2c 100644
--- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs
+++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs
@@ -1,6 +1,8 @@
using System;
+using System.Buffers;
using System.Collections.Generic;
using System.Net;
+using System.Threading.Tasks;
namespace StackExchange.Redis.KeyspaceIsolation
{
@@ -321,9 +323,30 @@ public RedisResult ScriptEvaluate(byte[] hash, RedisKey[]? keys = null, RedisVal
// TODO: The return value could contain prefixed keys. It might make sense to 'unprefix' those?
Inner.ScriptEvaluate(hash, ToInner(keys), values, flags);
- public RedisResult ScriptEvaluate(string script, RedisKey[]? keys = null, RedisValue[]? values = null, CommandFlags flags = CommandFlags.None) =>
- // TODO: The return value could contain prefixed keys. It might make sense to 'unprefix' those?
- Inner.ScriptEvaluate(script, ToInner(keys), values, flags);
+ public RedisResult ScriptEvaluate(string script, RedisKey[]? keys = null, RedisValue[]? values = null, CommandFlags flags = CommandFlags.None)
+ { // note we may end up using the memory overloads to enable better pooling etc usage
+ if (keys is null || keys.Length == 0) return Inner.ScriptEvaluate(script, keys, values, flags);
+ if (keys.Length == 1) return Inner.ScriptEvaluate(script, ToInner(keys[0]), values, flags);
+ if ((flags & CommandFlags.FireAndForget) != 0) return Inner.ScriptEvaluate(script, ToInnerCopy(keys), values, flags);
+
+ var result = Inner.ScriptEvaluate(script, ToInnerLease(keys, out var lease), values, flags);
+ ArrayPool.Shared.Return(lease); // happy to only recycle on success
+ return result;
+ }
+
+ public RedisResult ScriptEvaluate(string script, RedisKey key, ReadOnlyMemory values = default, CommandFlags flags = CommandFlags.None) =>
+ Inner.ScriptEvaluate(script, ToInner(key), values, flags);
+
+ public RedisResult ScriptEvaluate(string script, ReadOnlyMemory keys, ReadOnlyMemory values = default, CommandFlags flags = CommandFlags.None)
+ {
+ if (keys.Length == 0) return Inner.ScriptEvaluate(script, keys, values, flags);
+ if (keys.Length == 1) return Inner.ScriptEvaluate(script, ToInner(keys.Span[0]), values, flags);
+ if ((flags & CommandFlags.FireAndForget) != 0) return Inner.ScriptEvaluate(script, ToInnerCopy(keys), values, flags);
+
+ var result = Inner.ScriptEvaluate(script, ToInnerLease(keys, out var lease), values, flags);
+ ArrayPool.Shared.Return(lease); // happy to only recycle on success
+ return result;
+ }
public RedisResult ScriptEvaluate(LuaScript script, object? parameters = null, CommandFlags flags = CommandFlags.None) =>
// TODO: The return value could contain prefixed keys. It might make sense to 'unprefix' those?
@@ -337,9 +360,30 @@ public RedisResult ScriptEvaluateReadOnly(byte[] hash, RedisKey[]? keys = null,
// TODO: The return value could contain prefixed keys. It might make sense to 'unprefix' those?
Inner.ScriptEvaluateReadOnly(hash, ToInner(keys), values, flags);
- public RedisResult ScriptEvaluateReadOnly(string script, RedisKey[]? keys = null, RedisValue[]? values = null, CommandFlags flags = CommandFlags.None) =>
- // TODO: The return value could contain prefixed keys. It might make sense to 'unprefix' those?
- Inner.ScriptEvaluateReadOnly(script, ToInner(keys), values, flags);
+ public RedisResult ScriptEvaluateReadOnly(string script, RedisKey[]? keys = null, RedisValue[]? values = null, CommandFlags flags = CommandFlags.None)
+ { // note we may end up using the memory overloads to enable better pooling etc usage
+ if (keys is null || keys.Length == 0) return Inner.ScriptEvaluateReadOnly(script, keys, values, flags);
+ if (keys.Length == 1) return Inner.ScriptEvaluateReadOnly(script, ToInner(keys[0]), values, flags);
+ if ((flags & CommandFlags.FireAndForget) != 0) return Inner.ScriptEvaluateReadOnly(script, ToInnerCopy(keys), values, flags);
+
+ var result = Inner.ScriptEvaluateReadOnly(script, ToInnerLease(keys, out var lease), values, flags);
+ ArrayPool.Shared.Return(lease); // happy to only recycle on success
+ return result;
+ }
+
+ public RedisResult ScriptEvaluateReadOnly(string script, RedisKey key, ReadOnlyMemory values = default, CommandFlags flags = CommandFlags.None) =>
+ Inner.ScriptEvaluateReadOnly(script, ToInner(key), values, flags);
+
+ public RedisResult ScriptEvaluateReadOnly(string script, ReadOnlyMemory keys, ReadOnlyMemory values = default, CommandFlags flags = CommandFlags.None)
+ {
+ if (keys.Length == 0) return Inner.ScriptEvaluateReadOnly(script, keys, values, flags);
+ if (keys.Length == 1) return Inner.ScriptEvaluateReadOnly(script, ToInner(keys.Span[0]), values, flags);
+ if ((flags & CommandFlags.FireAndForget) != 0) return Inner.ScriptEvaluateReadOnly(script, ToInnerCopy(keys), values, flags);
+
+ var result = Inner.ScriptEvaluateReadOnly(script, ToInnerLease(keys, out var lease), values, flags);
+ ArrayPool.Shared.Return(lease); // happy to only recycle on success
+ return result;
+ }
public long SetAdd(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None) =>
Inner.SetAdd(ToInner(key), values, flags);
diff --git a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt
index 5f282702b..db3ce5e7d 100644
--- a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt
+++ b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt
@@ -1 +1,27 @@
-
\ No newline at end of file
+override StackExchange.Redis.RedisKeyOrValue.Equals(object? obj) -> bool
+override StackExchange.Redis.RedisKeyOrValue.GetHashCode() -> int
+override StackExchange.Redis.RedisKeyOrValue.ToString() -> string!
+StackExchange.Redis.IDatabase.ScriptEvaluate(string! script, StackExchange.Redis.RedisKey key, System.ReadOnlyMemory values = default(System.ReadOnlyMemory), StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisResult!
+StackExchange.Redis.IDatabase.ScriptEvaluate(string! script, System.ReadOnlyMemory keys, System.ReadOnlyMemory values = default(System.ReadOnlyMemory), StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisResult!
+StackExchange.Redis.IDatabase.ScriptEvaluateReadOnly(string! script, StackExchange.Redis.RedisKey key, System.ReadOnlyMemory values = default(System.ReadOnlyMemory), StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisResult!
+StackExchange.Redis.IDatabase.ScriptEvaluateReadOnly(string! script, System.ReadOnlyMemory keys, System.ReadOnlyMemory values = default(System.ReadOnlyMemory), StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisResult!
+StackExchange.Redis.IDatabaseAsync.ScriptEvaluateAsync(string! script, StackExchange.Redis.RedisKey key, System.ReadOnlyMemory values = default(System.ReadOnlyMemory), StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task!
+StackExchange.Redis.IDatabaseAsync.ScriptEvaluateAsync(string! script, System.ReadOnlyMemory keys, System.ReadOnlyMemory values = default(System.ReadOnlyMemory), StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task!
+StackExchange.Redis.IDatabaseAsync.ScriptEvaluateReadOnlyAsync(string! script, StackExchange.Redis.RedisKey key, System.ReadOnlyMemory values = default(System.ReadOnlyMemory), StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task!
+StackExchange.Redis.IDatabaseAsync.ScriptEvaluateReadOnlyAsync(string! script, System.ReadOnlyMemory keys, System.ReadOnlyMemory values = default(System.ReadOnlyMemory), StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task!
+StackExchange.Redis.RedisKeyOrValue
+StackExchange.Redis.RedisKeyOrValue.AsKey() -> StackExchange.Redis.RedisKey
+StackExchange.Redis.RedisKeyOrValue.AsValue() -> StackExchange.Redis.RedisValue
+StackExchange.Redis.RedisKeyOrValue.Equals(StackExchange.Redis.RedisKey other) -> bool
+StackExchange.Redis.RedisKeyOrValue.Equals(StackExchange.Redis.RedisKeyOrValue other) -> bool
+StackExchange.Redis.RedisKeyOrValue.Equals(StackExchange.Redis.RedisValue other) -> bool
+StackExchange.Redis.RedisKeyOrValue.IsKey.get -> bool
+StackExchange.Redis.RedisKeyOrValue.IsValue.get -> bool
+StackExchange.Redis.RedisKeyOrValue.RedisKeyOrValue() -> void
+static StackExchange.Redis.RedisKey.EmptyKeys.get -> System.ReadOnlyMemory
+static StackExchange.Redis.RedisKeyOrValue.explicit operator StackExchange.Redis.RedisKey(StackExchange.Redis.RedisKeyOrValue value) -> StackExchange.Redis.RedisKey
+static StackExchange.Redis.RedisKeyOrValue.explicit operator StackExchange.Redis.RedisValue(StackExchange.Redis.RedisKeyOrValue value) -> StackExchange.Redis.RedisValue
+static StackExchange.Redis.RedisKeyOrValue.implicit operator StackExchange.Redis.RedisKeyOrValue(StackExchange.Redis.RedisKey key) -> StackExchange.Redis.RedisKeyOrValue
+static StackExchange.Redis.RedisKeyOrValue.implicit operator StackExchange.Redis.RedisKeyOrValue(StackExchange.Redis.RedisValue value) -> StackExchange.Redis.RedisKeyOrValue
+static StackExchange.Redis.RedisKeyOrValue.Key(StackExchange.Redis.RedisKey key) -> StackExchange.Redis.RedisKeyOrValue
+static StackExchange.Redis.RedisKeyOrValue.Value(StackExchange.Redis.RedisValue value) -> StackExchange.Redis.RedisKeyOrValue
\ No newline at end of file
diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs
index 9df7ac742..53509121b 100644
--- a/src/StackExchange.Redis/RedisDatabase.cs
+++ b/src/StackExchange.Redis/RedisDatabase.cs
@@ -1514,7 +1514,7 @@ public Task ExecuteAsync(string command, ICollection