Skip to content

Commit 8a640a2

Browse files
christophstroblmp911de
authored andcommitted
DATAREDIS-694 - Add support for TOUCH.
We now support TOUCH for both Lettuce and Jedis in imperative and reactive (Lettuce only) KeyCommands. Original pull request: #284.
1 parent 5e77dee commit 8a640a2

13 files changed

+236
-4
lines changed

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,15 @@ public DataType type(byte[] key) {
12141214
return convertAndReturn(delegate.type(key), identityConverter);
12151215
}
12161216

1217+
/*
1218+
* (non-Javadoc)
1219+
* @see org.springframework.data.redis.connection.RedisKeyCommands#touch(byte[][])
1220+
*/
1221+
@Override
1222+
public Long touch(byte[]... keys) {
1223+
return convertAndReturn(delegate.touch(keys), identityConverter);
1224+
}
1225+
12171226
/*
12181227
* (non-Javadoc)
12191228
* @see org.springframework.data.redis.connection.RedisTxCommands#unwatch()
@@ -2505,6 +2514,16 @@ public DataType type(String key) {
25052514
return type(serialize(key));
25062515
}
25072516

2517+
/*
2518+
* (non-Javadoc)
2519+
* @see org.springframework.data.redis.connection.StringRedisConnection#touch(java.lang.String[])
2520+
*/
2521+
@Nullable
2522+
@Override
2523+
public Long touch(String... keys) {
2524+
return touch(serializeMulti(keys));
2525+
}
2526+
25082527
/*
25092528
* (non-Javadoc)
25102529
* @see org.springframework.data.redis.connection.StringRedisConnection#zAdd(java.lang.String, double, 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
@@ -75,6 +75,13 @@ default DataType type(byte[] pattern) {
7575
return keyCommands().type(pattern);
7676
}
7777

78+
/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
79+
@Override
80+
@Deprecated
81+
default Long touch(byte[]... keys) {
82+
return keyCommands().touch(keys);
83+
}
84+
7885
/** @deprecated in favor of {@link RedisConnection#keyCommands()}. */
7986
@Override
8087
@Deprecated

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.nio.ByteBuffer;
2222
import java.time.Duration;
2323
import java.time.Instant;
24+
import java.util.Collection;
2425
import java.util.List;
2526

2627
import org.reactivestreams.Publisher;
@@ -87,6 +88,28 @@ default Mono<DataType> type(ByteBuffer key) {
8788
*/
8889
Flux<CommandResponse<KeyCommand, DataType>> type(Publisher<KeyCommand> keys);
8990

91+
/**
92+
* Alter the last access time of given {@code key(s)}.
93+
*
94+
* @param keys must not be {@literal null}.
95+
* @return {@link Mono} emitting the number of keys touched.
96+
* @see <a href="http://redis.io/commands/touch">Redis Documentation: TOUCH</a>
97+
* @since 2.1
98+
*/
99+
default Mono<Long> touch(Collection<ByteBuffer> keys) {
100+
return touch(Mono.just(keys)).next().map(NumericResponse::getOutput);
101+
}
102+
103+
/**
104+
* Alter the last access time of given {@code key(s)}.
105+
*
106+
* @param keys must not be {@literal null}.
107+
* @return
108+
* @see <a href="http://redis.io/commands/touch">Redis Documentation: TOUCH</a>
109+
* @since 2.1
110+
*/
111+
Flux<NumericResponse<Collection<ByteBuffer>, Long>> touch(Publisher<Collection<ByteBuffer>> keys);
112+
90113
/**
91114
* Find all keys matching the given {@literal pattern}.
92115
*

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,17 @@ default Boolean exists(byte[] key) {
8080
@Nullable
8181
DataType type(byte[] key);
8282

83+
/**
84+
* Alter the last access time of given {@code key(s)}.
85+
*
86+
* @param keys must not be {@literal null}.
87+
* @return {@literal null} when used in pipeline / transaction.
88+
* @see <a href="http://redis.io/commands/touch">Redis Documentation: TOUCH</a>
89+
* @since 2.1
90+
*/
91+
@Nullable
92+
Long touch(byte[]... keys);
93+
8394
/**
8495
* Find all keys matching the given {@code pattern}.
8596
*

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,17 @@ interface StringTuple extends Tuple {
123123
*/
124124
DataType type(String key);
125125

126+
/**
127+
* Alter the last access time of given {@code key(s)}.
128+
*
129+
* @param keys must not be {@literal null}.
130+
* @return {@literal null} when used in pipeline / transaction.
131+
* @see <a href="http://redis.io/commands/touch">Redis Documentation: TOUCH</a>
132+
* @since 2.1
133+
*/
134+
@Nullable
135+
Long touch(String... keys);
136+
126137
/**
127138
* Find all keys matching the given {@code pattern}.
128139
*

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

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,37 @@ private static byte[][] getCommandArguments(byte[] key, Collection<byte[]> args)
176176
return commandArgs;
177177
}
178178

179+
/**
180+
* Execute the given command for the {@code key} provided potentially appending args. <br />
181+
* This method, other than {@link #execute(String, byte[]...)}, dispatches the command to the {@code key} serving
182+
* master node.
183+
*
184+
* <pre>
185+
* <code>
186+
* // SET foo bar EX 10 NX
187+
* execute("SET", "foo".getBytes(), asBinaryList("bar", "EX", 10, "NX")
188+
* </code>
189+
* </pre>
190+
*
191+
* @param command must not be {@literal null}.
192+
* @param keys must not be {@literal null}.
193+
* @param args must not be {@literal null}.
194+
* @return command result as delivered by the underlying Redis driver. Can be {@literal null}.
195+
* @since 2.1
196+
*/
197+
@Nullable
198+
public <T> List<T> execute(String command, Collection<byte[]> keys, Collection<byte[]> args) {
199+
200+
Assert.notNull(command, "Command must not be null!");
201+
Assert.notNull(keys, "Key must not be null!");
202+
Assert.notNull(args, "Args must not be null!");
203+
204+
return clusterCommandExecutor.executeMultiKeyCommand((JedisMultiKeyClusterCommandCallback<T>) (client, key) -> {
205+
return JedisClientUtils.execute(command, new byte[][] { key }, args.toArray(new byte[args.size()][]),
206+
() -> client);
207+
}, keys).resultsAsList();
208+
}
209+
179210
/*
180211
* (non-Javadoc)
181212
* @see org.springframework.data.redis.connection.RedisConnection#geoCommands()
@@ -829,8 +860,7 @@ static class JedisClusterNodeResourceProvider implements ClusterNodeResourceProv
829860

830861
PropertyAccessor accessor = new DirectFieldAccessFallbackBeanWrapper(cluster);
831862
this.connectionHandler = accessor.isReadableProperty("connectionHandler")
832-
? (JedisClusterConnectionHandler) accessor.getPropertyValue("connectionHandler")
833-
: null;
863+
? (JedisClusterConnectionHandler) accessor.getPropertyValue("connectionHandler") : null;
834864
} else {
835865
this.connectionHandler = null;
836866
}

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.ArrayList;
2121
import java.util.Arrays;
2222
import java.util.Collection;
23+
import java.util.Collections;
2324
import java.util.HashSet;
2425
import java.util.List;
2526
import java.util.Set;
@@ -97,6 +98,20 @@ public DataType type(byte[] key) {
9798
}
9899
}
99100

101+
/*
102+
* (non-Javadoc)
103+
* @see org.springframework.data.redis.connection.RedisKeyCommands#touch(byte[][])
104+
*/
105+
@Nullable
106+
@Override
107+
public Long touch(byte[]... keys) {
108+
109+
Assert.notNull(keys, "Keys must not be null!");
110+
111+
return connection.<Long> execute("TOUCH", Arrays.asList(keys), Collections.emptyList()).stream()
112+
.mapToLong(val -> val).sum();
113+
}
114+
100115
/*
101116
* (non-Javadoc)
102117
* @see org.springframework.data.redis.connection.RedisKeyCommands#keys(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
@@ -146,6 +146,19 @@ public DataType type(byte[] key) {
146146
}
147147
}
148148

149+
/*
150+
* (non-Javadoc)
151+
* @see org.springframework.data.redis.connection.RedisKeyCommands#touch(byte[][])
152+
*/
153+
@Nullable
154+
@Override
155+
public Long touch(byte[]... keys) {
156+
157+
Assert.notNull(keys, "Keys must not be null!");
158+
159+
return Long.class.cast(connection.execute("TOUCH", keys));
160+
}
161+
149162
/*
150163
* (non-Javadoc)
151164
* @see org.springframework.data.redis.connection.RedisKeyCommands#keys(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
@@ -150,6 +150,30 @@ public DataType type(byte[] key) {
150150
}
151151
}
152152

153+
/*
154+
* (non-Javadoc)
155+
* @see org.springframework.data.redis.connection.RedisKeyCommands#touch(byte[][])
156+
*/
157+
@Override
158+
public Long touch(byte[]... keys) {
159+
160+
Assert.notNull(keys, "Keys must not be null!");
161+
162+
try {
163+
if (isPipelined()) {
164+
pipeline(connection.newLettuceResult(getAsyncConnection().touch(keys)));
165+
return null;
166+
}
167+
if (isQueueing()) {
168+
transaction(connection.newLettuceTxResult(getConnection().touch(keys)));
169+
return null;
170+
}
171+
return getConnection().touch(keys);
172+
} catch (Exception ex) {
173+
throw convertLettuceAccessException(ex);
174+
}
175+
}
176+
153177
/*
154178
* (non-Javadoc)
155179
* @see org.springframework.data.redis.connection.RedisKeyCommands#keys(byte[])

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import reactor.core.publisher.Mono;
2121

2222
import java.nio.ByteBuffer;
23+
import java.util.Collection;
2324
import java.util.List;
2425
import java.util.stream.Collectors;
2526

@@ -86,6 +87,22 @@ public Flux<CommandResponse<KeyCommand, DataType>> type(Publisher<KeyCommand> co
8687
}));
8788
}
8889

90+
/*
91+
* (non-Javadoc)
92+
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#touch(org.reactivestreams.Publisher)
93+
*/
94+
@Override
95+
public Flux<NumericResponse<Collection<ByteBuffer>, Long>> touch(Publisher<Collection<ByteBuffer>> keysCollection) {
96+
97+
return connection.execute(cmd -> Flux.from(keysCollection).concatMap((keys) -> {
98+
99+
Assert.notEmpty(keys, "Keys must not be null!");
100+
101+
return cmd.touch(keys.toArray(new ByteBuffer[keys.size()]))
102+
.map((value) -> new NumericResponse<>(keys, value));
103+
}));
104+
}
105+
89106
/*
90107
* (non-Javadoc)
91108
* @see org.springframework.data.redis.connection.ReactiveRedisConnection.ReactiveKeyCommands#keys(org.reactivestreams.Publisher)

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2771,6 +2771,25 @@ public void hStrLenReturnsZeroWhenKeyDoesNotExist() {
27712771

27722772
actual.add(connection.hStrLen("hash-no-exist", "key-2"));
27732773

2774+
verifyResults(
2775+
Arrays.asList(new Object[] { 0L }));
2776+
}
2777+
2778+
@Test // DATAREDIS-694
2779+
public void touchReturnsNrOfKeysTouched() {
2780+
2781+
connection.set("touch.this", "Can't touch this! - oh-oh oh oh oh-oh-oh");
2782+
2783+
actual.add(connection.touch("touch.this", "touch.that"));
2784+
2785+
verifyResults(Arrays.asList(new Object[] { 1L }));
2786+
}
2787+
2788+
@Test // DATAREDIS-694
2789+
public void touchReturnsZeroIfNoKeysTouched() {
2790+
2791+
actual.add(connection.touch("touch.this", "touch.that"));
2792+
27742793
verifyResults(Arrays.asList(new Object[] { 0L }));
27752794
}
27762795

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

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,16 @@
2828

2929
import java.io.IOException;
3030
import java.nio.charset.Charset;
31-
import java.util.*;
31+
import java.util.Arrays;
32+
import java.util.Collection;
33+
import java.util.Collections;
34+
import java.util.HashMap;
35+
import java.util.HashSet;
36+
import java.util.LinkedHashMap;
37+
import java.util.List;
38+
import java.util.Map;
39+
import java.util.Properties;
40+
import java.util.Set;
3241
import java.util.concurrent.TimeUnit;
3342

3443
import org.junit.After;
@@ -375,7 +384,7 @@ public void executeWithKeyAndArgs() {
375384

376385
@Test(expected = IllegalArgumentException.class) // DATAREDIS-689
377386
public void executeWithNoKeyAndArgsThrowsException() {
378-
clusterConnection.execute("KEYS", null, Collections.singletonList("*".getBytes()));
387+
clusterConnection.execute("KEYS", (byte[]) null, Collections.singletonList("*".getBytes()));
379388
}
380389

381390
@Test // DATAREDIS-529
@@ -2195,4 +2204,19 @@ public void zUnionStoreShouldWorkForSameSlotKeys() {
21952204
assertThat(nativeConnection.zrange(SAME_SLOT_KEY_3_BYTES, 0, -1),
21962205
hasItems(VALUE_1_BYTES, VALUE_2_BYTES, VALUE_3_BYTES));
21972206
}
2207+
2208+
@Test // DATAREDIS-694
2209+
public void touchReturnsNrOfKeysTouched() {
2210+
2211+
nativeConnection.set(KEY_1, VALUE_1);
2212+
nativeConnection.set(KEY_2, VALUE_1);
2213+
2214+
assertThat(clusterConnection.keyCommands().touch(KEY_1_BYTES, KEY_2_BYTES, KEY_3_BYTES), is(2L));
2215+
}
2216+
2217+
@Test // DATAREDIS-694
2218+
public void touchReturnsZeroIfNoKeysTouched() {
2219+
assertThat(clusterConnection.keyCommands().touch(KEY_1_BYTES), is(0L));
2220+
}
2221+
21982222
}

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,4 +287,23 @@ public void shouldMoveToDatabase() {
287287
.verify();
288288
assertThat(nativeCommands.exists(KEY_1), is(0L));
289289
}
290+
291+
@Test // DATAREDIS-694
292+
public void touchReturnsNrOfKeysTouched() {
293+
294+
nativeCommands.set(KEY_1, VALUE_1);
295+
nativeCommands.set(KEY_2, VALUE_2);
296+
297+
StepVerifier.create(connection.keyCommands().touch(Arrays.asList(KEY_1_BBUFFER, KEY_2_BBUFFER, KEY_3_BBUFFER)))
298+
.expectNext(2L) //
299+
.verifyComplete();
300+
}
301+
302+
@Test // DATAREDIS-694
303+
public void touchReturnsZeroIfNoKeysTouched() {
304+
305+
StepVerifier.create(connection.keyCommands().touch(Arrays.asList(KEY_1_BBUFFER))) //
306+
.expectNext(0L) //
307+
.verifyComplete();
308+
}
290309
}

0 commit comments

Comments
 (0)