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)

View File

@@ -1128,6 +1128,38 @@ public abstract class AbstractConnectionIntegrationTests {
verifyResults(Arrays.asList(true, 1L, false));
}
@Test // DATAREDIS-693
@IfProfileValue(name = "redisVersion", value = "4.0+")
public void unlinkReturnsNrOfKeysRemoved() {
connection.set("unlink.this", "Can't track this!");
actual.add(connection.unlink("unlink.this", "unlink.that"));
verifyResults(Arrays.asList(new Object[] { 1L }));
}
@Test // DATAREDIS-693
@IfProfileValue(name = "redisVersion", value = "4.0+")
public void testUnlinkBatch() {
actual.add(connection.set("testing", "123"));
actual.add(connection.set("foo", "bar"));
actual.add(connection.unlink("testing", "foo"));
actual.add(connection.exists("testing"));
verifyResults(Arrays.asList(true, true, 2L, false));
}
@Test // DATAREDIS-693
@IfProfileValue(name = "redisVersion", value = "4.0+")
public void unlinkReturnsZeroIfNoKeysRemoved() {
actual.add(connection.unlink("unlink.this"));
verifyResults(Arrays.asList(new Object[] { 0L }));
}
@Test
public void testType() {
@@ -2790,26 +2822,6 @@ public abstract class AbstractConnectionIntegrationTests {
verifyResults(Arrays.asList(new Object[] { 0L }));
}
@Test // DATAREDIS-693
@IfProfileValue(name = "redisVersion", value = "4.0+")
public void unlinkReturnsNrOfKeysRemoved() {
connection.set("unlink.this", "Can't track this!");
actual.add(connection.unlink("unlink.this", "unlink.that"));
verifyResults(Arrays.asList(new Object[] { 1L }));
}
@Test // DATAREDIS-693
@IfProfileValue(name = "redisVersion", value = "4.0+")
public void unlinkReturnsZeroIfNoKeysRemoved() {
actual.add(connection.unlink("unlink.this"));
verifyResults(Arrays.asList(new Object[] { 0L }));
}
protected void verifyResults(List<Object> expected) {
assertEquals(expected, getResults());
}

View File

@@ -28,6 +28,8 @@ import java.nio.ByteBuffer;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
@@ -53,12 +55,12 @@ public class LettuceReactiveKeyCommandsTests extends LettuceReactiveCommandsTest
nativeCommands.set(KEY_1, VALUE_1);
assertThat(connection.keyCommands().exists(KEY_1_BBUFFER).block(), is(true));
StepVerifier.create(connection.keyCommands().exists(KEY_1_BBUFFER)).expectNext(true).verifyComplete();
}
@Test // DATAREDIS-525
public void existsShouldReturnFalseForNonExistingKeys() {
assertThat(connection.keyCommands().exists(KEY_1_BBUFFER).block(), is(false));
StepVerifier.create(connection.keyCommands().exists(KEY_1_BBUFFER)).expectNext(false).verifyComplete();
}
@Test // DATAREDIS-525
@@ -68,9 +70,9 @@ public class LettuceReactiveKeyCommandsTests extends LettuceReactiveCommandsTest
nativeCommands.sadd(KEY_2, VALUE_2);
nativeCommands.hset(KEY_3, KEY_1, VALUE_1);
assertThat(connection.keyCommands().type(KEY_1_BBUFFER).block(), is(DataType.STRING));
assertThat(connection.keyCommands().type(KEY_2_BBUFFER).block(), is(DataType.SET));
assertThat(connection.keyCommands().type(KEY_3_BBUFFER).block(), is(DataType.HASH));
StepVerifier.create(connection.keyCommands().type(KEY_1_BBUFFER)).expectNext(DataType.STRING).verifyComplete();
StepVerifier.create(connection.keyCommands().type(KEY_2_BBUFFER)).expectNext(DataType.SET).verifyComplete();
StepVerifier.create(connection.keyCommands().type(KEY_3_BBUFFER)).expectNext(DataType.HASH).verifyComplete();
}
@Test // DATAREDIS-525
@@ -84,8 +86,12 @@ public class LettuceReactiveKeyCommandsTests extends LettuceReactiveCommandsTest
nativeCommands.set(VALUE_2, KEY_2);
nativeCommands.set(VALUE_3, KEY_3);
assertThat(connection.keyCommands().keys(ByteBuffer.wrap("*".getBytes())).block(), hasSize(6));
assertThat(connection.keyCommands().keys(ByteBuffer.wrap("key*".getBytes())).block(), hasSize(3));
StepVerifier.create(connection.keyCommands().keys(ByteBuffer.wrap("*".getBytes())).flatMapIterable(it -> it)) //
.expectNextCount(6) //
.verifyComplete();
StepVerifier.create(connection.keyCommands().keys(ByteBuffer.wrap("key*".getBytes())).flatMapIterable(it -> it)) //
.expectNextCount(3).verifyComplete();
}
@Test // DATAREDIS-525
@@ -95,12 +101,12 @@ public class LettuceReactiveKeyCommandsTests extends LettuceReactiveCommandsTest
nativeCommands.set(KEY_2, VALUE_2);
nativeCommands.set(KEY_3, VALUE_3);
assertThat(connection.keyCommands().randomKey().block(), is(notNullValue()));
StepVerifier.create(connection.keyCommands().randomKey()).expectNextCount(1).verifyComplete();
}
@Test // DATAREDIS-525
public void randomKeyShouldReturnNullWhenNoKeyExists() {
assertThat(connection.keyCommands().randomKey().block(), is(nullValue()));
StepVerifier.create(connection.keyCommands().randomKey()).verifyComplete();
}
@Test // DATAREDIS-525
@@ -108,14 +114,17 @@ public class LettuceReactiveKeyCommandsTests extends LettuceReactiveCommandsTest
nativeCommands.set(KEY_1, VALUE_2);
assertThat(connection.keyCommands().rename(KEY_1_BBUFFER, KEY_2_BBUFFER).block(), is(true));
StepVerifier.create(connection.keyCommands().rename(KEY_1_BBUFFER, KEY_2_BBUFFER)).expectNext(true)
.verifyComplete();
assertThat(nativeCommands.exists(KEY_2), is(1L));
assertThat(nativeCommands.exists(KEY_1), is(0L));
}
@Test(expected = RedisSystemException.class) // DATAREDIS-525
@Test // DATAREDIS-525
public void renameShouldThrowErrorWhenKeyDoesNotExist() {
assertThat(connection.keyCommands().rename(KEY_1_BBUFFER, KEY_2_BBUFFER).block(), is(true));
StepVerifier.create(connection.keyCommands().rename(KEY_1_BBUFFER, KEY_2_BBUFFER))
.expectError(RedisSystemException.class).verify();
}
@Test // DATAREDIS-525
@@ -123,7 +132,8 @@ public class LettuceReactiveKeyCommandsTests extends LettuceReactiveCommandsTest
nativeCommands.set(KEY_1, VALUE_2);
assertThat(connection.keyCommands().rename(KEY_1_BBUFFER, KEY_2_BBUFFER).block(), is(true));
StepVerifier.create(connection.keyCommands().renameNX(KEY_1_BBUFFER, KEY_2_BBUFFER)).expectNext(true)
.verifyComplete();
assertThat(nativeCommands.exists(KEY_2), is(1L));
assertThat(nativeCommands.exists(KEY_1), is(0L));
@@ -135,7 +145,8 @@ public class LettuceReactiveKeyCommandsTests extends LettuceReactiveCommandsTest
nativeCommands.set(KEY_1, VALUE_2);
nativeCommands.set(KEY_2, VALUE_2);
assertThat(connection.keyCommands().renameNX(KEY_1_BBUFFER, KEY_2_BBUFFER).block(), is(false));
StepVerifier.create(connection.keyCommands().renameNX(KEY_1_BBUFFER, KEY_2_BBUFFER)).expectNext(false)
.verifyComplete();
}
@Test // DATAREDIS-525
@@ -143,8 +154,7 @@ public class LettuceReactiveKeyCommandsTests extends LettuceReactiveCommandsTest
nativeCommands.set(KEY_1, VALUE_1);
Mono<Long> result = connection.keyCommands().del(KEY_1_BBUFFER);
assertThat(result.block(), is(1L));
StepVerifier.create(connection.keyCommands().del(KEY_1_BBUFFER)).expectNext(1L).verifyComplete();
}
@Test // DATAREDIS-525
@@ -167,7 +177,7 @@ public class LettuceReactiveKeyCommandsTests extends LettuceReactiveCommandsTest
Mono<Long> result = connection.keyCommands().mDel(Arrays.asList(KEY_1_BBUFFER, KEY_2_BBUFFER));
assertThat(result.block(), is(2L));
StepVerifier.create(result).expectNext(2L).verifyComplete();
}
@Test // DATAREDIS-525
@@ -176,9 +186,62 @@ public class LettuceReactiveKeyCommandsTests extends LettuceReactiveCommandsTest
nativeCommands.set(KEY_1, VALUE_1);
nativeCommands.set(KEY_2, VALUE_2);
Flux<List<ByteBuffer>> input = Flux.just(Arrays.asList(KEY_1_BBUFFER, KEY_2_BBUFFER),
Collections.singletonList(KEY_1_BBUFFER));
Flux<Long> result = connection.keyCommands()
.mDel(
Flux.fromIterable(Arrays.asList(Arrays.asList(KEY_1_BBUFFER, KEY_2_BBUFFER), Arrays.asList(KEY_1_BBUFFER))))
.mDel(input)
.map(NumericResponse::getOutput);
StepVerifier.create(result).expectNextCount(2).verifyComplete();
}
@Test // DATAREDIS-693
@IfProfileValue(name = "redisVersion", value = "4.0.0+")
public void shouldUnlinkKeyCorrectly() {
nativeCommands.set(KEY_1, VALUE_1);
StepVerifier.create(connection.keyCommands().unlink(KEY_1_BBUFFER)).expectNext(1L).verifyComplete();
}
@Test // DATAREDIS-693
@IfProfileValue(name = "redisVersion", value = "4.0.0+")
public void shouldUnlinkKeysCorrectly() {
nativeCommands.set(KEY_1, VALUE_1);
nativeCommands.set(KEY_2, VALUE_2);
Flux<NumericResponse<KeyCommand, Long>> result = connection.keyCommands()
.unlink(Flux.fromIterable(Arrays.asList(new KeyCommand(KEY_1_BBUFFER), new KeyCommand(KEY_2_BBUFFER))));
StepVerifier.create(result).expectNextCount(2).verifyComplete();
}
@Test // DATAREDIS-693
@IfProfileValue(name = "redisVersion", value = "4.0.0+")
public void shouldUnlinkKeysInBatchCorrectly() {
nativeCommands.set(KEY_1, VALUE_1);
nativeCommands.set(KEY_2, VALUE_2);
Mono<Long> result = connection.keyCommands().mUnlink(Arrays.asList(KEY_1_BBUFFER, KEY_2_BBUFFER));
StepVerifier.create(result).expectNext(2L).verifyComplete();
}
@Test // DATAREDIS-693
@IfProfileValue(name = "redisVersion", value = "4.0.0+")
public void shouldUnlinkKeysInMultipleBatchesCorrectly() {
nativeCommands.set(KEY_1, VALUE_1);
nativeCommands.set(KEY_2, VALUE_2);
Flux<List<ByteBuffer>> input = Flux.just(Arrays.asList(KEY_1_BBUFFER, KEY_2_BBUFFER),
Collections.singletonList(KEY_1_BBUFFER));
Flux<Long> result = connection.keyCommands()
.mUnlink(input)
.map(NumericResponse::getOutput);
StepVerifier.create(result).expectNextCount(2).verifyComplete();
@@ -307,29 +370,7 @@ public class LettuceReactiveKeyCommandsTests extends LettuceReactiveCommandsTest
@Test // DATAREDIS-694
public void touchReturnsZeroIfNoKeysTouched() {
StepVerifier.create(connection.keyCommands().touch(Arrays.asList(KEY_1_BBUFFER))) //
.expectNext(0L) //
.verifyComplete();
}
@Test // DATAREDIS-693
@IfProfileValue(name = "redisVersion", value = "4.0.0+")
public void unlinkReturnsNrOfKeysRemoved() {
nativeCommands.set(KEY_1, VALUE_1);
nativeCommands.set(KEY_2, VALUE_2);
StepVerifier.create(connection.keyCommands().unlink(Arrays.asList(KEY_1_BBUFFER, KEY_2_BBUFFER, KEY_3_BBUFFER)))
.expectNext(2L) //
.verifyComplete();
}
@Test // DATAREDIS-693
@IfProfileValue(name = "redisVersion", value = "4.0.0+")
public void unlinkReturnsZeroIfNoKeysRemoved() {
StepVerifier.create(connection.keyCommands().unlink(Arrays.asList(KEY_1_BBUFFER))) //
StepVerifier.create(connection.keyCommands().touch(Collections.singletonList(KEY_1_BBUFFER))) //
.expectNext(0L) //
.verifyComplete();
}

View File

@@ -166,6 +166,36 @@ public class ReactiveRedisTemplateIntegrationTests<K, V> {
.verify();
}
@Test // DATAREDIS-693
public void unlink() {
K single = keyFactory.instance();
StepVerifier.create(redisTemplate.opsForValue().set(single, valueFactory.instance())).expectNext(true)
.verifyComplete();
StepVerifier.create(redisTemplate.unlink(single)).expectNext(1L).verifyComplete();
StepVerifier.create(redisTemplate.hasKey(single)).expectNext(false).verifyComplete();
}
@Test // DATAREDIS-693
public void unlinkMany() {
K key1 = keyFactory.instance();
K key2 = keyFactory.instance();
StepVerifier.create(redisTemplate.opsForValue().set(key1, valueFactory.instance())).expectNext(true)
.verifyComplete();
StepVerifier.create(redisTemplate.opsForValue().set(key2, valueFactory.instance())).expectNext(true)
.verifyComplete();
StepVerifier.create(redisTemplate.unlink(key1, key2)).expectNext(2L).verifyComplete();
StepVerifier.create(redisTemplate.hasKey(key1)).expectNext(false).verifyComplete();
StepVerifier.create(redisTemplate.hasKey(key2)).expectNext(false).verifyComplete();
}
@Test // DATAREDIS-683
@SuppressWarnings("unchecked")
public void executeScript() {