From 77de777da819f1e1a6913bb1ea2f6a3db8dcdfeb Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 15 Mar 2016 09:13:05 +0100 Subject: [PATCH] DATAREDIS-479 - Upgrade to jedis to 2.8.1. Update jedis driver to 2.8.1. Use binary method for SSCAN and implement ZSCAN and HSCAN methods. Move ScanOptions to ScanParams conversion to JedisConverters. Original Pull Request: #178 --- pom.xml | 2 +- .../jedis/JedisClusterConnection.java | 31 ++++++++--- .../connection/jedis/JedisConnection.java | 23 ++------ .../connection/jedis/JedisConverters.java | 24 +++++++++ .../jedis/JedisClusterConnectionTests.java | 54 +++++++++++++++++++ 5 files changed, 107 insertions(+), 27 deletions(-) diff --git a/pom.xml b/pom.xml index e2e0f007c..f50aae9ff 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ 1.4.8 2.2 3.4.2.Final - 2.8.0 + 2.8.1 0.7 06052013 1.01 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 b1c3aaa57..f7a87624b 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 @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015-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. @@ -74,6 +74,7 @@ import redis.clients.jedis.BinaryJedisPubSub; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisCluster; import redis.clients.jedis.JedisPool; +import redis.clients.jedis.ScanParams; import redis.clients.jedis.ZParams; /** @@ -1532,10 +1533,9 @@ public class JedisClusterConnection implements RedisClusterConnection { @Override protected ScanIteration doScan(long cursorId, ScanOptions options) { - redis.clients.jedis.ScanResult result = cluster.sscan(JedisConverters.toString(key), - Long.toString(cursorId)); - return new ScanIteration(Long.valueOf(result.getCursor()), - JedisConverters.stringListToByteList().convert(result.getResult())); + ScanParams params = JedisConverters.toScanParams(options); + redis.clients.jedis.ScanResult result = cluster.sscan(key, JedisConverters.toBytes(cursorId), params); + return new ScanIteration(Long.valueOf(result.getStringCursor()), result.getResult()); } }.open(); } @@ -2158,8 +2158,19 @@ public class JedisClusterConnection implements RedisClusterConnection { } @Override - public Cursor zScan(byte[] key, ScanOptions options) { - throw new UnsupportedOperationException("Jedis does currently not support binary zscan command."); + public Cursor zScan(final byte[] key, final ScanOptions options) { + return new ScanCursor(options) { + + @Override + protected ScanIteration doScan(long cursorId, ScanOptions options) { + + ScanParams params = JedisConverters.toScanParams(options); + + redis.clients.jedis.ScanResult result = cluster.zscan(key, JedisConverters.toBytes(cursorId), params); + return new ScanIteration(Long.valueOf(result.getStringCursor()), JedisConverters + .tuplesToTuples().convert(result.getResult())); + } + }.open(); } /* @@ -2387,7 +2398,11 @@ public class JedisClusterConnection implements RedisClusterConnection { @Override protected ScanIteration> doScan(long cursorId, ScanOptions options) { - throw new UnsupportedOperationException("Jedis does currently not support binary hscan"); + + ScanParams params = JedisConverters.toScanParams(options); + + redis.clients.jedis.ScanResult> result = cluster.hscan(key, JedisConverters.toBytes(cursorId), params); + return new ScanIteration>(Long.valueOf(result.getStringCursor()), result.getResult()); } }.open(); } 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 be7094d98..94ffd486a 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 @@ -60,7 +60,6 @@ import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringUtils; import redis.clients.jedis.BinaryJedis; import redis.clients.jedis.BinaryJedisPubSub; @@ -92,6 +91,7 @@ import redis.clients.util.Pool; * @author Konstantin Shchepanovskyi * @author David Liu * @author Milan Agatonovic + * @author Mark Paluch */ public class JedisConnection extends AbstractRedisConnection { @@ -3315,7 +3315,7 @@ public class JedisConnection extends AbstractRedisConnection { throw new UnsupportedOperationException("'SCAN' cannot be called in pipeline / transaction mode."); } - ScanParams params = prepareScanParams(options); + ScanParams params = JedisConverters.toScanParams(options); redis.clients.jedis.ScanResult result = jedis.scan(Long.toString(cursorId), params); return new ScanIteration(Long.valueOf(result.getStringCursor()), JedisConverters.stringListToByteList() .convert(result.getResult())); @@ -3352,7 +3352,7 @@ public class JedisConnection extends AbstractRedisConnection { throw new UnsupportedOperationException("'ZSCAN' cannot be called in pipeline / transaction mode."); } - ScanParams params = prepareScanParams(options); + ScanParams params = JedisConverters.toScanParams(options); ScanResult result = jedis.zscan(key, JedisConverters.toBytes(cursorId), params); return new ScanIteration(Long.valueOf(result.getStringCursor()), JedisConverters @@ -3389,7 +3389,7 @@ public class JedisConnection extends AbstractRedisConnection { throw new UnsupportedOperationException("'SSCAN' cannot be called in pipeline / transaction mode."); } - ScanParams params = prepareScanParams(options); + ScanParams params = JedisConverters.toScanParams(options); redis.clients.jedis.ScanResult result = jedis.sscan(key, JedisConverters.toBytes(cursorId), params); return new ScanIteration(Long.valueOf(result.getStringCursor()), result.getResult()); @@ -3424,7 +3424,7 @@ public class JedisConnection extends AbstractRedisConnection { throw new UnsupportedOperationException("'HSCAN' cannot be called in pipeline / transaction mode."); } - ScanParams params = prepareScanParams(options); + ScanParams params = JedisConverters.toScanParams(options); ScanResult> result = jedis.hscan(key, JedisConverters.toBytes(cursorId), params); return new ScanIteration>(Long.valueOf(result.getStringCursor()), result.getResult()); @@ -3432,19 +3432,6 @@ public class JedisConnection extends AbstractRedisConnection { }.open(); } - private ScanParams prepareScanParams(ScanOptions options) { - ScanParams sp = new ScanParams(); - if (!options.equals(ScanOptions.NONE)) { - if (options.getCount() != null) { - sp.count(options.getCount().intValue()); - } - if (StringUtils.hasText(options.getPattern())) { - sp.match(options.getPattern()); - } - } - return sp; - } - /** * Specifies if pipelined results should be converted to the expected data type. If false, results of * {@link #closePipeline()} and {@link #exec()} will be of the type returned by the Jedis driver 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 6913073ae..c241c60a6 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 @@ -42,6 +42,7 @@ import org.springframework.data.redis.connection.convert.ListConverter; import org.springframework.data.redis.connection.convert.MapConverter; import org.springframework.data.redis.connection.convert.SetConverter; import org.springframework.data.redis.connection.convert.StringToRedisClientInfoConverter; +import org.springframework.data.redis.core.ScanOptions; import org.springframework.data.redis.core.types.Expiration; import org.springframework.data.redis.core.types.RedisClientInfo; import org.springframework.util.Assert; @@ -51,6 +52,7 @@ import org.springframework.util.StringUtils; import redis.clients.jedis.BinaryClient.LIST_POSITION; import redis.clients.jedis.BitOP; +import redis.clients.jedis.ScanParams; import redis.clients.jedis.SortingParams; import redis.clients.util.SafeEncoder; @@ -432,4 +434,26 @@ abstract public class JedisConverters extends Converters { return buffer.array(); } + + /** + * Convert {@link ScanOptions} to Jedis {@link ScanParams}. + * + * @param options + * @return + */ + public static ScanParams toScanParams(ScanOptions options) { + + ScanParams sp = new ScanParams(); + + if (!options.equals(ScanOptions.NONE)) { + if (options.getCount() != null) { + sp.count(options.getCount().intValue()); + } + if (StringUtils.hasText(options.getPattern())) { + sp.match(options.getPattern()); + } + } + return sp; + } + } 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 da207dab9..527e3c1a0 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 @@ -20,6 +20,7 @@ import static org.hamcrest.collection.IsIterableContainingInOrder.*; import static org.hamcrest.number.IsCloseTo.*; import static org.junit.Assert.*; import static org.springframework.data.redis.connection.ClusterTestVariables.*; +import static org.springframework.data.redis.core.ScanOptions.scanOptions; import java.io.IOException; import java.util.Arrays; @@ -1769,6 +1770,33 @@ public class JedisClusterConnectionTests implements ClusterConnectionTests { public void zInterStoreShouldThrowExceptionWhenKeysDoNotMapToSameSlots() { clusterConnection.zInterStore(KEY_3_BYTES, KEY_1_BYTES, KEY_2_BYTES); } + + + /** + * @see DATAREDIS-479 + */ + @Test + public void zScanShouldReadEntireValueRange() { + + nativeConnection.zadd(KEY_1_BYTES, 2, VALUE_1_BYTES); + nativeConnection.zadd(KEY_1_BYTES, 1, VALUE_2_BYTES); + nativeConnection.zadd(KEY_1_BYTES, 4, VALUE_3_BYTES); + + Cursor tuples = clusterConnection.zScan(KEY_1_BYTES, ScanOptions.NONE); + + int count = 0; + while (tuples.hasNext()) { + + Tuple tuple = tuples.next(); + + assertThat(tuple.getValue(), anyOf(equalTo(VALUE_1_BYTES), equalTo(VALUE_2_BYTES), equalTo(VALUE_3_BYTES))); + assertThat(tuple.getScore(), anyOf(equalTo(1D), equalTo(2D), equalTo(4D))); + + count++; + } + + assertThat(count, equalTo(3)); + } /** * @see DATAREDIS-315 @@ -1952,6 +1980,32 @@ public class JedisClusterConnectionTests implements ClusterConnectionTests { assertThat(hGetAll.containsKey(KEY_2_BYTES), is(true)); assertThat(hGetAll.containsKey(KEY_3_BYTES), is(true)); } + + /** + * @see DATAREDIS-479 + */ + @Test + public void hScanShouldReadEntireValueRange() { + + clusterConnection.hSet(KEY_1_BYTES, KEY_1_BYTES, VALUE_1_BYTES); + clusterConnection.hSet(KEY_1_BYTES, KEY_2_BYTES, VALUE_2_BYTES); + clusterConnection.hSet(KEY_1_BYTES, KEY_3_BYTES, VALUE_3_BYTES); + + Cursor> cursor = clusterConnection + .hScan(KEY_1_BYTES, scanOptions().match("key*").build()); + + int i = 0; + while (cursor.hasNext()) { + + byte[] key = cursor.next().getKey(); + + assertThat(key, anyOf(equalTo(KEY_1_BYTES), equalTo(KEY_2_BYTES), equalTo(KEY_3_BYTES))); + + i++; + } + + assertThat(i, is(3)); + } /** * @see DATAREDIS-315