diff --git a/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutor.java b/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutor.java index 04d90af6c..c985dc5d0 100644 --- a/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutor.java +++ b/src/main/java/org/springframework/data/redis/connection/ClusterCommandExecutor.java @@ -24,6 +24,7 @@ import java.util.*; import java.util.Map.Entry; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; +import java.util.function.Function; import java.util.stream.Collectors; import org.springframework.beans.factory.DisposableBean; @@ -493,6 +494,22 @@ public class ClusterCommandExecutor implements DisposableBean { public byte[] getKey() { return key.getArray(); } + + /** + * Apply the {@link Function mapper function} to the value and return the mapped value. + * + * @param mapper must not be {@literal null}. + * @param type of the mapped value. + * @return the mapped value. + * @since 2.1 + */ + @Nullable + public U mapValue(Function mapper) { + + Assert.notNull(mapper, "Mapper function must not be null!"); + + return mapper.apply(getValue()); + } } /** diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java index cdf91a499..1d0422255 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java @@ -568,7 +568,13 @@ class JedisClusterKeyCommands implements RedisKeyCommands { @Nullable @Override public ValueEncoding encodingOf(byte[] key) { - throw new UnsupportedOperationException("Jedis does not support OBJECT ENCODING in cluster!"); + + Assert.notNull(key, "Key must not be null!"); + + return connection.getClusterCommandExecutor() + .executeCommandOnSingleNode((JedisClusterCommandCallback) client -> client.objectEncoding(key), + connection.clusterGetNodeForKey(key)) + .mapValue(JedisConverters::toEncoding); } /* @@ -578,7 +584,13 @@ class JedisClusterKeyCommands implements RedisKeyCommands { @Nullable @Override public Duration idletime(byte[] key) { - throw new UnsupportedOperationException("Jedis does not support OBJECT IDLETIME in cluster!"); + + Assert.notNull(key, "Key must not be null!"); + + return connection.getClusterCommandExecutor() + .executeCommandOnSingleNode((JedisClusterCommandCallback) client -> client.objectIdletime(key), + connection.clusterGetNodeForKey(key)) + .mapValue(Converters::secondsToDuration); } /* @@ -588,7 +600,14 @@ class JedisClusterKeyCommands implements RedisKeyCommands { @Nullable @Override public Long refcount(byte[] key) { - throw new UnsupportedOperationException("Jedis does not support OBJECT REFCOUNT in cluster!"); + + Assert.notNull(key, "Key must not be null!"); + + return connection.getClusterCommandExecutor() + .executeCommandOnSingleNode((JedisClusterCommandCallback) client -> client.objectRefcount(key), + connection.clusterGetNodeForKey(key)) + .getValue(); + } private DataAccessException convertJedisAccessException(Exception ex) { 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 58aa0bce8..9052d393c 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 @@ -28,6 +28,7 @@ import redis.clients.jedis.JedisPool; import java.io.IOException; import java.nio.charset.Charset; +import java.time.Duration; import java.util.*; import java.util.concurrent.TimeUnit; @@ -57,6 +58,7 @@ import org.springframework.data.redis.connection.RedisStringCommands.BitOperatio import org.springframework.data.redis.connection.RedisStringCommands.SetOption; import org.springframework.data.redis.connection.RedisZSetCommands.Range; import org.springframework.data.redis.connection.RedisZSetCommands.Tuple; +import org.springframework.data.redis.connection.ValueEncoding.RedisValueEncoding; import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.ScanOptions; import org.springframework.data.redis.core.types.Expiration; @@ -2292,4 +2294,43 @@ public class JedisClusterConnectionTests implements ClusterConnectionTests { org.springframework.data.domain.Range.of(Bound.inclusive(2L), Bound.unbounded())), is(16L)); } + @Test // DATAREDIS-716 + public void encodingReturnsCorrectly() { + + nativeConnection.set(KEY_1_BYTES, "1000".getBytes()); + + assertThat(clusterConnection.keyCommands().encodingOf(KEY_1_BYTES), is(RedisValueEncoding.INT)); + } + + @Test // DATAREDIS-716 + public void encodingReturnsVacantWhenKeyDoesNotExist() { + assertThat(clusterConnection.keyCommands().encodingOf(KEY_2_BYTES), is(RedisValueEncoding.VACANT)); + } + + @Test // DATAREDIS-716 + public void idletimeReturnsCorrectly() { + + nativeConnection.set(KEY_1_BYTES, VALUE_1_BYTES); + nativeConnection.get(KEY_1_BYTES); + + assertThat(clusterConnection.keyCommands().idletime(KEY_1_BYTES), is(Duration.ofSeconds(0))); + } + + @Test // DATAREDIS-716 + public void idldetimeReturnsNullWhenKeyDoesNotExist() { + assertThat(clusterConnection.keyCommands().idletime(KEY_3_BYTES), is(nullValue())); + } + + @Test // DATAREDIS-716 + public void refcountReturnsCorrectly() { + + nativeConnection.lpush(KEY_1_BYTES, VALUE_1_BYTES); + + assertThat(clusterConnection.keyCommands().refcount(KEY_1_BYTES), is(1L)); + } + + @Test // DATAREDIS-716 + public void refcountReturnsNullWhenKeyDoesNotExist() { + assertThat(clusterConnection.keyCommands().refcount(KEY_3_BYTES), is(nullValue())); + } } 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 8507abed8..4d03b6947 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 @@ -52,6 +52,7 @@ import org.springframework.data.redis.connection.RedisStringCommands.BitOperatio import org.springframework.data.redis.connection.RedisStringCommands.SetOption; import org.springframework.data.redis.connection.RedisZSetCommands.Range; import org.springframework.data.redis.connection.RedisZSetCommands.Tuple; +import org.springframework.data.redis.connection.ValueEncoding.RedisValueEncoding; import org.springframework.data.redis.connection.jedis.JedisConverters; import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.ScanOptions; @@ -2317,4 +2318,44 @@ public class LettuceClusterConnectionTests implements ClusterConnectionTests { assertThat(clusterConnection.stringCommands().bitPos(KEY_1_BYTES, true, org.springframework.data.domain.Range.of(Bound.inclusive(2L), Bound.unbounded())), is(16L)); } + + @Test // DATAREDIS-716 + public void encodingReturnsCorrectly() { + + nativeConnection.set(KEY_1, "1000"); + + assertThat(clusterConnection.keyCommands().encodingOf(KEY_1_BYTES), is(RedisValueEncoding.INT)); + } + + @Test // DATAREDIS-716 + public void encodingReturnsVacantWhenKeyDoesNotExist() { + assertThat(clusterConnection.keyCommands().encodingOf(KEY_2_BYTES), is(RedisValueEncoding.VACANT)); + } + + @Test // DATAREDIS-716 + public void idletimeReturnsCorrectly() { + + nativeConnection.set(KEY_1, VALUE_1); + nativeConnection.get(KEY_1); + + assertThat(clusterConnection.keyCommands().idletime(KEY_1_BYTES), is(Duration.ofSeconds(0))); + } + + @Test // DATAREDIS-716 + public void idldetimeReturnsNullWhenKeyDoesNotExist() { + assertThat(clusterConnection.keyCommands().idletime(KEY_3_BYTES), is(nullValue())); + } + + @Test // DATAREDIS-716 + public void refcountReturnsCorrectly() { + + nativeConnection.lpush(KEY_1, VALUE_1); + + assertThat(clusterConnection.keyCommands().refcount(KEY_1_BYTES), is(1L)); + } + + @Test // DATAREDIS-716 + public void refcountReturnsNullWhenKeyDoesNotExist() { + assertThat(clusterConnection.keyCommands().refcount(KEY_3_BYTES), is(nullValue())); + } }