DATAREDIS-693 - Polishing.

Add Template methods for unlink(…). Align unlink with del/mDel methods on reactive API using concatMap to retain order and provide batching/streaming. Reword Javadoc. Extend tests.

Replace blocking calls with StepVerifier in LettuceReactiveKeyCommandsTests.

Original pull request: #294.
This commit is contained in:
Mark Paluch
2017-11-20 11:06:46 +01:00
parent c11b8d89ea
commit 9775734f6f
11 changed files with 344 additions and 109 deletions

View File

@@ -264,31 +264,6 @@ public interface ReactiveKeyCommands {
*/
Flux<NumericResponse<KeyCommand, Long>> del(Publisher<KeyCommand> keys);
/**
* Unlink {@literal key}.
*
* @param keys must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/unlink">Redis Documentation: UNLINK</a>
* @since 2.1
*/
default Mono<Long> unlink(List<ByteBuffer> keys) {
Assert.notNull(keys, "Key must not be null!");
return unlink(Mono.just(keys)).next().map(NumericResponse::getOutput);
}
/**
* Unlink {@literal keys}.
*
* @param keys must not be {@literal null}.
* @return {@link Flux} of {@link NumericResponse} holding the {@literal key} removed along with the deletion result.
* @see <a href="http://redis.io/commands/unlink">Redis Documentation: UNLINK</a>
* @since 2.1
*/
Flux<NumericResponse<List<ByteBuffer>, Long>> unlink(Publisher<List<ByteBuffer>> keys);
/**
* Delete multiple {@literal keys} one in one batch.
*
@@ -312,6 +287,60 @@ public interface ReactiveKeyCommands {
*/
Flux<NumericResponse<List<ByteBuffer>, Long>> mDel(Publisher<List<ByteBuffer>> keys);
/**
* Unlink the {@code key} from the keyspace. Unlike with {@link #del(ByteBuffer)} the actual memory reclaiming here
* happens asynchronously.
*
* @param key must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/unlink">Redis Documentation: UNLINK</a>
* @since 2.1
*/
default Mono<Long> unlink(ByteBuffer key) {
Assert.notNull(key, "Keys must not be null!");
return unlink(Mono.just(key).map(KeyCommand::new)).next().map(NumericResponse::getOutput);
}
/**
* Unlink the {@code key} from the keyspace. Unlike with {@link #del(ByteBuffer)} the actual memory reclaiming here
* happens asynchronously.
*
* @param keys must not be {@literal null}.
* @return {@link Flux} of {@link NumericResponse} holding the {@literal key} removed along with the unlink result.
* @see <a href="http://redis.io/commands/unlink">Redis Documentation: UNLINK</a>
* @since 2.1
*/
Flux<NumericResponse<KeyCommand, Long>> unlink(Publisher<KeyCommand> keys);
/**
* Unlink the {@code keys} from the keyspace. Unlike with {@link #mDel(List)} the actual memory reclaiming here
* happens asynchronously.
*
* @param keys must not be {@literal null}.
* @return
* @see <a href="http://redis.io/commands/unlink">Redis Documentation: UNLINK</a>
* @since 2.1
*/
default Mono<Long> mUnlink(List<ByteBuffer> keys) {
Assert.notNull(keys, "Keys must not be null!");
return mUnlink(Mono.just(keys)).next().map(NumericResponse::getOutput);
}
/**
* Unlink the {@code keys} from the keyspace. Unlike with {@link #mDel(Publisher)} the actual memory reclaiming here
* happens asynchronously.
*
* @param keys must not be {@literal null}.
* @return {@link Flux} of {@link NumericResponse} holding the {@literal key} removed along with the deletion result.
* @see <a href="http://redis.io/commands/unlink">Redis Documentation: UNLINK</a>
* @since 2.1
*/
Flux<NumericResponse<List<ByteBuffer>, Long>> mUnlink(Publisher<List<ByteBuffer>> keys);
/**
* {@code EXPIRE}/{@code PEXPIRE} command parameters.
*

View File

@@ -71,8 +71,8 @@ public interface RedisKeyCommands {
Long del(byte[]... keys);
/**
* Unlinks the {@code keys} from the keyspace. Unlike with {@link #del(byte[]...)} the actual removal here happens
* asynchronously.
* Unlink the {@code keys} from the keyspace. Unlike with {@link #del(byte[]...)} the actual memory reclaiming here
* happens asynchronously.
*
* @param keys must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.

View File

@@ -114,8 +114,8 @@ public interface StringRedisConnection extends RedisConnection {
Long del(String... keys);
/**
* Unlinks the {@code keys} from the keyspace. Unlike with {@link #del(byte[]...)} the actual removal here happens
* asynchronously.
* Unlink the {@code keys} from the keyspace. Unlike with {@link #del(String...)} the actual memory reclaiming here
* happens asynchronously.
*
* @param keys must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.

View File

@@ -22,7 +22,6 @@ import reactor.core.publisher.Mono;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.reactivestreams.Publisher;
import org.springframework.data.redis.connection.DataType;
@@ -175,22 +174,6 @@ class LettuceReactiveKeyCommands implements ReactiveKeyCommands {
}));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#unlink(org.reactivestreams.Publisher)
*/
@Override
public Flux<NumericResponse<List<ByteBuffer>, Long>> unlink(Publisher<List<ByteBuffer>> keysCollection) {
return connection.execute(cmd -> Flux.from(keysCollection).flatMap((keys) -> {
Assert.notEmpty(keys, "Keys must not be null!");
return cmd.unlink(keys.stream().collect(Collectors.toList()).toArray(new ByteBuffer[keys.size()]))
.map((value) -> new NumericResponse<>(keys, value));
}));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#mDel(org.reactivestreams.Publisher)
@@ -202,11 +185,41 @@ class LettuceReactiveKeyCommands implements ReactiveKeyCommands {
Assert.notEmpty(keys, "Keys must not be null!");
return cmd.del(keys.stream().collect(Collectors.toList()).toArray(new ByteBuffer[keys.size()]))
return cmd.del(keys.toArray(new ByteBuffer[keys.size()]))
.map((value) -> new NumericResponse<>(keys, value));
}));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#unlink(org.reactivestreams.Publisher)
*/
@Override
public Flux<NumericResponse<KeyCommand, Long>> unlink(Publisher<KeyCommand> commands) {
return connection.execute(cmd -> Flux.from(commands).concatMap((command) -> {
Assert.notNull(command.getKey(), "Key must not be null!");
return cmd.unlink(command.getKey()).map((value) -> new NumericResponse<>(command, value));
}));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#mUnlink(org.reactivestreams.Publisher)
*/
@Override
public Flux<NumericResponse<List<ByteBuffer>, Long>> mUnlink(Publisher<List<ByteBuffer>> keysCollection) {
return connection.execute(cmd -> Flux.from(keysCollection).concatMap((keys) -> {
Assert.notEmpty(keys, "Keys must not be null!");
return cmd.unlink(keys.toArray(new ByteBuffer[keys.size()])).map((value) -> new NumericResponse<>(keys, value));
}));
}
/* (non-Javadoc)
* @see org.springframework.data.redis.connection.ReactiveKeyCommands#expire(org.reactivestreams.Publisher)
*/

View File

@@ -132,6 +132,28 @@ public interface ReactiveRedisOperations<K, V> {
*/
Mono<Long> delete(Publisher<K> keys);
/**
* Unlink the {@code key} from the keyspace. Unlike with {@link #delete(Object[])} the actual memory reclaiming here
* happens asynchronously.
*
* @param key must not be {@literal null}.
* @return The number of keys that were removed. {@literal null} when used in pipeline / transaction.
* @see <a href="http://redis.io/commands/unlink">Redis Documentation: UNLINK</a>
* @since 2.1
*/
Mono<Long> unlink(K... key);
/**
* Unlink the {@code keys} from the keyspace. Unlike with {@link #delete(Publisher)} the actual memory reclaiming here
* happens asynchronously.
*
* @param keys must not be {@literal null}.
* @return The number of keys that were removed. {@literal null} when used in pipeline / transaction.
* @see <a href="http://redis.io/commands/unlink">Redis Documentation: UNLINK</a>
* @since 2.1
*/
Mono<Long> unlink(Publisher<K> keys);
/**
* Set time to live for given {@code key}.
*

View File

@@ -304,6 +304,40 @@ public class ReactiveRedisTemplate<K, V> implements ReactiveRedisOperations<K, V
.map(CommandResponse::getOutput));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveRedisOperations#unlink(java.lang.Object[])
*/
@Override
@SafeVarargs
public final Mono<Long> unlink(K... keys) {
Assert.notNull(keys, "Keys must not be null!");
Assert.notEmpty(keys, "Keys must not be empty!");
Assert.noNullElements(keys, "Keys must not contain null elements!");
if (keys.length == 1) {
return createMono(connection -> connection.keyCommands().unlink(rawKey(keys[0])));
}
Mono<List<ByteBuffer>> listOfKeys = Flux.fromArray(keys).map(this::rawKey).collectList();
return createMono(connection -> listOfKeys.flatMap(rawKeys -> connection.keyCommands().mUnlink(rawKeys)));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveRedisOperations#unlink(org.reactivestreams.Publisher)
*/
@Override
public Mono<Long> unlink(Publisher<K> keys) {
Assert.notNull(keys, "Keys must not be null!");
return createMono(connection -> connection.keyCommands() //
.unlink(Flux.from(keys).map(this::rawKey).map(KeyCommand::new)) //
.map(CommandResponse::getOutput));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveRedisOperations#expire(java.lang.Object, java.time.Duration)

View File

@@ -194,6 +194,30 @@ public interface RedisOperations<K, V> {
@Nullable
Long delete(Collection<K> keys);
/**
* Unlink the {@code key} from the keyspace. Unlike with {@link #delete(Object)} the actual memory reclaiming here
* happens asynchronously.
*
* @param key must not be {@literal null}.
* @return The number of keys that were removed. {@literal null} when used in pipeline / transaction.
* @see <a href="http://redis.io/commands/unlink">Redis Documentation: UNLINK</a>
* @since 2.1
*/
@Nullable
Boolean unlink(K key);
/**
* Unlink the {@code keys} from the keyspace. Unlike with {@link #delete(Collection)} the actual memory reclaiming
* here happens asynchronously.
*
* @param keys must not be {@literal null}.
* @return The number of keys that were removed. {@literal null} when used in pipeline / transaction.
* @see <a href="http://redis.io/commands/unlink">Redis Documentation: UNLINK</a>
* @since 2.1
*/
@Nullable
Long unlink(Collection<K> keys);
/**
* Determine the type stored at {@code key}.
*

View File

@@ -727,6 +727,36 @@ public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperation
return execute(connection -> connection.del(rawKeys), true);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.RedisOperations#unlink(java.lang.Object)
*/
@Override
public Boolean unlink(K key) {
byte[] rawKey = rawKey(key);
Long result = execute(connection -> connection.unlink(rawKey), true);
return result != null && result.intValue() == 1;
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.RedisOperations#unlink(java.util.Collection)
*/
@Override
public Long unlink(Collection<K> keys) {
if (CollectionUtils.isEmpty(keys)) {
return 0L;
}
byte[][] rawKeys = rawKeys(keys);
return execute(connection -> connection.unlink(rawKeys), true);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.RedisOperations#hasKey(java.lang.Object)