diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs
index 207c03326..eb6903d8d 100644
--- a/src/StackExchange.Redis/Interfaces/IDatabase.cs
+++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs
@@ -1437,6 +1437,51 @@ public interface IDatabase : IRedis, IDatabaseAsync
/// This API should be considered an advanced feature; inappropriate use can be harmful.
RedisResult Execute(string command, ICollection args, CommandFlags flags = CommandFlags.None);
+ ///
+ /// Execute an arbitrary command against the server; this is primarily intended for executing modules,
+ /// but may also be used to provide access to new features that lack a direct API.
+ ///
+ /// Response must be represented as a RESP simple string, bulk string, or integer. Other response will
+ /// result in an error.
+ ///
+ /// The command to run.
+ /// The arguments to pass for the command.
+ /// A dynamic representation of the command's result.
+ /// This API should be considered an advanced feature; inappropriate use can be harmful.
+ Lease? ExecuteLease(string command, params object[] args);
+
+ ///
+ /// Execute an arbitrary command against the server; this is primarily intended for executing modules,
+ /// but may also be used to provide access to new features that lack a direct API.
+ ///
+ /// Response must be represented as a RESP simple string, bulk string, or integer. Other response will
+ /// result in an error.
+ ///
+ /// The command to run.
+ /// The arguments to pass for the command.
+ /// The flags to use for this operation.
+ /// A dynamic representation of the command's result.
+ /// This API should be considered an advanced feature; inappropriate use can be harmful.
+ Lease? ExecuteLease(string command, ICollection args, CommandFlags flags = CommandFlags.None);
+
+ ///
+ /// Execute an arbitrary command against the server; this is primarily intended for
+ /// executing modules, but may also be used to provide access to new features that lack
+ /// a direct API.
+ ///
+ /// Response must be represented as a RESP simple string, bulk string, or integer. Other response will
+ /// result in an error.
+ ///
+ /// This breaks out keys and args so collection can be reused, and avoid boxing.
+ /// The command to run.
+ /// The keys to use to chose slots in cluster mode. These are NOT passed as part of the command
+ /// The arguments to pass for the command.
+ /// The flags to use for this operation.
+ /// A dynamic representation of the command's result.
+ /// This API should be considered an advanced feature; inappropriate use can be harmful.
+ ///
+ Lease? ExecuteLeaseExplicit(string command, ICollection keys, ICollection args, CommandFlags flags = CommandFlags.None);
+
///
/// Execute a Lua script against the server.
///
diff --git a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs
index 9852c131c..b75516fdc 100644
--- a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs
+++ b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs
@@ -338,6 +338,15 @@ public interface IDatabaseAsync : IRedisAsync
///
Task ExecuteAsync(string command, ICollection? args, CommandFlags flags = CommandFlags.None);
+ ///
+ Task?> ExecuteLeaseAsync(string command, params object[] args);
+
+ ///
+ Task?> ExecuteLeaseAsync(string command, ICollection? args, CommandFlags flags = CommandFlags.None);
+
+ ///
+ Task?> ExecuteLeaseExplicitAsync(string command, ICollection keys, ICollection args, CommandFlags flags = CommandFlags.None);
+
///
Task ScriptEvaluateAsync(string script, RedisKey[]? keys = null, RedisValue[]? values = null, CommandFlags flags = CommandFlags.None);
diff --git a/src/StackExchange.Redis/Interfaces/IServer.cs b/src/StackExchange.Redis/Interfaces/IServer.cs
index fad2d4232..0c5c6632b 100644
--- a/src/StackExchange.Redis/Interfaces/IServer.cs
+++ b/src/StackExchange.Redis/Interfaces/IServer.cs
@@ -263,9 +263,26 @@ public partial interface IServer : IRedis
/// This API should be considered an advanced feature; inappropriate use can be harmful.
RedisResult Execute(string command, params object[] args);
+ ///
+ /// Execute an arbitrary command against the server; this is primarily intended for
+ /// executing modules, but may also be used to provide access to new features that lack
+ /// a direct API.
+ ///
+ /// Response must be represented as a RESP simple string, bulk string, or integer. Other response will
+ /// result in an error.
+ ///
+ /// The command to run.
+ /// The arguments to pass for the command.
+ /// A dynamic representation of the command's result.
+ /// This API should be considered an advanced feature; inappropriate use can be harmful.
+ Lease? ExecuteLease(string command, params object[] args);
+
///
Task ExecuteAsync(string command, params object[] args);
+ ///
+ Task?> ExecuteLeaseAsync(string command, params object[] args);
+
///
/// Execute an arbitrary command against the server; this is primarily intended for
/// executing modules, but may also be used to provide access to new features that lack
@@ -278,9 +295,48 @@ public partial interface IServer : IRedis
/// This API should be considered an advanced feature; inappropriate use can be harmful.
RedisResult Execute(string command, ICollection args, CommandFlags flags = CommandFlags.None);
+ ///
+ /// Execute an arbitrary command against the server; this is primarily intended for
+ /// executing modules, but may also be used to provide access to new features that lack
+ /// a direct API.
+ ///
+ /// Response must be represented as a RESP simple string, bulk string, or integer. Other response will
+ /// result in an error.
+ ///
+ /// The command to run.
+ /// The arguments to pass for the command.
+ /// The flags to use for this operation.
+ /// A dynamic representation of the command's result.
+ /// This API should be considered an advanced feature; inappropriate use can be harmful.
+ Lease? ExecuteLease(string command, ICollection args, CommandFlags flags = CommandFlags.None);
+
///
Task ExecuteAsync(string command, ICollection args, CommandFlags flags = CommandFlags.None);
+ ///
+ Task?> ExecuteLeaseAsync(string command, ICollection args, CommandFlags flags = CommandFlags.None);
+
+ ///
+ /// Execute an arbitrary command against the server; this is primarily intended for
+ /// executing modules, but may also be used to provide access to new features that lack
+ /// a direct API.
+ ///
+ /// Response must be represented as a RESP simple string, bulk string, or integer. Other response will
+ /// result in an error.
+ ///
+ /// This breaks out keys and args so collection can be reused, and avoid boxing.
+ /// The command to run.
+ /// The keys to use to chose slots in cluster mode. These are NOT passed as part of the command
+ /// The arguments to pass for the command.
+ /// The flags to use for this operation.
+ /// A dynamic representation of the command's result.
+ /// This API should be considered an advanced feature; inappropriate use can be harmful.
+ ///
+ Lease? ExecuteLeaseExplicit(string command, ICollection keys, ICollection args, CommandFlags flags = CommandFlags.None);
+
+ ///
+ Task?> ExecuteLeaseExplicitAsync(string command, ICollection keys, ICollection args, CommandFlags flags = CommandFlags.None);
+
///
/// Delete all the keys of all databases on the server.
///
diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs
index b97bba73b..506ad5b5e 100644
--- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs
+++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs
@@ -345,6 +345,15 @@ public Task ExecuteAsync(string command, params object[] args) =>
public Task ExecuteAsync(string command, ICollection? args, CommandFlags flags = CommandFlags.None) =>
Inner.ExecuteAsync(command, ToInner(args), flags);
+ public Task?> ExecuteLeaseAsync(string command, params object[] args) =>
+ Inner.ExecuteLeaseAsync(command, ToInner(args), CommandFlags.None);
+
+ public Task?> ExecuteLeaseAsync(string command, ICollection? args, CommandFlags flags = CommandFlags.None) =>
+ Inner.ExecuteLeaseAsync(command, ToInner(args), flags);
+
+ public Task?> ExecuteLeaseExplicitAsync(string command, ICollection keys, ICollection args, CommandFlags flags = CommandFlags.None) =>
+ throw new NotSupportedException($"{nameof(ExecuteLeaseExplicitAsync)} is not supported with key prefixes");
+
public Task ScriptEvaluateAsync(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);
diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs
index 75d93d0f9..794a84e62 100644
--- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs
+++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs
@@ -332,6 +332,15 @@ public RedisResult Execute(string command, params object[] args)
public RedisResult Execute(string command, ICollection args, CommandFlags flags = CommandFlags.None)
=> Inner.Execute(command, ToInner(args), flags);
+ public Lease? ExecuteLease(string command, params object[] args)
+ => Inner.ExecuteLease(command, ToInner(args), CommandFlags.None);
+
+ public Lease? ExecuteLease(string command, ICollection args, CommandFlags flags = CommandFlags.None)
+ => Inner.ExecuteLease(command, ToInner(args), flags);
+
+ public Lease? ExecuteLeaseExplicit(string command, ICollection keys, ICollection args, CommandFlags flags = CommandFlags.None)
+ => throw new NotSupportedException($"{nameof(ExecuteLeaseExplicit)} is not supported with key prefixes");
+
public RedisResult ScriptEvaluate(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.ScriptEvaluate(hash, ToInner(keys), values, flags);
diff --git a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt
index 91b0e1a43..c705b51f6 100644
--- a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt
+++ b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt
@@ -1 +1,13 @@
-#nullable enable
\ No newline at end of file
+#nullable enable
+StackExchange.Redis.IDatabase.ExecuteLease(string! command, params object![]! args) -> StackExchange.Redis.Lease?
+StackExchange.Redis.IDatabase.ExecuteLease(string! command, System.Collections.Generic.ICollection! args, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.Lease?
+StackExchange.Redis.IDatabase.ExecuteLeaseExplicit(string! command, System.Collections.Generic.ICollection! keys, System.Collections.Generic.ICollection! args, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.Lease?
+StackExchange.Redis.IDatabaseAsync.ExecuteLeaseAsync(string! command, params object![]! args) -> System.Threading.Tasks.Task?>!
+StackExchange.Redis.IDatabaseAsync.ExecuteLeaseAsync(string! command, System.Collections.Generic.ICollection? args, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task?>!
+StackExchange.Redis.IDatabaseAsync.ExecuteLeaseExplicitAsync(string! command, System.Collections.Generic.ICollection! keys, System.Collections.Generic.ICollection! args, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task?>!
+StackExchange.Redis.IServer.ExecuteLease(string! command, params object![]! args) -> StackExchange.Redis.Lease?
+StackExchange.Redis.IServer.ExecuteLease(string! command, System.Collections.Generic.ICollection! args, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.Lease?
+StackExchange.Redis.IServer.ExecuteLeaseAsync(string! command, params object![]! args) -> System.Threading.Tasks.Task?>!
+StackExchange.Redis.IServer.ExecuteLeaseAsync(string! command, System.Collections.Generic.ICollection! args, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task?>!
+StackExchange.Redis.IServer.ExecuteLeaseExplicit(string! command, System.Collections.Generic.ICollection! keys, System.Collections.Generic.ICollection! args, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.Lease?
+StackExchange.Redis.IServer.ExecuteLeaseExplicitAsync(string! command, System.Collections.Generic.ICollection! keys, System.Collections.Generic.ICollection! args, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task?>!
\ No newline at end of file
diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs
index 7468bdb64..e61a6cef3 100644
--- a/src/StackExchange.Redis/RedisDatabase.cs
+++ b/src/StackExchange.Redis/RedisDatabase.cs
@@ -1595,6 +1595,21 @@ public RedisResult Execute(string command, ICollection args, CommandFlag
return ExecuteSync(msg, ResultProcessor.ScriptResult)!;
}
+ public Lease? ExecuteLease(string command, params object[] args)
+ => ExecuteLease(command, args, CommandFlags.None);
+
+ public Lease? ExecuteLease(string command, ICollection args, CommandFlags flags = CommandFlags.None)
+ {
+ var msg = new ExecuteMessage(multiplexer?.CommandMap, Database, flags, command, args);
+ return ExecuteSync(msg, ResultProcessor.Lease);
+ }
+
+ public Lease? ExecuteLeaseExplicit(string command, ICollection keys, ICollection args, CommandFlags flags = CommandFlags.None)
+ {
+ var msg = new ExecuteExplicitMessage(multiplexer?.CommandMap, Database, flags, command, keys, args);
+ return ExecuteSync(msg, ResultProcessor.Lease);
+ }
+
public Task ExecuteAsync(string command, params object[] args)
=> ExecuteAsync(command, args, CommandFlags.None);
@@ -1604,6 +1619,21 @@ public Task ExecuteAsync(string command, ICollection? args,
return ExecuteAsync(msg, ResultProcessor.ScriptResult, defaultValue: RedisResult.NullSingle);
}
+ public Task?> ExecuteLeaseAsync(string command, params object[] args)
+ => ExecuteLeaseAsync(command, args, CommandFlags.None);
+
+ public Task?> ExecuteLeaseAsync(string command, ICollection? args, CommandFlags flags = CommandFlags.None)
+ {
+ var msg = new ExecuteMessage(multiplexer?.CommandMap, Database, flags, command, args);
+ return ExecuteAsync?>(msg, ResultProcessor.Lease!, defaultValue: null);
+ }
+
+ public Task?> ExecuteLeaseExplicitAsync(string command, ICollection? keys, ICollection? args, CommandFlags flags = CommandFlags.None)
+ {
+ var msg = new ExecuteExplicitMessage(multiplexer?.CommandMap, Database, flags, command, keys, args);
+ return ExecuteAsync?>(msg, ResultProcessor.Lease!, defaultValue: null);
+ }
+
public RedisResult ScriptEvaluate(string script, RedisKey[]? keys = null, RedisValue[]? values = null, CommandFlags flags = CommandFlags.None)
{
var command = ResultProcessor.ScriptLoadProcessor.IsSHA1(script) ? RedisCommand.EVALSHA : RedisCommand.EVAL;
@@ -4968,6 +4998,48 @@ public override int GetHashSlot(ServerSelectionStrategy serverSelectionStrategy)
public override int ArgCount => _args.Count;
}
+ internal sealed class ExecuteExplicitMessage : Message
+ {
+ private readonly ICollection _keys;
+ private readonly ICollection _args;
+ public new CommandBytes Command { get; }
+
+ public ExecuteExplicitMessage(CommandMap? map, int db, CommandFlags flags, string command, ICollection? keys, ICollection? args) : base(db, flags, RedisCommand.UNKNOWN)
+ {
+ if (args != null && args.Count >= PhysicalConnection.REDIS_MAX_ARGS) // using >= here because we will be adding 1 for the command itself (which is an arg for the purposes of the multi-bulk protocol)
+ {
+ throw ExceptionFactory.TooManyArgs(command, args.Count);
+ }
+ Command = map?.GetBytes(command) ?? default;
+ if (Command.IsEmpty) throw ExceptionFactory.CommandDisabled(command);
+ _keys = keys ?? Array.Empty();
+ _args = args ?? Array.Empty();
+ }
+
+ protected override void WriteImpl(PhysicalConnection physical)
+ {
+ physical.WriteHeader(RedisCommand.UNKNOWN, _args.Count, Command);
+ foreach (var arg in _args)
+ {
+ physical.WriteBulkString(arg);
+ }
+ }
+
+ public override string CommandString => Command.ToString();
+ public override string CommandAndKey => Command.ToString();
+
+ public override int GetHashSlot(ServerSelectionStrategy serverSelectionStrategy)
+ {
+ int slot = ServerSelectionStrategy.NoSlot;
+ foreach (var key in _keys)
+ {
+ slot = serverSelectionStrategy.CombineSlot(slot, key);
+ }
+ return slot;
+ }
+ public override int ArgCount => _args.Count;
+ }
+
private sealed class ScriptEvalMessage : Message, IMultiMessage
{
private readonly RedisKey[] keys;
diff --git a/src/StackExchange.Redis/RedisServer.cs b/src/StackExchange.Redis/RedisServer.cs
index 8810e1e2b..57497060c 100644
--- a/src/StackExchange.Redis/RedisServer.cs
+++ b/src/StackExchange.Redis/RedisServer.cs
@@ -1037,6 +1037,20 @@ public RedisResult Execute(string command, ICollection args, CommandFlag
return ExecuteSync(msg, ResultProcessor.ScriptResult, defaultValue: RedisResult.NullSingle);
}
+ public Lease? ExecuteLease(string command, params object[] args) => ExecuteLease(command, args, CommandFlags.None);
+
+ public Lease? ExecuteLease(string command, ICollection args, CommandFlags flags = CommandFlags.None)
+ {
+ var msg = new RedisDatabase.ExecuteMessage(multiplexer?.CommandMap, -1, flags, command, args);
+ return ExecuteSync(msg, ResultProcessor.Lease, defaultValue: null);
+ }
+
+ public Lease? ExecuteLeaseExplicit(string command, ICollection keys, ICollection args, CommandFlags flags = CommandFlags.None)
+ {
+ var msg = new RedisDatabase.ExecuteExplicitMessage(multiplexer?.CommandMap, -1, flags, command, keys, args);
+ return ExecuteSync(msg, ResultProcessor.Lease, defaultValue: null);
+ }
+
public Task ExecuteAsync(string command, params object[] args) => ExecuteAsync(command, args, CommandFlags.None);
public Task ExecuteAsync(string command, ICollection args, CommandFlags flags = CommandFlags.None)
@@ -1045,6 +1059,20 @@ public Task ExecuteAsync(string command, ICollection args,
return ExecuteAsync(msg, ResultProcessor.ScriptResult, defaultValue: RedisResult.NullSingle);
}
+ public Task?> ExecuteLeaseAsync(string command, params object[] args) => ExecuteLeaseAsync(command, args, CommandFlags.None);
+
+ public Task?> ExecuteLeaseAsync(string command, ICollection args, CommandFlags flags = CommandFlags.None)
+ {
+ var msg = new RedisDatabase.ExecuteMessage(multiplexer?.CommandMap, -1, flags, command, args);
+ return ExecuteAsync?>(msg, ResultProcessor.Lease!, defaultValue: null);
+ }
+
+ public Task?> ExecuteLeaseExplicitAsync(string command, ICollection keys, ICollection args, CommandFlags flags = CommandFlags.None)
+ {
+ var msg = new RedisDatabase.ExecuteExplicitMessage(multiplexer?.CommandMap, -1, flags, command, keys, args);
+ return ExecuteAsync?>(msg, ResultProcessor.Lease!, defaultValue: null);
+ }
+
///
/// For testing only.
///
diff --git a/tests/StackExchange.Redis.Tests/ExecuteTests.cs b/tests/StackExchange.Redis.Tests/ExecuteTests.cs
index 30012001a..ead205b94 100644
--- a/tests/StackExchange.Redis.Tests/ExecuteTests.cs
+++ b/tests/StackExchange.Redis.Tests/ExecuteTests.cs
@@ -1,4 +1,6 @@
-using System.Linq;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
@@ -38,4 +40,160 @@ public async Task ServerExecute()
actual = (string?)await server.ExecuteAsync("echo", "some value").ForAwait();
Assert.Equal("some value", actual);
}
+
+ [Fact]
+ public async Task DBExecuteLease()
+ {
+ using var conn = Create();
+
+ var db = conn.GetDatabase();
+ var key = Me();
+
+ // sync tests
+ {
+ db.StringSet(key, "hello world");
+
+ using var lease1 = db.ExecuteLease("GET", (RedisKey)key);
+ Assert.NotNull(lease1);
+
+ var value1 = lease1.DecodeString();
+ Assert.Equal("hello world", value1);
+
+ db.StringSet(key, "fizz buzz");
+
+ using var lease2 = db.ExecuteLease("GET", new List { (RedisKey)key });
+ Assert.NotNull(lease2);
+
+ var value2 = lease2.DecodeString();
+ Assert.Equal("fizz buzz", value2);
+ }
+
+ // async tests
+ {
+ await db.StringSetAsync(key, "foo bar");
+
+ using var lease3 = await db.ExecuteLeaseAsync("GET", (RedisKey)key);
+ Assert.NotNull(lease3);
+
+ var value3 = lease3.DecodeString();
+ Assert.Equal("foo bar", value3);
+
+ await db.StringSetAsync(key, "abc def");
+
+ using var lease4 = await db.ExecuteLeaseAsync("GET", new List { (RedisKey)key });
+ Assert.NotNull(lease4);
+
+ var value4 = lease4.DecodeString();
+ Assert.Equal("abc def", value4);
+ }
+ }
+
+ [Fact]
+ public async Task ServerExecuteLease()
+ {
+ using var conn = Create();
+
+ var server = conn.GetServer(conn.GetEndPoints().First());
+ var key = Me();
+
+ // sync tests
+ {
+ server.Execute("SET", key, "hello world");
+
+ using var lease1 = server.ExecuteLease("GET", (RedisKey)key);
+ Assert.NotNull(lease1);
+
+ var value1 = lease1.DecodeString();
+ Assert.Equal("hello world", value1);
+
+ server.Execute("SET", key, "fizz buzz");
+
+ using var lease2 = server.ExecuteLease("GET", new List { (RedisKey)key });
+ Assert.NotNull(lease2);
+
+ var value2 = lease2.DecodeString();
+ Assert.Equal("fizz buzz", value2);
+ }
+
+ // async tests
+ {
+ await server.ExecuteAsync("SET", key, "foo bar");
+
+ using var lease3 = await server.ExecuteLeaseAsync("GET", (RedisKey)key);
+ Assert.NotNull(lease3);
+
+ var value3 = lease3.DecodeString();
+ Assert.Equal("foo bar", value3);
+
+ await server.ExecuteAsync("SET", key, "abc def");
+
+ using var lease4 = await server.ExecuteLeaseAsync("GET", new List { (RedisKey)key });
+ Assert.NotNull(lease4);
+
+ var value4 = lease4.DecodeString();
+ Assert.Equal("abc def", value4);
+ }
+ }
+
+ [Fact]
+ public async Task DBExecuteLeaseExplicit()
+ {
+ using var conn = Create();
+
+ var db = conn.GetDatabase();
+ var key = Me();
+
+ // sync tests
+ {
+ db.StringSet(key, "hello world");
+
+ using var lease = db.ExecuteLeaseExplicit("GET", new[] { (RedisKey)key }, new[] { (RedisValue)key });
+ Assert.NotNull(lease);
+
+ var value = lease.DecodeString();
+ Assert.Equal("hello world", value);
+ }
+
+ // async tests
+ {
+ await db.StringSetAsync(key, "abc def");
+
+ using var lease = await db.ExecuteLeaseExplicitAsync("GET", new[] { (RedisKey)key }, new[] { (RedisValue)key });
+ Assert.NotNull(lease);
+
+ var value = lease.DecodeString();
+ Assert.Equal("abc def", value);
+ }
+ }
+
+ [Fact]
+ public async Task ServerExecuteLeaseExplicit()
+ {
+ using var conn = Create();
+
+ var server = conn.GetServer(conn.GetEndPoints().First());
+ var key = Me();
+
+ // sync tests
+ {
+ server.Execute("SET", key, "hello world");
+
+ using var lease = server.ExecuteLeaseExplicit("GET", new[] { (RedisKey)key }, new[] { (RedisValue)key });
+ Assert.NotNull(lease);
+
+ var value = lease.DecodeString();
+ Assert.Equal("hello world", value);
+ }
+
+ // async tests
+ {
+ await server.ExecuteAsync("SET", key, "foo bar");
+
+ using var lease = await server.ExecuteLeaseExplicitAsync("GET", new[] { (RedisKey)key }, new[] { (RedisValue)key });
+ Assert.NotNull(lease);
+
+ var value = lease.DecodeString();
+ Assert.Equal("foo bar", value);
+ }
+ }
}
diff --git a/tests/StackExchange.Redis.Tests/NamingTests.cs b/tests/StackExchange.Redis.Tests/NamingTests.cs
index 2990e04c4..3a4f6ebec 100644
--- a/tests/StackExchange.Redis.Tests/NamingTests.cs
+++ b/tests/StackExchange.Redis.Tests/NamingTests.cs
@@ -82,6 +82,10 @@ public void CheckDatabaseMethodsUseKeys(Type type)
case nameof(IDatabaseAsync.PublishAsync):
case nameof(IDatabase.Execute):
case nameof(IDatabaseAsync.ExecuteAsync):
+ case nameof(IDatabase.ExecuteLease):
+ case nameof(IDatabaseAsync.ExecuteLeaseAsync):
+ case nameof(IDatabase.ExecuteLeaseExplicit):
+ case nameof(IDatabaseAsync.ExecuteLeaseExplicitAsync):
case nameof(IDatabase.ScriptEvaluate):
case nameof(IDatabaseAsync.ScriptEvaluateAsync):
case nameof(IDatabase.StreamRead):
@@ -111,6 +115,10 @@ private static bool IgnoreMethodConventions(MethodInfo method)
case nameof(IDatabase.CreateTransaction):
case nameof(IDatabase.Execute):
case nameof(IDatabaseAsync.ExecuteAsync):
+ case nameof(IDatabase.ExecuteLease):
+ case nameof(IDatabaseAsync.ExecuteLeaseAsync):
+ case nameof(IDatabase.ExecuteLeaseExplicit):
+ case nameof(IDatabaseAsync.ExecuteLeaseExplicitAsync):
case nameof(IDatabase.IsConnected):
case nameof(IDatabase.SetScan):
case nameof(IDatabase.SortedSetScan):