Add support for LMOVE and BLMOVE.

Closes: #2039
Original Pull Request: #2107
This commit is contained in:
Mark Paluch
2021-06-30 11:21:04 +02:00
committed by Christoph Strobl
parent b6820f0f61
commit 54ad66b62c
28 changed files with 1453 additions and 8 deletions

View File

@@ -1405,6 +1405,39 @@ public abstract class AbstractConnectionIntegrationTests {
verifyResults(Arrays.asList(new Object[] { 1L, 2L, 3L, 4L, 4L }));
}
@Test // GH-2039
@EnabledOnCommand("LMOVE")
void testLMove() {
actual.add(connection.rPush("From", "hello"));
actual.add(connection.rPush("From", "big"));
actual.add(connection.rPush("From", "world"));
actual.add(connection.rPush("To", "bar"));
actual.add(connection.lMove("From", "To", RedisListCommands.Direction.LEFT, RedisListCommands.Direction.RIGHT));
actual.add(connection.lRange("From", 0, -1));
actual.add(connection.lRange("To", 0, -1));
verifyResults(Arrays.asList(1L, 2L, 3L, 1L, "hello", Arrays.asList("big", "world"), Arrays.asList("bar", "hello")));
}
@Test // GH-2039
@EnabledOnCommand("BLMOVE")
void testBLMove() {
actual.add(connection.rPush("From", "hello"));
actual.add(connection.rPush("From", "big"));
actual.add(
connection.bLMove("From", "To", RedisListCommands.Direction.LEFT, RedisListCommands.Direction.RIGHT, 0.01d));
actual.add(
connection.bLMove("From", "To", RedisListCommands.Direction.LEFT, RedisListCommands.Direction.RIGHT, 0.01d));
actual.add(
connection.bLMove("From", "To", RedisListCommands.Direction.LEFT, RedisListCommands.Direction.RIGHT, 0.01d));
actual.add(connection.lRange("From", 0, -1));
actual.add(connection.lRange("To", 0, -1));
verifyResults(Arrays.asList(1L, 2L, "hello", "big", null, Collections.emptyList(), Arrays.asList("hello", "big")));
}
@Test
void testLSet() {
actual.add(connection.rPush("PopList", "hello"));
@@ -1452,7 +1485,6 @@ public abstract class AbstractConnectionIntegrationTests {
actual.add(connection.lRange("PopList", 0, -1));
actual.add(connection.lRange("pop2", 0, -1));
verifyResults(Arrays.asList(1L, 2L, 1L, "world", Arrays.asList("hello"), Arrays.asList("world", "hey")));
}
@Test

View File

@@ -288,6 +288,12 @@ public interface ClusterConnectionTests {
// DATAREDIS-315
void lInsertShouldAddElementAtPositionCorrectly();
// GH-2039
void lMoveShouldMoveElementsCorrectly();
// GH-2039
void blMoveShouldMoveElementsCorrectly();
// DATAREDIS-315
void lLenShouldCountValuesCorrectly();

View File

@@ -23,6 +23,7 @@ import static org.springframework.data.redis.connection.BitFieldSubCommands.BitF
import static org.springframework.data.redis.connection.ClusterTestVariables.*;
import static org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit.*;
import static org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs.*;
import static org.springframework.data.redis.connection.RedisListCommands.*;
import static org.springframework.data.redis.connection.RedisZSetCommands.*;
import static org.springframework.data.redis.core.ScanOptions.*;
@@ -59,12 +60,12 @@ import org.springframework.data.redis.connection.DefaultTuple;
import org.springframework.data.redis.connection.RedisClusterNode;
import org.springframework.data.redis.connection.RedisClusterNode.SlotRange;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
import org.springframework.data.redis.connection.RedisListCommands.Position;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisStringCommands.BitOperation;
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.connection.ValueEncoding.RedisValueEncoding;
import org.springframework.data.redis.connection.RedisListCommands.*;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.script.DigestUtils;
@@ -1031,6 +1032,39 @@ public class JedisClusterConnectionTests implements ClusterConnectionTests {
assertThat(nativeConnection.lrange(KEY_1, 0, -1).get(2)).isEqualTo("booh!");
}
@Test // GH-2039
@EnabledOnCommand("LMOVE")
public void lMoveShouldMoveElementsCorrectly() {
nativeConnection.rpush(SAME_SLOT_KEY_1, VALUE_1, VALUE_2, VALUE_3);
assertThat(clusterConnection.lMove(SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES, Direction.RIGHT, Direction.LEFT))
.isEqualTo(VALUE_3_BYTES);
assertThat(clusterConnection.lMove(SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES, Direction.RIGHT, Direction.LEFT))
.isEqualTo(VALUE_2_BYTES);
assertThat(nativeConnection.lrange(SAME_SLOT_KEY_1, 0, -1)).containsExactly(VALUE_1);
assertThat(nativeConnection.lrange(SAME_SLOT_KEY_2, 0, -1)).containsExactly(VALUE_2, VALUE_3);
}
@Test // GH-2039
@EnabledOnCommand("BLMOVE")
public void blMoveShouldMoveElementsCorrectly() {
nativeConnection.rpush(SAME_SLOT_KEY_1, VALUE_2, VALUE_3);
assertThat(clusterConnection.lMove(SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES, Direction.RIGHT, Direction.LEFT))
.isEqualTo(VALUE_3_BYTES);
assertThat(clusterConnection.lMove(SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES, Direction.RIGHT, Direction.LEFT))
.isEqualTo(VALUE_2_BYTES);
assertThat(
clusterConnection.bLMove(SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES, Direction.RIGHT, Direction.LEFT, 0.01))
.isNull();
assertThat(nativeConnection.lrange(SAME_SLOT_KEY_1, 0, -1)).isEmpty();
assertThat(nativeConnection.lrange(SAME_SLOT_KEY_2, 0, -1)).containsExactly(VALUE_2, VALUE_3);
}
@Test // DATAREDIS-315
public void lLenShouldCountValuesCorrectly() {

View File

@@ -1067,6 +1067,38 @@ public class LettuceClusterConnectionTests implements ClusterConnectionTests {
assertThat(nativeConnection.lrange(KEY_1, 0, -1).get(2)).isEqualTo("booh!");
}
@Test // GH-2039
@EnabledOnCommand("LMOVE")
public void lMoveShouldMoveElementsCorrectly() {
nativeConnection.rpush(SAME_SLOT_KEY_1, VALUE_1, VALUE_2, VALUE_3);
assertThat(clusterConnection.lMove(SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES, RedisListCommands.Direction.RIGHT,
RedisListCommands.Direction.LEFT)).isEqualTo(VALUE_3_BYTES);
assertThat(clusterConnection.lMove(SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES, RedisListCommands.Direction.RIGHT,
RedisListCommands.Direction.LEFT)).isEqualTo(VALUE_2_BYTES);
assertThat(nativeConnection.lrange(SAME_SLOT_KEY_1, 0, -1)).containsExactly(VALUE_1);
assertThat(nativeConnection.lrange(SAME_SLOT_KEY_2, 0, -1)).containsExactly(VALUE_2, VALUE_3);
}
@Test // GH-2039
@EnabledOnCommand("BLMOVE")
public void blMoveShouldMoveElementsCorrectly() {
nativeConnection.rpush(SAME_SLOT_KEY_1, VALUE_2, VALUE_3);
assertThat(clusterConnection.lMove(SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES, RedisListCommands.Direction.RIGHT,
RedisListCommands.Direction.LEFT)).isEqualTo(VALUE_3_BYTES);
assertThat(clusterConnection.lMove(SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES, RedisListCommands.Direction.RIGHT,
RedisListCommands.Direction.LEFT)).isEqualTo(VALUE_2_BYTES);
assertThat(clusterConnection.bLMove(SAME_SLOT_KEY_1_BYTES, SAME_SLOT_KEY_2_BYTES, RedisListCommands.Direction.RIGHT,
RedisListCommands.Direction.LEFT, 0.01)).isNull();
assertThat(nativeConnection.lrange(SAME_SLOT_KEY_1, 0, -1)).isEmpty();
assertThat(nativeConnection.lrange(SAME_SLOT_KEY_2, 0, -1)).containsExactly(VALUE_2, VALUE_3);
}
@Test // DATAREDIS-315
public void lLenShouldCountValuesCorrectly() {

View File

@@ -29,6 +29,7 @@ import java.util.Arrays;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.Range;
import org.springframework.data.redis.connection.ReactiveListCommands;
import org.springframework.data.redis.connection.ReactiveListCommands.LPosCommand;
import org.springframework.data.redis.connection.ReactiveListCommands.PopResult;
import org.springframework.data.redis.connection.ReactiveListCommands.PushCommand;
@@ -201,6 +202,38 @@ public class LettuceReactiveListCommandIntegrationTests extends LettuceReactiveC
assertThat(nativeCommands.lrange(KEY_1, 0, -1)).containsExactly(VALUE_1, VALUE_2, VALUE_3);
}
@ParameterizedRedisTest // GH-2039
@EnabledOnCommand("LMOVE")
void lMoveShouldMoveValueCorrectly() {
nativeCommands.rpush(SAME_SLOT_KEY_1, VALUE_1, VALUE_2, VALUE_3);
nativeCommands.rpush(SAME_SLOT_KEY_2, VALUE_2, VALUE_3);
connection.listCommands().lMove(SAME_SLOT_KEY_1_BBUFFER, SAME_SLOT_KEY_2_BBUFFER,
ReactiveListCommands.Direction.RIGHT, ReactiveListCommands.Direction.LEFT).as(StepVerifier::create)
.expectNext(VALUE_3_BBUFFER).verifyComplete();
assertThat(nativeCommands.lrange(SAME_SLOT_KEY_1, 0, -1)).containsExactly(VALUE_1, VALUE_2);
assertThat(nativeCommands.lrange(SAME_SLOT_KEY_2, 0, -1)).containsExactly(VALUE_3, VALUE_2, VALUE_3);
}
@ParameterizedRedisTest // GH-2039
@EnabledOnCommand("LMOVE")
void blMoveShouldMoveValueCorrectly() {
nativeCommands.rpush(SAME_SLOT_KEY_1, VALUE_3);
nativeCommands.rpush(SAME_SLOT_KEY_2, VALUE_2, VALUE_3);
connection.listCommands()
.bLMove(SAME_SLOT_KEY_1_BBUFFER, SAME_SLOT_KEY_2_BBUFFER, ReactiveListCommands.Direction.RIGHT,
ReactiveListCommands.Direction.LEFT, Duration.ofMillis(10))
.as(StepVerifier::create).expectNext(VALUE_3_BBUFFER).verifyComplete();
connection.listCommands().bLMove(SAME_SLOT_KEY_1_BBUFFER, SAME_SLOT_KEY_2_BBUFFER,
ReactiveListCommands.Direction.RIGHT, ReactiveListCommands.Direction.LEFT, Duration.ofMillis(10))
.as(StepVerifier::create).verifyComplete();
assertThat(nativeCommands.lrange(SAME_SLOT_KEY_1, 0, -1)).isEmpty();
assertThat(nativeCommands.lrange(SAME_SLOT_KEY_2, 0, -1)).containsExactly(VALUE_3, VALUE_2, VALUE_3);
}
@ParameterizedRedisTest // DATAREDIS-525
void lSetSouldSetValueCorrectly() {

View File

@@ -267,7 +267,6 @@ public class DefaultListOperationsIntegrationIntegrationTests<K, V> {
}
@ParameterizedRedisTest // DATAREDIS-288
void testLeftPushAllCollection() {
assumeThat(redisTemplate.getConnectionFactory() instanceof LettuceConnectionFactory).isTrue();
@@ -300,6 +299,53 @@ public class DefaultListOperationsIntegrationIntegrationTests<K, V> {
.isThrownBy(() -> listOps.leftPushAll(keyFactory.instance(), (Collection<V>) null));
}
@ParameterizedRedisTest // GH-2039
@EnabledOnCommand("LMOVE")
void move() {
K source = keyFactory.instance();
K target = keyFactory.instance();
V v1 = valueFactory.instance();
V v2 = valueFactory.instance();
V v3 = valueFactory.instance();
V v4 = valueFactory.instance();
listOps.rightPushAll(source, v1, v2, v3, v4);
assertThat(listOps.move(ListOperations.MoveFrom.fromHead(source), ListOperations.MoveTo.toTail(target)))
.isEqualTo(v1);
assertThat(listOps.move(ListOperations.MoveFrom.fromTail(source), ListOperations.MoveTo.toHead(target)))
.isEqualTo(v4);
assertThat(listOps.range(source, 0, -1)).containsExactly(v2, v3);
assertThat(listOps.range(target, 0, -1)).containsExactly(v4, v1);
}
@ParameterizedRedisTest // GH-2039
@EnabledOnCommand("BLMOVE")
void moveWithTimeout() {
K source = keyFactory.instance();
K target = keyFactory.instance();
V v1 = valueFactory.instance();
V v4 = valueFactory.instance();
listOps.rightPushAll(source, v1, v4);
assertThat(listOps.move(ListOperations.MoveFrom.fromHead(source), ListOperations.MoveTo.toTail(target)))
.isEqualTo(v1);
assertThat(listOps.move(ListOperations.MoveFrom.fromTail(source), ListOperations.MoveTo.toHead(target)))
.isEqualTo(v4);
assertThat(listOps.move(ListOperations.MoveFrom.fromTail(source), ListOperations.MoveTo.toHead(target),
Duration.ofMillis(10))).isNull();
assertThat(listOps.range(source, 0, -1)).isEmpty();
assertThat(listOps.range(target, 0, -1)).containsExactly(v4, v1);
}
@ParameterizedRedisTest // DATAREDIS-1196
@EnabledOnCommand("LPOS")
void indexOf() {

View File

@@ -45,7 +45,6 @@ import org.springframework.data.redis.test.extension.parametrized.ParameterizedR
@SuppressWarnings("unchecked")
public class DefaultReactiveListOperationsIntegrationTests<K, V> {
private final ReactiveRedisTemplate<K, V> redisTemplate;
private final ReactiveListOperations<K, V> listOperations;
@@ -296,6 +295,71 @@ public class DefaultReactiveListOperationsIntegrationTests<K, V> {
.verifyComplete();
}
@ParameterizedRedisTest // GH-2039
@EnabledOnCommand("LMOVE")
void move() {
K source = keyFactory.instance();
K target = keyFactory.instance();
V v1 = valueFactory.instance();
V v2 = valueFactory.instance();
V v3 = valueFactory.instance();
V v4 = valueFactory.instance();
listOperations.rightPushAll(source, v1, v2, v3, v4).as(StepVerifier::create).expectNext(4L).verifyComplete();
listOperations.move(ListOperations.MoveFrom.fromHead(source), ListOperations.MoveTo.toTail(target))
.as(StepVerifier::create).expectNext(v1).verifyComplete();
listOperations.move(ListOperations.MoveFrom.fromTail(source), ListOperations.MoveTo.toHead(target))
.as(StepVerifier::create).expectNext(v4).verifyComplete();
listOperations.range(source, 0, -1) //
.as(StepVerifier::create) //
.expectNext(v2) //
.expectNext(v3) //
.verifyComplete();
listOperations.range(target, 0, -1) //
.as(StepVerifier::create) //
.expectNext(v4) //
.expectNext(v1) //
.verifyComplete();
}
@ParameterizedRedisTest // GH-2039
@EnabledOnCommand("BLMOVE")
void moveWithTimeout() {
K source = keyFactory.instance();
K target = keyFactory.instance();
V v1 = valueFactory.instance();
V v4 = valueFactory.instance();
listOperations.rightPushAll(source, v1, v4).as(StepVerifier::create).expectNext(2L).verifyComplete();
listOperations
.move(ListOperations.MoveFrom.fromHead(source), ListOperations.MoveTo.toTail(target), Duration.ofMillis(10))
.as(StepVerifier::create).expectNext(v1).verifyComplete();
listOperations
.move(ListOperations.MoveFrom.fromTail(source), ListOperations.MoveTo.toHead(target), Duration.ofMillis(10))
.as(StepVerifier::create).expectNext(v4).verifyComplete();
listOperations
.move(ListOperations.MoveFrom.fromTail(source), ListOperations.MoveTo.toHead(target), Duration.ofMillis(10))
.as(StepVerifier::create).verifyComplete();
listOperations.range(source, 0, -1) //
.as(StepVerifier::create) //
.verifyComplete();
listOperations.range(target, 0, -1) //
.as(StepVerifier::create) //
.expectNext(v4) //
.expectNext(v1) //
.verifyComplete();
}
@ParameterizedRedisTest // DATAREDIS-602
void set() {

View File

@@ -28,6 +28,7 @@ import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.data.redis.ObjectFactory;
import org.springframework.data.redis.connection.RedisListCommands;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.test.condition.EnabledOnCommand;
@@ -38,6 +39,7 @@ import org.springframework.data.redis.test.extension.parametrized.ParameterizedR
*
* @author Costin Leau
* @author Jennifer Hickey
* @author Mark Paluch
*/
public abstract class AbstractRedisListIntegrationTests<T> extends AbstractRedisCollectionIntegrationTests<T> {
@@ -238,6 +240,48 @@ public abstract class AbstractRedisListIntegrationTests<T> extends AbstractRedis
assertThatExceptionOfType(NoSuchElementException.class).isThrownBy(list::remove);
}
@ParameterizedRedisTest // GH-2039
@EnabledOnCommand("LMOVE")
void testMoveFirstTo() {
RedisList<T> target = new DefaultRedisList<T>(template.boundListOps(collection.getKey() + ":target"));
T t1 = getT();
T t2 = getT();
T t3 = getT();
list.add(t1);
list.add(t2);
list.add(t3);
assertThat(list.moveFirstTo(target, RedisListCommands.Direction.first())).isEqualTo(t1);
assertThat(list.moveFirstTo(target, RedisListCommands.Direction.first())).isEqualTo(t2);
assertThat(list.moveFirstTo(target, RedisListCommands.Direction.last())).isEqualTo(t3);
assertThat(list).isEmpty();
assertThat(target).hasSize(3).containsSequence(t2, t1, t3);
}
@ParameterizedRedisTest // GH-2039
@EnabledOnCommand("LMOVE")
void testMoveLastTo() {
RedisList<T> target = new DefaultRedisList<T>(template.boundListOps(collection.getKey() + ":target"));
T t1 = getT();
T t2 = getT();
T t3 = getT();
list.add(t1);
list.add(t2);
list.add(t3);
assertThat(list.moveLastTo(target, RedisListCommands.Direction.first())).isEqualTo(t3);
assertThat(list.moveLastTo(target, RedisListCommands.Direction.first())).isEqualTo(t2);
assertThat(list.moveLastTo(target, RedisListCommands.Direction.last())).isEqualTo(t1);
assertThat(list).isEmpty();
assertThat(target).hasSize(3).containsSequence(t2, t3, t1);
}
@ParameterizedRedisTest
void testRange() {
T t1 = getT();