diff --git a/src/main/asciidoc/new-features.adoc b/src/main/asciidoc/new-features.adoc index 53d43c50b..7d063b6ab 100644 --- a/src/main/asciidoc/new-features.adoc +++ b/src/main/asciidoc/new-features.adoc @@ -7,6 +7,7 @@ This section briefly covers items that are new and noteworthy in the latest rele == New in Spring Data Redis 2.6 * Support for `SubscriptionListener` when using `MessageListener` for subscription confirmation callbacks. `ReactiveRedisMessageListenerContainer` and `ReactiveRedisOperations` provide `receiveLater(…)` and `listenToLater(…)` methods to await until Redis acknowledges the subscription. +* Support Redis 6.2 commands (`LPOP`/`RPOP` with `count`). [[new-in-2.5.0]] == New in Spring Data Redis 2.5 diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveListCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveListCommands.java index cb7141684..33516275b 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveListCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveListCommands.java @@ -922,6 +922,7 @@ public interface ReactiveListCommands { * * @param count * @return a new {@link LSetCommand} with {@literal value} applied. + * @since 2.6 */ public PopCommand count(long count) { return new PopCommand(getKey(), count, direction); @@ -954,12 +955,13 @@ public interface ReactiveListCommands { } /** - * Removes and returns first element in list stored at {@literal key}. + * Removes and returns first {@code count} elements in list stored at {@literal key}. * * @param key must not be {@literal null}. * @param count * @return * @see Redis Documentation: LPOP + * @since 2.6 */ default Flux lPop(ByteBuffer key, long count) { @@ -983,12 +985,13 @@ public interface ReactiveListCommands { } /** - * Removes and returns last element in list stored at {@literal key}. + * Removes and returns last {@code count} elements in list stored at {@literal key}. * * @param key must not be {@literal null}. * @param count * @return * @see Redis Documentation: RPOP + * @since 2.6 */ default Flux rPop(ByteBuffer key, long count) { diff --git a/src/main/java/org/springframework/data/redis/connection/RedisListCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisListCommands.java index bf60d6a0a..50a58ca10 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisListCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisListCommands.java @@ -202,12 +202,13 @@ public interface RedisListCommands { byte[] lPop(byte[] key); /** - * Removes and returns first element in list stored at {@code key}. + * Removes and returns first {@code} elements in list stored at {@code key}. * * @param key must not be {@literal null}. * @param count * @return {@literal null} when key does not exist or used in pipeline / transaction. * @see Redis Documentation: LPOP + * @since 2.6 */ @Nullable List lPop(byte[] key, long count); @@ -223,12 +224,13 @@ public interface RedisListCommands { byte[] rPop(byte[] key); /** - * Removes and returns last element in list stored at {@code key}. + * Removes and returns last {@code} elements in list stored at {@code key}. * * @param key must not be {@literal null}. * @param count * @return {@literal null} when key does not exist or used in pipeline / transaction. * @see Redis Documentation: RPOP + * @since 2.6 */ @Nullable List rPop(byte[] key, long count); diff --git a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java index e57ccc4c4..5b812cedb 100644 --- a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java @@ -843,13 +843,14 @@ public interface StringRedisConnection extends RedisConnection { String lPop(String key); /** - * Removes and returns first element in list stored at {@code key}. + * Removes and returns first {@code} elements in list stored at {@code key}. * * @param key must not be {@literal null}. * @param count * @return * @see Redis Documentation: LPOP * @see RedisListCommands#lPop(byte[], long) + * @since 2.6 */ List lPop(String key, long count); @@ -864,13 +865,14 @@ public interface StringRedisConnection extends RedisConnection { String rPop(String key); /** - * Removes and returns last element in list stored at {@code key}. + * Removes and returns last {@code} elements in list stored at {@code key}. * * @param key must not be {@literal null}. * @param count * @return * @see Redis Documentation: RPOP * @see RedisListCommands#rPop(byte[], long) + * @since 2.6 */ List rPop(String key, long count); diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveListCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveListCommands.java index 409b103df..9fef5f6fc 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveListCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveListCommands.java @@ -277,6 +277,7 @@ class LettuceReactiveListCommands implements ReactiveListCommands { */ @Override public Flux>> popList(Publisher commands) { + return connection.execute(cmd -> Flux.from(commands).concatMap(command -> { Assert.notNull(command.getKey(), "Key must not be null!"); diff --git a/src/main/java/org/springframework/data/redis/core/BoundListOperations.java b/src/main/java/org/springframework/data/redis/core/BoundListOperations.java index bb4b4abec..f73661b95 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundListOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundListOperations.java @@ -200,6 +200,18 @@ public interface BoundListOperations extends BoundKeyOperations { @Nullable V leftPop(); + /** + * Removes and returns first {@code} elements in list stored at {@code key}. + * + * @param key must not be {@literal null}. + * @param count + * @return can be {@literal null}. + * @see Redis Documentation: LPOP + * @since 2.6 + */ + @Nullable + List leftPop(long count); + /** * Removes and returns first element from lists stored at the bound key .
* Blocks connection until element available or {@code timeout} reached. @@ -240,6 +252,18 @@ public interface BoundListOperations extends BoundKeyOperations { @Nullable V rightPop(); + /** + * Removes and returns last {@code} elements in list stored at {@code key}. + * + * @param key must not be {@literal null}. + * @param count + * @return can be {@literal null}. + * @see Redis Documentation: RPOP + * @since 2.6 + */ + @Nullable + List rightPop(long count); + /** * Removes and returns last element from lists stored at the bound key.
* Blocks connection until element available or {@code timeout} reached. diff --git a/src/main/java/org/springframework/data/redis/core/DefaultBoundListOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultBoundListOperations.java index b33d85b19..6fa5c0bb2 100644 --- a/src/main/java/org/springframework/data/redis/core/DefaultBoundListOperations.java +++ b/src/main/java/org/springframework/data/redis/core/DefaultBoundListOperations.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; import org.springframework.data.redis.connection.DataType; +import org.springframework.lang.Nullable; /** * Default implementation for {@link BoundListOperations}. @@ -82,6 +83,15 @@ class DefaultBoundListOperations extends DefaultBoundKeyOperations impl return ops.leftPop(getKey()); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.core.BoundListOperations#leftPop(long) + */ + @Override + public List leftPop(long count) { + return ops.leftPop(getKey(), count); + } + /* * (non-Javadoc) * @see org.springframework.data.redis.core.BoundListOperations#leftPop(long, java.util.concurrent.TimeUnit) @@ -163,6 +173,15 @@ class DefaultBoundListOperations extends DefaultBoundKeyOperations impl return ops.rightPop(getKey()); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.core.BoundListOperations#rightPop(long) + */ + @Override + public List rightPop(long count) { + return ops.rightPop(getKey(), count); + } + /* * (non-Javadoc) * @see org.springframework.data.redis.core.BoundListOperations#rightPop(long, java.util.concurrent.TimeUnit) diff --git a/src/main/java/org/springframework/data/redis/core/ListOperations.java b/src/main/java/org/springframework/data/redis/core/ListOperations.java index eb5a6589d..19125c491 100644 --- a/src/main/java/org/springframework/data/redis/core/ListOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ListOperations.java @@ -249,12 +249,13 @@ public interface ListOperations { V leftPop(K key); /** - * Removes and returns first element in list stored at {@code key}. + * Removes and returns first {@code} elements in list stored at {@code key}. * * @param key must not be {@literal null}. * @param count * @return can be {@literal null}. * @see Redis Documentation: LPOP + * @since 2.6 */ @Nullable List leftPop(K key, long count); @@ -303,12 +304,13 @@ public interface ListOperations { V rightPop(K key); /** - * Removes and returns last element in list stored at {@code key}. + * Removes and returns last {@code} elements in list stored at {@code key}. * * @param key must not be {@literal null}. * @param count * @return can be {@literal null}. * @see Redis Documentation: RPOP + * @since 2.6 */ @Nullable List rightPop(K key, long count); diff --git a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java index 61d553aad..4c6485fb4 100644 --- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java @@ -80,6 +80,7 @@ import org.springframework.data.redis.core.types.RedisClientInfo; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.test.condition.EnabledOnCommand; import org.springframework.data.redis.test.condition.EnabledOnRedisDriver; +import org.springframework.data.redis.test.condition.EnabledOnRedisVersion; import org.springframework.data.redis.test.condition.LongRunningTest; import org.springframework.data.redis.test.condition.RedisDriver; import org.springframework.data.redis.test.util.HexStringUtils; @@ -1286,6 +1287,16 @@ public abstract class AbstractConnectionIntegrationTests { verifyResults(Arrays.asList(new Object[] { 1L, 2L, "hello" })); } + @Test // GH-1987 + @EnabledOnRedisVersion("6.2") + void testLPopWithCount() { + actual.add(connection.rPush("PopList", "hello")); + actual.add(connection.rPush("PopList", "world")); + actual.add(connection.rPush("PopList", "42")); + actual.add(connection.lPop("PopList", 2)); + verifyResults(Arrays.asList(new Object[] { 1L, 2L, 3L, Arrays.asList("hello", "world") })); + } + @Test void testLRem() { actual.add(connection.rPush("PopList", "hello")); @@ -1335,6 +1346,16 @@ public abstract class AbstractConnectionIntegrationTests { verifyResults(Arrays.asList(new Object[] { 1L, 2L, "world" })); } + @Test // GH-1987 + @EnabledOnRedisVersion("6.2") + void testRPopWithCount() { + actual.add(connection.rPush("PopList", "hello")); + actual.add(connection.rPush("PopList", "world")); + actual.add(connection.rPush("PopList", "42")); + actual.add(connection.rPop("PopList", 2)); + verifyResults(Arrays.asList(new Object[] { 1L, 2L, 3L, Arrays.asList("42", "world") })); + } + @Test void testRPopLPush() { actual.add(connection.rPush("PopList", "hello")); diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java index 60191c856..c91725cbc 100644 --- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java @@ -670,14 +670,14 @@ public class DefaultStringRedisConnectionTests { verifyResults(Collections.singletonList(bar)); } - @Test + @Test // GH-1987 public void testLPopCountBytes() { doReturn(Collections.singletonList(barBytes)).when(nativeConnection).lPop(fooBytes, 2); actual.add(connection.lPop(fooBytes, 2)); verifyResults(Collections.singletonList(bytesList)); } - @Test + @Test // GH-1987 public void testLPopCount() { doReturn(Collections.singletonList(barBytes)).when(nativeConnection).lPop(fooBytes, 2); actual.add(connection.lPop(foo, 2)); @@ -852,14 +852,14 @@ public class DefaultStringRedisConnectionTests { verifyResults(Collections.singletonList(bar)); } - @Test + @Test // GH-1987 public void testRPopCountBytes() { doReturn(Collections.singletonList(barBytes)).when(nativeConnection).rPop(fooBytes, 2); actual.add(connection.rPop(fooBytes, 2)); verifyResults(Collections.singletonList(bytesList)); } - @Test + @Test // GH-1987 public void testRPopCount() { doReturn(Collections.singletonList(barBytes)).when(nativeConnection).rPop(fooBytes, 2); actual.add(connection.rPop(foo, 2)); diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java index 888e88bec..c1ef9034c 100644 --- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java +++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java @@ -509,6 +509,18 @@ public class DefaultStringRedisConnectionTxTests extends DefaultStringRedisConne super.testLPop(); } + @Test // GH-1987 + public void testLPopCountBytes() { + doReturn(Collections.singletonList(Collections.singletonList(barBytes))).when(nativeConnection).exec(); + super.testLPopCountBytes(); + } + + @Test // GH-1987 + public void testLPopCount() { + doReturn(Collections.singletonList(Collections.singletonList(barBytes))).when(nativeConnection).exec(); + super.testLPopCount(); + } + @Test public void testLPushBytes() { doReturn(Collections.singletonList(8L)).when(nativeConnection).exec(); @@ -653,6 +665,18 @@ public class DefaultStringRedisConnectionTxTests extends DefaultStringRedisConne super.testRPop(); } + @Test // GH-1987 + public void testRPopCountBytes() { + doReturn(Collections.singletonList(Collections.singletonList(barBytes))).when(nativeConnection).exec(); + super.testRPopCountBytes(); + } + + @Test // GH-1987 + public void testRPopCount() { + doReturn(Collections.singletonList(Collections.singletonList(barBytes))).when(nativeConnection).exec(); + super.testRPopCount(); + } + @Test public void testRPopLPushBytes() { doReturn(Arrays.asList(new Object[] { barBytes })).when(nativeConnection).exec();