From e8a30a79a03c70e5d32dfe8fa0f197df3a1106ab Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 7 Jul 2016 10:00:03 +0200 Subject: [PATCH] DATAREDIS-526 - Allow using ttl and pttl with TimeUnit on connection-level. We now support ttl and pttl accepting a TimeUnit on connection level to return the TTL in the requested time unit. By performing the conversion inside the connection we return correct results for plain, pipelining and transaction execution. Add also JavaDoc and @Override to ttl/pttl methods. Original pull request: #205. --- .../DefaultStringRedisConnection.java | 78 +++++++++- .../redis/connection/RedisKeyCommands.java | 30 +++- .../connection/StringRedisConnection.java | 143 ++++++++++++++++++ .../redis/connection/convert/Converters.java | 76 ++++++++++ .../jedis/JedisClusterConnection.java | 29 ++++ .../connection/jedis/JedisConnection.java | 69 +++++++++ .../connection/jredis/JredisConnection.java | 41 +++++ .../connection/lettuce/LettuceConnection.java | 69 +++++++++ .../redis/connection/srp/SrpConnection.java | 54 +++++++ .../data/redis/core/RedisTemplate.java | 7 +- .../AbstractConnectionIntegrationTests.java | 43 +++++- .../connection/ClusterConnectionTests.java | 13 +- ...ultStringRedisConnectionPipelineTests.java | 10 ++ ...tStringRedisConnectionPipelineTxTests.java | 11 ++ .../DefaultStringRedisConnectionTests.java | 13 ++ .../DefaultStringRedisConnectionTxTests.java | 10 ++ .../connection/RedisConnectionUnitTests.java | 10 ++ .../jedis/JedisClusterConnectionTests.java | 19 ++- .../JRedisConnectionIntegrationTests.java | 8 + .../LettuceClusterConnectionTests.java | 29 +++- .../redis/core/RedisClusterTemplateTests.java | 12 ++ .../data/redis/core/RedisTemplateTests.java | 59 ++++++++ 22 files changed, 816 insertions(+), 17 deletions(-) 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 4e4b4ecd3..48fc7cad7 100644 --- a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java @@ -26,6 +26,7 @@ import java.util.Map.Entry; import java.util.Properties; import java.util.Queue; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -938,7 +939,13 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco return result; } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisKeyCommands#ttl(byte[]) + */ + @Override public Long ttl(byte[] key) { + Long result = delegate.ttl(key); if (isFutureConversion()) { addResultConverter(identityConverter); @@ -946,6 +953,21 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco return result; } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisKeyCommands#ttl(byte[], java.util.concurrent.TimeUnit) + */ + @Override + public Long ttl(byte[] key, TimeUnit timeUnit) { + + Long result = delegate.ttl(key, timeUnit); + if (isFutureConversion()) { + addResultConverter(identityConverter); + } + + return result; + } + public DataType type(byte[] key) { DataType result = delegate.type(key); if (isFutureConversion()) { @@ -1329,11 +1351,33 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco return result; } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisKeyCommands#pTtl(byte[]) + */ + @Override public Long pTtl(byte[] key) { + Long result = delegate.pTtl(key); if (isFutureConversion()) { addResultConverter(identityConverter); } + + return result; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisKeyCommands#pTtl(byte[], java.util.concurrent.TimeUnit) + */ + @Override + public Long pTtl(byte[] key, TimeUnit timeUnit) { + + Long result = delegate.pTtl(key, timeUnit); + if (isFutureConversion()) { + addResultConverter(identityConverter); + } + return result; } @@ -2059,12 +2103,22 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco return result; } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.StringRedisConnection#ttl(java.lang.String) + */ + @Override public Long ttl(String key) { - Long result = delegate.ttl(serialize(key)); - if (isFutureConversion()) { - addResultConverter(identityConverter); - } - return result; + return ttl(serialize(key)); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.StringRedisConnection#ttl(java.lang.String, java.util.concurrent.TimeUnit) + */ + @Override + public Long ttl(String key, TimeUnit timeUnit) { + return ttl(serialize(key), timeUnit); } public DataType type(String key) { @@ -2689,10 +2743,24 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco return pExpireAt(serialize(key), unixTimeInMillis); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.StringRedisConnection#pTtl(java.lang.String) + */ + @Override public Long pTtl(String key) { return pTtl(serialize(key)); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.StringRedisConnection#pTtl(java.lang.String, java.util.concurrent.TimeUnit) + */ + @Override + public Long pTtl(String key, TimeUnit timeUnit) { + return pTtl(serialize(key), timeUnit); + } + public String scriptLoad(String script) { String result = delegate.scriptLoad(serialize(script)); if (isFutureConversion()) { diff --git a/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java index 1e10027d9..a3c70a708 100644 --- a/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/RedisKeyCommands.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2014 the original author or authors. + * Copyright 2011-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.data.redis.connection; import java.util.List; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.ScanOptions; @@ -26,6 +27,7 @@ import org.springframework.data.redis.core.ScanOptions; * * @author Costin Leau * @author Christoph Strobl + * @author Mark Paluch */ public interface RedisKeyCommands { @@ -186,7 +188,19 @@ public interface RedisKeyCommands { Long ttl(byte[] key); /** - * Get the time to live for {@code key} in milliseconds. + * Get the time to live for {@code key} in and convert it to the given {@link TimeUnit}. + *

+ * See http://redis.io/commands/ttl + * + * @param key + * @param timeUnit + * @return + * @since 1.8 + */ + Long ttl(byte[] key, TimeUnit timeUnit); + + /** + * Get the precise time to live for {@code key} in milliseconds. *

* See http://redis.io/commands/pttl * @@ -195,6 +209,18 @@ public interface RedisKeyCommands { */ Long pTtl(byte[] key); + /** + * Get the precise time to live for {@code key} in and convert it to the given {@link TimeUnit}. + *

+ * See http://redis.io/commands/pttl + * + * @param key + * @param timeUnit + * @return + * @since 1.8 + */ + Long pTtl(byte[] key, TimeUnit timeUnit); + /** * Sort the elements for {@code key}. *

diff --git a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java index a6682d515..7b2aab8e6 100644 --- a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java @@ -19,6 +19,7 @@ import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.springframework.data.geo.Circle; import org.springframework.data.geo.Distance; @@ -60,34 +61,176 @@ public interface StringRedisConnection extends RedisConnection { Object execute(String command); + /** + * Determine if given {@code key} exists. + *

+ * See http://redis.io/commands/exists + * + * @param key + * @return + */ Boolean exists(String key); + /** + * Delete given {@code keys}. + *

+ * See http://redis.io/commands/del + * + * @param keys + * @return The number of keys that were removed. + */ Long del(String... keys); + /** + * Determine the type stored at {@code key}. + *

+ * See http://redis.io/commands/type + * + * @param key + * @return + */ DataType type(String key); + /** + * Find all keys matching the given {@code pattern}. + *

+ * See http://redis.io/commands/keys + * + * @param pattern + * @return + */ Collection keys(String pattern); + /** + * Rename key {@code oleName} to {@code newName}. + *

+ * See http://redis.io/commands/rename + * + * @param oldName + * @param newName + */ void rename(String oldName, String newName); + /** + * Rename key {@code oleName} to {@code newName} only if {@code newName} does not exist. + *

+ * See http://redis.io/commands/renamenx + * + * @param oldName + * @param newName + * @return + */ Boolean renameNX(String oldName, String newName); + /** + * Set time to live for given {@code key} in seconds. + *

+ * See http://redis.io/commands/expire + * + * @param key + * @param seconds + * @return + */ Boolean expire(String key, long seconds); + /** + * Set time to live for given {@code key} in milliseconds. + *

+ * See http://redis.io/commands/pexpire + * + * @param key + * @param millis + * @return + */ Boolean pExpire(String key, long millis); + /** + * Set the expiration for given {@code key} as a {@literal UNIX} timestamp. + *

+ * See http://redis.io/commands/expireat + * + * @param key + * @param unixTime + * @return + */ Boolean expireAt(String key, long unixTime); + /** + * Set the expiration for given {@code key} as a {@literal UNIX} timestamp in milliseconds. + *

+ * See http://redis.io/commands/pexpireat + * + * @param key + * @param unixTimeInMillis + * @return + */ Boolean pExpireAt(String key, long unixTimeInMillis); + /** + * Remove the expiration from given {@code key}. + *

+ * See http://redis.io/commands/persist + * + * @param key + * @return + */ Boolean persist(String key); + /** + * Move given {@code key} to database with {@code index}. + *

+ * See http://redis.io/commands/move + * + * @param key + * @param dbIndex + * @return + */ Boolean move(String key, int dbIndex); + /** + * Get the time to live for {@code key} in seconds. + *

+ * See http://redis.io/commands/ttl + * + * @param key + * @return + */ Long ttl(String key); + /** + * Get the time to live for {@code key} in and convert it to the given {@link TimeUnit}. + *

+ * See http://redis.io/commands/ttl + * + * @param key + * @param timeUnit + * @return + * @since 1.8 + */ + Long ttl(String key, TimeUnit timeUnit); + + /** + * Get the precise time to live for {@code key} in milliseconds. + *

+ * See http://redis.io/commands/pttl + * + * @param key + * @return + */ Long pTtl(String key); + /** + * Get the precise time to live for {@code key} in and convert it to the given {@link TimeUnit}. + *

+ * See http://redis.io/commands/pttl + * + * @param key + * @param timeUnit + * @return + * @since 1.8 + */ + Long pTtl(String key, TimeUnit timeUnit); + String echo(String message); // sort commands 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 b462bc091..510d5752d 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 @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.springframework.core.convert.converter.Converter; import org.springframework.data.geo.Distance; @@ -43,6 +44,7 @@ import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation; import org.springframework.data.redis.connection.RedisNode.NodeType; import org.springframework.data.redis.connection.RedisZSetCommands.Tuple; import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.util.NumberUtils; import org.springframework.util.ObjectUtils; @@ -275,6 +277,80 @@ abstract public class Converters { + NumberUtils.parseNumber(microseconds, Long.class) / 1000L; } + /** + * Converts {@code seconds} to the given {@link TimeUnit}. + * + * @param seconds + * @param targetUnit must not be {@literal null}. + * @return + * @since 1.8 + */ + public static long secondsToTimeUnit(long seconds, TimeUnit targetUnit) { + + Assert.notNull(targetUnit, "TimeUnit must not be null!"); + + if (seconds > 0) { + return targetUnit.convert(seconds, TimeUnit.SECONDS); + } + + return seconds; + } + + /** + * Creates a new {@link Converter} to convert from seconds to the given {@link TimeUnit}. + * + * @param timeUnit muist not be {@literal null}. + * @return + * @since 1.8 + */ + public static Converter secondsToTimeUnit(final TimeUnit timeUnit) { + + return new Converter() { + + @Override + public Long convert(Long seconds) { + return secondsToTimeUnit(seconds, timeUnit); + } + }; + } + + /** + * Converts {@code milliseconds} to the given {@link TimeUnit}. + * + * @param milliseconds + * @param targetUnit must not be {@literal null}. + * @return + * @since 1.8 + */ + public static long millisecondsToTimeUnit(long milliseconds, TimeUnit targetUnit) { + + Assert.notNull(targetUnit, "TimeUnit must not be null!"); + + if (milliseconds > 0) { + return targetUnit.convert(milliseconds, TimeUnit.MILLISECONDS); + } + + return milliseconds; + } + + /** + * Creates a new {@link Converter} to convert from milliseconds to the given {@link TimeUnit}. + * + * @param timeUnit muist not be {@literal null}. + * @return + * @since 1.8 + */ + public static Converter millisecondsToTimeUnit(final TimeUnit timeUnit) { + + return new Converter() { + + @Override + public Long convert(Long seconds) { + return millisecondsToTimeUnit(seconds, timeUnit); + } + }; + } + /** * {@link Converter} capable of deserializing {@link GeoResults}. * diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java index e3b272bb5..03dc6948c 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java @@ -450,6 +450,19 @@ public class JedisClusterConnection implements RedisClusterConnection { throw convertJedisAccessException(ex); } } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisKeyCommands#ttl(byte[], java.util.concurrent.TimeUnit) + */ + @Override + public Long ttl(byte[] key, TimeUnit timeUnit) { + + try { + return Converters.secondsToTimeUnit(cluster.ttl(key), timeUnit); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } /* * (non-Javadoc) @@ -467,6 +480,22 @@ public class JedisClusterConnection implements RedisClusterConnection { }, topologyProvider.getTopology().getKeyServingMasterNode(key)).getValue(); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisKeyCommands#pTtl(byte[], java.util.concurrent.TimeUnit) + */ + @Override + public Long pTtl(final byte[] key, final TimeUnit timeUnit) { + + return clusterCommandExecutor.executeCommandOnSingleNode(new JedisClusterCommandCallback() { + + @Override + public Long doInCluster(Jedis client) { + return Converters.millisecondsToTimeUnit(client.pttl(key), timeUnit); + } + }, topologyProvider.getTopology().getKeyServingMasterNode(key)).getValue(); + } + /* * (non-Javadoc) * @see org.springframework.data.redis.connection.RedisKeyCommands#sort(byte[], org.springframework.data.redis.connection.SortParameters) diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnection.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnection.java index 10331e35b..6a7f75183 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnection.java @@ -53,6 +53,7 @@ import org.springframework.data.redis.connection.RedisZSetCommands; import org.springframework.data.redis.connection.ReturnType; import org.springframework.data.redis.connection.SortParameters; import org.springframework.data.redis.connection.Subscription; +import org.springframework.data.redis.connection.convert.Converters; import org.springframework.data.redis.connection.convert.ListConverter; import org.springframework.data.redis.connection.convert.TransactionResultConverter; import org.springframework.data.redis.core.Cursor; @@ -968,7 +969,15 @@ public class JedisConnection extends AbstractRedisConnection { } } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisKeyCommands#ttl(byte[]) + */ + @Override public Long ttl(byte[] key) { + + Assert.notNull(key, "Key must not be null!"); + try { if (isPipelined()) { pipeline(new JedisResult(pipeline.ttl(key))); @@ -978,12 +987,38 @@ public class JedisConnection extends AbstractRedisConnection { transaction(new JedisResult(transaction.ttl(key))); return null; } + return jedis.ttl(key); } catch (Exception ex) { throw convertJedisAccessException(ex); } } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisKeyCommands#ttl(byte[], java.util.concurrent.TimeUnit) + */ + @Override + public Long ttl(byte[] key, TimeUnit timeUnit) { + + Assert.notNull(key, "Key must not be null!"); + + try { + if (isPipelined()) { + pipeline(new JedisResult(pipeline.ttl(key), Converters.secondsToTimeUnit(timeUnit))); + return null; + } + if (isQueueing()) { + transaction(new JedisResult(transaction.ttl(key), Converters.secondsToTimeUnit(timeUnit))); + return null; + } + + return Converters.secondsToTimeUnit(jedis.ttl(key), timeUnit); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + public Boolean pExpire(byte[] key, long millis) { try { @@ -1017,7 +1052,15 @@ public class JedisConnection extends AbstractRedisConnection { } } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisKeyCommands#pTtl(byte[]) + */ + @Override public Long pTtl(byte[] key) { + + Assert.notNull(key, "Key must not be null!"); + try { if (isPipelined()) { pipeline(new JedisResult(pipeline.pttl(key))); @@ -1027,12 +1070,38 @@ public class JedisConnection extends AbstractRedisConnection { transaction(new JedisResult(transaction.pttl(key))); return null; } + return jedis.pttl(key); } catch (Exception ex) { throw convertJedisAccessException(ex); } } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisKeyCommands#pTtl(byte[], java.util.concurrent.TimeUnit) + */ + @Override + public Long pTtl(byte[] key, TimeUnit timeUnit) { + + Assert.notNull(key, "Key must not be null!"); + + try { + if (isPipelined()) { + pipeline(new JedisResult(pipeline.pttl(key), Converters.millisecondsToTimeUnit(timeUnit))); + return null; + } + if (isQueueing()) { + transaction(new JedisResult(transaction.pttl(key), Converters.millisecondsToTimeUnit(timeUnit))); + return null; + } + + return Converters.millisecondsToTimeUnit(jedis.pttl(key), timeUnit); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + public byte[] dump(byte[] key) { try { if (isPipelined()) { diff --git a/src/main/java/org/springframework/data/redis/connection/jredis/JredisConnection.java b/src/main/java/org/springframework/data/redis/connection/jredis/JredisConnection.java index d67f703f9..155dcab68 100644 --- a/src/main/java/org/springframework/data/redis/connection/jredis/JredisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/jredis/JredisConnection.java @@ -25,6 +25,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.jredis.ClientRuntimeException; import org.jredis.JRedis; @@ -50,6 +51,7 @@ import org.springframework.data.redis.connection.RedisNode; import org.springframework.data.redis.connection.ReturnType; import org.springframework.data.redis.connection.SortParameters; import org.springframework.data.redis.connection.Subscription; +import org.springframework.data.redis.connection.convert.Converters; import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.ScanOptions; import org.springframework.data.redis.core.types.Expiration; @@ -67,6 +69,7 @@ import org.springframework.util.ReflectionUtils; * @author Thomas Darimont * @author David Liu * @author Ninad Divadkar + * @author Mark Paluch * @deprecated since 1.7. Will be removed in subsequent version. */ @Deprecated @@ -369,10 +372,24 @@ public class JredisConnection extends AbstractRedisConnection { throw new UnsupportedOperationException(); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisKeyCommands#pTtl(byte[]) + */ + @Override public Long pTtl(byte[] key) { throw new UnsupportedOperationException(); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisKeyCommands#pTtl(byte[], java.util.concurrent.TimeUnit) + */ + @Override + public Long pTtl(byte[] key, TimeUnit timeUnit) { + throw new UnsupportedOperationException(); + } + public byte[] dump(byte[] key) { throw new UnsupportedOperationException(); } @@ -433,7 +450,15 @@ public class JredisConnection extends AbstractRedisConnection { throw new UnsupportedOperationException(); } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisKeyCommands#ttl(byte[]) + */ + @Override public Long ttl(byte[] key) { + + Assert.notNull(key, "Key must not be null!"); + try { return jredis.ttl(key); } catch (Exception ex) { @@ -441,6 +466,22 @@ public class JredisConnection extends AbstractRedisConnection { } } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisKeyCommands#ttl(byte[], java.util.concurrent.TimeUnit) + */ + @Override + public Long ttl(byte[] key, TimeUnit timeUnit) { + + Assert.notNull(key, "Key must not be null!"); + + try { + return Converters.secondsToTimeUnit(jredis.ttl(key), timeUnit); + } catch (Exception ex) { + throw convertJredisAccessException(ex); + } + } + public DataType type(byte[] key) { try { return JredisUtils.convertDataType(jredis.type(key)); diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java index e3fc5cab5..643c30480 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java @@ -60,6 +60,7 @@ import org.springframework.data.redis.connection.RedisSubscribedConnectionExcept import org.springframework.data.redis.connection.ReturnType; import org.springframework.data.redis.connection.SortParameters; import org.springframework.data.redis.connection.Subscription; +import org.springframework.data.redis.connection.convert.Converters; import org.springframework.data.redis.connection.convert.ListConverter; import org.springframework.data.redis.connection.convert.TransactionResultConverter; import org.springframework.data.redis.core.Cursor; @@ -949,7 +950,15 @@ public class LettuceConnection extends AbstractRedisConnection { } } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisKeyCommands#pTtl(byte[]) + */ + @Override public Long pTtl(byte[] key) { + + Assert.notNull(key, "Key must not be null!"); + try { if (isPipelined()) { pipeline(new LettuceResult(getAsyncConnection().pttl(key))); @@ -959,12 +968,38 @@ public class LettuceConnection extends AbstractRedisConnection { transaction(new LettuceTxResult(getConnection().pttl(key))); return null; } + return getConnection().pttl(key); } catch (Exception ex) { throw convertLettuceAccessException(ex); } } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisKeyCommands#pTtl(byte[], java.util.concurrent.TimeUnit) + */ + @Override + public Long pTtl(byte[] key, TimeUnit timeUnit) { + + Assert.notNull(key, "Key must not be null!"); + + try { + if (isPipelined()) { + pipeline(new LettuceResult(getAsyncConnection().pttl(key), Converters.millisecondsToTimeUnit(timeUnit))); + return null; + } + if (isQueueing()) { + transaction(new LettuceTxResult(getConnection().pttl(key), Converters.millisecondsToTimeUnit(timeUnit))); + return null; + } + + return Converters.millisecondsToTimeUnit(getConnection().pttl(key), timeUnit); + } catch (Exception ex) { + throw convertLettuceAccessException(ex); + } + } + public byte[] dump(byte[] key) { try { if (isPipelined()) { @@ -1131,7 +1166,15 @@ public class LettuceConnection extends AbstractRedisConnection { } } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisKeyCommands#ttl(byte[]) + */ + @Override public Long ttl(byte[] key) { + + Assert.notNull(key, "Key must not be null!"); + try { if (isPipelined()) { pipeline(new LettuceResult(getAsyncConnection().ttl(key))); @@ -1141,12 +1184,38 @@ public class LettuceConnection extends AbstractRedisConnection { transaction(new LettuceTxResult(getConnection().ttl(key))); return null; } + return getConnection().ttl(key); } catch (Exception ex) { throw convertLettuceAccessException(ex); } } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisKeyCommands#ttl(byte[], java.util.concurrent.TimeUnit) + */ + @Override + public Long ttl(byte[] key, TimeUnit timeUnit) { + + Assert.notNull(key, "Key must not be null!"); + + try { + if (isPipelined()) { + pipeline(new LettuceResult(getAsyncConnection().ttl(key), Converters.secondsToTimeUnit(timeUnit))); + return null; + } + if (isQueueing()) { + transaction(new LettuceTxResult(getConnection().ttl(key), Converters.secondsToTimeUnit(timeUnit))); + return null; + } + + return Converters.secondsToTimeUnit(getConnection().ttl(key), timeUnit); + } catch (Exception ex) { + throw convertLettuceAccessException(ex); + } + } + public DataType type(byte[] key) { try { if (isPipelined()) { diff --git a/src/main/java/org/springframework/data/redis/connection/srp/SrpConnection.java b/src/main/java/org/springframework/data/redis/connection/srp/SrpConnection.java index d02b92660..960b1d3fd 100644 --- a/src/main/java/org/springframework/data/redis/connection/srp/SrpConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/srp/SrpConnection.java @@ -28,6 +28,7 @@ import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import org.springframework.core.convert.converter.Converter; import org.springframework.dao.DataAccessException; @@ -49,6 +50,7 @@ import org.springframework.data.redis.connection.RedisSubscribedConnectionExcept import org.springframework.data.redis.connection.ReturnType; import org.springframework.data.redis.connection.SortParameters; import org.springframework.data.redis.connection.Subscription; +import org.springframework.data.redis.connection.convert.Converters; import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.ScanOptions; import org.springframework.data.redis.core.types.Expiration; @@ -77,6 +79,7 @@ import redis.reply.Reply; * @author Thomas Darimont * @author David Liu * @author Ninad Divadkar + * @author Mark Paluch * @deprecated since 1.7. Will be removed in subsequent version. */ @Deprecated @@ -765,18 +768,52 @@ public class SrpConnection extends AbstractRedisConnection { } } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisKeyCommands#ttl(byte[]) + */ + @Override public Long ttl(byte[] key) { + + Assert.notNull(key, "Key must not be null!"); + try { if (isPipelined()) { pipeline(new SrpResult(pipeline.ttl(key))); return null; } + return client.ttl(key).data(); } catch (Exception ex) { throw convertSrpAccessException(ex); } } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisKeyCommands#ttl(byte[], java.util.concurrent.TimeUnit) + */ + @Override + public Long ttl(byte[] key, TimeUnit timeUnit) { + + Assert.notNull(key, "Key must not be null!"); + + try { + if (isPipelined()) { + pipeline(new SrpResult(pipeline.ttl(key), Converters.secondsToTimeUnit(timeUnit))); + return null; + } + return Converters.secondsToTimeUnit(client.ttl(key).data(), timeUnit); + } catch (Exception ex) { + throw convertSrpAccessException(ex); + } + } + + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisKeyCommands#pTtl(byte[]) + */ + @Override public Long pTtl(byte[] key) { try { if (isPipelined()) { @@ -789,6 +826,23 @@ public class SrpConnection extends AbstractRedisConnection { } } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.RedisKeyCommands#pTtl(byte[], java.util.concurrent.TimeUnit) + */ + @Override + public Long pTtl(byte[] key, TimeUnit timeUnit) { + try { + if (isPipelined()) { + pipeline(new SrpResult(pipeline.pttl(key), Converters.millisecondsToTimeUnit(timeUnit))); + return null; + } + return Converters.millisecondsToTimeUnit(client.pttl(key).data(), timeUnit); + } catch (Exception ex) { + throw convertSrpAccessException(ex); + } + } + public byte[] dump(byte[] key) { try { if (isPipelined()) { diff --git a/src/main/java/org/springframework/data/redis/core/RedisTemplate.java b/src/main/java/org/springframework/data/redis/core/RedisTemplate.java index e042b70e4..6ea8b595e 100644 --- a/src/main/java/org/springframework/data/redis/core/RedisTemplate.java +++ b/src/main/java/org/springframework/data/redis/core/RedisTemplate.java @@ -715,14 +715,11 @@ public class RedisTemplate extends RedisAccessor implements RedisOperation return execute(new RedisCallback() { public Long doInRedis(RedisConnection connection) { - Long expire; try { - expire = connection.pTtl(rawKey); - return expire < 0 ? expire : timeUnit.convert(expire, TimeUnit.MILLISECONDS); + return connection.pTtl(rawKey, timeUnit); } catch (Exception e) { // Driver may not support pTtl or we may be running on Redis 2.4 - expire = connection.ttl(rawKey); - return expire < 0 ? expire : timeUnit.convert(expire, TimeUnit.SECONDS); + return connection.ttl(rawKey, timeUnit); } } }, true); 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 394277735..0de0d960e 100644 --- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java @@ -967,6 +967,22 @@ public abstract class AbstractConnectionIntegrationTests { verifyResults(Arrays.asList(new Object[] { -1L })); } + /** + * @see DATAREDIS-526 + */ + @Test + public void testTtlWithTimeUnit() { + + connection.set("whatup", "yo"); + actual.add(connection.expire("whatup", 10)); + actual.add(connection.ttl("whatup", TimeUnit.MILLISECONDS)); + + List results = getResults(); + + assertTrue((Long) results.get(1) > TimeUnit.SECONDS.toMillis(5)); + assertTrue((Long) results.get(1) <= TimeUnit.SECONDS.toMillis(10)); + } + @Test @IfProfileValue(name = "redisVersion", value = "2.6+") public void testPTtlNoExpire() { @@ -978,25 +994,48 @@ public abstract class AbstractConnectionIntegrationTests { @Test @IfProfileValue(name = "redisVersion", value = "2.6+") public void testPTtl() { + connection.set("whatup", "yo"); - actual.add(connection.pExpire("whatup", 9000l)); + actual.add(connection.pExpire("whatup", TimeUnit.SECONDS.toMillis(10))); actual.add(connection.pTtl("whatup")); + List results = getResults(); + assertTrue((Long) results.get(1) > -1); } + /** + * @see DATAREDIS-526 + */ + @Test + @IfProfileValue(name = "redisVersion", value = "2.6+") + public void testPTtlWithTimeUnit() { + + connection.set("whatup", "yo"); + actual.add(connection.pExpire("whatup", TimeUnit.MINUTES.toMillis(10))); + actual.add(connection.pTtl("whatup", TimeUnit.SECONDS)); + + List results = getResults(); + + assertTrue((Long) results.get(1) > TimeUnit.MINUTES.toSeconds(9)); + assertTrue((Long) results.get(1) <= TimeUnit.MINUTES.toSeconds(10)); + } + @Test @IfProfileValue(name = "redisVersion", value = "2.6+") public void testDumpAndRestore() { + connection.set("testing", "12"); actual.add(connection.dump("testing".getBytes())); List results = getResults(); initConnection(); + actual.add(connection.del("testing")); actual.add((connection.get("testing"))); connection.restore("testing".getBytes(), 0, (byte[]) results.get(results.size() - 1)); actual.add(connection.get("testing")); - verifyResults(Arrays.asList(new Object[] { 1l, null, "12" })); + + verifyResults(Arrays.asList(new Object[] { 1L, null, "12" })); } @Test diff --git a/src/test/java/org/springframework/data/redis/connection/ClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/ClusterConnectionTests.java index feb5ae59c..6eac68666 100644 --- a/src/test/java/org/springframework/data/redis/connection/ClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/ClusterConnectionTests.java @@ -19,6 +19,7 @@ import org.springframework.data.geo.Point; /** * @author Christoph Strobl + * @author Mark Paluch */ public interface ClusterConnectionTests { @@ -119,7 +120,7 @@ public interface ClusterConnectionTests { /** * @see DATAREDIS-315 */ - void persistShoudRemoveTTL(); + void persistShouldRemoveTTL(); /** * @see DATAREDIS-315 @@ -141,6 +142,11 @@ public interface ClusterConnectionTests { */ void ttlShouldReturnMinusTwoWhenKeyDoesNotExist(); + /** + * @see DATAREDIS-526 + */ + void ttlWithTimeUnitShouldReturnMinusTwoWhenKeyDoesNotExist(); + /** * @see DATAREDIS-315 */ @@ -156,6 +162,11 @@ public interface ClusterConnectionTests { */ void pTtlShouldReturnMinusTwoWhenKeyDoesNotExist(); + /** + * @see DATAREDIS-526 + */ + void pTtlWithTimeUnitShouldReturnMinusTwoWhenKeyDoesNotExist(); + /** * @see DATAREDIS-315 */ diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTests.java index fa46b76c0..92254be85 100644 --- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTests.java +++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTests.java @@ -33,6 +33,7 @@ import org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit; * @author Jennifer Hickey * @author Christoph Strobl * @author Ninad Divadkar + * @author Mark Paluch */ public class DefaultStringRedisConnectionPipelineTests extends DefaultStringRedisConnectionTests { @@ -973,6 +974,15 @@ public class DefaultStringRedisConnectionPipelineTests extends DefaultStringRedi super.testTtl(); } + /** + * @see DATAREDIS-526 + */ + @Override + public void testTtlWithTimeUnit() { + doReturn(Arrays.asList(new Object[] { 5L })).when(nativeConnection).closePipeline(); + super.testTtlWithTimeUnit(); + } + @Test public void testTypeBytes() { doReturn(Arrays.asList(new Object[] { DataType.HASH })).when(nativeConnection).closePipeline(); diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java index 0707c42bd..ef61a56d5 100644 --- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java +++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionPipelineTxTests.java @@ -32,6 +32,7 @@ import org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit; * @author Jennifer Hickey * @author Christoph Strobl * @author Ninad Divadkar + * @author Mark Paluch */ public class DefaultStringRedisConnectionPipelineTxTests extends DefaultStringRedisConnectionTxTests { @@ -1047,6 +1048,16 @@ public class DefaultStringRedisConnectionPipelineTxTests extends DefaultStringRe super.testTtl(); } + /** + * @see DATAREDIS-526 + */ + @Override + public void testTtlWithTimeUnit() { + + doReturn(Arrays.asList(new Object[] { Arrays.asList(new Object[] { 5L }) })).when(nativeConnection).closePipeline(); + super.testTtlWithTimeUnit(); + } + @Test public void testTypeBytes() { doReturn(Arrays.asList(new Object[] { Arrays.asList(new Object[] { DataType.HASH }) })).when(nativeConnection) diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java index 9d9dc8cf8..908f14f75 100644 --- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.junit.Before; import org.junit.Test; @@ -1193,6 +1194,18 @@ public class DefaultStringRedisConnectionTests { verifyResults(Arrays.asList(new Object[] { 5l })); } + /** + * @see DATAREDIS-526 + */ + @Test + public void testTtlWithTimeUnit() { + + doReturn(5L).when(nativeConnection).ttl(fooBytes, TimeUnit.SECONDS); + + actual.add(connection.ttl(foo, TimeUnit.SECONDS)); + verifyResults(Arrays.asList(new Object[] { 5L })); + } + @Test public void testTypeBytes() { doReturn(DataType.HASH).when(nativeConnection).type(fooBytes); diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java index 6a991b44d..7eda5424a 100644 --- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java +++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTxTests.java @@ -959,6 +959,16 @@ public class DefaultStringRedisConnectionTxTests extends DefaultStringRedisConne super.testTtl(); } + /** + * @see DATAREDIS-526 + */ + @Override + public void testTtlWithTimeUnit() { + + doReturn(Arrays.asList(new Object[] { 5L })).when(nativeConnection).exec(); + super.testTtl(); + } + @Test public void testTypeBytes() { doReturn(Arrays.asList(new Object[] { DataType.HASH })).when(nativeConnection).exec(); diff --git a/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java index a4805a15e..66ee14ea3 100644 --- a/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java +++ b/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.junit.Before; import org.junit.Test; @@ -44,6 +45,7 @@ import org.springframework.util.ObjectUtils; * @author Thomas Darimont * @author David Liu * @author Ninad Divadkar + * @author Mark Paluch */ public class RedisConnectionUnitTests { @@ -688,6 +690,10 @@ public class RedisConnectionUnitTests { return delegate.ttl(key); } + public Long ttl(byte[] key, TimeUnit timeUnit) { + return delegate.pTtl(key, timeUnit); + } + public List bLPop(int timeout, byte[]... keys) { return delegate.bLPop(timeout, keys); } @@ -708,6 +714,10 @@ public class RedisConnectionUnitTests { return delegate.pTtl(key); } + public Long pTtl(byte[] key, TimeUnit timeUnit) { + return delegate.pTtl(key, timeUnit); + } + public Cursor sScan(byte[] key, ScanOptions options) { return delegate.sScan(key, options); } diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java index 5d0a4455a..b57eb0a00 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java @@ -39,6 +39,7 @@ import java.util.ListIterator; import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.junit.After; import org.junit.Before; @@ -378,7 +379,7 @@ public class JedisClusterConnectionTests implements ClusterConnectionTests { * @see DATAREDIS-315 */ @Test - public void persistShoudRemoveTTL() { + public void persistShouldRemoveTTL() { nativeConnection.setex(KEY_1_BYTES, 10, VALUE_1_BYTES); @@ -428,6 +429,14 @@ public class JedisClusterConnectionTests implements ClusterConnectionTests { assertThat(clusterConnection.ttl(KEY_1_BYTES), is(-2L)); } + /** + * @see DATAREDIS-526 + */ + @Test + public void ttlWithTimeUnitShouldReturnMinusTwoWhenKeyDoesNotExist() { + assertThat(clusterConnection.ttl(KEY_1_BYTES, TimeUnit.HOURS), is(-2L)); + } + /** * @see DATAREDIS-315 */ @@ -459,6 +468,14 @@ public class JedisClusterConnectionTests implements ClusterConnectionTests { assertThat(clusterConnection.pTtl(KEY_1_BYTES), is(-2L)); } + /** + * @see DATAREDIS-526 + */ + @Test + public void pTtlWithTimeUnitShouldReturnMinusTwoWhenKeyDoesNotExist() { + assertThat(clusterConnection.pTtl(KEY_1_BYTES, TimeUnit.HOURS), is(-2L)); + } + /** * @see DATAREDIS-315 */ diff --git a/src/test/java/org/springframework/data/redis/connection/jredis/JRedisConnectionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/jredis/JRedisConnectionIntegrationTests.java index 7dd209d02..23433beeb 100644 --- a/src/test/java/org/springframework/data/redis/connection/jredis/JRedisConnectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jredis/JRedisConnectionIntegrationTests.java @@ -349,6 +349,14 @@ public class JRedisConnectionIntegrationTests extends AbstractConnectionIntegrat super.testPTtlNoExpire(); } + /** + * @see DATAREDIS-526 + */ + @Test(expected = UnsupportedOperationException.class) + public void testPTtlWithTimeUnit() { + super.testPTtlWithTimeUnit(); + } + @Test(expected = UnsupportedOperationException.class) public void testDumpAndRestore() { super.testDumpAndRestore(); diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java index decfce555..fa0b5ffec 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java @@ -377,7 +377,7 @@ public class LettuceClusterConnectionTests implements ClusterConnectionTests { * @see DATAREDIS-315 */ @Test - public void persistShoudRemoveTTL() { + public void persistShouldRemoveTTL() { nativeConnection.setex(KEY_1, 10, VALUE_1); @@ -427,6 +427,14 @@ public class LettuceClusterConnectionTests implements ClusterConnectionTests { assertThat(clusterConnection.ttl(KEY_1_BYTES), is(-2L)); } + /** + * @see DATAREDIS-526 + */ + @Test + public void ttlWithTimeUnitShouldReturnMinusTwoWhenKeyDoesNotExist() { + assertThat(clusterConnection.ttl(KEY_1_BYTES, TimeUnit.HOURS), is(-2L)); + } + /** * @see DATAREDIS-315 */ @@ -438,6 +446,17 @@ public class LettuceClusterConnectionTests implements ClusterConnectionTests { assertThat(clusterConnection.ttl(KEY_1_BYTES), is(-1L)); } + /** + * @see DATAREDIS-526 + */ + @Test + public void ttlWithTimeUnitShouldReturnMinusOneWhenKeyDoesNotHaveExpirationSet() { + + nativeConnection.set(KEY_1, VALUE_1); + + assertThat(clusterConnection.ttl(KEY_1_BYTES, TimeUnit.SECONDS), is(-1L)); + } + /** * @see DATAREDIS-315 */ @@ -458,6 +477,14 @@ public class LettuceClusterConnectionTests implements ClusterConnectionTests { assertThat(clusterConnection.pTtl(KEY_1_BYTES), is(-2L)); } + /** + * @see DATAREDIS-526 + */ + @Test + public void pTtlWithTimeUnitShouldReturnMinusTwoWhenKeyDoesNotExist() { + assertThat(clusterConnection.pTtl(KEY_1_BYTES, TimeUnit.SECONDS), is(-2L)); + } + /** * @see DATAREDIS-315 */ diff --git a/src/test/java/org/springframework/data/redis/core/RedisClusterTemplateTests.java b/src/test/java/org/springframework/data/redis/core/RedisClusterTemplateTests.java index a0b6e554b..ee491d755 100644 --- a/src/test/java/org/springframework/data/redis/core/RedisClusterTemplateTests.java +++ b/src/test/java/org/springframework/data/redis/core/RedisClusterTemplateTests.java @@ -128,6 +128,18 @@ public class RedisClusterTemplateTests extends RedisTemplateTests { super.testSortBulkMapper(); } + @Test + @Ignore("This one fails when using GET options on numbers") + public void testGetExpireMillisUsingTransactions() { + super.testGetExpireMillisUsingTransactions(); + } + + @Test + @Ignore("This one fails when using GET options on numbers") + public void testGetExpireMillisUsingPipelining() { + super.testGetExpireMillisUsingPipelining(); + } + @Parameters public static Collection testParams() { diff --git a/src/test/java/org/springframework/data/redis/core/RedisTemplateTests.java b/src/test/java/org/springframework/data/redis/core/RedisTemplateTests.java index d325ff429..a4ea5d763 100644 --- a/src/test/java/org/springframework/data/redis/core/RedisTemplateTests.java +++ b/src/test/java/org/springframework/data/redis/core/RedisTemplateTests.java @@ -599,6 +599,65 @@ public class RedisTemplateTests { assertThat(ttl, lessThan(25L)); } + /** + * @see DATAREDIS-526 + */ + @Test + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void testGetExpireMillisUsingTransactions() { + + assumeTrue(redisTemplate.getConnectionFactory() instanceof JedisConnectionFactory + || redisTemplate.getConnectionFactory() instanceof LettuceConnectionFactory); + + final K key = keyFactory.instance(); + List result = redisTemplate.execute(new SessionCallback>() { + + @Override + public List execute(RedisOperations operations) throws DataAccessException { + + operations.multi(); + operations.boundValueOps(key).set(valueFactory.instance()); + operations.expire(key, 1, TimeUnit.DAYS); + operations.getExpire(key, TimeUnit.HOURS); + + return operations.exec(); + } + }); + + assertThat(result, hasSize(2)); + assertThat(((Long) result.get(1)), greaterThanOrEqualTo(23L)); + assertThat(((Long) result.get(1)), lessThan(25L)); + } + + /** + * @see DATAREDIS-526 + */ + @Test + @SuppressWarnings({ "unchecked", "rawtypes" }) + public void testGetExpireMillisUsingPipelining() { + + assumeTrue(redisTemplate.getConnectionFactory() instanceof JedisConnectionFactory + || redisTemplate.getConnectionFactory() instanceof LettuceConnectionFactory); + + final K key = keyFactory.instance(); + List result = redisTemplate.executePipelined(new SessionCallback() { + + @Override + public Object execute(RedisOperations operations) throws DataAccessException { + + operations.boundValueOps(key).set(valueFactory.instance()); + operations.expire(key, 1, TimeUnit.DAYS); + operations.getExpire(key, TimeUnit.HOURS); + + return null; + } + }); + + assertThat(result, hasSize(2)); + assertThat(((Long) result.get(1)), greaterThanOrEqualTo(23L)); + assertThat(((Long) result.get(1)), lessThan(25L)); + } + @Test public void testGetExpireMillisNotSupported() {