diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 97d4f1d45..a12a5b801 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -6,6 +6,7 @@ - fix potential errors getting socket bytes (#1836 via NickCraver) - logging additions (.NET Version and timestamps) for better debugging (#1796 via philon-msft) - add: `Condition` API (transactions) now supports `StreamLengthEqual` and variants (#1807 via AlphaGremlin) +- Add support for count argument to `ListLeftPop`, `ListLeftPopAsync`, `ListRightPop`, and `ListRightPopAsync` (#1850 via jjfmarket) - fix potential task/thread exhaustion from the backlog processor (#1854 via mgravell) ## 2.2.62 diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs index a4d14ab2f..31e4e3bc9 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabase.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs @@ -639,6 +639,17 @@ public interface IDatabase : IRedis, IDatabaseAsync /// https://redis.io/commands/lpop RedisValue ListLeftPop(RedisKey key, CommandFlags flags = CommandFlags.None); + /// + /// Removes and returns count elements from the tail of the list stored at key. + /// If there are less elements in the list than count, removes and returns all the elements in the list. + /// + /// The key of the list. + /// The number of items to remove. + /// The flags to use for this operation. + /// Array of values that were popped, or nil if the key doesn't exist. + /// https://redis.io/commands/lpop + RedisValue[] ListLeftPop(RedisKey key, long count, CommandFlags flags = CommandFlags.None); + /// /// Insert the specified value at the head of the list stored at key. If key does not exist, it is created as empty list before performing the push operations. /// @@ -719,6 +730,17 @@ public interface IDatabase : IRedis, IDatabaseAsync /// https://redis.io/commands/rpop RedisValue ListRightPop(RedisKey key, CommandFlags flags = CommandFlags.None); + /// + /// Removes and returns count elements from the head of the list stored at key. + /// If there are less elements in the list than count, removes and returns all the elements in the list. + /// + /// The key of the list. + /// tThe number of items to remove. + /// The flags to use for this operation. + /// Array of values that were popped, or nil if the key doesn't exist. + /// https://redis.io/commands/rpop + RedisValue[] ListRightPop(RedisKey key, long count, CommandFlags flags = CommandFlags.None); + /// /// Atomically returns and removes the last element (tail) of the list stored at source, and pushes the element at the first element (head) of the list stored at destination. /// diff --git a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs index 496722667..79d0d6ff0 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs @@ -616,6 +616,17 @@ public interface IDatabaseAsync : IRedisAsync /// https://redis.io/commands/lpop Task ListLeftPopAsync(RedisKey key, CommandFlags flags = CommandFlags.None); + /// + /// Removes and returns count elements from the head of the list stored at key. + /// If the list contains less than count elements, removes and returns the number of elements in the list + /// + /// The key of the list. + /// The number of elements to remove + /// The flags to use for this operation. + /// Array of values that were popped, or nil if the key doesn't exist + /// https://redis.io/commands/lpop + Task ListLeftPopAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None); + /// /// Insert the specified value at the head of the list stored at key. If key does not exist, it is created as empty list before performing the push operations. /// @@ -696,6 +707,17 @@ public interface IDatabaseAsync : IRedisAsync /// https://redis.io/commands/rpop Task ListRightPopAsync(RedisKey key, CommandFlags flags = CommandFlags.None); + /// + /// Removes and returns count elements from the end the list stored at key. + /// If the list contains less than count elements, removes and returns the number of elements in the list + /// + /// The key of the list. + /// The number of elements to pop + /// The flags to use for this operation. + /// Array of values that were popped, or nil if the key doesn't exist + /// https://redis.io/commands/rpop + Task ListRightPopAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None); + /// /// Atomically returns and removes the last element (tail) of the list stored at source, and pushes the element at the first element (head) of the list stored at destination. /// diff --git a/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs b/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs index e6f6f506a..88585d3ef 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs @@ -301,6 +301,11 @@ public RedisValue ListLeftPop(RedisKey key, CommandFlags flags = CommandFlags.No return Inner.ListLeftPop(ToInner(key), flags); } + public RedisValue[] ListLeftPop(RedisKey key, long count, CommandFlags flags = CommandFlags.None) + { + return Inner.ListLeftPop(ToInner(key), count, flags); + } + public long ListLeftPush(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None) { return Inner.ListLeftPush(ToInner(key), values, flags); @@ -336,6 +341,11 @@ public RedisValue ListRightPop(RedisKey key, CommandFlags flags = CommandFlags.N return Inner.ListRightPop(ToInner(key), flags); } + public RedisValue[] ListRightPop(RedisKey key, long count, CommandFlags flags = CommandFlags.None) + { + return Inner.ListRightPop(ToInner(key), count, flags); + } + public RedisValue ListRightPopLeftPush(RedisKey source, RedisKey destination, CommandFlags flags = CommandFlags.None) { return Inner.ListRightPopLeftPush(ToInner(source), ToInner(destination), flags); diff --git a/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs b/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs index d4b124f86..fc5f9abeb 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs @@ -286,6 +286,11 @@ public Task ListLeftPopAsync(RedisKey key, CommandFlags flags = Comm return Inner.ListLeftPopAsync(ToInner(key), flags); } + public Task ListLeftPopAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None) + { + return Inner.ListLeftPopAsync(ToInner(key), count, flags); + } + public Task ListLeftPushAsync(RedisKey key, RedisValue[] values, CommandFlags flags = CommandFlags.None) { return Inner.ListLeftPushAsync(ToInner(key), values, flags); @@ -321,6 +326,11 @@ public Task ListRightPopAsync(RedisKey key, CommandFlags flags = Com return Inner.ListRightPopAsync(ToInner(key), flags); } + public Task ListRightPopAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None) + { + return Inner.ListRightPopAsync(ToInner(key), count, flags); + } + public Task ListRightPopLeftPushAsync(RedisKey source, RedisKey destination, CommandFlags flags = CommandFlags.None) { return Inner.ListRightPopLeftPushAsync(ToInner(source), ToInner(destination), flags); diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs index be3b5d991..825b511b1 100644 --- a/src/StackExchange.Redis/RedisDatabase.cs +++ b/src/StackExchange.Redis/RedisDatabase.cs @@ -922,12 +922,24 @@ public RedisValue ListLeftPop(RedisKey key, CommandFlags flags = CommandFlags.No return ExecuteSync(msg, ResultProcessor.RedisValue); } + public RedisValue[] ListLeftPop(RedisKey key, long count, CommandFlags flags = CommandFlags.None) + { + var msg = Message.Create(Database, flags, RedisCommand.LPOP, key, count); + return ExecuteSync(msg, ResultProcessor.RedisValueArray); + } + public Task ListLeftPopAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { var msg = Message.Create(Database, flags, RedisCommand.LPOP, key); return ExecuteAsync(msg, ResultProcessor.RedisValue); } + public Task ListLeftPopAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None) + { + var msg = Message.Create(Database, flags, RedisCommand.LPOP, key, count); + return ExecuteAsync(msg, ResultProcessor.RedisValueArray); + } + public long ListLeftPush(RedisKey key, RedisValue value, When when = When.Always, CommandFlags flags = CommandFlags.None) { WhenAlwaysOrExists(when); @@ -1016,12 +1028,24 @@ public RedisValue ListRightPop(RedisKey key, CommandFlags flags = CommandFlags.N return ExecuteSync(msg, ResultProcessor.RedisValue); } + public RedisValue[] ListRightPop(RedisKey key, long count, CommandFlags flags = CommandFlags.None) + { + var msg = Message.Create(Database, flags, RedisCommand.RPOP, key, count); + return ExecuteSync(msg, ResultProcessor.RedisValueArray); + } + public Task ListRightPopAsync(RedisKey key, CommandFlags flags = CommandFlags.None) { var msg = Message.Create(Database, flags, RedisCommand.RPOP, key); return ExecuteAsync(msg, ResultProcessor.RedisValue); } + public Task ListRightPopAsync(RedisKey key, long count, CommandFlags flags = CommandFlags.None) + { + var msg = Message.Create(Database, flags, RedisCommand.RPOP, key, count); + return ExecuteAsync(msg, ResultProcessor.RedisValueArray); + } + public RedisValue ListRightPopLeftPush(RedisKey source, RedisKey destination, CommandFlags flags = CommandFlags.None) { var msg = Message.Create(Database, flags, RedisCommand.RPOPLPUSH, source, destination); @@ -1933,7 +1957,7 @@ public bool StreamCreateConsumerGroup(RedisKey key, RedisValue groupName, RedisV key, groupName, position, - true, + true, flags); } @@ -2251,7 +2275,7 @@ public Task StreamReadGroupAsync(RedisKey key, RedisValue groupNa false, flags); } - + public Task StreamReadGroupAsync(RedisKey key, RedisValue groupName, RedisValue consumerName, RedisValue? position = null, int? count = null, bool noAck = false, CommandFlags flags = CommandFlags.None) { var actualPosition = position ?? StreamPosition.NewMessages; @@ -2815,7 +2839,7 @@ private Message GetMultiStreamReadMessage(StreamPosition[] streamPositions, int? * [7] = id1 * [8] = id2 * [9] = id3 - * + * * */ var pairCount = streamPositions.Length; diff --git a/tests/StackExchange.Redis.Tests/DatabaseWrapperTests.cs b/tests/StackExchange.Redis.Tests/DatabaseWrapperTests.cs index f9b539394..1455e0603 100644 --- a/tests/StackExchange.Redis.Tests/DatabaseWrapperTests.cs +++ b/tests/StackExchange.Redis.Tests/DatabaseWrapperTests.cs @@ -369,6 +369,13 @@ public void ListLeftPop() mock.Verify(_ => _.ListLeftPop("prefix:key", CommandFlags.None)); } + [Fact] + public void ListLeftPop_1() + { + wrapper.ListLeftPop("key", 123, CommandFlags.None); + mock.Verify(_ => _.ListLeftPop("prefix:key", 123, CommandFlags.None)); + } + [Fact] public void ListLeftPush_1() { @@ -420,6 +427,13 @@ public void ListRightPop() mock.Verify(_ => _.ListRightPop("prefix:key", CommandFlags.None)); } + [Fact] + public void ListRightPop_1() + { + wrapper.ListRightPop("key", 123, CommandFlags.None); + mock.Verify(_ => _.ListRightPop("prefix:key", 123, CommandFlags.None)); + } + [Fact] public void ListRightPopLeftPush() { diff --git a/tests/StackExchange.Redis.Tests/WrapperBaseTests.cs b/tests/StackExchange.Redis.Tests/WrapperBaseTests.cs index 3f1ee9f28..286fb8c5a 100644 --- a/tests/StackExchange.Redis.Tests/WrapperBaseTests.cs +++ b/tests/StackExchange.Redis.Tests/WrapperBaseTests.cs @@ -330,6 +330,13 @@ public void ListLeftPopAsync() mock.Verify(_ => _.ListLeftPopAsync("prefix:key", CommandFlags.None)); } + [Fact] + public void ListLeftPopAsync_1() + { + wrapper.ListLeftPopAsync("key", 123, CommandFlags.None); + mock.Verify(_ => _.ListLeftPopAsync("prefix:key", 123, CommandFlags.None)); + } + [Fact] public void ListLeftPushAsync_1() { @@ -381,6 +388,13 @@ public void ListRightPopAsync() mock.Verify(_ => _.ListRightPopAsync("prefix:key", CommandFlags.None)); } + [Fact] + public void ListRightPopAsync_1() + { + wrapper.ListRightPopAsync("key", 123, CommandFlags.None); + mock.Verify(_ => _.ListRightPopAsync("prefix:key", 123, CommandFlags.None)); + } + [Fact] public void ListRightPopLeftPushAsync() {