diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java index 7c4dc34b91..c70add4dcf 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java @@ -3303,6 +3303,15 @@ public Long time() { return convertAndReturn(this.delegate.time(), identityConverter); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisServerCommands#microseconds() + */ + @Override + public Long microseconds() { + return convertAndReturn(this.delegate.microseconds(), identityConverter); + } + /* * (non-Javadoc) * @see org.springframework.data.redis.connection.StringRedisConnection#getClientList() diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java index 0cf72c98a7..d8c80598a8 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java @@ -1394,6 +1394,13 @@ default Long time() { return serverCommands().time(); } + /** @deprecated in favor of {@link RedisConnection#serverCommands()}. */ + @Override + @Deprecated + default Long microseconds() { + return serverCommands().microseconds(); + } + /** @deprecated in favor of {@link RedisConnection#serverCommands()}. */ @Override @Deprecated diff --git a/src/main/java/org/springframework/data/redis/connection/RedisServerCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisServerCommands.java index 8b5e2805e3..7164b2fcef 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisServerCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisServerCommands.java @@ -183,6 +183,16 @@ default void bgWriteAof() { @Nullable Long time(); + /** + * Request server timestamp using {@code TIME} command. + * + * @return current server time in microseconds or {@literal null} when used in pipeline / transaction. + * @since 1.1 + * @see Redis Documentation: TIME + */ + @Nullable + Long microseconds(); + /** * Closes a given client connection identified by {@literal host:port}. * diff --git a/src/main/java/org/springframework/data/redis/connection/convert/Converters.java b/src/main/java/org/springframework/data/redis/connection/convert/Converters.java index c773e8cfc3..1e8780f729 100644 --- a/src/main/java/org/springframework/data/redis/connection/convert/Converters.java +++ b/src/main/java/org/springframework/data/redis/connection/convert/Converters.java @@ -302,6 +302,18 @@ public static List toObjects(Set tuples) { return tupleArgs; } + /** + * Returns the timestamp constructed from the given {@code seconds} and {@code microseconds}. + * + * @param seconds server time in seconds + * @param microseconds elapsed microseconds in current second + * @return + */ + public static Long toTimeMicros(String seconds, String microseconds) { + return TimeUnit.SECONDS.toMicros(NumberUtils.parseNumber(seconds, Long.class)) + + NumberUtils.parseNumber(microseconds, Long.class); + } + /** * Returns the timestamp constructed from the given {@code seconds} and {@code microseconds}. * @@ -313,7 +325,6 @@ public static Long toTimeMillis(String seconds, String microseconds) { return NumberUtils.parseNumber(seconds, Long.class) * 1000L + NumberUtils.parseNumber(microseconds, Long.class) / 1000L; } - /** * Converts {@code seconds} to the given {@link TimeUnit}. * diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterServerCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterServerCommands.java index f19b36a5ab..edc544ffee 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterServerCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterServerCommands.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Map.Entry; import java.util.Properties; +import java.util.concurrent.TimeUnit; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.redis.connection.ClusterCommandExecutor.MultiNodeResult; @@ -399,6 +400,17 @@ public Long time() { .executeCommandOnArbitraryNode((JedisClusterCommandCallback>) BinaryJedis::time).getValue()); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisServerCommands#time() + */ + @Override + public Long microseconds() { + + return convertListOfStringToMicros(connection.getClusterCommandExecutor() + .executeCommandOnArbitraryNode((JedisClusterCommandCallback>) BinaryJedis::time).getValue()); + } + /* * (non-Javadoc) * @see org.springframework.data.redis.connection.RedisClusterServerCommands#time(org.springframework.data.redis.connection.RedisClusterNode) @@ -517,12 +529,15 @@ public void migrate(byte[] key, RedisNode target, int dbIndex, @Nullable Migrate } private Long convertListOfStringToTime(List serverTimeInformation) { + return TimeUnit.MICROSECONDS.toMillis(convertListOfStringToMicros(serverTimeInformation)); + } + private Long convertListOfStringToMicros(List serverTimeInformation) { Assert.notEmpty(serverTimeInformation, "Received invalid result from server. Expected 2 items in collection."); Assert.isTrue(serverTimeInformation.size() == 2, "Received invalid number of arguments from redis server. Expected 2 received " + serverTimeInformation.size()); - return Converters.toTimeMillis(serverTimeInformation.get(0), serverTimeInformation.get(1)); + return Converters.toTimeMicros(serverTimeInformation.get(0), serverTimeInformation.get(1)); } private NodeResult executeCommandOnSingleNode(JedisClusterCommandCallback cmd, RedisClusterNode node) { diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java index b7f00edd3f..16e16f657e 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java @@ -193,7 +193,7 @@ public byte[] convert(SetOption source) { Assert.isTrue(source.size() == 2, "Received invalid nr of arguments from redis server. Expected 2 received " + source.size()); - return toTimeMillis(source.get(0), source.get(1)); + return toTimeMicros(source.get(0), source.get(1)); }; GEO_COORDINATE_TO_POINT_CONVERTER = geoCoordinate -> geoCoordinate != null diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisServerCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisServerCommands.java index 93fbffb605..02243d3905 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisServerCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisServerCommands.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Properties; +import java.util.concurrent.TimeUnit; import org.springframework.data.redis.connection.RedisNode; import org.springframework.data.redis.connection.RedisServerCommands; @@ -353,7 +354,16 @@ public void resetConfigStats() { */ @Override public Long time() { + Long time = microseconds(); + return time == null ? null : TimeUnit.MICROSECONDS.toMillis(time); + } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisServerCommands#time() + */ + @Override + public Long microseconds() { try { if (isPipelined()) { pipeline(connection.newJedisResult(connection.getRequiredPipeline().time(), JedisConverters.toTimeConverter())); diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterServerCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterServerCommands.java index 039534a592..3a5474d2d9 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterServerCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterServerCommands.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Properties; +import java.util.concurrent.TimeUnit; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.redis.connection.ClusterCommandExecutor.MultiNodeResult; @@ -312,7 +313,16 @@ public void resetConfigStats(RedisClusterNode node) { */ @Override public Long time() { + Long time = microseconds(); + return time == null ? null : TimeUnit.MICROSECONDS.toMillis(time); + } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.lettuce.LettuceServerCommands#time() + */ + @Override + public Long microseconds() { return convertListOfStringToTime(connection.getClusterCommandExecutor() .executeCommandOnArbitraryNode((LettuceClusterCommandCallback>) RedisServerCommands::time) .getValue()); @@ -384,12 +394,15 @@ private MultiNodeResult executeCommandOnAllNodes(final LettuceClusterComm } private static Long convertListOfStringToTime(List serverTimeInformation) { + return convertListOfStringToMicros(serverTimeInformation) / 1000; + } + private static Long convertListOfStringToMicros(List serverTimeInformation) { Assert.notEmpty(serverTimeInformation, "Received invalid result from server. Expected 2 items in collection."); Assert.isTrue(serverTimeInformation.size() == 2, "Received invalid number of arguments from redis server. Expected 2 received " + serverTimeInformation.size()); - return Converters.toTimeMillis(LettuceConverters.toString(serverTimeInformation.get(0)), + return Converters.toTimeMicros(LettuceConverters.toString(serverTimeInformation.get(0)), LettuceConverters.toString(serverTimeInformation.get(1))); } } diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java index 44be5a7254..53820eddcf 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java @@ -304,7 +304,7 @@ private Set parseFlags(Set source) { Assert.isTrue(source.size() == 2, "Received invalid nr of arguments from redis server. Expected 2 received " + source.size()); - return toTimeMillis(toString(source.get(0)), toString(source.get(1))); + return toTimeMicros(toString(source.get(0)), toString(source.get(1))); }; GEO_COORDINATE_TO_POINT_CONVERTER = geoCoordinate -> geoCoordinate != null diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceServerCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceServerCommands.java index ae12e18a97..7df3f09576 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceServerCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceServerCommands.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Properties; +import java.util.concurrent.TimeUnit; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.connection.RedisNode; @@ -361,7 +362,16 @@ public void resetConfigStats() { */ @Override public Long time() { + Long time = microseconds(); + return time == null ? null : TimeUnit.MICROSECONDS.toMillis(time); + } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisServerCommands#time() + */ + @Override + public Long microseconds() { try { if (isPipelined()) { pipeline(connection.newLettuceResult(getAsyncConnection().time(), LettuceConverters.toTimeConverter())); diff --git a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java index 0659a322c0..ab13b0385c 100644 --- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java @@ -2077,6 +2077,17 @@ public void testGetTimeShouldRequestServerTime() { assertThat((Long) results.get(0) > 0).isTrue(); } + @Test + public void testGetMicrosecondsShouldRequestServerTime() { + + actual.add(connection.microseconds()); + + List results = getResults(); + assertThat(results).isNotEmpty(); + assertThat(results.get(0)).isNotNull(); + assertThat((Long) results.get(0) > 0).isTrue(); + } + @Test // DATAREDIS-269 public void clientSetNameWorksCorrectly() { connection.setClientName("foo".getBytes());