Skip to content

Commit c11b8d8

Browse files
christophstroblmp911de
authored andcommitted
DATAREDIS-693 - Add support for UNLINK.
We now support UNLINK for both Jedis & Lettuce throughout the single node and cluster connection. Reactive support is only available for Lettuce. Original pull request: #294.
1 parent 3ec25ee commit c11b8d8

13 files changed

+230
-0
lines changed

src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,15 @@ public Long del(byte[]... keys) {
252252
return convertAndReturn(delegate.del(keys), identityConverter);
253253
}
254254

255+
/*
256+
* (non-Javadoc)
257+
* @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][])
258+
*/
259+
@Override
260+
public Long unlink(byte[]... keys) {
261+
return convertAndReturn(delegate.unlink(keys), identityConverter);
262+
}
263+
255264
/*
256265
* (non-Javadoc)
257266
* @see org.springframework.data.redis.connection.RedisTxCommands#discard()
@@ -1785,6 +1794,15 @@ public Long del(String... keys) {
17851794
return del(serializeMulti(keys));
17861795
}
17871796

1797+
/*
1798+
* (non-Javadoc)
1799+
* @see org.springframework.data.redis.connection.StringRedisConnection#unlink(java.lang.String[])
1800+
*/
1801+
@Override
1802+
public Long unlink(String... keys) {
1803+
return unlink(serializeMulti(keys));
1804+
}
1805+
17881806
/*
17891807
* (non-Javadoc)
17901808
* @see org.springframework.data.redis.connection.StringRedisConnection#echo(java.lang.String)

src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ default Long del(byte[]... keys) {
6868
return keyCommands().del(keys);
6969
}
7070

71+
/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
72+
@Override
73+
@Deprecated
74+
default Long unlink(byte[]... keys) {
75+
return keyCommands().unlink(keys);
76+
}
77+
7178
/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
7279
@Override
7380
@Deprecated

src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,31 @@ default Mono<Long> del(ByteBuffer key) {
264264
*/
265265
Flux<NumericResponse<KeyCommand, Long>> del(Publisher<KeyCommand> keys);
266266

267+
/**
268+
* Unlink {@literal key}.
269+
*
270+
* @param keys must not be {@literal null}.
271+
* @return
272+
* @see <a href="http://redis.io/commands/unlink">Redis Documentation: UNLINK</a>
273+
* @since 2.1
274+
*/
275+
default Mono<Long> unlink(List<ByteBuffer> keys) {
276+
277+
Assert.notNull(keys, "Key must not be null!");
278+
279+
return unlink(Mono.just(keys)).next().map(NumericResponse::getOutput);
280+
}
281+
282+
/**
283+
* Unlink {@literal keys}.
284+
*
285+
* @param keys must not be {@literal null}.
286+
* @return {@link Flux} of {@link NumericResponse} holding the {@literal key} removed along with the deletion result.
287+
* @see <a href="http://redis.io/commands/unlink">Redis Documentation: UNLINK</a>
288+
* @since 2.1
289+
*/
290+
Flux<NumericResponse<List<ByteBuffer>, Long>> unlink(Publisher<List<ByteBuffer>> keys);
291+
267292
/**
268293
* Delete multiple {@literal keys} one in one batch.
269294
*

src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,18 @@ default Boolean exists(byte[] key) {
7070
@Nullable
7171
Long del(byte[]... keys);
7272

73+
/**
74+
* Unlinks the {@code keys} from the keyspace. Unlike with {@link #del(byte[]...)} the actual removal here happens
75+
* asynchronously.
76+
*
77+
* @param keys must not be {@literal null}.
78+
* @return {@literal null} when used in pipeline / transaction.
79+
* @see <a href="http://redis.io/commands/unlink">Redis Documentation: UNLINK</a>
80+
* @since 2.1
81+
*/
82+
@Nullable
83+
Long unlink(byte[]... keys);
84+
7385
/**
7486
* Determine the type stored at {@code key}.
7587
*

src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,18 @@ interface StringTuple extends Tuple {
113113
*/
114114
Long del(String... keys);
115115

116+
/**
117+
* Unlinks the {@code keys} from the keyspace. Unlike with {@link #del(byte[]...)} the actual removal here happens
118+
* asynchronously.
119+
*
120+
* @param keys must not be {@literal null}.
121+
* @return {@literal null} when used in pipeline / transaction.
122+
* @see <a href="http://redis.io/commands/unlink">Redis Documentation: UNLINK</a>
123+
* @since 2.1
124+
*/
125+
@Nullable
126+
Long unlink(String... keys);
127+
116128
/**
117129
* Determine the type stored at {@code key}.
118130
*

src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,20 @@ public Long del(byte[]... keys) {
8181
.resultsAsList().size();
8282
}
8383

84+
/*
85+
* (non-Javadoc)
86+
* @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][])
87+
*/
88+
@Nullable
89+
@Override
90+
public Long unlink(byte[]... keys) {
91+
92+
Assert.notNull(keys, "Keys must not be null!");
93+
94+
return connection.<Long> execute("UNLINK", Arrays.asList(keys), Collections.emptyList()).stream()
95+
.mapToLong(val -> val).sum();
96+
}
97+
8498
/*
8599
* (non-Javadoc)
86100
* @see org.springframework.data.redis.connection.RedisKeyCommands#type(byte[])

src/main/java/org/springframework/data/redis/connection/jedis/JedisKeyCommands.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,19 @@ public Long del(byte[]... keys) {
120120
}
121121
}
122122

123+
/*
124+
* (non-Javadoc)
125+
* @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][])
126+
*/
127+
@Nullable
128+
@Override
129+
public Long unlink(byte[]... keys) {
130+
131+
Assert.notNull(keys, "Keys must not be null!");
132+
133+
return Long.class.cast(connection.execute("UNLINK", keys));
134+
}
135+
123136
/*
124137
* (non-Javadoc)
125138
* @see org.springframework.data.redis.connection.RedisKeyCommands#type(byte[])

src/main/java/org/springframework/data/redis/connection/lettuce/LettuceKeyCommands.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,30 @@ public Long del(byte[]... keys) {
126126
}
127127
}
128128

129+
/*
130+
* (non-Javadoc)
131+
* @see org.springframework.data.redis.connection.RedisKeyCommands#unlink(byte[][])
132+
*/
133+
@Override
134+
public Long unlink(byte[]... keys) {
135+
136+
Assert.notNull(keys, "Keys must not be null!");
137+
138+
try {
139+
if (isPipelined()) {
140+
pipeline(connection.newLettuceResult(getAsyncConnection().unlink(keys)));
141+
return null;
142+
}
143+
if (isQueueing()) {
144+
transaction(connection.newLettuceResult(getAsyncConnection().unlink(keys)));
145+
return null;
146+
}
147+
return getConnection().unlink(keys);
148+
} catch (Exception ex) {
149+
throw convertLettuceAccessException(ex);
150+
}
151+
}
152+
129153
/*
130154
* (non-Javadoc)
131155
* @see org.springframework.data.redis.connection.RedisKeyCommands#type(byte[])

src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,22 @@ public Flux<NumericResponse<KeyCommand, Long>> del(Publisher<KeyCommand> command
175175
}));
176176
}
177177

178+
/*
179+
* (non-Javadoc)
180+
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#unlink(org.reactivestreams.Publisher)
181+
*/
182+
@Override
183+
public Flux<NumericResponse<List<ByteBuffer>, Long>> unlink(Publisher<List<ByteBuffer>> keysCollection) {
184+
185+
return connection.execute(cmd -> Flux.from(keysCollection).flatMap((keys) -> {
186+
187+
Assert.notEmpty(keys, "Keys must not be null!");
188+
189+
return cmd.unlink(keys.stream().collect(Collectors.toList()).toArray(new ByteBuffer[keys.size()]))
190+
.map((value) -> new NumericResponse<>(keys, value));
191+
}));
192+
}
193+
178194
/*
179195
* (non-Javadoc)
180196
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#mDel(org.reactivestreams.Publisher)

src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2790,6 +2790,26 @@ public void touchReturnsZeroIfNoKeysTouched() {
27902790
verifyResults(Arrays.asList(new Object[] { 0L }));
27912791
}
27922792

2793+
@Test // DATAREDIS-693
2794+
@IfProfileValue(name = "redisVersion", value = "4.0+")
2795+
public void unlinkReturnsNrOfKeysRemoved() {
2796+
2797+
connection.set("unlink.this", "Can't track this!");
2798+
2799+
actual.add(connection.unlink("unlink.this", "unlink.that"));
2800+
2801+
verifyResults(Arrays.asList(new Object[] { 1L }));
2802+
}
2803+
2804+
@Test // DATAREDIS-693
2805+
@IfProfileValue(name = "redisVersion", value = "4.0+")
2806+
public void unlinkReturnsZeroIfNoKeysRemoved() {
2807+
2808+
actual.add(connection.unlink("unlink.this"));
2809+
2810+
verifyResults(Arrays.asList(new Object[] { 0L }));
2811+
}
2812+
27932813
protected void verifyResults(List<Object> expected) {
27942814
assertEquals(expected, getResults());
27952815
}

src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2219,4 +2219,18 @@ public void touchReturnsZeroIfNoKeysTouched() {
22192219
assertThat(clusterConnection.keyCommands().touch(KEY_1_BYTES), is(0L));
22202220
}
22212221

2222+
@Test // DATAREDIS-693
2223+
public void unlinkReturnsNrOfKeysTouched() {
2224+
2225+
nativeConnection.set(KEY_1, VALUE_1);
2226+
nativeConnection.set(KEY_2, VALUE_1);
2227+
2228+
assertThat(clusterConnection.keyCommands().unlink(KEY_1_BYTES, KEY_2_BYTES, KEY_3_BYTES), is(2L));
2229+
}
2230+
2231+
@Test // DATAREDIS-693
2232+
public void unlinkReturnsZeroIfNoKeysTouched() {
2233+
assertThat(clusterConnection.keyCommands().unlink(KEY_1_BYTES), is(0L));
2234+
}
2235+
22222236
}

src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2182,4 +2182,32 @@ public void zUnionStoreShouldWorkForSameSlotKeys() {
21822182

21832183
assertThat(nativeConnection.zrange(SAME_SLOT_KEY_3, 0, -1), hasItems(VALUE_1, VALUE_2, VALUE_3));
21842184
}
2185+
2186+
@Test // DATAREDIS-694
2187+
public void touchReturnsNrOfKeysTouched() {
2188+
2189+
nativeConnection.set(KEY_1, VALUE_1);
2190+
nativeConnection.set(KEY_2, VALUE_1);
2191+
2192+
assertThat(clusterConnection.keyCommands().touch(KEY_1_BYTES, KEY_2_BYTES, KEY_3_BYTES), is(2L));
2193+
}
2194+
2195+
@Test // DATAREDIS-694
2196+
public void touchReturnsZeroIfNoKeysTouched() {
2197+
assertThat(clusterConnection.keyCommands().touch(KEY_1_BYTES), is(0L));
2198+
}
2199+
2200+
@Test // DATAREDIS-693
2201+
public void unlinkReturnsNrOfKeysTouched() {
2202+
2203+
nativeConnection.set(KEY_1, VALUE_1);
2204+
nativeConnection.set(KEY_2, VALUE_1);
2205+
2206+
assertThat(clusterConnection.keyCommands().unlink(KEY_1_BYTES, KEY_2_BYTES, KEY_3_BYTES), is(2L));
2207+
}
2208+
2209+
@Test // DATAREDIS-693
2210+
public void unlinkReturnsZeroIfNoKeysTouched() {
2211+
assertThat(clusterConnection.keyCommands().unlink(KEY_1_BYTES), is(0L));
2212+
}
21852213
}

src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsTests.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,14 @@
2929
import java.time.Instant;
3030
import java.util.Arrays;
3131

32+
import org.junit.Rule;
3233
import org.junit.Test;
3334
import org.springframework.data.redis.RedisSystemException;
3435
import org.springframework.data.redis.connection.DataType;
3536
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
3637
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
38+
import org.springframework.data.redis.test.util.MinimumRedisVersionRule;
39+
import org.springframework.test.annotation.IfProfileValue;
3740

3841
/**
3942
* Integration tests for {@link LettuceReactiveKeyCommands}.
@@ -43,6 +46,8 @@
4346
*/
4447
public class LettuceReactiveKeyCommandsTests extends LettuceReactiveCommandsTestsBase {
4548

49+
public @Rule MinimumRedisVersionRule versionRule = new MinimumRedisVersionRule();
50+
4651
@Test // DATAREDIS-525
4752
public void existsShouldReturnTrueForExistingKeys() {
4853

@@ -306,4 +311,26 @@ public void touchReturnsZeroIfNoKeysTouched() {
306311
.expectNext(0L) //
307312
.verifyComplete();
308313
}
314+
315+
@Test // DATAREDIS-693
316+
@IfProfileValue(name = "redisVersion", value = "4.0.0+")
317+
public void unlinkReturnsNrOfKeysRemoved() {
318+
319+
nativeCommands.set(KEY_1, VALUE_1);
320+
nativeCommands.set(KEY_2, VALUE_2);
321+
322+
StepVerifier.create(connection.keyCommands().unlink(Arrays.asList(KEY_1_BBUFFER, KEY_2_BBUFFER, KEY_3_BBUFFER)))
323+
.expectNext(2L) //
324+
.verifyComplete();
325+
326+
}
327+
328+
@Test // DATAREDIS-693
329+
@IfProfileValue(name = "redisVersion", value = "4.0.0+")
330+
public void unlinkReturnsZeroIfNoKeysRemoved() {
331+
332+
StepVerifier.create(connection.keyCommands().unlink(Arrays.asList(KEY_1_BBUFFER))) //
333+
.expectNext(0L) //
334+
.verifyComplete();
335+
}
309336
}

0 commit comments

Comments
 (0)