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

@@ -736,6 +736,44 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
return convertAndReturn(delegate.lInsert(key, where, pivot, value), Converters.identityConverter());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisListCommands#lMove(byte[], byte[], org.springframework.data.redis.connection.RedisListCommands.Direction, org.springframework.data.redis.connection.RedisListCommands.Direction)
*/
@Override
public byte[] lMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to) {
return convertAndReturn(delegate.lMove(sourceKey, destinationKey, from, to), Converters.identityConverter());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisListCommands#bLMove(byte[], byte[], org.springframework.data.redis.connection.RedisListCommands.Direction, org.springframework.data.redis.connection.RedisListCommands.Direction, double)
*/
@Override
public byte[] bLMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to, double timeout) {
return convertAndReturn(delegate.bLMove(sourceKey, destinationKey, from, to, timeout),
Converters.identityConverter());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.StringRedisConnection#lMove(java.lang.String, java.lang.String, org.springframework.data.redis.connection.RedisListCommands.Direction, org.springframework.data.redis.connection.RedisListCommands.Direction)
*/
@Override
public String lMove(String sourceKey, String destinationKey, Direction from, Direction to) {
return convertAndReturn(delegate.lMove(serialize(sourceKey), serialize(destinationKey), from, to), bytesToString);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.StringRedisConnection#bLMove(java.lang.String, java.lang.String, org.springframework.data.redis.connection.RedisListCommands.Direction, org.springframework.data.redis.connection.RedisListCommands.Direction, double)
*/
@Override
public String bLMove(String sourceKey, String destinationKey, Direction from, Direction to, double timeout) {
return convertAndReturn(delegate.bLMove(serialize(sourceKey), serialize(destinationKey), from, to, timeout),
bytesToString);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisListCommands#lLen(byte[])

View File

@@ -714,6 +714,20 @@ public interface DefaultedRedisConnection extends RedisConnection {
return listCommands().lInsert(key, where, pivot, value);
}
/** @deprecated in favor of {@link RedisConnection#listCommands()}}. */
@Override
@Deprecated
default byte[] lMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to) {
return listCommands().lMove(sourceKey, destinationKey, from, to);
}
/** @deprecated in favor of {@link RedisConnection#listCommands()}}. */
@Override
@Deprecated
default byte[] bLMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to, double timeout) {
return listCommands().bLMove(sourceKey, destinationKey, from, to, timeout);
}
/** @deprecated in favor of {@link RedisConnection#listCommands()}}. */
@Override
@Deprecated

View File

@@ -51,7 +51,28 @@ public interface ReactiveListCommands {
* @author Christoph Strobl
*/
enum Direction {
LEFT, RIGHT
LEFT, RIGHT;
/**
* Alias for {@link Direction#LEFT}.
*
* @since 2.6
* @return
*/
public static Direction first() {
return LEFT;
}
/**
* Alias for {@link Direction#RIGHT}.
*
* @since 2.6
* @return
*/
public static Direction last() {
return RIGHT;
}
}
/**
@@ -635,6 +656,189 @@ public interface ReactiveListCommands {
*/
Flux<NumericResponse<LInsertCommand, Long>> lInsert(Publisher<LInsertCommand> commands);
/**
* {@code LMOVE} command parameters.
*
* @author Mark Paluch
* @since 2.6
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
*/
class LMoveCommand extends KeyCommand {
private final @Nullable ByteBuffer destinationKey;
private final @Nullable Direction from;
private final @Nullable Direction to;
public LMoveCommand(@Nullable ByteBuffer sourceKey, @Nullable ByteBuffer destinationKey, @Nullable Direction from,
@Nullable Direction to) {
super(sourceKey);
this.destinationKey = destinationKey;
this.from = from;
this.to = to;
}
/**
* Creates a new {@link LMoveCommand} given a {@link ByteBuffer sourceKey}.
*
* @param sourceKey must not be {@literal null}.
* @param sourceDirection must not be {@literal null}.
* @return a new {@link LMoveCommand} for {@link ByteBuffer value}.
*/
public static LMoveCommand from(ByteBuffer sourceKey, Direction sourceDirection) {
Assert.notNull(sourceKey, "Source key must not be null!");
Assert.notNull(sourceDirection, "Direction must not be null!");
return new LMoveCommand(sourceKey, null, sourceDirection, null);
}
/**
* Applies the {@link ByteBuffer destinationKey}. Constructs a new command instance with all previously configured
* properties.
*
* @param destinationKey must not be {@literal null}.
* @param to must not be {@literal null}.
* @return a new {@link LMoveCommand} with {@literal pivot} applied.
*/
public LMoveCommand to(ByteBuffer destinationKey, Direction destinationDirection) {
Assert.notNull(destinationKey, "Destination key must not be null!");
Assert.notNull(destinationDirection, "Direction must not be null!");
return new LMoveCommand(getKey(), destinationKey, from, destinationDirection);
}
/**
* Applies the {@link Duration timeout}. Constructs a new command instance with all previously configured
* properties.
*
* @param timeout must not be {@literal null}.
* @return a new {@link LMoveCommand} with {@literal pivot} applied.
*/
public BLMoveCommand timeout(Duration timeout) {
Assert.notNull(timeout, "Timeout must not be null!");
return new BLMoveCommand(getKey(), destinationKey, from, to, timeout);
}
@Nullable
public ByteBuffer getDestinationKey() {
return destinationKey;
}
@Nullable
public Direction getFrom() {
return from;
}
@Nullable
public Direction getTo() {
return to;
}
}
/**
* {@code BLMOVE} command parameters.
*
* @author Mark Paluch
* @since 2.6
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
*/
class BLMoveCommand extends LMoveCommand {
private final @Nullable Duration timeout;
private BLMoveCommand(@Nullable ByteBuffer sourceKey, @Nullable ByteBuffer destinationKey, @Nullable Direction from,
@Nullable Direction to, @Nullable Duration timeout) {
super(sourceKey, destinationKey, from, to);
this.timeout = timeout;
}
@Nullable
public Duration getTimeout() {
return timeout;
}
}
/**
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
* {@code to} argument) of the list stored at {@code destinationKey}.
*
* @param sourceKey must not be {@literal null}.
* @param destinationKey must not be {@literal null}.
* @param from must not be {@literal null}.
* @param to must not be {@literal null}.
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
*/
default Mono<ByteBuffer> lMove(ByteBuffer sourceKey, ByteBuffer destinationKey, Direction from, Direction to) {
Assert.notNull(sourceKey, "Source key must not be null!");
Assert.notNull(destinationKey, "Destination key must not be null!");
Assert.notNull(from, "From direction must not be null!");
Assert.notNull(to, "To direction must not be null!");
return lMove(Mono.just(LMoveCommand.from(sourceKey, from).to(destinationKey, to))).map(CommandResponse::getOutput)
.next();
}
/**
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
* {@code to} argument) of the list stored at {@code destinationKey}.
*
* @param commands must not be {@literal null}.
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
*/
Flux<ByteBufferResponse<LMoveCommand>> lMove(Publisher<? extends LMoveCommand> commands);
/**
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
* {@code to} argument) of the list stored at {@code destinationKey}.
*
* @param sourceKey must not be {@literal null}.
* @param destinationKey must not be {@literal null}.
* @param from must not be {@literal null}.
* @param to must not be {@literal null}.
* @param timeout
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
*/
default Mono<ByteBuffer> bLMove(ByteBuffer sourceKey, ByteBuffer destinationKey, Direction from, Direction to,
Duration timeout) {
Assert.notNull(sourceKey, "Source key must not be null!");
Assert.notNull(destinationKey, "Destination key must not be null!");
Assert.notNull(from, "From direction must not be null!");
Assert.notNull(to, "To direction must not be null!");
Assert.notNull(timeout, "Timeout must not be null!");
Assert.isTrue(!timeout.isNegative(), "Timeout must not be negative!");
return bLMove(Mono.just(BLMoveCommand.from(sourceKey, from).to(destinationKey, to).timeout(timeout)))
.map(CommandResponse::getOutput).next();
}
/**
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
* {@code to} argument) of the list stored at {@code destinationKey}.
* <p/>
* <b>Blocks connection</b> until element available or {@code timeout} reached.
*
* @param commands must not be {@literal null}.
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
*/
Flux<ByteBufferResponse<BLMoveCommand>> bLMove(Publisher<BLMoveCommand> commands);
/**
* {@code LSET} command parameters.
*

View File

@@ -37,6 +37,33 @@ public interface RedisListCommands {
BEFORE, AFTER
}
/**
* List move direction.
*
* @since 2.6
*/
enum Direction {
LEFT, RIGHT;
/**
* Alias for {@link Direction#LEFT}.
*
* @return
*/
public static Direction first() {
return LEFT;
}
/**
* Alias for {@link Direction#RIGHT}.
*
* @return
*/
public static Direction last() {
return RIGHT;
}
}
/**
* Append {@code values} to {@code key}.
*
@@ -169,6 +196,43 @@ public interface RedisListCommands {
@Nullable
Long lInsert(byte[] key, Position where, byte[] pivot, byte[] value);
/**
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
* {@code to} argument) of the list stored at {@code destinationKey}.
*
* @param sourceKey must not be {@literal null}.
* @param destinationKey must not be {@literal null}.
* @param from must not be {@literal null}.
* @param to must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
* @see #bLMove(byte[], byte[], Direction, Direction, double)
*/
@Nullable
byte[] lMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to);
/**
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
* {@code to} argument) of the list stored at {@code destinationKey}.
* <p/>
* <b>Blocks connection</b> until element available or {@code timeout} reached.
*
* @param sourceKey must not be {@literal null}.
* @param destinationKey must not be {@literal null}.
* @param from must not be {@literal null}.
* @param to must not be {@literal null}.
* @param timeout
* @return {@literal null} when used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
* @see #lMove(byte[], byte[], Direction, Direction)
*/
@Nullable
byte[] bLMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to, double timeout);
/**
* Set the {@code value} list element at {@code index}.
*

View File

@@ -846,6 +846,43 @@ public interface StringRedisConnection extends RedisConnection {
*/
Long lInsert(String key, Position where, String pivot, String value);
/**
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
* {@code to} argument) of the list stored at {@code destinationKey}.
*
* @param sourceKey must not be {@literal null}.
* @param destinationKey must not be {@literal null}.
* @param from must not be {@literal null}.
* @param to must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
* @see #bLMove(byte[], byte[], Direction, Direction, double)
* @see #lMove(byte[], byte[], Direction, Direction)
*/
@Nullable
String lMove(String sourceKey, String destinationKey, Direction from, Direction to);
/**
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
* {@code to} argument) of the list stored at {@code destinationKey}.
*
* @param sourceKey must not be {@literal null}.
* @param destinationKey must not be {@literal null}.
* @param from must not be {@literal null}.
* @param to must not be {@literal null}.
* @param timeout
* @return {@literal null} when used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: BLMOVE</a>
* @see #lMove(byte[], byte[], Direction, Direction)
* @see #bLMove(byte[], byte[], Direction, Direction, double)
*/
@Nullable
String bLMove(String sourceKey, String destinationKey, Direction from, Direction to, double timeout);
/**
* Set the {@code value} list element at {@code index}.
*

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.redis.connection.jedis;
import redis.clients.jedis.args.ListDirection;
import redis.clients.jedis.params.LPosParams;
import java.util.Arrays;
@@ -220,6 +221,46 @@ class JedisClusterListCommands implements RedisListCommands {
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisListCommands#lMove(byte[], byte[], org.springframework.data.redis.connection.RedisListCommands.Direction, org.springframework.data.redis.connection.RedisListCommands.Direction)
*/
@Override
public byte[] lMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to) {
Assert.notNull(sourceKey, "Source key must not be null!");
Assert.notNull(destinationKey, "Destination key must not be null!");
Assert.notNull(from, "From direction must not be null!");
Assert.notNull(to, "To direction must not be null!");
try {
return connection.getCluster().lmove(sourceKey, destinationKey, ListDirection.valueOf(from.name()),
ListDirection.valueOf(to.name()));
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisListCommands#bLMove(byte[], byte[], org.springframework.data.redis.connection.RedisListCommands.Direction, org.springframework.data.redis.connection.RedisListCommands.Direction, double)
*/
@Override
public byte[] bLMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to, double timeout) {
Assert.notNull(sourceKey, "Source key must not be null!");
Assert.notNull(destinationKey, "Destination key must not be null!");
Assert.notNull(from, "From direction must not be null!");
Assert.notNull(to, "To direction must not be null!");
try {
return connection.getCluster().blmove(sourceKey, destinationKey, ListDirection.valueOf(from.name()),
ListDirection.valueOf(to.name()), timeout);
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisListCommands#lSet(byte[], long, byte[])

View File

@@ -18,6 +18,7 @@ package org.springframework.data.redis.connection.jedis;
import redis.clients.jedis.BinaryJedis;
import redis.clients.jedis.MultiKeyPipelineBase;
import redis.clients.jedis.Protocol;
import redis.clients.jedis.args.ListDirection;
import redis.clients.jedis.params.LPosParams;
import java.util.Collections;
@@ -177,6 +178,38 @@ class JedisListCommands implements RedisListCommands {
JedisConverters.toListPosition(where), pivot, value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisListCommands#lMove(byte[], byte[], org.springframework.data.redis.connection.RedisListCommands.Direction, org.springframework.data.redis.connection.RedisListCommands.Direction)
*/
@Override
public byte[] lMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to) {
Assert.notNull(sourceKey, "Source key must not be null!");
Assert.notNull(destinationKey, "Destination key must not be null!");
Assert.notNull(from, "From direction must not be null!");
Assert.notNull(to, "To direction must not be null!");
return connection.invoke().just(BinaryJedis::lmove, MultiKeyPipelineBase::lmove, sourceKey, destinationKey,
ListDirection.valueOf(from.name()), ListDirection.valueOf(to.name()));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisListCommands#bLMove(byte[], byte[], org.springframework.data.redis.connection.RedisListCommands.Direction, org.springframework.data.redis.connection.RedisListCommands.Direction, double)
*/
@Override
public byte[] bLMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to, double timeout) {
Assert.notNull(sourceKey, "Source key must not be null!");
Assert.notNull(destinationKey, "Destination key must not be null!");
Assert.notNull(from, "From direction must not be null!");
Assert.notNull(to, "To direction must not be null!");
return connection.invoke().just(BinaryJedis::blmove, MultiKeyPipelineBase::blmove, sourceKey, destinationKey,
ListDirection.valueOf(from.name()), ListDirection.valueOf(to.name()), timeout);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisListCommands#lSet(byte[], long, byte[])

View File

@@ -47,6 +47,7 @@ import org.springframework.data.redis.connection.RedisClusterNode.SlotRange;
import org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation;
import org.springframework.data.redis.connection.RedisGeoCommands.GeoRadiusCommandArgs;
import org.springframework.data.redis.connection.RedisListCommands.Direction;
import org.springframework.data.redis.connection.RedisListCommands.Position;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisNode.NodeType;
@@ -1025,6 +1026,27 @@ public abstract class LettuceConverters extends Converters {
return getUpperBound(range).orElse(INDEXED_RANGE_END);
}
static LMoveArgs toLmoveArgs(Enum<?> from, Enum<?> to) {
if (from.name().equals(Direction.LEFT.name()) && to.name().equals(Direction.LEFT.name())) {
return LMoveArgs.Builder.leftLeft();
}
if (from.name().equals(Direction.LEFT.name()) && to.name().equals(Direction.RIGHT.name())) {
return LMoveArgs.Builder.leftRight();
}
if (from.name().equals(Direction.RIGHT.name()) && to.name().equals(Direction.LEFT.name())) {
return LMoveArgs.Builder.rightLeft();
}
if (from.name().equals(Direction.RIGHT.name()) && to.name().equals(Direction.RIGHT.name())) {
return LMoveArgs.Builder.rightRight();
}
throw new IllegalArgumentException(String.format("Unsupported combination of arguments: %s/%s", from, to));
}
/**
* @author Christoph Strobl
* @since 1.8

View File

@@ -72,7 +72,8 @@ class LettuceListCommands implements RedisListCommands {
return connection.invoke().just(RedisListAsyncCommands::lpos, key, element, count, args);
}
return connection.invoke().from(RedisListAsyncCommands::lpos, key, element, args).getOrElse(Collections::singletonList, Collections::emptyList);
return connection.invoke().from(RedisListAsyncCommands::lpos, key, element, args)
.getOrElse(Collections::singletonList, Collections::emptyList);
}
/*
@@ -176,6 +177,39 @@ class LettuceListCommands implements RedisListCommands {
value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisListCommands#lMove(byte[], byte[], org.springframework.data.redis.connection.RedisListCommands.Direction, org.springframework.data.redis.connection.RedisListCommands.Direction)
*/
@Override
public byte[] lMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to) {
Assert.notNull(sourceKey, "Source key must not be null!");
Assert.notNull(destinationKey, "Destination key must not be null!");
Assert.notNull(from, "From direction must not be null!");
Assert.notNull(to, "To direction must not be null!");
return connection.invoke().just(RedisListAsyncCommands::lmove, sourceKey, destinationKey,
LettuceConverters.toLmoveArgs(from, to));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisListCommands#bLMove(byte[], byte[], org.springframework.data.redis.connection.RedisListCommands.Direction, org.springframework.data.redis.connection.RedisListCommands.Direction, double)
*/
@Override
public byte[] bLMove(byte[] sourceKey, byte[] destinationKey, Direction from, Direction to, double timeout) {
Assert.notNull(sourceKey, "Source key must not be null!");
Assert.notNull(destinationKey, "Destination key must not be null!");
Assert.notNull(from, "From direction must not be null!");
Assert.notNull(to, "To direction must not be null!");
return connection.invoke(connection.getAsyncDedicatedConnection()).just(RedisListAsyncCommands::blmove, sourceKey,
destinationKey, LettuceConverters.toLmoveArgs(from, to), timeout);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisListCommands#lSet(byte[], long, byte[])

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.redis.connection.lettuce;
import io.lettuce.core.LMoveArgs;
import io.lettuce.core.LPosArgs;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -22,8 +23,10 @@ import reactor.core.publisher.Mono;
import java.nio.ByteBuffer;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import org.reactivestreams.Publisher;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.Range;
import org.springframework.data.redis.connection.ReactiveListCommands;
@@ -34,6 +37,7 @@ import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyComm
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
import org.springframework.data.redis.connection.ReactiveRedisConnection.RangeCommand;
import org.springframework.data.redis.connection.RedisListCommands.Position;
import org.springframework.data.redis.core.TimeoutUtils;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@@ -212,6 +216,50 @@ class LettuceReactiveListCommands implements ReactiveListCommands {
}));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveListCommands#lMove(Publisher)
*/
@Override
public Flux<ByteBufferResponse<LMoveCommand>> lMove(Publisher<? extends LMoveCommand> commands) {
return connection.execute(cmd -> Flux.from(commands).concatMap(command -> {
Assert.notNull(command.getKey(), "Source key must not be null!");
Assert.notNull(command.getFrom(), "Source direction must not be null!");
Assert.notNull(command.getDestinationKey(), "Destination key must not be null!");
Assert.notNull(command.getTo(), "Destination direction must not be null!");
LMoveArgs lMoveArgs = LettuceConverters.toLmoveArgs(command.getFrom(), command.getTo());
return cmd.lmove(command.getKey(), command.getDestinationKey(), lMoveArgs)
.map(value -> new ByteBufferResponse<>(command, value));
}));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveListCommands#bLMove(Publisher)
*/
@Override
public Flux<ByteBufferResponse<BLMoveCommand>> bLMove(Publisher<BLMoveCommand> commands) {
return connection.executeDedicated(cmd -> Flux.from(commands).concatMap(command -> {
Assert.notNull(command.getKey(), "Source key must not be null!");
Assert.notNull(command.getFrom(), "Source direction must not be null!");
Assert.notNull(command.getDestinationKey(), "Destination key must not be null!");
Assert.notNull(command.getTo(), "Destination direction must not be null!");
Assert.notNull(command.getTimeout(), "Timeout must not be null!");
LMoveArgs lMoveArgs = LettuceConverters.toLmoveArgs(command.getFrom(), command.getTo());
double timeout = TimeoutUtils.toDoubleSeconds(command.getTimeout().toMillis(), TimeUnit.MILLISECONDS);
return cmd.blmove(command.getKey(), command.getDestinationKey(), lMoveArgs, timeout)
.map(value -> new ByteBufferResponse<>(command, value));
}));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveListCommands#lSet(org.reactivestreams.Publisher)

View File

@@ -19,6 +19,7 @@ import java.time.Duration;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.springframework.data.redis.connection.RedisListCommands.Direction;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -139,6 +140,58 @@ public interface BoundListOperations<K, V> extends BoundKeyOperations<K> {
@Nullable
Long rightPush(V pivot, V value);
/**
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
* list stored at the bound key, and pushes the element at the first/last element (head/tail depending on the
* {@code to} argument) of the list stored at {@code destinationKey}.
*
* @param from must not be {@literal null}.
* @param destinationKey must not be {@literal null}.
* @param to must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
*/
@Nullable
V move(Direction from, K destinationKey, Direction to);
/**
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
* list stored at the bound key, and pushes the element at the first/last element (head/tail depending on the
* {@code to} argument) of the list stored at {@code destinationKey}.
* <p/>
* <b>Blocks connection</b> until element available or {@code timeout} reached.
*
* @param from must not be {@literal null}.
* @param destinationKey must not be {@literal null}.
* @param to must not be {@literal null}.
* @param timeout
* @return {@literal null} when used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
*/
@Nullable
V move(Direction from, K destinationKey, Direction to, Duration timeout);
/**
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
* list stored at the bound key, and pushes the element at the first/last element (head/tail depending on the
* {@code to} argument) of the list stored at {@code destinationKey}.
* <p/>
* <b>Blocks connection</b> until element available or {@code timeout} reached.
*
* @param from must not be {@literal null}.
* @param destinationKey must not be {@literal null}.
* @param to must not be {@literal null}.
* @param timeout
* @param unit
* @return {@literal null} when used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
*/
@Nullable
V move(Direction from, K destinationKey, Direction to, long timeout, TimeUnit unit);
/**
* Set the {@code value} list element at {@code index}.
*

View File

@@ -15,16 +15,18 @@
*/
package org.springframework.data.redis.core;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.springframework.data.redis.connection.DataType;
import org.springframework.lang.Nullable;
import org.springframework.data.redis.connection.RedisListCommands.Direction;
/**
* Default implementation for {@link BoundListOperations}.
*
* @author Costin Leau
* @author Mark Paluch
*/
class DefaultBoundListOperations<K, V> extends DefaultBoundKeyOperations<K> implements BoundListOperations<K, V> {
@@ -227,6 +229,33 @@ class DefaultBoundListOperations<K, V> extends DefaultBoundKeyOperations<K> impl
return ops.rightPush(getKey(), pivot, value);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.BoundListOperations#move(org.springframework.data.redis.connection.RedisListCommands.Direction, java.lang.Object, org.springframework.data.redis.connection.RedisListCommands.Direction)
*/
@Override
public V move(Direction from, K destinationKey, Direction to) {
return ops.move(getKey(), from, destinationKey, to);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.BoundListOperations#move(org.springframework.data.redis.connection.RedisListCommands.Direction, java.lang.Object, org.springframework.data.redis.connection.RedisListCommands.Direction, java.time.Duration)
*/
@Override
public V move(Direction from, K destinationKey, Direction to, Duration timeout) {
return ops.move(getKey(), from, destinationKey, to, timeout);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.BoundListOperations#move(org.springframework.data.redis.connection.RedisListCommands.Direction, java.lang.Object, org.springframework.data.redis.connection.RedisListCommands.Direction, long, java.util.concurrent.TimeUnit)
*/
@Override
public V move(Direction from, K destinationKey, Direction to, long timeout, TimeUnit unit) {
return ops.move(getKey(), from, destinationKey, to, timeout, unit);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.BoundListOperations#trim(long, long)

View File

@@ -20,6 +20,7 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisListCommands.Direction;
import org.springframework.data.redis.connection.RedisListCommands.Position;
import org.springframework.util.CollectionUtils;
@@ -363,6 +364,40 @@ class DefaultListOperations<K, V> extends AbstractOperations<K, V> implements Li
}, true);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ListOperations#move(java.lang.Object, org.springframework.data.redis.connection.RedisListCommands.Direction, java.lang.Object, org.springframework.data.redis.connection.RedisListCommands.Direction)
*/
@Override
public V move(K sourceKey, Direction from, K destinationKey, Direction to) {
byte[] rawDestKey = rawKey(destinationKey);
return execute(new ValueDeserializingRedisCallback(sourceKey) {
@Override
protected byte[] inRedis(byte[] rawSourceKey, RedisConnection connection) {
return connection.lMove(rawSourceKey, rawDestKey, from, to);
}
}, true);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ListOperations#move(java.lang.Object, org.springframework.data.redis.connection.RedisListCommands.Direction, java.lang.Object, org.springframework.data.redis.connection.RedisListCommands.Direction, long, java.util.concurrent.TimeUnit)
*/
@Override
public V move(K sourceKey, Direction from, K destinationKey, Direction to, long timeout, TimeUnit unit) {
byte[] rawDestKey = rawKey(destinationKey);
return execute(new ValueDeserializingRedisCallback(sourceKey) {
@Override
protected byte[] inRedis(byte[] rawSourceKey, RedisConnection connection) {
return connection.bLMove(rawSourceKey, rawDestKey, from, to, TimeoutUtils.toDoubleSeconds(timeout, unit));
}
}, true);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ListOperations#set(java.lang.Object, long, java.lang.Object)

View File

@@ -28,6 +28,7 @@ import java.util.function.Function;
import org.reactivestreams.Publisher;
import org.springframework.data.redis.connection.ReactiveListCommands;
import org.springframework.data.redis.connection.ReactiveListCommands.Direction;
import org.springframework.data.redis.connection.ReactiveListCommands.LPosCommand;
import org.springframework.data.redis.connection.RedisListCommands.Position;
import org.springframework.data.redis.serializer.RedisSerializationContext;
@@ -199,6 +200,39 @@ class DefaultReactiveListOperations<K, V> implements ReactiveListOperations<K, V
return createMono(connection -> connection.lInsert(rawKey(key), Position.AFTER, rawValue(pivot), rawValue(value)));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveListOperations#move(K, Direction, K, Direction)
*/
@Override
public Mono<V> move(K sourceKey, Direction from, K destinationKey, Direction to) {
Assert.notNull(sourceKey, "Source key must not be null!");
Assert.notNull(destinationKey, "Destination key must not be null!");
Assert.notNull(from, "From direction must not be null!");
Assert.notNull(to, "To direction must not be null!");
return createMono(
connection -> connection.lMove(rawKey(sourceKey), rawKey(destinationKey), from, to).map(this::readValue));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveListOperations#move(K, Direction, K, Direction, Duration)
*/
@Override
public Mono<V> move(K sourceKey, Direction from, K destinationKey, Direction to, Duration timeout) {
Assert.notNull(sourceKey, "Source key must not be null!");
Assert.notNull(destinationKey, "Destination key must not be null!");
Assert.notNull(from, "From direction must not be null!");
Assert.notNull(to, "To direction must not be null!");
Assert.notNull(timeout, "Timeout must not be null!");
return createMono(connection -> connection.bLMove(rawKey(sourceKey), rawKey(destinationKey), from, to, timeout)
.map(this::readValue));
}
/* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveListOperations#set(java.lang.Object, long, java.lang.Object)
*/

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.data.redis.core;
import static org.springframework.data.redis.connection.RedisListCommands.*;
import java.time.Duration;
import java.util.Collection;
import java.util.List;
@@ -181,6 +183,168 @@ public interface ListOperations<K, V> {
@Nullable
Long rightPush(K key, V pivot, V value);
/**
* Value object representing the {@code where from} part for the {@code LMOVE} command.
*
* @param <K>
* @since 2.6
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
*/
class MoveFrom<K> {
final K key;
final Direction direction;
MoveFrom(K key, Direction direction) {
this.key = key;
this.direction = direction;
}
public static <K> MoveFrom<K> fromHead(K key) {
return new MoveFrom<>(key, Direction.first());
}
public static <K> MoveFrom<K> fromTail(K key) {
return new MoveFrom<>(key, Direction.last());
}
}
/**
* Value object representing the {@code where to} from part for the {@code LMOVE} command.
*
* @param <K>
* @since 2.6
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
*/
class MoveTo<K> {
final K key;
final Direction direction;
MoveTo(K key, Direction direction) {
this.key = key;
this.direction = direction;
}
public static <K> MoveTo<K> toHead(K key) {
return new MoveTo<>(key, Direction.first());
}
public static <K> MoveTo<K> toTail(K key) {
return new MoveTo<>(key, Direction.last());
}
}
/**
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
* {@code to} argument) of the list stored at {@code destinationKey}.
*
* @param from must not be {@literal null}.
* @param to must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
*/
@Nullable
default V move(MoveFrom<K> from, MoveTo<K> to) {
Assert.notNull(from, "Move from must not be null");
Assert.notNull(to, "Move to must not be null");
return move(from.key, from.direction, to.key, to.direction);
}
/**
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
* {@code to} argument) of the list stored at {@code destinationKey}.
*
* @param sourceKey must not be {@literal null}.
* @param from must not be {@literal null}.
* @param destinationKey must not be {@literal null}.
* @param to must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
*/
@Nullable
V move(K sourceKey, Direction from, K destinationKey, Direction to);
/**
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
* {@code to} argument) of the list stored at {@code destinationKey}.
* <p/>
* <b>Blocks connection</b> until element available or {@code timeout} reached.
*
* @param from must not be {@literal null}.
* @param to must not be {@literal null}.
* @param timeout must not be {@literal null} or negative.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
*/
@Nullable
default V move(MoveFrom<K> from, MoveTo<K> to, Duration timeout) {
Assert.notNull(from, "Move from must not be null");
Assert.notNull(to, "Move to must not be null");
Assert.notNull(timeout, "Timeout must not be null");
Assert.isTrue(!timeout.isNegative(), "Timeout must not be negative");
return move(from.key, from.direction, to.key, to.direction,
TimeoutUtils.toMillis(timeout.toMillis(), TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS);
}
/**
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
* {@code to} argument) of the list stored at {@code destinationKey}.
* <p/>
* <b>Blocks connection</b> until element available or {@code timeout} reached.
*
* @param sourceKey must not be {@literal null}.
* @param from must not be {@literal null}.
* @param destinationKey must not be {@literal null}.
* @param to must not be {@literal null}.
* @param timeout must not be {@literal null} or negative.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
*/
@Nullable
default V move(K sourceKey, Direction from, K destinationKey, Direction to, Duration timeout) {
Assert.notNull(timeout, "Timeout must not be null");
Assert.isTrue(!timeout.isNegative(), "Timeout must not be negative");
return move(sourceKey, from, destinationKey, to, TimeoutUtils.toMillis(timeout.toMillis(), TimeUnit.MILLISECONDS),
TimeUnit.MILLISECONDS);
}
/**
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
* {@code to} argument) of the list stored at {@code destinationKey}.
* <p/>
* <b>Blocks connection</b> until element available or {@code timeout} reached.
*
* @param sourceKey must not be {@literal null}.
* @param from must not be {@literal null}.
* @param destinationKey must not be {@literal null}.
* @param to must not be {@literal null}.
* @param timeout
* @param unit
* @return {@literal null} when used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
*/
@Nullable
V move(K sourceKey, Direction from, K destinationKey, Direction to, long timeout, TimeUnit unit);
/**
* Set the {@code value} list element at {@code index}.
*

View File

@@ -15,12 +15,18 @@
*/
package org.springframework.data.redis.core;
import static org.springframework.data.redis.connection.ReactiveListCommands.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
import java.util.Collection;
import org.springframework.data.redis.core.ListOperations.MoveFrom;
import org.springframework.data.redis.core.ListOperations.MoveTo;
import org.springframework.util.Assert;
/**
* Redis list specific operations.
*
@@ -165,6 +171,83 @@ public interface ReactiveListOperations<K, V> {
*/
Mono<Long> rightPush(K key, V pivot, V value);
/**
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
* {@code to} argument) of the list stored at {@code destinationKey}.
*
* @param from must not be {@literal null}.
* @param to must not be {@literal null}.
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
*/
default Mono<V> move(MoveFrom<K> from, MoveTo<K> to) {
Assert.notNull(from, "Move from must not be null");
Assert.notNull(to, "Move to must not be null");
return move(from.key, Direction.valueOf(from.direction.name()), to.key, Direction.valueOf(to.direction.name()));
}
/**
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
* {@code to} argument) of the list stored at {@code destinationKey}.
*
* @param sourceKey must not be {@literal null}.
* @param from must not be {@literal null}.
* @param destinationKey must not be {@literal null}.
* @param to must not be {@literal null}.
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/lmove">Redis Documentation: LMOVE</a>
*/
Mono<V> move(K sourceKey, Direction from, K destinationKey, Direction to);
/**
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
* {@code to} argument) of the list stored at {@code destinationKey}.
* <p/>
* <b>Blocks connection</b> until element available or {@code timeout} reached.
*
* @param from must not be {@literal null}.
* @param to must not be {@literal null}.
* @param timeout
* @return
* @since 2.6
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
*/
default Mono<V> move(MoveFrom<K> from, MoveTo<K> to, Duration timeout) {
Assert.notNull(from, "Move from must not be null");
Assert.notNull(to, "Move to must not be null");
Assert.notNull(timeout, "Timeout must not be null");
Assert.isTrue(!timeout.isNegative(), "Timeout must not be negative");
return move(from.key, Direction.valueOf(from.direction.name()), to.key, Direction.valueOf(to.direction.name()),
timeout);
}
/**
* Atomically returns and removes the first/last element (head/tail depending on the {@code from} argument) of the
* list stored at {@code sourceKey}, and pushes the element at the first/last element (head/tail depending on the
* {@code to} argument) of the list stored at {@code destinationKey}.
* <p/>
* <b>Blocks connection</b> until element available or {@code timeout} reached.
*
* @param sourceKey must not be {@literal null}.
* @param from must not be {@literal null}.
* @param destinationKey must not be {@literal null}.
* @param to must not be {@literal null}.
* @param timeout
* @return {@literal null} when used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/blmove">Redis Documentation: BLMOVE</a>
*/
Mono<V> move(K sourceKey, Direction from, K destinationKey, Direction to, Duration timeout);
/**
* Set the {@code value} list element at {@code index}.
*

View File

@@ -64,7 +64,26 @@ public abstract class TimeoutUtils {
*/
public static long toSeconds(long timeout, TimeUnit unit) {
return roundUpIfNecessary(timeout, unit.toSeconds(timeout));
}
/**
* Converts the given timeout to seconds with a fraction of seconds.
*
* @param timeout The timeout to convert
* @param unit The timeout's unit
* @return The converted timeout
* @since 2.6
*/
public static double toDoubleSeconds(long timeout, TimeUnit unit) {
switch (unit) {
case MILLISECONDS:
case MICROSECONDS:
case NANOSECONDS:
return unit.toMillis(timeout) / 1000d;
default:
return unit.toSeconds(timeout);
}
}
/**

View File

@@ -24,9 +24,11 @@ import java.util.NoSuchElementException;
import java.util.concurrent.TimeUnit;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.RedisListCommands;
import org.springframework.data.redis.core.BoundListOperations;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Default implementation for {@link RedisList}. Suitable for not just lists, but also queues (FIFO ordering) or stacks
@@ -36,6 +38,7 @@ import org.springframework.lang.Nullable;
*
* @author Costin Leau
* @author Christoph Strobl
* @author Mark Paluch
*/
public class DefaultRedisList<E> extends AbstractRedisCollection<E> implements RedisList<E> {
@@ -100,6 +103,78 @@ public class DefaultRedisList<E> extends AbstractRedisCollection<E> implements R
capped = (maxSize > 0);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisList#moveFirstTo(org.springframework.data.redis.support.collections.RedisList, org.springframework.data.redis.connection.RedisListCommands.Direction)
*/
@Override
public E moveFirstTo(RedisList<E> destination, RedisListCommands.Direction destinationPosition) {
Assert.notNull(destination, "Destination must not be null");
Assert.notNull(destinationPosition, "Destination position must not be null");
E result = listOps.move(RedisListCommands.Direction.first(), destination.getKey(), destinationPosition);
potentiallyCap(destination);
return result;
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisList#moveFirstTo(org.springframework.data.redis.support.collections.RedisList, org.springframework.data.redis.connection.RedisListCommands.Direction, long, java.util.concurrent.TimeUnit)
*/
@Override
public E moveFirstTo(RedisList<E> destination, RedisListCommands.Direction destinationPosition, long timeout,
TimeUnit unit) {
Assert.notNull(destination, "Destination must not be null");
Assert.notNull(destinationPosition, "Destination position must not be null");
Assert.notNull(unit, "TimeUnit must not be null");
E result = listOps.move(RedisListCommands.Direction.first(), destination.getKey(), destinationPosition, timeout,
unit);
potentiallyCap(destination);
return result;
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisList#moveLastTo(org.springframework.data.redis.support.collections.RedisList, org.springframework.data.redis.connection.RedisListCommands.Direction)
*/
@Override
public E moveLastTo(RedisList<E> destination, RedisListCommands.Direction destinationPosition) {
Assert.notNull(destination, "Destination must not be null");
Assert.notNull(destinationPosition, "Destination position must not be null");
E result = listOps.move(RedisListCommands.Direction.last(), destination.getKey(), destinationPosition);
potentiallyCap(destination);
return result;
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisList#moveLastTo(org.springframework.data.redis.support.collections.RedisList, org.springframework.data.redis.connection.RedisListCommands.Direction, long, java.util.concurrent.TimeUnit)
*/
@Override
public E moveLastTo(RedisList<E> destination, RedisListCommands.Direction destinationPosition, long timeout,
TimeUnit unit) {
Assert.notNull(destination, "Destination must not be null");
Assert.notNull(destinationPosition, "Destination position must not be null");
Assert.notNull(unit, "TimeUnit must not be null");
E result = listOps.move(RedisListCommands.Direction.last(), destination.getKey(), destinationPosition, timeout,
unit);
potentiallyCap(destination);
return result;
}
private void potentiallyCap(RedisList<E> destination) {
if (destination instanceof DefaultRedisList) {
((DefaultRedisList<Object>) destination).cap();
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisList#range(long, long)

View File

@@ -15,19 +15,146 @@
*/
package org.springframework.data.redis.support.collections;
import static org.springframework.data.redis.connection.RedisListCommands.*;
import java.time.Duration;
import java.util.Deque;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.TimeUnit;
import org.springframework.data.redis.core.TimeoutUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Redis extension for the {@link List} contract. Supports {@link List}, {@link Queue} and {@link Deque} contracts as
* well as their equivalent blocking siblings {@link BlockingDeque} and {@link BlockingDeque}.
*
* @author Costin Leau
* @author Mark Paluch
*/
public interface RedisList<E> extends RedisCollection<E>, List<E>, BlockingDeque<E> {
/**
* Atomically returns and removes the first element of the list stored at the bound key, and pushes the element at the
* first/last element (head/tail depending on the {@link Direction destinationPosition} argument) of the list stored
* at {@link RedisList destination}.
*
* @param destination must not be {@literal null}.
* @param destinationPosition must not be {@literal null}.
* @return
* @since 2.6
* @see Direction#first()
* @see Direction#last()
*/
@Nullable
E moveFirstTo(RedisList<E> destination, Direction destinationPosition);
/**
* Atomically returns and removes the first element of the list stored at the bound key, and pushes the element at the
* first/last element (head/tail depending on the {@link Direction destinationPosition} argument) of the list stored
* at {@link RedisList destination}.
* <p/>
* <b>Blocks connection</b> until element available or {@code timeout} reached.
*
* @param destination must not be {@literal null}.
* @param destinationPosition must not be {@literal null}.
* @param timeout must not be {@literal null} or negative.
* @return
* @since 2.6
* @see Direction#first()
* @see Direction#last()
*/
@Nullable
default E moveFirstTo(RedisList<E> destination, Direction destinationPosition, Duration timeout) {
Assert.notNull(timeout, "Timeout must not be null");
Assert.isTrue(!timeout.isNegative(), "Timeout must not be negative");
return moveFirstTo(destination, destinationPosition,
TimeoutUtils.toMillis(timeout.toMillis(), TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS);
}
/**
* Atomically returns and removes the first element of the list stored at the bound key, and pushes the element at the
* first/last element (head/tail depending on the {@link Direction destinationPosition} argument) of the list stored
* at {@link RedisList destination}.
* <p/>
* <b>Blocks connection</b> until element available or {@code timeout} reached.
*
* @param destination must not be {@literal null}.
* @param destinationPosition must not be {@literal null}.
* @param timeout
* @param unit must not be {@literal null}.
* @return
* @since 2.6
* @see Direction#first()
* @see Direction#last()
*/
@Nullable
E moveFirstTo(RedisList<E> destination, Direction destinationPosition, long timeout, TimeUnit unit);
/**
* Atomically returns and removes the last element of the list stored at the bound key, and pushes the element at the
* first/last element (head/tail depending on the {@link Direction destinationPosition} argument) of the list stored
* at {@link RedisList destination}.
*
* @param destination must not be {@literal null}.
* @param destinationPosition must not be {@literal null}.
* @return
* @since 2.6
* @see Direction#first()
* @see Direction#last()
*/
@Nullable
E moveLastTo(RedisList<E> destination, Direction destinationPosition);
/**
* Atomically returns and removes the last element of the list stored at the bound key, and pushes the element at the
* first/last element (head/tail depending on the {@link Direction destinationPosition} argument) of the list stored
* at {@link RedisList destination}.
* <p/>
* <b>Blocks connection</b> until element available or {@code timeout} reached.
*
* @param destination must not be {@literal null}.
* @param destinationPosition must not be {@literal null}.
* @param timeout must not be {@literal null} or negative.
* @return
* @since 2.6
* @see Direction#first()
* @see Direction#last()
*/
@Nullable
default E moveLastTo(RedisList<E> destination, Direction destinationPosition, Duration timeout) {
Assert.notNull(timeout, "Timeout must not be null");
Assert.isTrue(!timeout.isNegative(), "Timeout must not be negative");
return moveLastTo(destination, destinationPosition,
TimeoutUtils.toMillis(timeout.toMillis(), TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS);
}
/**
* Atomically returns and removes the last element of the list stored at the bound key, and pushes the element at the
* first/last element (head/tail depending on the {@link Direction destinationPosition} argument) of the list stored
* at {@link RedisList destination}.
* <p/>
* <b>Blocks connection</b> until element available or {@code timeout} reached.
*
* @param destination must not be {@literal null}.
* @param destinationPosition must not be {@literal null}.
* @param timeout
* @param unit must not be {@literal null}.
* @return
* @since 2.6
* @see Direction#first()
* @see Direction#last()
*/
@Nullable
E moveLastTo(RedisList<E> destination, Direction destinationPosition, long timeout, TimeUnit unit);
List<E> range(long begin, long end);
RedisList<E> trim(int begin, int end);