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):