Skip to content

Commit e67d71b

Browse files
christophstroblmp911de
authored andcommitted
DATAREDIS-513 - Fix RedisServerCommands.time() failure when in pipeline mode.
We fixed a glitch in Jedis/Lettuce RedisConneciton TIME command when used in pipeline or transaction mode. Original pull request: #199.
1 parent d203458 commit e67d71b

File tree

5 files changed

+93
-45
lines changed

5 files changed

+93
-45
lines changed

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

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
/*
22
* Copyright 2011-2016 the original author or authors.
3-
*
3+
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
7-
*
7+
*
88
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
9+
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
1212
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -47,7 +47,7 @@
4747
import org.springframework.data.redis.connection.ReturnType;
4848
import org.springframework.data.redis.connection.SortParameters;
4949
import org.springframework.data.redis.connection.Subscription;
50-
import org.springframework.data.redis.connection.convert.Converters;
50+
import org.springframework.data.redis.connection.convert.ListConverter;
5151
import org.springframework.data.redis.connection.convert.TransactionResultConverter;
5252
import org.springframework.data.redis.core.Cursor;
5353
import org.springframework.data.redis.core.KeyBoundCursor;
@@ -788,8 +788,8 @@ public Boolean expire(byte[] key, long seconds) {
788788

789789
/*
790790
* @see DATAREDIS-286 to avoid overflow in Jedis
791-
*
792-
* TODO Remove this workaround when we upgrade to a Jedis version that contains a
791+
*
792+
* TODO Remove this workaround when we upgrade to a Jedis version that contains a
793793
* fix for: https://github.com/xetorthio/jedis/pull/575
794794
*/
795795
if (seconds > Integer.MAX_VALUE) {
@@ -3181,13 +3181,21 @@ public <T> T evalSha(byte[] scriptSha1, ReturnType returnType, int numKeys, byte
31813181
@Override
31823182
public Long time() {
31833183

3184-
List<String> serverTimeInformation = this.jedis.time();
3184+
try {
31853185

3186-
Assert.notEmpty(serverTimeInformation, "Received invalid result from server. Expected 2 items in collection.");
3187-
Assert.isTrue(serverTimeInformation.size() == 2,
3188-
"Received invalid nr of arguments from redis server. Expected 2 received " + serverTimeInformation.size());
3186+
if (isPipelined()) {
3187+
pipeline(new JedisResult(pipeline.time(), JedisConverters.toTimeConverter()));
3188+
return null;
3189+
}
31893190

3190-
return Converters.toTimeMillis(serverTimeInformation.get(0), serverTimeInformation.get(1));
3191+
if (isQueueing()) {
3192+
transaction(new JedisResult(transaction.time(), JedisConverters.toTimeConverter()));
3193+
return null;
3194+
}
3195+
return JedisConverters.toTimeConverter().convert(jedis.time());
3196+
} catch (Exception ex) {
3197+
throw convertJedisAccessException(ex);
3198+
}
31913199
}
31923200

31933201
/*

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

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858

5959
/**
6060
* Jedis type converters.
61-
*
61+
*
6262
* @author Jennifer Hickey
6363
* @author Christoph Strobl
6464
* @author Thomas Darimont
@@ -79,6 +79,7 @@ abstract public class JedisConverters extends Converters {
7979
private static final Converter<Object, RedisClusterNode> OBJECT_TO_CLUSTER_NODE_CONVERTER;
8080
private static final Converter<Expiration, byte[]> EXPIRATION_TO_COMMAND_OPTION_CONVERTER;
8181
private static final Converter<SetOption, byte[]> SET_OPTION_TO_COMMAND_OPTION_CONVERTER;
82+
private static final Converter<List<String>, Long> STRING_LIST_TO_TIME_CONVERTER;
8283

8384
public static final byte[] PLUS_BYTES;
8485
public static final byte[] MINUS_BYTES;
@@ -165,6 +166,19 @@ public byte[] convert(SetOption source) {
165166
}
166167

167168
};
169+
170+
STRING_LIST_TO_TIME_CONVERTER = new Converter<List<String>, Long>() {
171+
172+
@Override
173+
public Long convert(List<String> source) {
174+
175+
Assert.notEmpty(source, "Received invalid result from server. Expected 2 items in collection.");
176+
Assert.isTrue(source.size() == 2,
177+
"Received invalid nr of arguments from redis server. Expected 2 received " + source.size());
178+
179+
return toTimeMillis(source.get(0), source.get(1));
180+
}
181+
};
168182
}
169183

170184
public static Converter<String, byte[]> stringToBytes() {
@@ -173,7 +187,7 @@ public static Converter<String, byte[]> stringToBytes() {
173187

174188
/**
175189
* {@link ListConverter} converting jedis {@link redis.clients.jedis.Tuple} to {@link Tuple}.
176-
*
190+
*
177191
* @return
178192
* @since 1.4
179193
*/
@@ -343,7 +357,7 @@ public static BitOP toBitOp(BitOperation bitOp) {
343357
/**
344358
* Converts a given {@link Boundary} to its binary representation suitable for {@literal ZRANGEBY*} commands, despite
345359
* {@literal ZRANGEBYLEX}.
346-
*
360+
*
347361
* @param boundary
348362
* @param defaultValue
349363
* @return
@@ -360,7 +374,7 @@ public static byte[] boundaryToBytesForZRange(Boundary boundary, byte[] defaultV
360374

361375
/**
362376
* Converts a given {@link Boundary} to its binary representation suitable for ZRANGEBYLEX command.
363-
*
377+
*
364378
* @param boundary
365379
* @return
366380
* @since 1.6
@@ -382,7 +396,7 @@ public static byte[] boundaryToBytesForZRangeByLex(Boundary boundary, byte[] def
382396
* <dt>{@link TimeUnit#MILLISECONDS}</dt>
383397
* <dd>{@code PX}</dd>
384398
* </dl>
385-
*
399+
*
386400
* @param expiration
387401
* @return
388402
* @since 1.7
@@ -401,7 +415,7 @@ public static byte[] toSetCommandExPxArgument(Expiration expiration) {
401415
* <dt>{@link SetOption#SET_IF_PRESENT}</dt>
402416
* <dd>{@code XX}</dd>
403417
* </dl>
404-
*
418+
*
405419
* @param option
406420
* @return
407421
* @since 1.7
@@ -442,9 +456,9 @@ private static byte[] boundaryToBytes(Boundary boundary, byte[] inclPrefix, byte
442456
* @return
443457
*/
444458
public static ScanParams toScanParams(ScanOptions options) {
445-
459+
446460
ScanParams sp = new ScanParams();
447-
461+
448462
if (!options.equals(ScanOptions.NONE)) {
449463
if (options.getCount() != null) {
450464
sp.count(options.getCount().intValue());
@@ -456,4 +470,8 @@ public static ScanParams toScanParams(ScanOptions options) {
456470
return sp;
457471
}
458472

473+
static Converter<List<String>, Long> toTimeConverter() {
474+
return STRING_LIST_TO_TIME_CONVERTER;
475+
}
476+
459477
}

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

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
import org.springframework.data.redis.connection.ReturnType;
5555
import org.springframework.data.redis.connection.SortParameters;
5656
import org.springframework.data.redis.connection.Subscription;
57-
import org.springframework.data.redis.connection.convert.Converters;
5857
import org.springframework.data.redis.connection.convert.TransactionResultConverter;
5958
import org.springframework.data.redis.core.Cursor;
6059
import org.springframework.data.redis.core.KeyBoundCursor;
@@ -111,7 +110,7 @@
111110
/**
112111
* {@code RedisConnection} implementation on top of <a href="https://github.com/mp911de/lettuce">Lettuce</a> Redis
113112
* client.
114-
*
113+
*
115114
* @author Costin Leau
116115
* @author Jennifer Hickey
117116
* @author Christoph Strobl
@@ -254,7 +253,7 @@ public T convert(Object source) {
254253

255254
/**
256255
* Instantiates a new lettuce connection.
257-
*
256+
*
258257
* @param timeout The connection timeout (in milliseconds)
259258
* @param client The {@link RedisClient} to use when instantiating a native connection
260259
*/
@@ -264,7 +263,7 @@ public LettuceConnection(long timeout, RedisClient client) {
264263

265264
/**
266265
* Instantiates a new lettuce connection.
267-
*
266+
*
268267
* @param timeout The connection timeout (in milliseconds) * @param client The {@link RedisClient} to use when
269268
* instantiating a pub/sub connection
270269
* @param pool The connection pool to use for all other native connections
@@ -275,7 +274,7 @@ public LettuceConnection(long timeout, RedisClient client, LettucePool pool) {
275274

276275
/**
277276
* Instantiates a new lettuce connection.
278-
*
277+
*
279278
* @param sharedConnection A native connection that is shared with other {@link LettuceConnection}s. Will not be used
280279
* for transactions or blocking operations
281280
* @param timeout The connection timeout (in milliseconds)
@@ -288,7 +287,7 @@ public LettuceConnection(com.lambdaworks.redis.RedisAsyncConnection<byte[], byte
288287

289288
/**
290289
* Instantiates a new lettuce connection.
291-
*
290+
*
292291
* @param sharedConnection A native connection that is shared with other {@link LettuceConnection}s. Should not be
293292
* used for transactions or blocking operations
294293
* @param timeout The connection timeout (in milliseconds)
@@ -347,7 +346,7 @@ public Object execute(String command, byte[]... args) {
347346

348347
/**
349348
* 'Native' or 'raw' execution of the given command along-side the given arguments.
350-
*
349+
*
351350
* @see RedisCommands#execute(String, byte[]...)
352351
* @param command Command to execute
353352
* @param commandOutputTypeHint Type of Output to use, may be (may be {@literal null}).
@@ -3085,16 +3084,19 @@ public void subscribe(MessageListener listener, byte[]... channels) {
30853084
*/
30863085
@Override
30873086
public Long time() {
3088-
try {
3089-
3090-
List<byte[]> result = getConnection().time();
30913087

3092-
Assert.notEmpty(result, "Received invalid result from server. Expected 2 items in collection.");
3093-
Assert.isTrue(result.size() == 2, "Received invalid nr of arguments from redis server. Expected 2 received "
3094-
+ result.size());
3088+
try {
30953089

3096-
return Converters.toTimeMillis(new String(result.get(0)), new String(result.get(1)));
3090+
if (isPipelined()) {
3091+
pipeline(new LettuceResult(getAsyncConnection().time(), LettuceConverters.toTimeConverter()));
3092+
return null;
3093+
}
3094+
if (isQueueing()) {
3095+
transaction(new LettuceTxResult(getConnection().time(), LettuceConverters.toTimeConverter()));
3096+
return null;
3097+
}
30973098

3099+
return LettuceConverters.toTimeConverter().convert(getConnection().time());
30983100
} catch (Exception ex) {
30993101
throw convertLettuceAccessException(ex);
31003102
}
@@ -3406,7 +3408,7 @@ private <T> T failsafeReadScanValues(List<?> source, @SuppressWarnings("rawtypes
34063408
/**
34073409
* Specifies if pipelined and transaction results should be converted to the expected data type. If false, results of
34083410
* {@link #closePipeline()} and {@link #exec()} will be of the type returned by the Lettuce driver
3409-
*
3411+
*
34103412
* @param convertPipelineAndTxResults Whether or not to convert pipeline and tx results
34113413
*/
34123414
public void setConvertPipelineAndTxResults(boolean convertPipelineAndTxResults) {
@@ -3643,7 +3645,7 @@ protected RedisSentinelConnection getSentinelConnection(RedisNode sentinel) {
36433645

36443646
/**
36453647
* {@link TypeHints} provide {@link CommandOutput} information for a given {@link CommandType}.
3646-
*
3648+
*
36473649
* @since 1.2.1
36483650
*/
36493651
static class TypeHints {
@@ -3804,7 +3806,7 @@ static class TypeHints {
38043806

38053807
/**
38063808
* Returns the {@link CommandOutput} mapped for given {@link CommandType} or {@link ByteArrayOutput} as default.
3807-
*
3809+
*
38083810
* @param type
38093811
* @return {@link ByteArrayOutput} as default when no matching {@link CommandOutput} available.
38103812
*/
@@ -3815,7 +3817,7 @@ public CommandOutput getTypeHint(CommandType type) {
38153817

38163818
/**
38173819
* Returns the {@link CommandOutput} mapped for given {@link CommandType} given {@link CommandOutput} as default.
3818-
*
3820+
*
38193821
* @param type
38203822
* @return
38213823
*/

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767

6868
/**
6969
* Lettuce type converters
70-
*
70+
*
7171
* @author Jennifer Hickey
7272
* @author Christoph Strobl
7373
* @author Thomas Darimont
@@ -91,6 +91,7 @@ abstract public class LettuceConverters extends Converters {
9191
private static final Converter<String[], List<RedisClientInfo>> STRING_TO_LIST_OF_CLIENT_INFO = new StringToRedisClientInfoConverter();
9292
private static final Converter<Partitions, List<RedisClusterNode>> PARTITIONS_TO_CLUSTER_NODES;
9393
private static Converter<com.lambdaworks.redis.cluster.models.partitions.RedisClusterNode, RedisClusterNode> CLUSTER_NODE_TO_CLUSTER_NODE_CONVERTER;
94+
private static final Converter<List<byte[]>, Long> BYTES_LIST_TO_TIME_CONVERTER;
9495

9596
public static final byte[] PLUS_BYTES;
9697
public static final byte[] MINUS_BYTES;
@@ -284,6 +285,19 @@ private Set<Flag> parseFlags(Set<NodeFlag> source) {
284285
MINUS_BYTES = toBytes("-");
285286
POSITIVE_INFINITY_BYTES = toBytes("+inf");
286287
NEGATIVE_INFINITY_BYTES = toBytes("-inf");
288+
289+
BYTES_LIST_TO_TIME_CONVERTER = new Converter<List<byte[]>, Long>() {
290+
291+
@Override
292+
public Long convert(List<byte[]> source) {
293+
294+
Assert.notEmpty(source, "Received invalid result from server. Expected 2 items in collection.");
295+
Assert.isTrue(source.size() == 2,
296+
"Received invalid nr of arguments from redis server. Expected 2 received " + source.size());
297+
298+
return toTimeMillis(LettuceConverters.toString(source.get(0)), LettuceConverters.toString(source.get(1)));
299+
}
300+
};
287301
}
288302

289303
public static List<Tuple> toTuple(List<byte[]> list) {
@@ -620,7 +634,7 @@ public static RedisClusterNode toRedisClusterNode(
620634

621635
/**
622636
* Converts a given {@link Expiration} and {@link SetOption} to the according {@link SetArgs}.<br />
623-
*
637+
*
624638
* @param expiration can be {@literal null}.
625639
* @param option can be {@literal null}.
626640
* @since 1.7
@@ -657,4 +671,7 @@ public static SetArgs toSetArgs(Expiration expiration, SetOption option) {
657671
return args;
658672
}
659673

674+
static Converter<List<byte[]>, Long> toTimeConverter() {
675+
return BYTES_LIST_TO_TIME_CONVERTER;
676+
}
660677
}

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@
1515
*/
1616
package org.springframework.data.redis.connection;
1717

18-
import static org.hamcrest.CoreMatchers.*;
19-
import static org.hamcrest.number.IsCloseTo.*;
18+
import static org.hamcrest.Matchers.*;
2019
import static org.junit.Assert.*;
2120
import static org.junit.Assume.*;
2221
import static org.springframework.data.redis.SpinBarrier.*;
@@ -76,7 +75,7 @@
7675

7776
/**
7877
* Base test class for AbstractConnection integration tests
79-
*
78+
*
8079
* @author Costin Leau
8180
* @author Jennifer Hickey
8281
* @author Christoph Strobl
@@ -1922,13 +1921,17 @@ public void testLastSave() {
19221921

19231922
/**
19241923
* @see DATAREDIS-206
1924+
* @see DATAREDIS-513
19251925
*/
19261926
@Test
19271927
public void testGetTimeShouldRequestServerTime() {
19281928

1929-
Long time = connectionFactory.getConnection().time();
1930-
assertThat(time, notNullValue());
1931-
assertThat(time > 0, equalTo(true));
1929+
actual.add(connection.time());
1930+
1931+
List<Object> results = getResults();
1932+
assertThat(results, is(not(empty())));
1933+
assertThat(results.get(0), notNullValue());
1934+
assertThat((Long) results.get(0) > 0, equalTo(true));
19321935
}
19331936

19341937
/**

0 commit comments

Comments
 (0)