Polishing.

Extend copy(…) command with replace argument. Add support for reactive copy(…). Fix connection unit tests. Reorder methods.

Add since tags.

See #2040
Original pull request: #2059.
This commit is contained in:
Mark Paluch
2021-06-15 11:53:51 +02:00
parent 1a4eef347e
commit 7cd0669e86
21 changed files with 340 additions and 103 deletions

View File

@@ -241,6 +241,15 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
delegate.close();
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[], boolean)
*/
@Override
public Boolean copy(byte[] sourceKey, byte[] targetKey, boolean replace) {
return convertAndReturn(delegate.copy(sourceKey, targetKey, replace), Converters.identityConverter());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisServerCommands#dbSize()
@@ -268,6 +277,7 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
return convertAndReturn(delegate.decrBy(key, value), Converters.identityConverter());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#del(byte[][])
@@ -277,15 +287,6 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
return convertAndReturn(delegate.del(keys), Converters.identityConverter());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[])
*/
@Override
public Boolean copy(byte[] sourceKey, byte[] targetKey) {
return convertAndReturn(delegate.copy(sourceKey, targetKey), Converters.identityConverter());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][])
@@ -1900,6 +1901,14 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
return convertAndReturn(delegate.bRPopLPush(timeout, serialize(srcKey), serialize(dstKey)), bytesToString);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.StringRedisConnection#copy(java.lang.String, java.lang.String, boolean)
*/
@Override
public Boolean copy(String sourceKey, String targetKey, boolean replace) {
return copy(serialize(sourceKey), serialize(targetKey), replace);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.StringRedisConnection#decr(java.lang.String)
@@ -1927,14 +1936,6 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
return del(serializeMulti(keys));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.StringRedisConnection#copy(java.lang.String[])
*/
@Override
public Boolean copy(String sourceKey, String targetKey) {
return copy(serialize(sourceKey), serialize(targetKey));
}
/*
* (non-Javadoc)

View File

@@ -64,6 +64,13 @@ public interface DefaultedRedisConnection extends RedisConnection {
// KEY COMMANDS
/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
@Override
@Deprecated
default Boolean copy(byte[] sourceKey, byte[] targetKey, boolean replace) {
return keyCommands().copy(sourceKey, targetKey, replace);
}
/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
@Override
@Deprecated
@@ -85,13 +92,6 @@ public interface DefaultedRedisConnection extends RedisConnection {
return keyCommands().del(keys);
}
/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
@Override
@Deprecated
default Boolean copy(byte[] sourceKey, byte[] targetKey) {
return keyCommands().copy(sourceKey, targetKey);
}
/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
@Override
@Deprecated

View File

@@ -43,6 +43,124 @@ import org.springframework.util.Assert;
*/
public interface ReactiveKeyCommands {
/**
* {@code MOVE} command parameters.
*
* @author Mark Paluch
* @see <a href="https://redis.io/commands/move">Redis Documentation: MOVE</a>
*/
class CopyCommand extends KeyCommand {
private final @Nullable ByteBuffer target;
private final boolean replace;
private final @Nullable Integer database;
public CopyCommand(ByteBuffer key, @Nullable ByteBuffer target, boolean replace, @Nullable Integer database) {
super(key);
this.target = target;
this.replace = replace;
this.database = database;
}
/**
* Creates a new {@link CopyCommand} given a {@link ByteBuffer key}.
*
* @param key must not be {@literal null}.
* @return a new {@link CopyCommand} for {@link ByteBuffer key}.
*/
public static CopyCommand key(ByteBuffer key) {
Assert.notNull(key, "Key must not be null!");
return new CopyCommand(key, null, false, null);
}
/**
* Applies the {@link ByteBuffer targetKey}. Constructs a new command instance with all previously configured
* properties.
*
* @param targetKey must not be {@literal null}.
* @return a new {@link CopyCommand} with {@literal database} applied.
*/
public CopyCommand to(ByteBuffer targetKey) {
Assert.notNull(targetKey, "Key must not be null!");
return new CopyCommand(getKey(), targetKey, isReplace(), database);
}
/**
* Applies {@code replace}. Constructs a new command instance with all previously configured properties.
*
* @param key must not be {@literal null}.
* @return a new {@link CopyCommand} with {@literal replace} applied.
*/
public CopyCommand replace(boolean replace) {
return new CopyCommand(getKey(), this.target, replace, database);
}
/**
* Applies the {@literal database} index. Constructs a new command instance with all previously configured
* properties.
*
* @param database
* @return a new {@link CopyCommand} with {@literal database} applied.
*/
public CopyCommand database(int database) {
return new CopyCommand(getKey(), this.target, isReplace(), database);
}
/**
* @return can be {@literal null}.
*/
@Nullable
public ByteBuffer getTarget() {
return target;
}
public boolean isReplace() {
return replace;
}
/**
* @return can be {@literal null}.
*/
@Nullable
public Integer getDatabase() {
return database;
}
}
/**
* Copy given {@code key} to a target {@code key}.
*
* @param sourceKey must not be {@literal null}.
* @param targetKey must not be {@literal null}.
* @param replace whether to replace existing keys.
* @return
* @see <a href="https://redis.io/commands/copy">Redis Documentation: COPY</a>
* @since 2.6
*/
default Mono<Boolean> copy(ByteBuffer sourceKey, ByteBuffer targetKey, boolean replace) {
Assert.notNull(sourceKey, "Source key must not be null!");
Assert.notNull(targetKey, "Targetk ey must not be null!");
return copy(Mono.just(CopyCommand.key(sourceKey).to(targetKey).replace(replace))).next()
.map(BooleanResponse::getOutput);
}
/**
* Copy keys one-by-one.
*
* @param commands must not be {@literal null}.
* @return {@link Flux} of {@link BooleanResponse} holding the {@literal key} to move along with the copy result.
* @see <a href="https://redis.io/commands/copy">Redis Documentation: COPY</a>
* @since 2.6
*/
Flux<BooleanResponse<CopyCommand>> copy(Publisher<CopyCommand> commands);
/**
* Determine if given {@literal key} exists.
*
@@ -670,7 +788,7 @@ public interface ReactiveKeyCommands {
* Creates a new {@link MoveCommand} given a {@link ByteBuffer key}.
*
* @param key must not be {@literal null}.
* @return a new {@link ExpireCommand} for {@link ByteBuffer key}.
* @return a new {@link MoveCommand} for {@link ByteBuffer key}.
*/
public static MoveCommand key(ByteBuffer key) {

View File

@@ -35,6 +35,19 @@ import org.springframework.util.Assert;
*/
public interface RedisKeyCommands {
/**
* Copy given {@code sourceKey} to {@code targetKey}.
*
* @param sourceKey must not be {@literal null}.
* @param targetKey must not be {@literal null}.
* @param replace whether to replace existing keys.
* @return {@literal null} when used in pipeline / transaction.
* @see <a href="https://redis.io/commands/copy">Redis Documentation: COPY</a>
* @since 2.6
*/
@Nullable
Boolean copy(byte[] sourceKey, byte[] targetKey, boolean replace);
/**
* Determine if given {@code key} exists.
*
@@ -72,17 +85,6 @@ public interface RedisKeyCommands {
@Nullable
Long del(byte[]... keys);
/**
* Copy given {@code sourceKey} to {@code targetKey}.
*
* @param sourceKey must not be {@literal null}.
* @param targetKey must not be {@literal null}.
* @return
* @see <a href="https://redis.io/commands/copy">Redis Documentation: COPY</a>
*/
@Nullable
Boolean copy(byte[] sourceKey, byte[] targetKey);
/**
* Unlink the {@code keys} from the keyspace. Unlike with {@link #del(byte[]...)} the actual memory reclaiming here
* happens asynchronously.

View File

@@ -139,11 +139,12 @@ public interface StringRedisConnection extends RedisConnection {
*
* @param sourceKey must not be {@literal null}.
* @param targetKey must not be {@literal null}.
* @return
* @param replace whether to replace existing keys.
* @return {@literal null} when used in pipeline / transaction.
* @see <a href="https://redis.io/commands/copy">Redis Documentation: COPY</a>
* @see RedisKeyCommands#copy(byte[], byte[])
*/
Boolean copy(String sourceKey, String targetKey);
Boolean copy(String sourceKey, String targetKey, boolean replace);
/**
* Unlink the {@code keys} from the keyspace. Unlike with {@link #del(String...)} the actual memory reclaiming here

View File

@@ -64,6 +64,19 @@ class JedisClusterKeyCommands implements RedisKeyCommands {
this.connection = connection;
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[])
*/
@Override
public Boolean copy(byte[] sourceKey, byte[] targetKey, boolean replace) {
Assert.notNull(sourceKey, "source key must not be null!");
Assert.notNull(targetKey, "target key must not be null!");
return connection.getCluster().copy(sourceKey, targetKey, replace);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#del(byte[][])
@@ -88,18 +101,6 @@ class JedisClusterKeyCommands implements RedisKeyCommands {
.resultsAsList().size();
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[])
*/
@Override
public Boolean copy(byte[] sourceKey, byte[] targetKey) {
Assert.notNull(sourceKey, "source key must not be null!");
Assert.notNull(targetKey, "target key must not be null!");
return connection.getCluster().copy(sourceKey, targetKey, false);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][])

View File

@@ -95,11 +95,12 @@ class JedisKeyCommands implements RedisKeyCommands {
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[])
*/
public Boolean copy(byte[] sourceKey, byte[] targetKey) {
public Boolean copy(byte[] sourceKey, byte[] targetKey, boolean replace) {
Assert.notNull(sourceKey, "source key must not be null!");
Assert.notNull(targetKey, "target key must not be null!");
return connection.invoke().just(BinaryJedis::copy, MultiKeyPipelineBase::copy, sourceKey, targetKey, false);
return connection.invoke().just(BinaryJedis::copy, MultiKeyPipelineBase::copy, sourceKey, targetKey, replace);
}
/*

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.redis.connection.lettuce;
import io.lettuce.core.CopyArgs;
import io.lettuce.core.KeyScanCursor;
import io.lettuce.core.RestoreArgs;
import io.lettuce.core.ScanArgs;
@@ -52,6 +53,20 @@ class LettuceKeyCommands implements RedisKeyCommands {
this.connection = connection;
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[])
*/
@Override
public Boolean copy(byte[] sourceKey, byte[] targetKey, boolean replace) {
Assert.notNull(sourceKey, "source key must not be null!");
Assert.notNull(targetKey, "target key must not be null!");
return connection.invoke().just(RedisKeyAsyncCommands::copy, sourceKey, targetKey,
CopyArgs.Builder.replace(replace));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#exists(byte[])
@@ -91,17 +106,6 @@ class LettuceKeyCommands implements RedisKeyCommands {
return connection.invoke().just(RedisKeyAsyncCommands::del, keys);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisKeyCommands#copy(byte[], byte[])
*/
@Override
public Boolean copy(byte[] sourceKey, byte[] targetKey) {
Assert.notNull(sourceKey, "source key must not be null!");
Assert.notNull(targetKey, "target key must not be null!");
return connection.invoke().just(RedisKeyAsyncCommands::copy, targetKey, sourceKey);
}
/*
* (non-Javadoc)

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.redis.connection.lettuce;
import io.lettuce.core.CopyArgs;
import io.lettuce.core.ScanStream;
import io.lettuce.core.api.reactive.RedisKeyReactiveCommands;
import reactor.core.publisher.Flux;
@@ -59,6 +60,27 @@ class LettuceReactiveKeyCommands implements ReactiveKeyCommands {
this.connection = connection;
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#copy(org.reactivestreams.Publisher)
*/
@Override
public Flux<BooleanResponse<CopyCommand>> copy(Publisher<CopyCommand> commands) {
return connection.execute(cmd -> Flux.from(commands).concatMap((command) -> {
Assert.notNull(command.getKey(), "Key must not be null!");
CopyArgs copyArgs = CopyArgs.Builder.replace(command.isReplace());
if (command.getDatabase() != null) {
copyArgs.destinationDb(command.getDatabase());
}
return cmd.copy(command.getKey(), command.getTarget(), copyArgs)
.map((value) -> new BooleanResponse<>(command, value));
}));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#exists(org.reactivestreams.Publisher)

View File

@@ -163,6 +163,17 @@ public interface ReactiveRedisOperations<K, V> {
// Methods dealing with Redis Keys
// -------------------------------------------------------------------------
/**
* Copy given {@code sourceKey} to {@code targetKey}.
*
* @param sourceKey must not be {@literal null}.
* @param targetKey must not be {@literal null}.
* @return
* @see <a href="https://redis.io/commands/copy">Redis Documentation: COPY</a>
* @since 2.6
*/
Mono<Boolean> copy(K sourceKey, K targetKey, boolean replace);
/**
* Determine if given {@code key} exists.
*

View File

@@ -256,6 +256,19 @@ public class ReactiveRedisTemplate<K, V> implements ReactiveRedisOperations<K, V
// Methods dealing with Redis keys
// -------------------------------------------------------------------------
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveRedisOperations#copy(java.lang.Object, java.lang.Object, boolean)
*/
@Override
public Mono<Boolean> copy(K sourceKey, K targetKey, boolean replace) {
Assert.notNull(sourceKey, "Source key must not be null!");
Assert.notNull(targetKey, "Target key must not be null!");
return createMono(connection -> connection.keyCommands().copy(rawKey(sourceKey), rawKey(targetKey), replace));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveRedisOperations#hasKey(java.lang.Object)

View File

@@ -158,6 +158,19 @@ public interface RedisOperations<K, V> {
// Methods dealing with Redis Keys
// -------------------------------------------------------------------------
/**
* Copy given {@code sourceKey} to {@code targetKey}.
*
* @param sourceKey must not be {@literal null}.
* @param targetKey must not be {@literal null}.
* @param replace whether the key was copied. {@literal null} when used in pipeline / transaction.
* @return
* @see <a href="https://redis.io/commands/copy">Redis Documentation: COPY</a>
* @since 2.6
*/
@Nullable
Boolean copy(K sourceKey, K targetKey, boolean replace);
/**
* Determine if given {@code key} exists.
*
@@ -180,6 +193,7 @@ public interface RedisOperations<K, V> {
@Nullable
Long countExistingKeys(Collection<K> keys);
/**
* Delete given {@code key}.
*
@@ -200,17 +214,6 @@ public interface RedisOperations<K, V> {
@Nullable
Long delete(Collection<K> keys);
/**
* Copy given {@code sourceKey} to {@code targetKey}.
*
* @param sourceKey must not be {@literal null}.
* @param targetKey must not be {@literal null}.
* @return
* @see <a href="https://redis.io/commands/copy">Redis Documentation: COPY</a>
*/
@Nullable
Boolean copy(K sourceKey, K targetKey);
/**
* Unlink the {@code key} from the keyspace. Unlike with {@link #delete(Object)} the actual memory reclaiming here
* happens asynchronously.

View File

@@ -698,6 +698,19 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
return raw == null ? Collections.emptyList() : raw;
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.RedisOperations#delete(java.lang.Object, java.lang.Object, boolean)
*/
@Override
public Boolean copy(K source, K target, boolean replace) {
byte[] sourceKey = rawKey(source);
byte[] targetKey = rawKey(target);
return execute(connection -> connection.copy(sourceKey, targetKey, replace), true);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.RedisOperations#delete(java.lang.Object)
@@ -708,18 +721,9 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
byte[] rawKey = rawKey(key);
Long result = execute(connection -> connection.del(rawKey), true);
return result != null && result.intValue() == 1;
}
@Override
public Boolean copy(K source, K target) {
byte[] sourceKey = rawKey(source);
byte[] targetKey = rawKey(target);
return execute(connection -> connection.copy(sourceKey, targetKey), true);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.RedisOperations#delete(java.util.Collection)