diff --git a/Makefile b/Makefile index 1ac2bb9..6a784d8 100644 --- a/Makefile +++ b/Makefile @@ -153,6 +153,42 @@ cluster-enabled yes cluster-config-file /tmp/redis_cluster_node3.conf endef +define REDIS_CLUSTER_NODE4_CONF +daemonize yes +port 7382 +cluster-node-timeout 50 +pidfile /tmp/redis_cluster_node4.pid +logfile /tmp/redis_cluster_node4.log +save "" +appendonly no +cluster-enabled yes +cluster-config-file /tmp/redis_cluster_node4.conf +endef + +define REDIS_CLUSTER_NODE5_CONF +daemonize yes +port 7383 +cluster-node-timeout 5000 +pidfile /tmp/redis_cluster_node5.pid +logfile /tmp/redis_cluster_node5.log +save "" +appendonly no +cluster-enabled yes +cluster-config-file /tmp/redis_cluster_node5.conf +endef + +define REDIS_CLUSTER_NODE6_CONF +daemonize yes +port 7384 +cluster-node-timeout 5000 +pidfile /tmp/redis_cluster_node6.pid +logfile /tmp/redis_cluster_node6.log +save "" +appendonly no +cluster-enabled yes +cluster-config-file /tmp/redis_cluster_node6.conf +endef + export REDIS1_CONF export REDIS2_CONF export REDIS3_CONF @@ -166,6 +202,9 @@ export REDIS_SENTINEL3 export REDIS_CLUSTER_NODE1_CONF export REDIS_CLUSTER_NODE2_CONF export REDIS_CLUSTER_NODE3_CONF +export REDIS_CLUSTER_NODE4_CONF +export REDIS_CLUSTER_NODE5_CONF +export REDIS_CLUSTER_NODE6_CONF start: cleanup echo "$$REDIS1_CONF" | redis-server - @@ -183,6 +222,9 @@ start: cleanup echo "$$REDIS_CLUSTER_NODE1_CONF" | redis-server - echo "$$REDIS_CLUSTER_NODE2_CONF" | redis-server - echo "$$REDIS_CLUSTER_NODE3_CONF" | redis-server - + echo "$$REDIS_CLUSTER_NODE4_CONF" | redis-server - + echo "$$REDIS_CLUSTER_NODE5_CONF" | redis-server - + echo "$$REDIS_CLUSTER_NODE6_CONF" | redis-server - cleanup: - rm -vf /tmp/redis_cluster_node*.conf 2>/dev/null @@ -202,12 +244,18 @@ stop: kill `cat /tmp/redis_cluster_node1.pid` || true kill `cat /tmp/redis_cluster_node2.pid` || true kill `cat /tmp/redis_cluster_node3.pid` || true + kill `cat /tmp/redis_cluster_node4.pid` || true + kill `cat /tmp/redis_cluster_node5.pid` || true + kill `cat /tmp/redis_cluster_node6.pid` || true rm -f /tmp/sentinel1.conf rm -f /tmp/sentinel2.conf rm -f /tmp/sentinel3.conf rm -f /tmp/redis_cluster_node1.conf rm -f /tmp/redis_cluster_node2.conf rm -f /tmp/redis_cluster_node3.conf + rm -f /tmp/redis_cluster_node4.conf + rm -f /tmp/redis_cluster_node5.conf + rm -f /tmp/redis_cluster_node6.conf test: make start @@ -215,6 +263,11 @@ test: mvn -Dtest=${TEST} clean compile test make stop +package: + make start + mvn clean package + make stop + deploy: make start mvn clean deploy diff --git a/README.md b/README.md index 70664a1..bce8aa1 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ Redis cluster [specification](http://redis.io/topics/cluster-spec) (still under Set jedisClusterNodes = new HashSet(); //Jedis Cluster will attempt to discover cluster nodes automatically jedisClusterNodes.add(new HostAndPort("127.0.0.1", 7379)); -JedisCluster jc = new JedisCluster(jedisClusterNode); +JedisCluster jc = new JedisCluster(jedisClusterNodes); jc.set("foo", "bar"); String value = jc.get("foo"); ``` diff --git a/build.gradle b/build.gradle index 30af731..3905e3e 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ apply plugin: 'maven' apply plugin: 'eclipse' group = 'com.googlecode.jedis' -archiveBaseName = 'jedis' +archivesBaseName = 'jedis' version = '1.5.0' repositories { diff --git a/pom.xml b/pom.xml index 1be71dd..f2a2a9d 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ jar redis.clients jedis - 2.5.0-SNAPSHOT + 2.5.3-SNAPSHOT Jedis Jedis is a blazingly small and sane Redis java client. https://github.com/xetorthio/jedis @@ -47,7 +47,7 @@ localhost:6379,localhost:6380,localhost:6381,localhost:6382,localhost:6383,localhost:6384,localhost:6385 localhost:26379,localhost:26380,localhost:26381 - localhost:7379,localhost:7380,localhost:7381 + localhost:7379,localhost:7380,localhost:7381,localhost:7382,localhost:7383,localhost:7384,localhost:7385 github diff --git a/src/main/java/redis/clients/jedis/BasicRedisPipeline.java b/src/main/java/redis/clients/jedis/BasicRedisPipeline.java index bec6e8c..c56a972 100644 --- a/src/main/java/redis/clients/jedis/BasicRedisPipeline.java +++ b/src/main/java/redis/clients/jedis/BasicRedisPipeline.java @@ -1,5 +1,7 @@ package redis.clients.jedis; +import java.util.List; + /** * Pipelined responses for all of the low level, non key related commands */ @@ -24,6 +26,8 @@ public interface BasicRedisPipeline { Response flushAll(); Response info(); + + Response> time(); Response dbSize(); diff --git a/src/main/java/redis/clients/jedis/BinaryClient.java b/src/main/java/redis/clients/jedis/BinaryClient.java index a921b06..2668864 100644 --- a/src/main/java/redis/clients/jedis/BinaryClient.java +++ b/src/main/java/redis/clients/jedis/BinaryClient.java @@ -1,17 +1,8 @@ package redis.clients.jedis; -import static redis.clients.jedis.Protocol.toByteArray; -import static redis.clients.jedis.Protocol.Command.*; -import static redis.clients.jedis.Protocol.Keyword.ENCODING; -import static redis.clients.jedis.Protocol.Keyword.IDLETIME; -import static redis.clients.jedis.Protocol.Keyword.LEN; -import static redis.clients.jedis.Protocol.Keyword.LIMIT; -import static redis.clients.jedis.Protocol.Keyword.NO; -import static redis.clients.jedis.Protocol.Keyword.ONE; -import static redis.clients.jedis.Protocol.Keyword.REFCOUNT; -import static redis.clients.jedis.Protocol.Keyword.RESET; -import static redis.clients.jedis.Protocol.Keyword.STORE; -import static redis.clients.jedis.Protocol.Keyword.WITHSCORES; +import redis.clients.jedis.Protocol.Command; +import redis.clients.jedis.Protocol.Keyword; +import redis.clients.util.SafeEncoder; import java.util.ArrayList; import java.util.List; @@ -19,9 +10,14 @@ import java.util.Map; import java.util.Set; import java.util.Map.Entry; -import redis.clients.jedis.Protocol.Command; -import redis.clients.jedis.Protocol.Keyword; -import redis.clients.util.SafeEncoder; +import static redis.clients.jedis.Protocol.Command.*; +import static redis.clients.jedis.Protocol.Command.EXISTS; +import static redis.clients.jedis.Protocol.Command.PSUBSCRIBE; +import static redis.clients.jedis.Protocol.Command.PUNSUBSCRIBE; +import static redis.clients.jedis.Protocol.Command.SUBSCRIBE; +import static redis.clients.jedis.Protocol.Command.UNSUBSCRIBE; +import static redis.clients.jedis.Protocol.Keyword.*; +import static redis.clients.jedis.Protocol.toByteArray; public class BinaryClient extends Connection { public enum LIST_POSITION { @@ -204,6 +200,10 @@ public class BinaryClient extends Connection { sendCommand(INCRBY, key, toByteArray(integer)); } + public void incrByFloat(final byte[] key, final double value) { + sendCommand(INCRBYFLOAT, key, toByteArray(value)); + } + public void incr(final byte[] key) { sendCommand(INCR, key); } @@ -321,7 +321,7 @@ public class BinaryClient extends Connection { public void sadd(final byte[] key, final byte[]... members) { sendCommand(SADD, joinParameters(key, members)); } - + public void smembers(final byte[] key) { sendCommand(SMEMBERS, key); } @@ -949,7 +949,15 @@ public class BinaryClient extends Connection { public void getbit(byte[] key, long offset) { sendCommand(GETBIT, key, toByteArray(offset)); } - + + public void bitpos(final byte[] key, final boolean value, final BitPosParams params) { + final List args = new ArrayList(); + args.add(key); + args.add(toByteArray(value)); + args.addAll(params.getParams()); + sendCommand(BITPOS, args.toArray(new byte[args.size()][])); + } + public void setrange(byte[] key, long offset, byte[] value) { sendCommand(SETRANGE, key, toByteArray(offset), value); } @@ -975,9 +983,6 @@ public class BinaryClient extends Connection { } public void resetState() { - if (isInMulti()) - discard(); - if (isInWatch()) unwatch(); } @@ -1111,7 +1116,7 @@ public class BinaryClient extends Connection { sendCommand(RESTORE, key, toByteArray(ttl), serializedValue); } - public void pexpire(final byte[] key, final int milliseconds) { + public void pexpire(final byte[] key, final long milliseconds) { sendCommand(PEXPIRE, key, toByteArray(milliseconds)); } @@ -1123,10 +1128,6 @@ public class BinaryClient extends Connection { sendCommand(PTTL, key); } - public void incrByFloat(final byte[] key, final double increment) { - sendCommand(INCRBYFLOAT, key, toByteArray(increment)); - } - public void psetex(final byte[] key, final int milliseconds, final byte[] value) { sendCommand(PSETEX, key, toByteArray(milliseconds), value); @@ -1176,61 +1177,6 @@ public class BinaryClient extends Connection { sendCommand(HINCRBYFLOAT, key, field, toByteArray(increment)); } - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public void scan(int cursor, final ScanParams params) { - final List args = new ArrayList(); - args.add(toByteArray(cursor)); - args.addAll(params.getParams()); - sendCommand(SCAN, args.toArray(new byte[args.size()][])); - } - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public void hscan(final byte[] key, int cursor, final ScanParams params) { - final List args = new ArrayList(); - args.add(key); - args.add(toByteArray(cursor)); - args.addAll(params.getParams()); - sendCommand(HSCAN, args.toArray(new byte[args.size()][])); - } - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public void sscan(final byte[] key, int cursor, final ScanParams params) { - final List args = new ArrayList(); - args.add(key); - args.add(toByteArray(cursor)); - args.addAll(params.getParams()); - sendCommand(SSCAN, args.toArray(new byte[args.size()][])); - } - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public void zscan(final byte[] key, int cursor, final ScanParams params) { - final List args = new ArrayList(); - args.add(key); - args.add(toByteArray(cursor)); - args.addAll(params.getParams()); - sendCommand(ZSCAN, args.toArray(new byte[args.size()][])); - } - public void scan(final byte[] cursor, final ScanParams params) { final List args = new ArrayList(); args.add(cursor); @@ -1273,4 +1219,20 @@ public class BinaryClient extends Connection { public void asking() { sendCommand(Command.ASKING); } + + public void pfadd(final byte[] key, final byte[]... elements) { + sendCommand(PFADD, joinParameters(key, elements)); + } + + public void pfcount(final byte[] key) { + sendCommand(PFCOUNT, key); + } + + public void pfcount(final byte[]...keys) { + sendCommand(PFCOUNT, keys); + } + + public void pfmerge(final byte[] destkey, final byte[]... sourcekeys) { + sendCommand(PFMERGE, joinParameters(destkey, sourcekeys)); + } } diff --git a/src/main/java/redis/clients/jedis/BinaryJedis.java b/src/main/java/redis/clients/jedis/BinaryJedis.java index d061389..09adf46 100644 --- a/src/main/java/redis/clients/jedis/BinaryJedis.java +++ b/src/main/java/redis/clients/jedis/BinaryJedis.java @@ -4,6 +4,7 @@ import static redis.clients.jedis.Protocol.toByteArray; import java.io.Closeable; import java.net.URI; +import java.util.AbstractMap; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; @@ -22,7 +23,9 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands, MultiKeyBinaryCommands, AdvancedBinaryJedisCommands, BinaryScriptingCommands, Closeable { protected Client client = null; - + protected Transaction transaction = null; + protected Pipeline pipeline = null; + public BinaryJedis(final String host) { URI uri = URI.create(host); if (uri.getScheme() != null && uri.getScheme().equals("redis")) { @@ -633,6 +636,37 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands, return client.getIntegerReply(); } + /** + * INCRBYFLOAT work just like {@link #incrBy(byte[]) INCRBY} but increments + * by floats instead of integers. + *

+ * INCRBYFLOAT commands are limited to double precision floating point + * values. + *

+ * Note: this is actually a string operation, that is, in Redis there are + * not "double" types. Simply the string stored at the key is parsed as a + * base double precision floating point value, incremented, and then + * converted back as a string. There is no DECRYBYFLOAT but providing a + * negative value will work as expected. + *

+ * Time complexity: O(1) + * + * @see #incr(byte[]) + * @see #decr(byte[]) + * @see #decrBy(byte[], long) + * + * @param key + * @param integer + * @return Integer reply, this commands will reply with the new value of key + * after the increment. + */ + public Double incrByFloat(final byte[] key, final double integer) { + checkIsInMulti(); + client.incrByFloat(key, integer); + String dval = client.getBulkReply(); + return (dval != null ? new Double(dval) : null); + } + /** * Increment the number stored at key by one. If the key does not exist or * contains a value of a wrong type, set the key to the value of "0" before @@ -825,6 +859,33 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands, return client.getIntegerReply(); } + /** + * Increment the number stored at field in the hash at key by a double + * precision floating point value. If key does not exist, a new key holding + * a hash is created. If field does not exist or holds a string, the value + * is set to 0 before applying the operation. Since the value argument is + * signed you can use this command to perform both increments and + * decrements. + *

+ * The range of values supported by HINCRBYFLOAT is limited to double + * precision floating point values. + *

+ * Time complexity: O(1) + * + * @param key + * @param field + * @param value + * @return Double precision floating point reply The new value at field + * after the increment operation. + */ + public Double hincrByFloat(final byte[] key, final byte[] field, + final double value) { + checkIsInMulti(); + client.hincrByFloat(key, field, value); + final String dval = client.getBulkReply(); + return (dval != null ? new Double(dval) : null); + } + /** * Test for existence of a specified field in a hash. * @@ -1694,7 +1755,9 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands, public Transaction multi() { client.multi(); - return new Transaction(client); + client.getOne(); // expected OK + transaction = new Transaction(client); + return transaction; } @Deprecated @@ -1707,9 +1770,10 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands, public List multi(final TransactionBlock jedisTransaction) { List results = null; jedisTransaction.setClient(client); - client.multi(); - jedisTransaction.execute(); - results = jedisTransaction.exec(); + client.multi(); + client.getOne(); // expected OK + jedisTransaction.execute(); + results = jedisTransaction.exec(); return results; } @@ -1729,8 +1793,24 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands, } public void resetState() { - client.resetState(); - client.getAll(); + if (client.isConnected()) { + if (transaction != null) { + transaction.clear(); + } + + if (pipeline != null) { + pipeline.clear(); + } + + if (client.isInWatch()) { + unwatch(); + } + + client.resetState(); + } + + transaction = null; + pipeline = null; } public String watch(final byte[]... keys) { @@ -1744,7 +1824,7 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands, } @Override - public void close() { + public void close() { client.close(); } @@ -2146,7 +2226,7 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands, } public Pipeline pipelined() { - Pipeline pipeline = new Pipeline(); + pipeline = new Pipeline(); pipeline.setClient(client); return pipeline; } @@ -3155,6 +3235,16 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands, return client.getIntegerReply() == 1; } + public Long bitpos(final byte[] key, final boolean value) { + return bitpos(key, value, new BitPosParams()); + } + + public Long bitpos(final byte[] key, final boolean value, + final BitPosParams params) { + client.bitpos(key, value, params); + return client.getIntegerReply(); + } + public Long setrange(byte[] key, long offset, byte[] value) { client.setrange(key, offset, value); return client.getIntegerReply(); @@ -3343,7 +3433,7 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands, return client.getStatusCodeReply(); } - public Long pexpire(final byte[] key, final int milliseconds) { + public Long pexpire(final byte[] key, final long milliseconds) { checkIsInMulti(); client.pexpire(key, milliseconds); return client.getIntegerReply(); @@ -3361,13 +3451,6 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands, return client.getIntegerReply(); } - public Double incrByFloat(final byte[] key, final double increment) { - checkIsInMulti(); - client.incrByFloat(key, increment); - String relpy = client.getBulkReply(); - return (relpy != null ? new Double(relpy) : null); - } - public String psetex(final byte[] key, final int milliseconds, final byte[] value) { checkIsInMulti(); @@ -3425,14 +3508,6 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands, return client.getStatusCodeReply(); } - public Double hincrByFloat(final byte[] key, final byte[] field, - double increment) { - checkIsInMulti(); - client.hincrByFloat(key, field, increment); - String relpy = client.getBulkReply(); - return (relpy != null ? new Double(relpy) : null); - } - /** * Syncrhonous replication of Redis as described here: * http://antirez.com/news/66 @@ -3446,4 +3521,99 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands, return client.getIntegerReply(); } + @Override + public Long pfadd(final byte[] key, final byte[]... elements) { + checkIsInMulti(); + client.pfadd(key, elements); + return client.getIntegerReply(); + } + + @Override + public long pfcount(final byte[] key) { + checkIsInMulti(); + client.pfcount(key); + return client.getIntegerReply(); + } + + @Override + public String pfmerge(final byte[] destkey, final byte[]... sourcekeys) { + checkIsInMulti(); + client.pfmerge(destkey, sourcekeys); + return client.getStatusCodeReply(); + } + + @Override + public Long pfcount(byte[]... keys) { + checkIsInMulti(); + client.pfcount(keys); + return client.getIntegerReply(); + } + + public ScanResult scan(final byte[] cursor) { + return scan(cursor, new ScanParams()); + } + + public ScanResult scan(final byte[] cursor, final ScanParams params) { + checkIsInMulti(); + client.scan(cursor, params); + List result = client.getObjectMultiBulkReply(); + byte[] newcursor = (byte[]) result.get(0); + List rawResults = (List) result.get(1); + return new ScanResult(newcursor, rawResults); + } + + public ScanResult> hscan(final byte[] key, + final byte[] cursor) { + return hscan(key, cursor, new ScanParams()); + } + + public ScanResult> hscan(final byte[] key, + final byte[] cursor, final ScanParams params) { + checkIsInMulti(); + client.hscan(key, cursor, params); + List result = client.getObjectMultiBulkReply(); + byte[] newcursor = (byte[]) result.get(0); + List> results = new ArrayList>(); + List rawResults = (List) result.get(1); + Iterator iterator = rawResults.iterator(); + while (iterator.hasNext()) { + results.add(new AbstractMap.SimpleEntry(iterator + .next(), iterator.next())); + } + return new ScanResult>(newcursor, results); + } + + public ScanResult sscan(final byte[] key, final byte[] cursor) { + return sscan(key, cursor, new ScanParams()); + } + + public ScanResult sscan(final byte[] key, final byte[] cursor, + final ScanParams params) { + checkIsInMulti(); + client.sscan(key, cursor, params); + List result = client.getObjectMultiBulkReply(); + byte[] newcursor = (byte[]) result.get(0); + List rawResults = (List) result.get(1); + return new ScanResult(newcursor, rawResults); + } + + public ScanResult zscan(final byte[] key, final byte[] cursor) { + return zscan(key, cursor, new ScanParams()); + } + + public ScanResult zscan(final byte[] key, final byte[] cursor, + final ScanParams params) { + checkIsInMulti(); + client.zscan(key, cursor, params); + List result = client.getObjectMultiBulkReply(); + byte[] newcursor = (byte[]) result.get(0); + List results = new ArrayList(); + List rawResults = (List) result.get(1); + Iterator iterator = rawResults.iterator(); + while (iterator.hasNext()) { + results.add(new Tuple(iterator.next(), Double.valueOf(SafeEncoder + .encode(iterator.next())))); + } + return new ScanResult(newcursor, results); + } } diff --git a/src/main/java/redis/clients/jedis/BinaryJedisCommands.java b/src/main/java/redis/clients/jedis/BinaryJedisCommands.java index 3920c67..164c71c 100644 --- a/src/main/java/redis/clients/jedis/BinaryJedisCommands.java +++ b/src/main/java/redis/clients/jedis/BinaryJedisCommands.java @@ -47,6 +47,8 @@ public interface BinaryJedisCommands { Long incrBy(byte[] key, long integer); + Double incrByFloat(byte[] key, double value); + Long incr(byte[] key); Long append(byte[] key, byte[] value); @@ -65,6 +67,8 @@ public interface BinaryJedisCommands { Long hincrBy(byte[] key, byte[] field, long value); + Double hincrByFloat(byte[] key, byte[] field, double value); + Boolean hexists(byte[] key, byte[] field); Long hdel(byte[] key, byte[]... field); @@ -111,6 +115,8 @@ public interface BinaryJedisCommands { byte[] srandmember(byte[] key); + List srandmember(final byte[] key, final int count); + Long strlen(byte[] key); Long zadd(byte[] key, double score, byte[] member); @@ -220,4 +226,8 @@ public interface BinaryJedisCommands { Long bitcount(final byte[] key); Long bitcount(final byte[] key, long start, long end); + + Long pfadd(final byte[] key, final byte[]... elements); + + long pfcount(final byte[] key); } diff --git a/src/main/java/redis/clients/jedis/BinaryRedisPipeline.java b/src/main/java/redis/clients/jedis/BinaryRedisPipeline.java index 799557d..98ebc73 100644 --- a/src/main/java/redis/clients/jedis/BinaryRedisPipeline.java +++ b/src/main/java/redis/clients/jedis/BinaryRedisPipeline.java @@ -34,7 +34,7 @@ public interface BinaryRedisPipeline { Response getSet(byte[] key, byte[] value); - Response getrange(byte[] key, long startOffset, long endOffset); + Response getrange(byte[] key, long startOffset, long endOffset); Response hdel(byte[] key, byte[]... field); @@ -219,4 +219,8 @@ public interface BinaryRedisPipeline { Response bitcount(byte[] key); Response bitcount(byte[] key, long start, long end); + + Response pfadd(final byte[] key, final byte[]... elements); + + Response pfcount(final byte[] key); } diff --git a/src/main/java/redis/clients/jedis/BinaryShardedJedis.java b/src/main/java/redis/clients/jedis/BinaryShardedJedis.java index b879d55..c34a574 100644 --- a/src/main/java/redis/clients/jedis/BinaryShardedJedis.java +++ b/src/main/java/redis/clients/jedis/BinaryShardedJedis.java @@ -110,6 +110,11 @@ public class BinaryShardedJedis extends Sharded return j.incrBy(key, integer); } + public Double incrByFloat(byte[] key, double integer) { + Jedis j = getShard(key); + return j.incrByFloat(key, integer); + } + public Long incr(byte[] key) { Jedis j = getShard(key); return j.incr(key); @@ -155,6 +160,11 @@ public class BinaryShardedJedis extends Sharded return j.hincrBy(key, field, value); } + public Double hincrByFloat(byte[] key, byte[] field, double value) { + Jedis j = getShard(key); + return j.hincrByFloat(key, field, value); + } + public Boolean hexists(byte[] key, byte[] field) { Jedis j = getShard(key); return j.hexists(key, field); @@ -290,6 +300,12 @@ public class BinaryShardedJedis extends Sharded return j.srandmember(key); } + @Override + public List srandmember(byte[] key, int count) { + Jedis j = getShard(key); + return j.srandmember(key, count); + } + public Long zadd(byte[] key, double score, byte[] member) { Jedis j = getShard(key); return j.zadd(key, score, member); @@ -594,5 +610,17 @@ public class BinaryShardedJedis extends Sharded Jedis j = getShard(key); return j.bitcount(key, start, end); } + + @Override + public Long pfadd(final byte[] key, final byte[]... elements) { + Jedis j = getShard(key); + return j.pfadd(key, elements); + } + + @Override + public long pfcount(final byte[] key) { + Jedis j = getShard(key); + return j.pfcount(key); + } } \ No newline at end of file diff --git a/src/main/java/redis/clients/jedis/BitPosParams.java b/src/main/java/redis/clients/jedis/BitPosParams.java new file mode 100644 index 0000000..e480686 --- /dev/null +++ b/src/main/java/redis/clients/jedis/BitPosParams.java @@ -0,0 +1,27 @@ +package redis.clients.jedis; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public class BitPosParams { + private List params = new ArrayList(); + + protected BitPosParams() { + } + + public BitPosParams(long start) { + params.add(Protocol.toByteArray(start)); + } + + public BitPosParams(long start, long end) { + this(start); + + params.add(Protocol.toByteArray(end)); + } + + public Collection getParams() { + return Collections.unmodifiableCollection(params); + } +} diff --git a/src/main/java/redis/clients/jedis/BuilderFactory.java b/src/main/java/redis/clients/jedis/BuilderFactory.java index 76d013e..8c119b6 100755 --- a/src/main/java/redis/clients/jedis/BuilderFactory.java +++ b/src/main/java/redis/clients/jedis/BuilderFactory.java @@ -9,6 +9,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import redis.clients.util.JedisByteHashMap; import redis.clients.util.SafeEncoder; public class BuilderFactory { @@ -104,6 +105,26 @@ public class BuilderFactory { }; + public static final Builder> PUBSUB_NUMSUB_MAP = new Builder>() { + @SuppressWarnings("unchecked") + public Map build(Object data) { + final List flatHash = (List) data; + final Map hash = new HashMap(); + final Iterator iterator = flatHash.iterator(); + while (iterator.hasNext()) { + hash.put(SafeEncoder.encode((byte[]) iterator.next()), + String.valueOf((Long) iterator.next())); + } + + return hash; + } + + public String toString() { + return "PUBSUB_NUMSUB_MAP"; + } + + }; + public static final Builder> STRING_SET = new Builder>() { @SuppressWarnings("unchecked") public Set build(Object data) { @@ -170,7 +191,7 @@ public class BuilderFactory { @SuppressWarnings("unchecked") public Map build(Object data) { final List flatHash = (List) data; - final Map hash = new HashMap(); + final Map hash = new JedisByteHashMap(); final Iterator iterator = flatHash.iterator(); while (iterator.hasNext()) { hash.put(iterator.next(), iterator.next()); diff --git a/src/main/java/redis/clients/jedis/Client.java b/src/main/java/redis/clients/jedis/Client.java index f0d9f82..9802696 100644 --- a/src/main/java/redis/clients/jedis/Client.java +++ b/src/main/java/redis/clients/jedis/Client.java @@ -647,6 +647,9 @@ public class Client extends BinaryClient implements Commands { getbit(SafeEncoder.encode(key), offset); } + public void bitpos(final String key, final boolean value, final BitPosParams params) { + bitpos(SafeEncoder.encode(key), value, params); + } public void setrange(String key, long offset, String value) { setrange(SafeEncoder.encode(key), offset, SafeEncoder.encode(value)); } @@ -795,7 +798,7 @@ public class Client extends BinaryClient implements Commands { restore(SafeEncoder.encode(key), ttl, serializedValue); } - public void pexpire(final String key, final int milliseconds) { + public void pexpire(final String key, final long milliseconds) { pexpire(SafeEncoder.encode(key), milliseconds); } @@ -851,36 +854,6 @@ public class Client extends BinaryClient implements Commands { increment); } - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public void hscan(final String key, int cursor, final ScanParams params) { - hscan(SafeEncoder.encode(key), cursor, params); - } - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public void sscan(final String key, int cursor, final ScanParams params) { - sscan(SafeEncoder.encode(key), cursor, params); - } - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public void zscan(final String key, int cursor, final ScanParams params) { - zscan(SafeEncoder.encode(key), cursor, params); - } - public void scan(final String cursor, final ScanParams params) { scan(SafeEncoder.encode(cursor), params); } @@ -969,4 +942,60 @@ public class Client extends BinaryClient implements Commands { cluster(Protocol.CLUSTER_SETSLOT, String.valueOf(slot), Protocol.CLUSTER_SETSLOT_IMPORTING, nodeId); } + + public void pfadd(String key, final String... elements) { + pfadd(SafeEncoder.encode(key), SafeEncoder.encodeMany(elements)); + } + + public void pfcount(final String key) { + pfcount(SafeEncoder.encode(key)); + } + + public void pfcount(final String...keys) { + pfcount(SafeEncoder.encodeMany(keys)); + } + + public void pfmerge(final String destkey, final String... sourcekeys) { + pfmerge(SafeEncoder.encode(destkey), SafeEncoder.encodeMany(sourcekeys)); + } +public void clusterSetSlotStable(final int slot) { + cluster(Protocol.CLUSTER_SETSLOT, String.valueOf(slot), + Protocol.CLUSTER_SETSLOT_STABLE); + } + + public void clusterForget(final String nodeId) { + cluster(Protocol.CLUSTER_FORGET, nodeId); + } + + public void clusterFlushSlots() { + cluster(Protocol.CLUSTER_FLUSHSLOT); + } + + public void clusterKeySlot(final String key) { + cluster(Protocol.CLUSTER_KEYSLOT, key); + } + + public void clusterCountKeysInSlot(final int slot) { + cluster(Protocol.CLUSTER_COUNTKEYINSLOT, String.valueOf(slot)); + } + + public void clusterSaveConfig() { + cluster(Protocol.CLUSTER_SAVECONFIG); + } + + public void clusterReplicate(final String nodeId) { + cluster(Protocol.CLUSTER_REPLICATE, nodeId); + } + + public void clusterSlaves(final String nodeId) { + cluster(Protocol.CLUSTER_SLAVES, nodeId); + } + + public void clusterFailover() { + cluster(Protocol.CLUSTER_FAILOVER); + } + + public void clusterSlots() { + cluster(Protocol.CLUSTER_SLOTS); + } } diff --git a/src/main/java/redis/clients/jedis/ClusterCommands.java b/src/main/java/redis/clients/jedis/ClusterCommands.java index fff4533..1dac7fe 100644 --- a/src/main/java/redis/clients/jedis/ClusterCommands.java +++ b/src/main/java/redis/clients/jedis/ClusterCommands.java @@ -20,4 +20,24 @@ public interface ClusterCommands { String clusterSetSlotMigrating(final int slot, final String nodeId); String clusterSetSlotImporting(final int slot, final String nodeId); + + String clusterSetSlotStable(final int slot); + + String clusterForget(final String nodeId); + + String clusterFlushSlots(); + + Long clusterKeySlot(final String key); + + Long clusterCountKeysInSlot(final int slot); + + String clusterSaveConfig(); + + String clusterReplicate(final String nodeId); + + List clusterSlaves(final String nodeId); + + String clusterFailover(); + + List clusterSlots(); } diff --git a/src/main/java/redis/clients/jedis/Commands.java b/src/main/java/redis/clients/jedis/Commands.java index a104ca9..9261d6d 100644 --- a/src/main/java/redis/clients/jedis/Commands.java +++ b/src/main/java/redis/clients/jedis/Commands.java @@ -61,6 +61,8 @@ public interface Commands { public void incrBy(final String key, final long integer); + public void incrByFloat(final String key, final double value); + public void incr(final String key); public void append(final String key, final String value); @@ -79,6 +81,8 @@ public interface Commands { public void hincrBy(final String key, final String field, final long value); + public void hincrByFloat(final String key, final String field, final double value); + public void hexists(final String key, final String field); public void hdel(final String key, final String... fields); @@ -297,38 +301,6 @@ public interface Commands { public void bitop(BitOP op, final String destKey, String... srcKeys); - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public void scan(int cursor, final ScanParams params); - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public void hscan(final String key, int cursor, final ScanParams params); - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public void sscan(final String key, int cursor, final ScanParams params); - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public void zscan(final String key, int cursor, final ScanParams params); - public void scan(final String cursor, final ScanParams params); public void hscan(final String key, final String cursor, final ScanParams params); diff --git a/src/main/java/redis/clients/jedis/Connection.java b/src/main/java/redis/clients/jedis/Connection.java index e913d68..c57b170 100644 --- a/src/main/java/redis/clients/jedis/Connection.java +++ b/src/main/java/redis/clients/jedis/Connection.java @@ -11,7 +11,6 @@ import java.util.List; import redis.clients.jedis.Protocol.Command; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisDataException; -import redis.clients.jedis.exceptions.JedisException; import redis.clients.util.RedisInputStream; import redis.clients.util.RedisOutputStream; import redis.clients.util.SafeEncoder; @@ -22,9 +21,10 @@ public class Connection implements Closeable { private Socket socket; private RedisOutputStream outputStream; private RedisInputStream inputStream; - private int pipelinedCommands = 0; private int timeout = Protocol.DEFAULT_TIMEOUT; + private boolean broken = false; + public Socket getSocket() { return socket; } @@ -45,7 +45,8 @@ public class Connection implements Closeable { socket.setKeepAlive(true); socket.setSoTimeout(0); } catch (SocketException ex) { - throw new JedisException(ex); + broken = true; + throw new JedisConnectionException(ex); } } @@ -54,7 +55,8 @@ public class Connection implements Closeable { socket.setSoTimeout(timeout); socket.setKeepAlive(false); } catch (SocketException ex) { - throw new JedisException(ex); + broken = true; + throw new JedisConnectionException(ex); } } @@ -63,14 +65,6 @@ public class Connection implements Closeable { this.host = host; } - protected void flush() { - try { - outputStream.flush(); - } catch (IOException e) { - throw new JedisConnectionException(e); - } - } - protected Connection sendCommand(final Command cmd, final String... args) { final byte[][] bargs = new byte[args.length][]; for (int i = 0; i < args.length; i++) { @@ -80,17 +74,27 @@ public class Connection implements Closeable { } protected Connection sendCommand(final Command cmd, final byte[]... args) { - connect(); - Protocol.sendCommand(outputStream, cmd, args); - pipelinedCommands++; - return this; + try { + connect(); + Protocol.sendCommand(outputStream, cmd, args); + return this; + } catch (JedisConnectionException ex) { + // Any other exceptions related to connection? + broken = true; + throw ex; + } } protected Connection sendCommand(final Command cmd) { - connect(); - Protocol.sendCommand(outputStream, cmd, new byte[0][]); - pipelinedCommands++; - return this; + try { + connect(); + Protocol.sendCommand(outputStream, cmd, new byte[0][]); + return this; + } catch (JedisConnectionException ex) { + // Any other exceptions related to connection? + broken = true; + throw ex; + } } public Connection(final String host, final int port) { @@ -139,6 +143,7 @@ public class Connection implements Closeable { outputStream = new RedisOutputStream(socket.getOutputStream()); inputStream = new RedisInputStream(socket.getInputStream()); } catch (IOException ex) { + broken = true; throw new JedisConnectionException(ex); } } @@ -147,7 +152,7 @@ public class Connection implements Closeable { @Override public void close() { disconnect(); - } + } public void disconnect() { if (isConnected()) { @@ -158,6 +163,7 @@ public class Connection implements Closeable { socket.close(); } } catch (IOException ex) { + broken = true; throw new JedisConnectionException(ex); } } @@ -171,8 +177,7 @@ public class Connection implements Closeable { protected String getStatusCodeReply() { flush(); - pipelinedCommands--; - final byte[] resp = (byte[]) Protocol.read(inputStream); + final byte[] resp = (byte[]) readProtocolWithCheckingBroken(); if (null == resp) { return null; } else { @@ -191,14 +196,12 @@ public class Connection implements Closeable { public byte[] getBinaryBulkReply() { flush(); - pipelinedCommands--; - return (byte[]) Protocol.read(inputStream); + return (byte[]) readProtocolWithCheckingBroken(); } public Long getIntegerReply() { flush(); - pipelinedCommands--; - return (Long) Protocol.read(inputStream); + return (Long) readProtocolWithCheckingBroken(); } public List getMultiBulkReply() { @@ -208,53 +211,62 @@ public class Connection implements Closeable { @SuppressWarnings("unchecked") public List getBinaryMultiBulkReply() { flush(); - pipelinedCommands--; - return (List) Protocol.read(inputStream); - } - - public void resetPipelinedCount() { - pipelinedCommands = 0; + return (List) readProtocolWithCheckingBroken(); } @SuppressWarnings("unchecked") public List getRawObjectMultiBulkReply() { - return (List) Protocol.read(inputStream); + return (List) readProtocolWithCheckingBroken(); } public List getObjectMultiBulkReply() { - flush(); - pipelinedCommands--; - return getRawObjectMultiBulkReply(); + flush(); + return getRawObjectMultiBulkReply(); } @SuppressWarnings("unchecked") public List getIntegerMultiBulkReply() { - flush(); - pipelinedCommands--; - return (List) Protocol.read(inputStream); - } - - public List getAll() { - return getAll(0); - } - - public List getAll(int except) { - List all = new ArrayList(); - flush(); - while (pipelinedCommands > except) { - try { - all.add(Protocol.read(inputStream)); - } catch (JedisDataException e) { - all.add(e); - } - pipelinedCommands--; - } - return all; + flush(); + return (List) Protocol.read(inputStream); } public Object getOne() { flush(); - pipelinedCommands--; - return Protocol.read(inputStream); + return readProtocolWithCheckingBroken(); + } + + public boolean isBroken() { + return broken; + } + + protected void flush() { + try { + outputStream.flush(); + } catch (IOException ex) { + broken = true; + throw new JedisConnectionException(ex); + } + } + + protected Object readProtocolWithCheckingBroken() { + try { + return Protocol.read(inputStream); + } catch (JedisConnectionException exc) { + broken = true; + throw exc; + } + } + + public List getMany(int count) { + flush(); + List responses = new ArrayList(); + for (int i = 0; i < count; i++) { + try { + responses.add(readProtocolWithCheckingBroken()); + } catch (JedisDataException e) { + responses.add(e); + } + } + return responses; } } diff --git a/src/main/java/redis/clients/jedis/Jedis.java b/src/main/java/redis/clients/jedis/Jedis.java index ff5bd89..d55f880 100644 --- a/src/main/java/redis/clients/jedis/Jedis.java +++ b/src/main/java/redis/clients/jedis/Jedis.java @@ -1,23 +1,20 @@ package redis.clients.jedis; -import java.net.URI; -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - import redis.clients.jedis.BinaryClient.LIST_POSITION; +import redis.clients.util.Pool; import redis.clients.util.SafeEncoder; import redis.clients.util.Slowlog; +import java.net.URI; +import java.util.*; +import java.util.Map.Entry; + public class Jedis extends BinaryJedis implements JedisCommands, MultiKeyCommands, AdvancedJedisCommands, ScriptingCommands, BasicCommands, ClusterCommands { + + protected Pool dataSource = null; + public Jedis(final String host) { super(host); } @@ -547,6 +544,31 @@ public class Jedis extends BinaryJedis implements JedisCommands, return client.getIntegerReply(); } + /** + * INCRBYFLOAT + *

+ * INCRBYFLOAT commands are limited to double precision floating point values. + *

+ * Note: this is actually a string operation, that is, in Redis there are + * not "double" types. Simply the string stored at the key is parsed as a + * base double precision floating point value, incremented, and then + * converted back as a string. There is no DECRYBYFLOAT but providing a + * negative value will work as expected. + *

+ * Time complexity: O(1) + * + * @param key + * @param value + * @return Double reply, this commands will reply with the new value of key + * after the increment. + */ + public Double incrByFloat(final String key, final double value) { + checkIsInMulti(); + client.incrByFloat(key, value); + String dval = client.getBulkReply(); + return (dval != null ? new Double(dval) : null); + } + /** * Increment the number stored at key by one. If the key does not exist or * contains a value of a wrong type, set the key to the value of "0" before @@ -739,6 +761,32 @@ public class Jedis extends BinaryJedis implements JedisCommands, return client.getIntegerReply(); } + /** + * Increment the number stored at field in the hash at key by a double + * precision floating point value. If key does not exist, + * a new key holding a hash is created. If field does not + * exist or holds a string, the value is set to 0 before applying the + * operation. Since the value argument is signed you can use this command to + * perform both increments and decrements. + *

+ * The range of values supported by HINCRBYFLOAT is limited to + * double precision floating point values. + *

+ * Time complexity: O(1) + * + * @param key + * @param field + * @param value + * @return Double precision floating point reply The new value at field after the increment + * operation. + */ + public Double hincrByFloat(final String key, final String field, final double value) { + checkIsInMulti(); + client.hincrByFloat(key, field, value); + final String dval = client.getBulkReply(); + return (dval != null ? new Double(dval) : null); + } + /** * Test for existence of a specified field in a hash. * @@ -1068,8 +1116,8 @@ public class Jedis extends BinaryJedis implements JedisCommands, /** * Atomically return and remove the first (LPOP) or last (RPOP) element of - * the list. For example if the list contains the elements "a","b","c" LPOP - * will return "a" and the list will become "b","c". + * the list. For example if the list contains the elements "a","b","c" RPOP + * will return "c" and the list will become "a","b". *

* If the key does not exist or the list is already empty the special value * 'nil' is returned. @@ -2688,6 +2736,15 @@ public class Jedis extends BinaryJedis implements JedisCommands, client.getrange(key, startOffset, endOffset); return client.getBulkReply(); } + + public Long bitpos(final String key, final boolean value) { + return bitpos(key, value, new BitPosParams()); + } + + public Long bitpos(final String key, final boolean value, final BitPosParams params) { + client.bitpos(key, value, params); + return client.getIntegerReply(); + } /** * Retrieve the configuration of a running Redis server. Not all the @@ -3089,7 +3146,7 @@ public class Jedis extends BinaryJedis implements JedisCommands, return client.getStatusCodeReply(); } - public Long pexpire(final String key, final int milliseconds) { + public Long pexpire(final String key, final long milliseconds) { checkIsInMulti(); client.pexpire(key, milliseconds); return client.getIntegerReply(); @@ -3107,12 +3164,6 @@ public class Jedis extends BinaryJedis implements JedisCommands, return client.getIntegerReply(); } - public Double incrByFloat(final String key, final double increment) { - checkIsInMulti(); - client.incrByFloat(key, increment); - String relpy = client.getBulkReply(); - return (relpy != null ? new Double(relpy) : null); - } public String psetex(final String key, final int milliseconds, final String value) { @@ -3153,143 +3204,10 @@ public class Jedis extends BinaryJedis implements JedisCommands, return client.getStatusCodeReply(); } - public Double hincrByFloat(final String key, final String field, - double increment) { - checkIsInMulti(); - client.hincrByFloat(key, field, increment); - String relpy = client.getBulkReply(); - return (relpy != null ? new Double(relpy) : null); - } - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public ScanResult scan(int cursor) { - return scan(cursor, new ScanParams()); - } - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public ScanResult scan(int cursor, final ScanParams params) { - checkIsInMulti(); - client.scan(cursor, params); - List result = client.getObjectMultiBulkReply(); - int newcursor = Integer.parseInt(new String((byte[]) result.get(0))); - List results = new ArrayList(); - List rawResults = (List) result.get(1); - for (byte[] bs : rawResults) { - results.add(SafeEncoder.encode(bs)); - } - return new ScanResult(newcursor, results); - } - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public ScanResult> hscan(final String key, - int cursor) { - return hscan(key, cursor, new ScanParams()); - } - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public ScanResult> hscan(final String key, - int cursor, final ScanParams params) { - checkIsInMulti(); - client.hscan(key, cursor, params); - List result = client.getObjectMultiBulkReply(); - int newcursor = Integer.parseInt(new String((byte[]) result.get(0))); - List> results = new ArrayList>(); - List rawResults = (List) result.get(1); - Iterator iterator = rawResults.iterator(); - while (iterator.hasNext()) { - results.add(new AbstractMap.SimpleEntry(SafeEncoder - .encode(iterator.next()), SafeEncoder.encode(iterator - .next()))); - } - return new ScanResult>(newcursor, results); - } - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public ScanResult sscan(final String key, int cursor) { - return sscan(key, cursor, new ScanParams()); - } - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public ScanResult sscan(final String key, int cursor, - final ScanParams params) { - checkIsInMulti(); - client.sscan(key, cursor, params); - List result = client.getObjectMultiBulkReply(); - int newcursor = Integer.parseInt(new String((byte[]) result.get(0))); - List results = new ArrayList(); - List rawResults = (List) result.get(1); - for (byte[] bs : rawResults) { - results.add(SafeEncoder.encode(bs)); - } - return new ScanResult(newcursor, results); - } - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public ScanResult zscan(final String key, int cursor) { - return zscan(key, cursor, new ScanParams()); - } - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public ScanResult zscan(final String key, int cursor, - final ScanParams params) { - checkIsInMulti(); - client.zscan(key, cursor, params); - List result = client.getObjectMultiBulkReply(); - int newcursor = Integer.parseInt(new String((byte[]) result.get(0))); - List results = new ArrayList(); - List rawResults = (List) result.get(1); - Iterator iterator = rawResults.iterator(); - while (iterator.hasNext()) { - results.add(new Tuple(SafeEncoder.encode(iterator.next()), Double - .valueOf(SafeEncoder.encode(iterator.next())))); - } - return new ScanResult(newcursor, results); - } - public ScanResult scan(final String cursor) { return scan(cursor, new ScanParams()); } - + public ScanResult scan(final String cursor, final ScanParams params) { checkIsInMulti(); client.scan(cursor, params); @@ -3302,12 +3220,12 @@ public class Jedis extends BinaryJedis implements JedisCommands, } return new ScanResult(newcursor, results); } - + public ScanResult> hscan(final String key, final String cursor) { return hscan(key, cursor, new ScanParams()); } - + public ScanResult> hscan(final String key, final String cursor, final ScanParams params) { checkIsInMulti(); @@ -3320,15 +3238,15 @@ public class Jedis extends BinaryJedis implements JedisCommands, while (iterator.hasNext()) { results.add(new AbstractMap.SimpleEntry(SafeEncoder .encode(iterator.next()), SafeEncoder.encode(iterator - .next()))); + .next()))); } return new ScanResult>(newcursor, results); } - + public ScanResult sscan(final String key, final String cursor) { return sscan(key, cursor, new ScanParams()); } - + public ScanResult sscan(final String key, final String cursor, final ScanParams params) { checkIsInMulti(); @@ -3342,11 +3260,11 @@ public class Jedis extends BinaryJedis implements JedisCommands, } return new ScanResult(newcursor, results); } - + public ScanResult zscan(final String key, final String cursor) { return zscan(key, cursor, new ScanParams()); } - + public ScanResult zscan(final String key, final String cursor, final ScanParams params) { checkIsInMulti(); @@ -3416,6 +3334,67 @@ public class Jedis extends BinaryJedis implements JedisCommands, client.clusterSetSlotImporting(slot, nodeId); return client.getStatusCodeReply(); } + + public String clusterSetSlotStable(final int slot) { + checkIsInMulti(); + client.clusterSetSlotStable(slot); + return client.getStatusCodeReply(); + } + + public String clusterForget(final String nodeId) { + checkIsInMulti(); + client.clusterForget(nodeId); + return client.getStatusCodeReply(); + } + + public String clusterFlushSlots() { + checkIsInMulti(); + client.clusterFlushSlots(); + return client.getStatusCodeReply(); + } + + public Long clusterKeySlot(final String key) { + checkIsInMulti(); + client.clusterKeySlot(key); + return client.getIntegerReply(); + } + + public Long clusterCountKeysInSlot(final int slot) { + checkIsInMulti(); + client.clusterCountKeysInSlot(slot); + return client.getIntegerReply(); + } + + public String clusterSaveConfig() { + checkIsInMulti(); + client.clusterSaveConfig(); + return client.getStatusCodeReply(); + } + + public String clusterReplicate(final String nodeId) { + checkIsInMulti(); + client.clusterReplicate(nodeId); + return client.getStatusCodeReply(); + } + + public List clusterSlaves(final String nodeId) { + checkIsInMulti(); + client.clusterSlaves(nodeId); + return client.getMultiBulkReply(); + } + + public String clusterFailover() { + checkIsInMulti(); + client.clusterFailover(); + return client.getStatusCodeReply(); + } + + @Override + public List clusterSlots() { + checkIsInMulti(); + client.clusterSlots(); + return client.getObjectMultiBulkReply(); + } public String asking() { checkIsInMulti(); @@ -3438,8 +3417,50 @@ public class Jedis extends BinaryJedis implements JedisCommands, public Map pubsubNumSub(String... channels) { checkIsInMulti(); client.pubsubNumSub(channels); - return BuilderFactory.STRING_MAP + return BuilderFactory.PUBSUB_NUMSUB_MAP .build(client.getBinaryMultiBulkReply()); } + @Override + public void close() { + if (dataSource != null) { + if (client.isBroken()) { + this.dataSource.returnBrokenResource(this); + } else { + this.dataSource.returnResource(this); + } + } else { + client.close(); + } + } + + public void setDataSource(Pool jedisPool) { + this.dataSource = jedisPool; + } + + public Long pfadd(final String key, final String... elements) { + checkIsInMulti(); + client.pfadd(key, elements); + return client.getIntegerReply(); + } + + public long pfcount(final String key) { + checkIsInMulti(); + client.pfcount(key); + return client.getIntegerReply(); + } + + @Override + public long pfcount(String... keys) { + checkIsInMulti(); + client.pfcount(keys); + return client.getIntegerReply(); + } + + public String pfmerge(final String destkey, final String... sourcekeys) { + checkIsInMulti(); + client.pfmerge(destkey, sourcekeys); + return client.getStatusCodeReply(); + } + } diff --git a/src/main/java/redis/clients/jedis/JedisCluster.java b/src/main/java/redis/clients/jedis/JedisCluster.java index 8f8b693..f939d54 100644 --- a/src/main/java/redis/clients/jedis/JedisCluster.java +++ b/src/main/java/redis/clients/jedis/JedisCluster.java @@ -1,13 +1,14 @@ package redis.clients.jedis; +import redis.clients.jedis.BinaryClient.LIST_POSITION; + +import java.io.Closeable; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import redis.clients.jedis.BinaryClient.LIST_POSITION; - -public class JedisCluster implements JedisCommands, BasicCommands { +public class JedisCluster implements JedisCommands, BasicCommands, Closeable { public static final short HASHSLOTS = 16384; private static final int DEFAULT_TIMEOUT = 1; private static final int DEFAULT_MAX_REDIRECTIONS = 5; @@ -32,6 +33,21 @@ public class JedisCluster implements JedisCommands, BasicCommands { this.timeout = timeout; this.maxRedirections = maxRedirections; } + + @Override + public void close() { + if (connectionHandler != null) { + for (JedisPool pool : connectionHandler.getNodes().values()) { + try { + if (pool != null) { + pool.destroy(); + } + } catch (Exception e) { + // pass + } + } + } + } @Override public String set(final String key, final String value) { @@ -44,6 +60,18 @@ public class JedisCluster implements JedisCommands, BasicCommands { }.run(key); } + @Override + public String set(final String key, final String value, final String nxxx, + final String expx, final long time) { + return new JedisClusterCommand(connectionHandler, timeout, + maxRedirections) { + @Override + public String execute(Jedis connection) { + return connection.set(key, value, nxxx, expx, time); + } + }.run(key); + } + @Override public String get(final String key) { return new JedisClusterCommand(connectionHandler, timeout, @@ -382,7 +410,7 @@ public class JedisCluster implements JedisCommands, BasicCommands { maxRedirections) { @Override public Long execute(Jedis connection) { - return connection.hdel(key); + return connection.hlen(key); } }.run(key); } @@ -611,6 +639,17 @@ public class JedisCluster implements JedisCommands, BasicCommands { }.run(key); } + @Override + public List srandmember(final String key, final int count) { + return new JedisClusterCommand>(connectionHandler, timeout, + maxRedirections) { + @Override + public List execute(Jedis connection) { + return connection.srandmember(key, count); + } + }.run(key); + } + @Override public Long strlen(final String key) { return new JedisClusterCommand(connectionHandler, timeout, @@ -1441,58 +1480,6 @@ public class JedisCluster implements JedisCommands, BasicCommands { return null; } - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - @Override - public ScanResult> hscan(final String key, - final int cursor) { - return new JedisClusterCommand>>( - connectionHandler, timeout, maxRedirections) { - @Override - public ScanResult> execute(Jedis connection) { - return connection.hscan(key, cursor); - } - }.run(null); - } - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - @Override - public ScanResult sscan(final String key, final int cursor) { - return new JedisClusterCommand>(connectionHandler, - timeout, maxRedirections) { - @Override - public ScanResult execute(Jedis connection) { - return connection.sscan(key, cursor); - } - }.run(null); - } - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - @Override - public ScanResult zscan(final String key, final int cursor) { - return new JedisClusterCommand>(connectionHandler, - timeout, maxRedirections) { - @Override - public ScanResult execute(Jedis connection) { - return connection.zscan(key, cursor); - } - }.run(null); - } - @Override public ScanResult> hscan(final String key, final String cursor) { @@ -1527,4 +1514,26 @@ public class JedisCluster implements JedisCommands, BasicCommands { }.run(null); } + @Override + public Long pfadd(final String key, final String... elements) { + return new JedisClusterCommand(connectionHandler, + timeout, maxRedirections) { + @Override + public Long execute(Jedis connection) { + return connection.pfadd(key, elements); + } + }.run(key); + } + + @Override + public long pfcount(final String key) { + return new JedisClusterCommand(connectionHandler, + timeout, maxRedirections) { + @Override + public Long execute(Jedis connection) { + return connection.pfcount(key); + } + }.run(key); + } + } diff --git a/src/main/java/redis/clients/jedis/JedisClusterCommand.java b/src/main/java/redis/clients/jedis/JedisClusterCommand.java index c1be912..051d5cd 100644 --- a/src/main/java/redis/clients/jedis/JedisClusterCommand.java +++ b/src/main/java/redis/clients/jedis/JedisClusterCommand.java @@ -4,7 +4,6 @@ import redis.clients.jedis.exceptions.JedisAskDataException; import redis.clients.jedis.exceptions.JedisClusterException; import redis.clients.jedis.exceptions.JedisClusterMaxRedirectionsException; import redis.clients.jedis.exceptions.JedisConnectionException; -import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.exceptions.JedisMovedDataException; import redis.clients.jedis.exceptions.JedisRedirectionException; import redis.clients.util.JedisClusterCRC16; diff --git a/src/main/java/redis/clients/jedis/JedisClusterConnectionHandler.java b/src/main/java/redis/clients/jedis/JedisClusterConnectionHandler.java index 94f4c75..e6eb01f 100644 --- a/src/main/java/redis/clients/jedis/JedisClusterConnectionHandler.java +++ b/src/main/java/redis/clients/jedis/JedisClusterConnectionHandler.java @@ -1,13 +1,13 @@ package redis.clients.jedis; -import java.util.HashMap; -import java.util.Map; -import java.util.Random; -import java.util.Set; +import java.util.*; import redis.clients.jedis.exceptions.JedisConnectionException; +import redis.clients.util.ClusterNodeInformation; +import redis.clients.util.ClusterNodeInformationParser; public abstract class JedisClusterConnectionHandler { + public static ClusterNodeInformationParser nodeInfoParser = new ClusterNodeInformationParser(); protected Map nodes = new HashMap(); protected Map slots = new HashMap(); @@ -65,69 +65,40 @@ public abstract class JedisClusterConnectionHandler { setNodeIfNotExist(node); } } - + private void discoverClusterNodesAndSlots(Jedis jedis) { String localNodes = jedis.clusterNodes(); for (String nodeInfo : localNodes.split("\n")) { - HostAndPort node = getHostAndPortFromNodeLine(nodeInfo, jedis); - setNodeIfNotExist(node); - - JedisPool nodePool = nodes.get(getNodeKey(node)); - populateNodeSlots(nodeInfo, nodePool); - } - } - - private void setNodeIfNotExist(HostAndPort node) { - String nodeKey = getNodeKey(node); - if (nodes.containsKey(nodeKey)) - return; - - JedisPool nodePool = new JedisPool(node.getHost(), node.getPort()); - nodes.put(nodeKey, nodePool); - } + ClusterNodeInformation clusterNodeInfo = nodeInfoParser.parse( + nodeInfo, new HostAndPort(jedis.getClient().getHost(), + jedis.getClient().getPort())); - private void populateNodeSlots(String nodeInfo, JedisPool nodePool) { - String[] nodeInfoArray = nodeInfo.split(" "); - if (nodeInfoArray.length > 7) { - for (int i = 8; i < nodeInfoArray.length; i++) { - processSlot(nodeInfoArray[i], nodePool); - } + HostAndPort targetNode = clusterNodeInfo.getNode(); + setNodeIfNotExist(targetNode); + assignSlotsToNode(clusterNodeInfo.getAvailableSlots(), targetNode); } } - private void processSlot(String slot, JedisPool nodePool) { - if (slot.contains("-")) { - String[] slotRange = slot.split("-"); - for (int i = Integer.valueOf(slotRange[0]); i <= Integer - .valueOf(slotRange[1]); i++) { - slots.put(i, nodePool); - } - } else { - slots.put(Integer.valueOf(slot), nodePool); - } - } - - private HostAndPort getHostAndPortFromNodeLine(String nodeInfo, - Jedis currentConnection) { - String stringHostAndPort = nodeInfo.split(" ", 3)[1]; - if (":0".equals(stringHostAndPort)) { - return new HostAndPort(currentConnection.getClient().getHost(), - currentConnection.getClient().getPort()); - } - String[] arrayHostAndPort = stringHostAndPort.split(":"); - return new HostAndPort(arrayHostAndPort[0], - Integer.valueOf(arrayHostAndPort[1])); - } - public void assignSlotToNode(int slot, HostAndPort targetNode) { JedisPool targetPool = nodes.get(getNodeKey(targetNode)); - if (targetPool != null) { - slots.put(slot, targetPool); - } else { + if (targetPool == null) { setNodeIfNotExist(targetNode); - targetPool = nodes.get(getNodeKey(targetNode)); + } + slots.put(slot, targetPool); + } + + public void assignSlotsToNode(List targetSlots, + HostAndPort targetNode) { + JedisPool targetPool = nodes.get(getNodeKey(targetNode)); + + if (targetPool == null) { + setNodeIfNotExist(targetNode); + targetPool = nodes.get(getNodeKey(targetNode)); + } + + for (Integer slot : targetSlots) { slots.put(slot, targetPool); } } @@ -144,4 +115,13 @@ public abstract class JedisClusterConnectionHandler { protected String getNodeKey(Client client) { return client.getHost() + ":" + client.getPort(); } + + private void setNodeIfNotExist(HostAndPort node) { + String nodeKey = getNodeKey(node); + if (nodes.containsKey(nodeKey)) + return; + + JedisPool nodePool = new JedisPool(node.getHost(), node.getPort()); + nodes.put(nodeKey, nodePool); + } } diff --git a/src/main/java/redis/clients/jedis/JedisCommands.java b/src/main/java/redis/clients/jedis/JedisCommands.java index 907b328..a657468 100644 --- a/src/main/java/redis/clients/jedis/JedisCommands.java +++ b/src/main/java/redis/clients/jedis/JedisCommands.java @@ -10,6 +10,9 @@ import java.util.Set; public interface JedisCommands { String set(String key, String value); + String set(String key, String value, String nxxx, + String expx, long time); + String get(String key); Boolean exists(String key); @@ -110,6 +113,8 @@ public interface JedisCommands { String srandmember(String key); + List srandmember(String key, int count); + Long strlen(String key); Long zadd(String key, double score, String member); @@ -220,33 +225,14 @@ public interface JedisCommands { Long bitcount(final String key, long start, long end); - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - ScanResult> hscan(final String key, int cursor); - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - ScanResult sscan(final String key, int cursor); - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - ScanResult zscan(final String key, int cursor); - ScanResult> hscan(final String key, final String cursor); ScanResult sscan(final String key, final String cursor); ScanResult zscan(final String key, final String cursor); + + Long pfadd(final String key, final String... elements); + + long pfcount(final String key); + } diff --git a/src/main/java/redis/clients/jedis/JedisPool.java b/src/main/java/redis/clients/jedis/JedisPool.java index 8e34d19..c6d2cb3 100644 --- a/src/main/java/redis/clients/jedis/JedisPool.java +++ b/src/main/java/redis/clients/jedis/JedisPool.java @@ -79,6 +79,13 @@ public class JedisPool extends Pool { database, clientName)); } + @Override + public Jedis getResource() { + Jedis jedis = super.getResource(); + jedis.setDataSource(this); + return jedis; + } + public void returnBrokenResource(final Jedis resource) { if (resource != null) { returnBrokenResourceObject(resource); @@ -91,4 +98,12 @@ public class JedisPool extends Pool { returnResourceObject(resource); } } + + public int getNumActive() { + if (this.internalPool == null || this.internalPool.isClosed()) { + return -1; + } + + return this.internalPool.getNumActive(); + } } diff --git a/src/main/java/redis/clients/jedis/JedisPubSub.java b/src/main/java/redis/clients/jedis/JedisPubSub.java index 7dfcf07..2ded002 100644 --- a/src/main/java/redis/clients/jedis/JedisPubSub.java +++ b/src/main/java/redis/clients/jedis/JedisPubSub.java @@ -162,12 +162,6 @@ public abstract class JedisPubSub { /* Invalidate instance since this thread is no longer listening */ this.client = null; - - /* - * Reset pipeline count because subscribe() calls would have increased - * it but nothing decremented it. - */ - client.resetPipelinedCount(); } public int getSubscribedChannels() { diff --git a/src/main/java/redis/clients/jedis/JedisSentinelPool.java b/src/main/java/redis/clients/jedis/JedisSentinelPool.java index 224ca99..0ff0dff 100644 --- a/src/main/java/redis/clients/jedis/JedisSentinelPool.java +++ b/src/main/java/redis/clients/jedis/JedisSentinelPool.java @@ -74,19 +74,6 @@ public class JedisSentinelPool extends Pool { initPool(master); } - public void returnBrokenResource(final Jedis resource) { - if (resource != null) { - returnBrokenResourceObject(resource); - } - } - - public void returnResource(final Jedis resource) { - if (resource != null) { - resource.resetState(); - returnResourceObject(resource); - } - } - private volatile HostAndPort currentHostMaster; public void destroy() { @@ -175,6 +162,26 @@ public class JedisSentinelPool extends Pool { return new HostAndPort(host, port); } + @Override + public Jedis getResource() { + Jedis jedis = super.getResource(); + jedis.setDataSource(this); + return jedis; + } + + public void returnBrokenResource(final Jedis resource) { + if (resource != null) { + returnBrokenResourceObject(resource); + } + } + + public void returnResource(final Jedis resource) { + if (resource != null) { + resource.resetState(); + returnResourceObject(resource); + } + } + protected class JedisPubSubAdapter extends JedisPubSub { @Override public void onMessage(String channel, String message) { diff --git a/src/main/java/redis/clients/jedis/MultiKeyBinaryCommands.java b/src/main/java/redis/clients/jedis/MultiKeyBinaryCommands.java index e6ea8fd..186d822 100644 --- a/src/main/java/redis/clients/jedis/MultiKeyBinaryCommands.java +++ b/src/main/java/redis/clients/jedis/MultiKeyBinaryCommands.java @@ -69,4 +69,8 @@ public interface MultiKeyBinaryCommands { byte[] randomBinaryKey(); Long bitop(BitOP op, final byte[] destKey, byte[]... srcKeys); + + String pfmerge(final byte[] destkey, final byte[]... sourcekeys); + + Long pfcount(byte[]... keys); } diff --git a/src/main/java/redis/clients/jedis/MultiKeyBinaryRedisPipeline.java b/src/main/java/redis/clients/jedis/MultiKeyBinaryRedisPipeline.java index fd71016..a3f8716 100644 --- a/src/main/java/redis/clients/jedis/MultiKeyBinaryRedisPipeline.java +++ b/src/main/java/redis/clients/jedis/MultiKeyBinaryRedisPipeline.java @@ -65,4 +65,8 @@ public interface MultiKeyBinaryRedisPipeline { Response randomKeyBinary(); Response bitop(BitOP op, final byte[] destKey, byte[]... srcKeys); + + Response pfmerge(final byte[] destkey, final byte[]... sourcekeys); + + Response pfcount(final byte[] ... keys); } diff --git a/src/main/java/redis/clients/jedis/MultiKeyCommands.java b/src/main/java/redis/clients/jedis/MultiKeyCommands.java index f03f82c..9a464d1 100644 --- a/src/main/java/redis/clients/jedis/MultiKeyCommands.java +++ b/src/main/java/redis/clients/jedis/MultiKeyCommands.java @@ -70,13 +70,9 @@ public interface MultiKeyCommands { Long bitop(BitOP op, final String destKey, String... srcKeys); - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - ScanResult scan(int cursor); - ScanResult scan(final String cursor); + + String pfmerge(final String destkey, final String... sourcekeys); + + long pfcount(final String...keys); } diff --git a/src/main/java/redis/clients/jedis/MultiKeyCommandsPipeline.java b/src/main/java/redis/clients/jedis/MultiKeyCommandsPipeline.java index 92c8d5a..e2de238 100644 --- a/src/main/java/redis/clients/jedis/MultiKeyCommandsPipeline.java +++ b/src/main/java/redis/clients/jedis/MultiKeyCommandsPipeline.java @@ -64,4 +64,8 @@ public interface MultiKeyCommandsPipeline { Response randomKey(); Response bitop(BitOP op, final String destKey, String... srcKeys); + + Response pfmerge(final String destkey, final String... sourcekeys); + + Response pfcount(final String...keys); } diff --git a/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java b/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java index fa7ae6e..31054ce 100644 --- a/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java +++ b/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java @@ -367,6 +367,11 @@ abstract class MultiKeyPipelineBase extends PipelineBase implements client.info(); return getResponse(BuilderFactory.STRING); } + + public Response> time() { + client.time(); + return getResponse(BuilderFactory.STRING_LIST); + } public Response dbSize() { client.dbSize(); @@ -446,4 +451,28 @@ abstract class MultiKeyPipelineBase extends PipelineBase implements client.clusterSetSlotImporting(slot, nodeId); return getResponse(BuilderFactory.STRING); } + + @Override + public Response pfmerge(byte[] destkey, byte[]... sourcekeys) { + client.pfmerge(destkey, sourcekeys); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response pfmerge(String destkey, String... sourcekeys) { + client.pfmerge(destkey, sourcekeys); + return getResponse(BuilderFactory.STRING); + } + + @Override + public Response pfcount(String...keys) { + client.pfcount(keys); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response pfcount(final byte[] ... keys) { + client.pfcount(keys); + return getResponse(BuilderFactory.LONG); + } } diff --git a/src/main/java/redis/clients/jedis/Pipeline.java b/src/main/java/redis/clients/jedis/Pipeline.java index 2687f84..ba17387 100755 --- a/src/main/java/redis/clients/jedis/Pipeline.java +++ b/src/main/java/redis/clients/jedis/Pipeline.java @@ -69,13 +69,25 @@ public class Pipeline extends MultiKeyPipelineBase { return client; } + public void clear() { + if (isInMulti()) { + discard(); + } + + sync(); + } + + public boolean isInMulti() { + return currentMulti != null; + } + /** * Syncronize pipeline by reading all responses. This operation close the * pipeline. In order to get return values from pipelined commands, capture * the different Response of the commands you execute. */ public void sync() { - List unformatted = client.getAll(); + List unformatted = client.getMany(getPipelinedResponseLength()); for (Object o : unformatted) { generateResponse(o); } @@ -90,7 +102,7 @@ public class Pipeline extends MultiKeyPipelineBase { * @return A list of all the responses in the order you executed them. */ public List syncAndReturnAll() { - List unformatted = client.getAll(); + List unformatted = client.getMany(getPipelinedResponseLength()); List formatted = new ArrayList(); for (Object o : unformatted) { @@ -104,12 +116,17 @@ public class Pipeline extends MultiKeyPipelineBase { } public Response discard() { + if (currentMulti == null) + throw new JedisDataException("DISCARD without MULTI"); client.discard(); currentMulti = null; return getResponse(BuilderFactory.STRING); } public Response> exec() { + if (currentMulti == null) + throw new JedisDataException("EXEC without MULTI"); + client.exec(); Response> response = super.getResponse(currentMulti); currentMulti.setResponseDependency(response); @@ -118,11 +135,14 @@ public class Pipeline extends MultiKeyPipelineBase { } public Response multi() { + if (currentMulti != null) + throw new JedisDataException("MULTI calls can not be nested"); + client.multi(); Response response = getResponse(BuilderFactory.STRING); // Expecting // OK currentMulti = new MultiResponseBuilder(); return response; } - + } diff --git a/src/main/java/redis/clients/jedis/PipelineBase.java b/src/main/java/redis/clients/jedis/PipelineBase.java index db37b2e..58d06e1 100644 --- a/src/main/java/redis/clients/jedis/PipelineBase.java +++ b/src/main/java/redis/clients/jedis/PipelineBase.java @@ -142,7 +142,25 @@ abstract class PipelineBase extends Queable implements BinaryRedisPipeline, getClient(key).getbit(key, offset); return getResponse(BuilderFactory.BOOLEAN); } - + + public Response bitpos(final String key, final boolean value) { + return bitpos(key, value, new BitPosParams()); + } + + public Response bitpos(final String key, final boolean value, final BitPosParams params) { + getClient(key).bitpos(key, value, params); + return getResponse(BuilderFactory.LONG); + } + + public Response bitpos(final byte[] key, final boolean value) { + return bitpos(key, value, new BitPosParams()); + } + + public Response bitpos(final byte[] key, final boolean value, final BitPosParams params) { + getClient(key).bitpos(key, value, params); + return getResponse(BuilderFactory.LONG); + } + public Response getrange(String key, long startOffset, long endOffset) { getClient(key).getrange(key, startOffset, endOffset); @@ -159,9 +177,9 @@ abstract class PipelineBase extends Queable implements BinaryRedisPipeline, return getResponse(BuilderFactory.BYTE_ARRAY); } - public Response getrange(byte[] key, long startOffset, long endOffset) { + public Response getrange(byte[] key, long startOffset, long endOffset) { getClient(key).getrange(key, startOffset, endOffset); - return getResponse(BuilderFactory.LONG); + return getResponse(BuilderFactory.BYTE_ARRAY); } public Response hdel(String key, String... field) { @@ -1130,12 +1148,12 @@ abstract class PipelineBase extends Queable implements BinaryRedisPipeline, return getResponse(BuilderFactory.LONG); } - public Response pexpire(String key, int milliseconds) { + public Response pexpire(String key, long milliseconds) { getClient(key).pexpire(key, milliseconds); return getResponse(BuilderFactory.LONG); } - public Response pexpire(byte[] key, int milliseconds) { + public Response pexpire(byte[] key, long milliseconds) { getClient(key).pexpire(key, milliseconds); return getResponse(BuilderFactory.LONG); } @@ -1254,4 +1272,28 @@ abstract class PipelineBase extends Queable implements BinaryRedisPipeline, return getResponse(BuilderFactory.STRING); } + @Override + public Response pfadd(byte[] key, byte[]... elements) { + getClient(key).pfadd(key, elements); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response pfcount(byte[] key) { + getClient(key).pfcount(key); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response pfadd(String key, String... elements) { + getClient(key).pfadd(key, elements); + return getResponse(BuilderFactory.LONG); + } + + @Override + public Response pfcount(String key) { + getClient(key).pfcount(key); + return getResponse(BuilderFactory.LONG); + } + } diff --git a/src/main/java/redis/clients/jedis/PipelineBlock.java b/src/main/java/redis/clients/jedis/PipelineBlock.java index 50181ba..24bae84 100644 --- a/src/main/java/redis/clients/jedis/PipelineBlock.java +++ b/src/main/java/redis/clients/jedis/PipelineBlock.java @@ -7,5 +7,9 @@ package redis.clients.jedis; * @see https://github.com/xetorthio/jedis/pull/498 */ public abstract class PipelineBlock extends Pipeline { + // For shadowing + @SuppressWarnings("unused") + private Client client; + public abstract void execute(); } diff --git a/src/main/java/redis/clients/jedis/Protocol.java b/src/main/java/redis/clients/jedis/Protocol.java index 76e74ef..dcc225e 100644 --- a/src/main/java/redis/clients/jedis/Protocol.java +++ b/src/main/java/redis/clients/jedis/Protocol.java @@ -50,6 +50,16 @@ public final class Protocol { public static final String CLUSTER_SETSLOT_NODE = "node"; public static final String CLUSTER_SETSLOT_MIGRATING = "migrating"; public static final String CLUSTER_SETSLOT_IMPORTING = "importing"; + public static final String CLUSTER_SETSLOT_STABLE = "stable"; + public static final String CLUSTER_FORGET = "forget"; + public static final String CLUSTER_FLUSHSLOT = "flushslots"; + public static final String CLUSTER_KEYSLOT = "keyslot"; + public static final String CLUSTER_COUNTKEYINSLOT = "countkeysinslot"; + public static final String CLUSTER_SAVECONFIG = "saveconfig"; + public static final String CLUSTER_REPLICATE = "replicate"; + public static final String CLUSTER_SLAVES = "slaves"; + public static final String CLUSTER_FAILOVER = "failover"; + public static final String CLUSTER_SLOTS = "slots"; public static final String PUBSUB_CHANNELS= "channels"; public static final String PUBSUB_NUMSUB = "numsub"; public static final String PUBSUB_NUM_PAT = "numpat"; @@ -208,7 +218,7 @@ public final class Protocol { } public static enum Command { - PING, SET, GET, QUIT, EXISTS, DEL, TYPE, FLUSHDB, KEYS, RANDOMKEY, RENAME, RENAMENX, RENAMEX, DBSIZE, EXPIRE, EXPIREAT, TTL, SELECT, MOVE, FLUSHALL, GETSET, MGET, SETNX, SETEX, MSET, MSETNX, DECRBY, DECR, INCRBY, INCR, APPEND, SUBSTR, HSET, HGET, HSETNX, HMSET, HMGET, HINCRBY, HEXISTS, HDEL, HLEN, HKEYS, HVALS, HGETALL, RPUSH, LPUSH, LLEN, LRANGE, LTRIM, LINDEX, LSET, LREM, LPOP, RPOP, RPOPLPUSH, SADD, SMEMBERS, SREM, SPOP, SMOVE, SCARD, SISMEMBER, SINTER, SINTERSTORE, SUNION, SUNIONSTORE, SDIFF, SDIFFSTORE, SRANDMEMBER, ZADD, ZRANGE, ZREM, ZINCRBY, ZRANK, ZREVRANK, ZREVRANGE, ZCARD, ZSCORE, MULTI, DISCARD, EXEC, WATCH, UNWATCH, SORT, BLPOP, BRPOP, AUTH, SUBSCRIBE, PUBLISH, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBSUB, ZCOUNT, ZRANGEBYSCORE, ZREVRANGEBYSCORE, ZREMRANGEBYRANK, ZREMRANGEBYSCORE, ZUNIONSTORE, ZINTERSTORE, ZLEXCOUNT, ZRANGEBYLEX, ZREMRANGEBYLEX, SAVE, BGSAVE, BGREWRITEAOF, LASTSAVE, SHUTDOWN, INFO, MONITOR, SLAVEOF, CONFIG, STRLEN, SYNC, LPUSHX, PERSIST, RPUSHX, ECHO, LINSERT, DEBUG, BRPOPLPUSH, SETBIT, GETBIT, SETRANGE, GETRANGE, EVAL, EVALSHA, SCRIPT, SLOWLOG, OBJECT, BITCOUNT, BITOP, SENTINEL, DUMP, RESTORE, PEXPIRE, PEXPIREAT, PTTL, INCRBYFLOAT, PSETEX, CLIENT, TIME, MIGRATE, HINCRBYFLOAT, SCAN, HSCAN, SSCAN, ZSCAN, WAIT, CLUSTER, ASKING; + PING, SET, GET, QUIT, EXISTS, DEL, TYPE, FLUSHDB, KEYS, RANDOMKEY, RENAME, RENAMENX, RENAMEX, DBSIZE, EXPIRE, EXPIREAT, TTL, SELECT, MOVE, FLUSHALL, GETSET, MGET, SETNX, SETEX, MSET, MSETNX, DECRBY, DECR, INCRBY, INCR, APPEND, SUBSTR, HSET, HGET, HSETNX, HMSET, HMGET, HINCRBY, HEXISTS, HDEL, HLEN, HKEYS, HVALS, HGETALL, RPUSH, LPUSH, LLEN, LRANGE, LTRIM, LINDEX, LSET, LREM, LPOP, RPOP, RPOPLPUSH, SADD, SMEMBERS, SREM, SPOP, SMOVE, SCARD, SISMEMBER, SINTER, SINTERSTORE, SUNION, SUNIONSTORE, SDIFF, SDIFFSTORE, SRANDMEMBER, ZADD, ZRANGE, ZREM, ZINCRBY, ZRANK, ZREVRANK, ZREVRANGE, ZCARD, ZSCORE, MULTI, DISCARD, EXEC, WATCH, UNWATCH, SORT, BLPOP, BRPOP, AUTH, SUBSCRIBE, PUBLISH, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBSUB, ZCOUNT, ZRANGEBYSCORE, ZREVRANGEBYSCORE, ZREMRANGEBYRANK, ZREMRANGEBYSCORE, ZUNIONSTORE, ZINTERSTORE, ZLEXCOUNT, ZRANGEBYLEX, ZREMRANGEBYLEX, SAVE, BGSAVE, BGREWRITEAOF, LASTSAVE, SHUTDOWN, INFO, MONITOR, SLAVEOF, CONFIG, STRLEN, SYNC, LPUSHX, PERSIST, RPUSHX, ECHO, LINSERT, DEBUG, BRPOPLPUSH, SETBIT, GETBIT, BITPOS, SETRANGE, GETRANGE, EVAL, EVALSHA, SCRIPT, SLOWLOG, OBJECT, BITCOUNT, BITOP, SENTINEL, DUMP, RESTORE, PEXPIRE, PEXPIREAT, PTTL, INCRBYFLOAT, PSETEX, CLIENT, TIME, MIGRATE, HINCRBYFLOAT, SCAN, HSCAN, SSCAN, ZSCAN, WAIT, CLUSTER, ASKING, PFADD, PFCOUNT, PFMERGE; public final byte[] raw; diff --git a/src/main/java/redis/clients/jedis/Queable.java b/src/main/java/redis/clients/jedis/Queable.java index 769bf16..2cd5265 100644 --- a/src/main/java/redis/clients/jedis/Queable.java +++ b/src/main/java/redis/clients/jedis/Queable.java @@ -24,4 +24,11 @@ public class Queable { return lr; } + protected boolean hasPipelinedResponse() { + return pipelinedResponses.size() > 0; + } + + protected int getPipelinedResponseLength() { + return pipelinedResponses.size(); + } } diff --git a/src/main/java/redis/clients/jedis/RedisPipeline.java b/src/main/java/redis/clients/jedis/RedisPipeline.java index 2961a68..63e3257 100644 --- a/src/main/java/redis/clients/jedis/RedisPipeline.java +++ b/src/main/java/redis/clients/jedis/RedisPipeline.java @@ -197,4 +197,8 @@ public interface RedisPipeline { Response bitcount(String key); Response bitcount(String key, long start, long end); + + Response pfadd(final String key, final String... elements); + + Response pfcount(final String key); } diff --git a/src/main/java/redis/clients/jedis/ScanParams.java b/src/main/java/redis/clients/jedis/ScanParams.java index 980bb13..a8b8ac7 100644 --- a/src/main/java/redis/clients/jedis/ScanParams.java +++ b/src/main/java/redis/clients/jedis/ScanParams.java @@ -13,15 +13,24 @@ import redis.clients.util.SafeEncoder; public class ScanParams { private List params = new ArrayList(); public final static String SCAN_POINTER_START = String.valueOf(0); + public final static byte[] SCAN_POINTER_START_BINARY = SafeEncoder.encode(SCAN_POINTER_START); - public void match(final String pattern) { + public ScanParams match(final byte[] pattern) { + params.add(MATCH.raw); + params.add(pattern); + return this; + } + + public ScanParams match(final String pattern) { params.add(MATCH.raw); params.add(SafeEncoder.encode(pattern)); + return this; } - public void count(final int count) { + public ScanParams count(final int count) { params.add(COUNT.raw); params.add(Protocol.toByteArray(count)); + return this; } public Collection getParams() { diff --git a/src/main/java/redis/clients/jedis/ScanResult.java b/src/main/java/redis/clients/jedis/ScanResult.java index 9afe27d..84af540 100644 --- a/src/main/java/redis/clients/jedis/ScanResult.java +++ b/src/main/java/redis/clients/jedis/ScanResult.java @@ -2,41 +2,26 @@ package redis.clients.jedis; import java.util.List; +import redis.clients.util.SafeEncoder; + public class ScanResult { - private String cursor; + private byte[] cursor; private List results; - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public ScanResult(int cursor, List results) { - this.cursor = String.valueOf(cursor); - this.results = results; + public ScanResult(String cursor, List results) { + this(SafeEncoder.encode(cursor), results); } - public ScanResult(String cursor, List results) { + public ScanResult(byte[] cursor, List results) { this.cursor = cursor; this.results = results; } - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - * @return int(currently), but will be changed to String, so be careful to prepare! - */ - public int getCursor() { - return Integer.parseInt(cursor); + + public String getCursor() { + return SafeEncoder.encode(cursor); } - - /** - * FIXME: This method should be changed to getCursor() on next major release - */ - public String getStringCursor() { + + public byte[] getCursorAsBytes() { return cursor; } diff --git a/src/main/java/redis/clients/jedis/ShardedJedis.java b/src/main/java/redis/clients/jedis/ShardedJedis.java index 2211467..19debb9 100644 --- a/src/main/java/redis/clients/jedis/ShardedJedis.java +++ b/src/main/java/redis/clients/jedis/ShardedJedis.java @@ -1,15 +1,22 @@ package redis.clients.jedis; +import java.io.Closeable; +import redis.clients.jedis.BinaryClient.LIST_POSITION; +import redis.clients.util.Hashing; + import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.regex.Pattern; -import redis.clients.jedis.BinaryClient.LIST_POSITION; -import redis.clients.util.Hashing; +import redis.clients.util.Pool; + +public class ShardedJedis extends BinaryShardedJedis implements JedisCommands, + Closeable { + + protected Pool dataSource = null; -public class ShardedJedis extends BinaryShardedJedis implements JedisCommands { public ShardedJedis(List shards) { super(shards); } @@ -32,6 +39,13 @@ public class ShardedJedis extends BinaryShardedJedis implements JedisCommands { return j.set(key, value); } + @Override + public String set(String key, String value, String nxxx, String expx, + long time) { + Jedis j = getShard(key); + return j.set(key, value, nxxx, expx, time); + } + public String get(String key) { Jedis j = getShard(key); return j.get(key); @@ -132,6 +146,11 @@ public class ShardedJedis extends BinaryShardedJedis implements JedisCommands { return j.incrBy(key, integer); } + public Double incrByFloat(String key, double integer) { + Jedis j = getShard(key); + return j.incrByFloat(key, integer); + } + public Long incr(String key) { Jedis j = getShard(key); return j.incr(key); @@ -177,6 +196,11 @@ public class ShardedJedis extends BinaryShardedJedis implements JedisCommands { return j.hincrBy(key, field, value); } + public Double hincrByFloat(String key, String field, double value) { + Jedis j = getShard(key); + return j.hincrByFloat(key, field, value); + } + public Boolean hexists(String key, String field) { Jedis j = getShard(key); return j.hexists(key, field); @@ -322,6 +346,12 @@ public class ShardedJedis extends BinaryShardedJedis implements JedisCommands { return j.srandmember(key); } + @Override + public List srandmember(String key, int count) { + Jedis j = getShard(key); + return j.srandmember(key, count); + } + public Long zadd(String key, double score, String member) { Jedis j = getShard(key); return j.zadd(key, score, member); @@ -544,52 +574,63 @@ public class ShardedJedis extends BinaryShardedJedis implements JedisCommands { return j.bitcount(key, start, end); } - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public ScanResult> hscan(String key, int cursor) { - Jedis j = getShard(key); - return j.hscan(key, cursor); - } - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public ScanResult sscan(String key, int cursor) { - Jedis j = getShard(key); - return j.sscan(key, cursor); - } - - @Deprecated - /** - * This method is deprecated due to bug (scan cursor should be unsigned long) - * And will be removed on next major release - * @see https://github.com/xetorthio/jedis/issues/531 - */ - public ScanResult zscan(String key, int cursor) { - Jedis j = getShard(key); - return j.zscan(key, cursor); - } - public ScanResult> hscan(String key, final String cursor) { Jedis j = getShard(key); return j.hscan(key, cursor); } - + public ScanResult sscan(String key, final String cursor) { Jedis j = getShard(key); return j.sscan(key, cursor); } - + public ScanResult zscan(String key, final String cursor) { Jedis j = getShard(key); return j.zscan(key, cursor); } + + @Override + public void close() { + if (dataSource != null) { + boolean broken = false; + + for (Jedis jedis : getAllShards()) { + if (jedis.getClient().isBroken()) { + broken = true; + } + } + + if (broken) { + dataSource.returnBrokenResource(this); + } else { + this.resetState(); + dataSource.returnResource(this); + } + + } else { + disconnect(); + } + } + + public void setDataSource(Pool shardedJedisPool) { + this.dataSource = shardedJedisPool; + } + + public void resetState() { + for (Jedis jedis : getAllShards()) { + jedis.resetState(); + } + } + + public Long pfadd(String key, String... elements) { + Jedis j = getShard(key); + return j.pfadd(key, elements); + } + + @Override + public long pfcount(String key) { + Jedis j = getShard(key); + return j.pfcount(key); + } } diff --git a/src/main/java/redis/clients/jedis/ShardedJedisPool.java b/src/main/java/redis/clients/jedis/ShardedJedisPool.java index dd56ac1..f50f3e7 100644 --- a/src/main/java/redis/clients/jedis/ShardedJedisPool.java +++ b/src/main/java/redis/clients/jedis/ShardedJedisPool.java @@ -32,6 +32,28 @@ public class ShardedJedisPool extends Pool { super(poolConfig, new ShardedJedisFactory(shards, algo, keyTagPattern)); } + @Override + public ShardedJedis getResource() { + ShardedJedis jedis = super.getResource(); + jedis.setDataSource(this); + return jedis; + } + + @Override + public void returnBrokenResource(final ShardedJedis resource) { + if (resource != null) { + returnBrokenResourceObject(resource); + } + } + + @Override + public void returnResource(final ShardedJedis resource) { + if (resource != null) { + resource.resetState(); + returnResourceObject(resource); + } + } + /** * PoolableObjectFactory custom impl. */ diff --git a/src/main/java/redis/clients/jedis/Transaction.java b/src/main/java/redis/clients/jedis/Transaction.java index 69d50d2..e6088ff 100644 --- a/src/main/java/redis/clients/jedis/Transaction.java +++ b/src/main/java/redis/clients/jedis/Transaction.java @@ -31,9 +31,16 @@ public class Transaction extends MultiKeyPipelineBase { return client; } + public void clear() { + if (inTransaction) { + discard(); + } + } + public List exec() { + // Discard QUEUED or ERROR + client.getMany(getPipelinedResponseLength()); client.exec(); - client.getAll(1); // Discard all but the last reply List unformatted = client.getObjectMultiBulkReply(); if (unformatted == null) { @@ -51,8 +58,9 @@ public class Transaction extends MultiKeyPipelineBase { } public List> execGetResponse() { + // Discard QUEUED or ERROR + client.getMany(getPipelinedResponseLength()); client.exec(); - client.getAll(1); // Discard all but the last reply List unformatted = client.getObjectMultiBulkReply(); if (unformatted == null) { @@ -66,11 +74,15 @@ public class Transaction extends MultiKeyPipelineBase { } public String discard() { + client.getMany(getPipelinedResponseLength()); client.discard(); - client.getAll(1); // Discard all but the last reply inTransaction = false; clean(); return client.getStatusCodeReply(); } + public void setClient(Client client) { + this.client = client; + } + } \ No newline at end of file diff --git a/src/main/java/redis/clients/jedis/TransactionBlock.java b/src/main/java/redis/clients/jedis/TransactionBlock.java index 86f7c44..c038ac2 100644 --- a/src/main/java/redis/clients/jedis/TransactionBlock.java +++ b/src/main/java/redis/clients/jedis/TransactionBlock.java @@ -9,6 +9,10 @@ import redis.clients.jedis.exceptions.JedisException; * @see https://github.com/xetorthio/jedis/pull/498 */ public abstract class TransactionBlock extends Transaction { + // For shadowing + @SuppressWarnings("unused") + private Client client; + public TransactionBlock(Client client) { super(client); } @@ -19,6 +23,6 @@ public abstract class TransactionBlock extends Transaction { public abstract void execute() throws JedisException; public void setClient(Client client) { - this.client = client; + super.setClient(client); } } diff --git a/src/main/java/redis/clients/jedis/ZParams.java b/src/main/java/redis/clients/jedis/ZParams.java index 7b585f3..6b42174 100644 --- a/src/main/java/redis/clients/jedis/ZParams.java +++ b/src/main/java/redis/clients/jedis/ZParams.java @@ -23,9 +23,15 @@ public class ZParams { private List params = new ArrayList(); - public ZParams weights(final int... weights) { + /** + * Set weights. + * + * @param weights + * weights. + */ + public ZParams weights(final double... weights) { params.add(WEIGHTS.raw); - for (final int weight : weights) { + for (final double weight : weights) { params.add(Protocol.toByteArray(weight)); } diff --git a/src/main/java/redis/clients/util/ClusterNodeInformation.java b/src/main/java/redis/clients/util/ClusterNodeInformation.java new file mode 100644 index 0000000..a3833d0 --- /dev/null +++ b/src/main/java/redis/clients/util/ClusterNodeInformation.java @@ -0,0 +1,48 @@ +package redis.clients.util; + +import redis.clients.jedis.HostAndPort; + +import java.util.ArrayList; +import java.util.List; + +public class ClusterNodeInformation { + private HostAndPort node; + private List availableSlots; + private List slotsBeingImported; + private List slotsBeingMigrated; + + public ClusterNodeInformation(HostAndPort node) { + this.node = node; + this.availableSlots = new ArrayList(); + this.slotsBeingImported = new ArrayList(); + this.slotsBeingMigrated = new ArrayList(); + } + + public void addAvailableSlot(int slot) { + availableSlots.add(slot); + } + + public void addSlotBeingImported(int slot) { + slotsBeingImported.add(slot); + } + + public void addSlotBeingMigrated(int slot) { + slotsBeingMigrated.add(slot); + } + + public HostAndPort getNode() { + return node; + } + + public List getAvailableSlots() { + return availableSlots; + } + + public List getSlotsBeingImported() { + return slotsBeingImported; + } + + public List getSlotsBeingMigrated() { + return slotsBeingMigrated; + } +} diff --git a/src/main/java/redis/clients/util/ClusterNodeInformationParser.java b/src/main/java/redis/clients/util/ClusterNodeInformationParser.java new file mode 100644 index 0000000..3c10c95 --- /dev/null +++ b/src/main/java/redis/clients/util/ClusterNodeInformationParser.java @@ -0,0 +1,80 @@ +package redis.clients.util; + +import redis.clients.jedis.HostAndPort; + +public class ClusterNodeInformationParser { + private static final String SLOT_IMPORT_IDENTIFIER = "-<-"; + private static final String SLOT_IN_TRANSITION_IDENTIFIER = "["; + public static final int SLOT_INFORMATIONS_START_INDEX = 8; + public static final int HOST_AND_PORT_INDEX = 1; + + public ClusterNodeInformation parse(String nodeInfo, HostAndPort current) { + String[] nodeInfoPartArray = nodeInfo.split(" "); + + HostAndPort node = getHostAndPortFromNodeLine(nodeInfoPartArray, + current); + ClusterNodeInformation info = new ClusterNodeInformation(node); + + if (nodeInfoPartArray.length >= SLOT_INFORMATIONS_START_INDEX) { + String[] slotInfoPartArray = extractSlotParts(nodeInfoPartArray); + fillSlotInformation(slotInfoPartArray, info); + } + + return info; + } + + private String[] extractSlotParts(String[] nodeInfoPartArray) { + String[] slotInfoPartArray = new String[nodeInfoPartArray.length + - SLOT_INFORMATIONS_START_INDEX]; + for (int i = SLOT_INFORMATIONS_START_INDEX; i < nodeInfoPartArray.length; i++) { + slotInfoPartArray[i - SLOT_INFORMATIONS_START_INDEX] = nodeInfoPartArray[i]; + } + return slotInfoPartArray; + } + + public HostAndPort getHostAndPortFromNodeLine(String[] nodeInfoPartArray, + HostAndPort current) { + String stringHostAndPort = nodeInfoPartArray[HOST_AND_PORT_INDEX]; + + String[] arrayHostAndPort = stringHostAndPort.split(":"); + return new HostAndPort( + arrayHostAndPort[0].isEmpty() ? current.getHost() + : arrayHostAndPort[0], + arrayHostAndPort[1].isEmpty() ? current.getPort() : Integer + .valueOf(arrayHostAndPort[1])); + } + + private void fillSlotInformation(String[] slotInfoPartArray, + ClusterNodeInformation info) { + for (String slotRange : slotInfoPartArray) { + fillSlotInformationFromSlotRange(slotRange, info); + } + } + + private void fillSlotInformationFromSlotRange(String slotRange, + ClusterNodeInformation info) { + if (slotRange.startsWith(SLOT_IN_TRANSITION_IDENTIFIER)) { + // slot is in transition + int slot = Integer.parseInt(slotRange.substring(1).split("-")[0]); + + if (slotRange.contains(SLOT_IMPORT_IDENTIFIER)) { + // import + info.addSlotBeingImported(slot); + } else { + // migrate (->-) + info.addSlotBeingMigrated(slot); + } + } else if (slotRange.contains("-")) { + // slot range + String[] slotRangePart = slotRange.split("-"); + for (int slot = Integer.valueOf(slotRangePart[0]); slot <= Integer + .valueOf(slotRangePart[1]); slot++) { + info.addAvailableSlot(slot); + } + } else { + // single slot + info.addAvailableSlot(Integer.valueOf(slotRange)); + } + } + +} diff --git a/src/main/java/redis/clients/util/RedisOutputStream.java b/src/main/java/redis/clients/util/RedisOutputStream.java index 4dba859..dcec7fa 100644 --- a/src/main/java/redis/clients/util/RedisOutputStream.java +++ b/src/main/java/redis/clients/util/RedisOutputStream.java @@ -34,10 +34,10 @@ public final class RedisOutputStream extends FilterOutputStream { } public void write(final byte b) throws IOException { - buf[count++] = b; if (count == buf.length) { flushBuffer(); } + buf[count++] = b; } public void write(final byte[] b) throws IOException { @@ -63,10 +63,10 @@ public final class RedisOutputStream extends FilterOutputStream { final int size = in.length(); for (int i = 0; i != size; ++i) { - buf[count++] = (byte) in.charAt(i); if (count == buf.length) { flushBuffer(); } + buf[count++] = (byte) in.charAt(i); } writeCrLf(); @@ -111,19 +111,19 @@ public final class RedisOutputStream extends FilterOutputStream { char c = str.charAt(i); if (!(c < 0x80)) break; - buf[count++] = (byte) c; if (count == buf.length) { flushBuffer(); } + buf[count++] = (byte) c; } for (; i < strLen; i++) { char c = str.charAt(i); if (c < 0x80) { - buf[count++] = (byte) c; if (count == buf.length) { flushBuffer(); } + buf[count++] = (byte) c; } else if (c < 0x800) { if (2 >= buf.length - count) { flushBuffer(); diff --git a/src/test/java/redis/clients/jedis/tests/HostAndPortUtil.java b/src/test/java/redis/clients/jedis/tests/HostAndPortUtil.java index cb7a58b..b2c9cf0 100644 --- a/src/test/java/redis/clients/jedis/tests/HostAndPortUtil.java +++ b/src/test/java/redis/clients/jedis/tests/HostAndPortUtil.java @@ -27,6 +27,10 @@ public class HostAndPortUtil { clusterHostAndPortList.add(new HostAndPort("localhost", 7379)); clusterHostAndPortList.add(new HostAndPort("localhost", 7380)); clusterHostAndPortList.add(new HostAndPort("localhost", 7381)); + clusterHostAndPortList.add(new HostAndPort("localhost", 7382)); + clusterHostAndPortList.add(new HostAndPort("localhost", 7383)); + clusterHostAndPortList.add(new HostAndPort("localhost", 7384)); + clusterHostAndPortList.add(new HostAndPort("localhost", 7385)); String envRedisHosts = System.getProperty("redis-hosts"); String envSentinelHosts = System.getProperty("sentinel-hosts"); diff --git a/src/test/java/redis/clients/jedis/tests/JedisClusterNodeInformationParserTest.java b/src/test/java/redis/clients/jedis/tests/JedisClusterNodeInformationParserTest.java new file mode 100644 index 0000000..14b830f --- /dev/null +++ b/src/test/java/redis/clients/jedis/tests/JedisClusterNodeInformationParserTest.java @@ -0,0 +1,63 @@ +package redis.clients.jedis.tests; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import redis.clients.jedis.HostAndPort; +import redis.clients.util.ClusterNodeInformation; +import redis.clients.util.ClusterNodeInformationParser; + +public class JedisClusterNodeInformationParserTest extends Assert { + private ClusterNodeInformationParser parser; + + @Before + public void setUp() { + parser = new ClusterNodeInformationParser(); + } + + @Test + public void testParseNodeMyself() { + String nodeInfo = "9b0d2ab38ee31482c95fdb2c7847a0d40e88d518 :7379 myself,master - 0 0 1 connected 0-5460"; + HostAndPort current = new HostAndPort("localhost", 7379); + ClusterNodeInformation clusterNodeInfo = parser + .parse(nodeInfo, current); + assertEquals(clusterNodeInfo.getNode(), current); + } + + @Test + public void testParseNormalState() { + String nodeInfo = "5f4a2236d00008fba7ac0dd24b95762b446767bd 192.168.0.3:7380 master - 0 1400598804016 2 connected 5461-10922"; + HostAndPort current = new HostAndPort("localhost", 7379); + ClusterNodeInformation clusterNodeInfo = parser + .parse(nodeInfo, current); + assertNotEquals(clusterNodeInfo.getNode(), current); + assertEquals(clusterNodeInfo.getNode(), new HostAndPort("192.168.0.3", + 7380)); + + for (int slot = 5461; slot <= 10922; slot++) { + assertTrue(clusterNodeInfo.getAvailableSlots().contains(slot)); + } + + assertTrue(clusterNodeInfo.getSlotsBeingImported().isEmpty()); + assertTrue(clusterNodeInfo.getSlotsBeingMigrated().isEmpty()); + } + + @Test + public void testParseSlotBeingMigrated() { + String nodeInfo = "5f4a2236d00008fba7ac0dd24b95762b446767bd :7379 myself,master - 0 0 1 connected 0-5459 [5460->-5f4a2236d00008fba7ac0dd24b95762b446767bd] [5461-<-5f4a2236d00008fba7ac0dd24b95762b446767bd]"; + HostAndPort current = new HostAndPort("localhost", 7379); + ClusterNodeInformation clusterNodeInfo = parser + .parse(nodeInfo, current); + assertEquals(clusterNodeInfo.getNode(), current); + + for (int slot = 0; slot <= 5459; slot++) { + assertTrue(clusterNodeInfo.getAvailableSlots().contains(slot)); + } + + assertEquals(1, clusterNodeInfo.getSlotsBeingMigrated().size()); + assertTrue(clusterNodeInfo.getSlotsBeingMigrated().contains(5460)); + assertEquals(1, clusterNodeInfo.getSlotsBeingImported().size()); + assertTrue(clusterNodeInfo.getSlotsBeingImported().contains(5461)); + } + +} diff --git a/src/test/java/redis/clients/jedis/tests/JedisClusterReplicateTest.java b/src/test/java/redis/clients/jedis/tests/JedisClusterReplicateTest.java new file mode 100644 index 0000000..39d9ff1 --- /dev/null +++ b/src/test/java/redis/clients/jedis/tests/JedisClusterReplicateTest.java @@ -0,0 +1,167 @@ +package redis.clients.jedis.tests; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import redis.clients.jedis.HostAndPort; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisCluster; +import redis.clients.jedis.exceptions.JedisDataException; +import redis.clients.jedis.exceptions.JedisException; +import redis.clients.jedis.tests.utils.JedisClusterTestUtil; + +public class JedisClusterReplicateTest { + private static Jedis node5; + private static Jedis node6; + + private HostAndPort nodeInfo5 = HostAndPortUtil.getClusterServers().get(4); + private HostAndPort nodeInfo6 = HostAndPortUtil.getClusterServers().get(5); + + private static int TIMEOUT = 15000; // cluster-node-timeout * 3 + + @Before + public void setUp() throws InterruptedException { + node5 = new Jedis(nodeInfo5.getHost(), nodeInfo5.getPort(), TIMEOUT); + node5.connect(); + node5.flushAll(); + + node6 = new Jedis(nodeInfo6.getHost(), nodeInfo6.getPort(), TIMEOUT); + node6.connect(); + // cannot flushall - it will be slave + + // ---- configure cluster + + // add nodes to cluster + node5.clusterMeet("127.0.0.1", nodeInfo6.getPort()); + + JedisClusterTestUtil.assertNodeIsKnown(node5, JedisClusterTestUtil.getNodeId(node6.clusterNodes()), 1000); + JedisClusterTestUtil.assertNodeIsKnown(node6, JedisClusterTestUtil.getNodeId(node5.clusterNodes()), 1000); + + // split available slots across the three nodes + int[] node5Slots = new int[JedisCluster.HASHSLOTS]; + for (int i = 0 ; i < JedisCluster.HASHSLOTS; i++) { + node5Slots[i] = i; + } + + node5.clusterAddSlots(node5Slots); + + JedisClusterTestUtil.waitForClusterReady(node5); + + // replicate full 1on1 + node6.clusterReplicate(JedisClusterTestUtil.getNodeId(node5 + .clusterNodes())); + + Map replMap = new HashMap(); + replMap.put(node5, node6); + + waitForReplicateReady(replMap, TIMEOUT); + JedisClusterTestUtil.waitForClusterReady(node5, node6); + } + + private void waitForReplicateReady(Map replMap, int timeoutMs) { + int interval = 100; + + for (int timeout = 0; timeout <= timeoutMs; timeout += interval) { + for (Entry entry : replMap.entrySet()) { + Jedis master = entry.getKey(); + Jedis slave = entry.getValue(); + + String masterNodeId = JedisClusterTestUtil.getNodeId(master + .clusterNodes()); + String slaveNodeId = JedisClusterTestUtil.getNodeId(slave + .clusterNodes()); + + try { + List slaves = master.clusterSlaves(masterNodeId); + + if (slaves.size() > 0 && slaves.get(0).contains(slaveNodeId)) { + return; + } + } catch (JedisDataException e) { + if (!e.getMessage().startsWith("ERR The specified node is not a master")) + throw e; + + // retry... + } + } + + try { + Thread.sleep(interval); + } catch (InterruptedException e) { + } + } + + throw new JedisException("there seems to replication error"); + } + + @After + public void tearDown() throws InterruptedException { + // clear all slots + int[] slotsToDelete = new int[JedisCluster.HASHSLOTS]; + for (int i = 0; i < JedisCluster.HASHSLOTS; i++) { + slotsToDelete[i] = i; + } + + node5.clusterDelSlots(slotsToDelete); + } + + @Test + public void testClusterReplicate() { + // we're already replicate 1on1 + List slaveInfos = node5.clusterSlaves(JedisClusterTestUtil + .getNodeId(node5.clusterNodes())); + assertEquals(1, slaveInfos.size()); + assertTrue(slaveInfos.get(0).contains( + JedisClusterTestUtil.getNodeId(node6.clusterNodes()))); + } + + @Test + public void testClusterFailover() throws InterruptedException { + Set jedisClusterNode = new HashSet(); + jedisClusterNode.add(new HostAndPort(nodeInfo5.getHost(), nodeInfo5.getPort())); + JedisCluster jc = new JedisCluster(jedisClusterNode); + + jc.set("51", "foo"); + // node5 is responsible of taking care of slot for key "51" (7186) + + node6.clusterFailover(); + + try { + // wait for failover + Map replMap = new HashMap(); + replMap.put(node6, node5); + waitForReplicateReady(replMap, TIMEOUT); + JedisClusterTestUtil.waitForClusterReady(node5, node6); + + List slaveInfos = node6.clusterSlaves(JedisClusterTestUtil + .getNodeId(node6.clusterNodes())); + assertEquals(1, slaveInfos.size()); + assertTrue(slaveInfos.get(0).contains( + JedisClusterTestUtil.getNodeId(node5.clusterNodes()))); + } finally { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + } + + // rollback + node5.clusterFailover(); + + Map replMap = new HashMap(); + replMap.put(node5, node6); + waitForReplicateReady(replMap, TIMEOUT); + JedisClusterTestUtil.waitForClusterReady(node5, node6); + } + } +} diff --git a/src/test/java/redis/clients/jedis/tests/JedisClusterTest.java b/src/test/java/redis/clients/jedis/tests/JedisClusterTest.java index c138e8d..6dfac6d 100644 --- a/src/test/java/redis/clients/jedis/tests/JedisClusterTest.java +++ b/src/test/java/redis/clients/jedis/tests/JedisClusterTest.java @@ -1,6 +1,9 @@ package redis.clients.jedis.tests; +import java.util.ArrayList; import java.util.HashSet; +import java.util.Iterator; +import java.util.List; import java.util.Set; import org.junit.After; @@ -12,20 +15,26 @@ import org.junit.Test; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisCluster; +import redis.clients.jedis.JedisPool; import redis.clients.jedis.exceptions.JedisAskDataException; import redis.clients.jedis.exceptions.JedisClusterException; import redis.clients.jedis.exceptions.JedisClusterMaxRedirectionsException; +import redis.clients.jedis.exceptions.JedisConnectionException; +import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.exceptions.JedisMovedDataException; +import redis.clients.jedis.tests.utils.JedisClusterTestUtil; import redis.clients.util.JedisClusterCRC16; public class JedisClusterTest extends Assert { - private Jedis node1; + private static Jedis node1; private static Jedis node2; private static Jedis node3; + private static Jedis node4; private HostAndPort nodeInfo1 = HostAndPortUtil.getClusterServers().get(0); private HostAndPort nodeInfo2 = HostAndPortUtil.getClusterServers().get(1); private HostAndPort nodeInfo3 = HostAndPortUtil.getClusterServers().get(2); + private HostAndPort nodeInfo4 = HostAndPortUtil.getClusterServers().get(3); @Before public void setUp() throws InterruptedException { @@ -40,6 +49,10 @@ public class JedisClusterTest extends Assert { node3 = new Jedis(nodeInfo3.getHost(), nodeInfo3.getPort()); node3.connect(); node3.flushAll(); + + node4 = new Jedis(nodeInfo4.getHost(), nodeInfo4.getPort()); + node4.connect(); + node4.flushAll(); // ---- configure cluster @@ -66,29 +79,53 @@ public class JedisClusterTest extends Assert { node2.clusterAddSlots(node2Slots); node3.clusterAddSlots(node3Slots); - waitForClusterReady(); + JedisClusterTestUtil.waitForClusterReady(node1, node2, node3); } @AfterClass public static void cleanUp() { int slotTest = JedisClusterCRC16.getSlot("test"); int slot51 = JedisClusterCRC16.getSlot("51"); - String node3Id = getNodeId(node3.clusterNodes()); + + String node1Id = JedisClusterTestUtil.getNodeId(node1.clusterNodes()); + String node2Id = JedisClusterTestUtil.getNodeId(node2.clusterNodes()); + String node3Id = JedisClusterTestUtil.getNodeId(node3.clusterNodes()); node2.clusterSetSlotNode(slotTest, node3Id); node2.clusterSetSlotNode(slot51, node3Id); node2.clusterDelSlots(slotTest, slot51); + + // forget about all nodes + node1.clusterForget(node2Id); + node1.clusterForget(node3Id); + node2.clusterForget(node1Id); + node2.clusterForget(node3Id); + node3.clusterForget(node1Id); + node3.clusterForget(node2Id); } @After - public void tearDown() { + public void tearDown() throws InterruptedException { // clear all slots int[] slotsToDelete = new int[JedisCluster.HASHSLOTS]; for (int i = 0; i < JedisCluster.HASHSLOTS; i++) { slotsToDelete[i] = i; } + node1.clusterDelSlots(slotsToDelete); node2.clusterDelSlots(slotsToDelete); node3.clusterDelSlots(slotsToDelete); + + clearAnyInconsistentMigration(node1); + clearAnyInconsistentMigration(node2); + clearAnyInconsistentMigration(node3); + } + + private void clearAnyInconsistentMigration(Jedis node) { + // FIXME: it's too slow... apply pipeline if possible + List slots = getInconsistentSlots(node.clusterNodes()); + for (Integer slot : slots) { + node.clusterSetSlotStable(slot); + } } @Test(expected = JedisMovedDataException.class) @@ -112,7 +149,7 @@ public class JedisClusterTest extends Assert { @Test(expected = JedisAskDataException.class) public void testThrowAskException() { int keySlot = JedisClusterCRC16.getSlot("test"); - String node3Id = getNodeId(node3.clusterNodes()); + String node3Id = JedisClusterTestUtil.getNodeId(node3.clusterNodes()); node2.clusterSetSlotMigrating(keySlot, node3Id); node2.get("test"); } @@ -122,7 +159,7 @@ public class JedisClusterTest extends Assert { Set jedisClusterNode = new HashSet(); jedisClusterNode.add(new HostAndPort("127.0.0.1", 7379)); JedisCluster jc = new JedisCluster(jedisClusterNode); - assertEquals(jc.getClusterNodes().size(), 3); + assertEquals(3, jc.getClusterNodes().size()); } @Test @@ -146,7 +183,7 @@ public class JedisClusterTest extends Assert { node3.clusterDelSlots(slot51); node3.clusterAddSlots(slot51); - waitForClusterReady(); + JedisClusterTestUtil.waitForClusterReady(node1, node2, node3); jc.set("51", "foo"); assertEquals("foo", jc.get("51")); } @@ -157,8 +194,8 @@ public class JedisClusterTest extends Assert { jedisClusterNode.add(new HostAndPort("127.0.0.1", 7379)); JedisCluster jc = new JedisCluster(jedisClusterNode); int slot51 = JedisClusterCRC16.getSlot("51"); - node3.clusterSetSlotImporting(slot51, getNodeId(node2.clusterNodes())); - node2.clusterSetSlotMigrating(slot51, getNodeId(node3.clusterNodes())); + node3.clusterSetSlotImporting(slot51, JedisClusterTestUtil.getNodeId(node2.clusterNodes())); + node2.clusterSetSlotMigrating(slot51, JedisClusterTestUtil.getNodeId(node3.clusterNodes())); jc.set("51", "foo"); assertEquals("foo", jc.get("51")); } @@ -178,7 +215,7 @@ public class JedisClusterTest extends Assert { JedisCluster jc = new JedisCluster(jedisClusterNode); int slot51 = JedisClusterCRC16.getSlot("51"); // This will cause an infinite redirection loop - node2.clusterSetSlotMigrating(slot51, getNodeId(node3.clusterNodes())); + node2.clusterSetSlotMigrating(slot51, JedisClusterTestUtil.getNodeId(node3.clusterNodes())); jc.set("51", "foo"); } @@ -190,25 +227,208 @@ public class JedisClusterTest extends Assert { assertEquals(JedisClusterCRC16.getSlot("foo{bar}{zap}"), JedisClusterCRC16.getSlot("bar")); } - private static String getNodeId(String infoOutput) { - for (String infoLine : infoOutput.split("\n")) { - if (infoLine.contains("myself")) { - return infoLine.split(" ")[0]; - } - } - return ""; + @Test + public void testClusterForgetNode() throws InterruptedException { + // at first, join node4 to cluster + node1.clusterMeet("127.0.0.1", nodeInfo4.getPort()); + + String node7Id = JedisClusterTestUtil.getNodeId(node4.clusterNodes()); + + JedisClusterTestUtil.assertNodeIsKnown(node3, node7Id, 1000); + JedisClusterTestUtil.assertNodeIsKnown(node2, node7Id, 1000); + JedisClusterTestUtil.assertNodeIsKnown(node1, node7Id, 1000); + + assertNodeHandshakeEnded(node3, 1000); + assertNodeHandshakeEnded(node2, 1000); + assertNodeHandshakeEnded(node1, 1000); + + assertEquals(4, node1.clusterNodes().split("\n").length); + assertEquals(4, node2.clusterNodes().split("\n").length); + assertEquals(4, node3.clusterNodes().split("\n").length); + + // do cluster forget + node1.clusterForget(node7Id); + node2.clusterForget(node7Id); + node3.clusterForget(node7Id); + + JedisClusterTestUtil.assertNodeIsUnknown(node1, node7Id, 1000); + JedisClusterTestUtil.assertNodeIsUnknown(node2, node7Id, 1000); + JedisClusterTestUtil.assertNodeIsUnknown(node3, node7Id, 1000); + + assertEquals(3, node1.clusterNodes().split("\n").length); + assertEquals(3, node2.clusterNodes().split("\n").length); + assertEquals(3, node3.clusterNodes().split("\n").length); } - - private void waitForClusterReady() throws InterruptedException { - boolean clusterOk = false; - while (!clusterOk) { - if (node1.clusterInfo().split("\n")[0].contains("ok") - && node2.clusterInfo().split("\n")[0].contains("ok") - && node3.clusterInfo().split("\n")[0].contains("ok")) { - clusterOk = true; + + @Test + public void testClusterFlushSlots() { + String slotRange = getNodeServingSlotRange(node1.clusterNodes()); + assertNotNull(slotRange); + + try { + node1.clusterFlushSlots(); + assertNull(getNodeServingSlotRange(node1.clusterNodes())); + } finally { + // rollback + String[] rangeInfo = slotRange.split("-"); + int lower = Integer.parseInt(rangeInfo[0]); + int upper = Integer.parseInt(rangeInfo[1]); + + int[] node1Slots = new int[upper - lower + 1]; + for (int i = 0 ; lower <= upper ; ) { + node1Slots[i++] = lower++; } - Thread.sleep(50); + node1.clusterAddSlots(node1Slots); } } + @Test + public void testClusterKeySlot() { + // It assumes JedisClusterCRC16 is correctly implemented + assertEquals(node1.clusterKeySlot("foo{bar}zap}").intValue(), JedisClusterCRC16.getSlot("foo{bar}zap")); + assertEquals(node1.clusterKeySlot("{user1000}.following").intValue(), JedisClusterCRC16.getSlot("{user1000}.following")); + } + + @Test + public void testClusterCountKeysInSlot() { + Set jedisClusterNode = new HashSet(); + jedisClusterNode.add(new HostAndPort(nodeInfo1.getHost(), nodeInfo1.getPort())); + JedisCluster jc = new JedisCluster(jedisClusterNode); + + for (int index = 0 ; index < 5 ; index++) { + jc.set("foo{bar}" + index, "hello"); + } + + int slot = JedisClusterCRC16.getSlot("foo{bar}"); + assertEquals(5, node1.clusterCountKeysInSlot(slot).intValue()); + } + + @Test + public void testStableSlotWhenMigratingNodeOrImportingNodeIsNotSpecified() throws InterruptedException { + Set jedisClusterNode = new HashSet(); + jedisClusterNode.add(new HostAndPort(nodeInfo1.getHost(), nodeInfo1.getPort())); + JedisCluster jc = new JedisCluster(jedisClusterNode); + + int slot51 = JedisClusterCRC16.getSlot("51"); + jc.set("51", "foo"); + // node2 is responsible of taking care of slot51 (7186) + + node3.clusterSetSlotImporting(slot51, JedisClusterTestUtil.getNodeId(node2.clusterNodes())); + assertEquals("foo", jc.get("51")); + node3.clusterSetSlotStable(slot51); + assertEquals("foo", jc.get("51")); + + node2.clusterSetSlotMigrating(slot51, JedisClusterTestUtil.getNodeId(node3.clusterNodes())); + //assertEquals("foo", jc.get("51")); // it leads Max Redirections + node2.clusterSetSlotStable(slot51); + assertEquals("foo", jc.get("51")); + } + + @Test + public void testCloseable() { + Set jedisClusterNode = new HashSet(); + jedisClusterNode.add(new HostAndPort(nodeInfo1.getHost(), nodeInfo1.getPort())); + + JedisCluster jc = null; + try { + jc = new JedisCluster(jedisClusterNode); + jc.set("51", "foo"); + } finally { + if (jc != null) { + jc.close(); + } + } + + Iterator poolIterator = jc.getClusterNodes().values().iterator(); + while (poolIterator.hasNext()) { + JedisPool pool = poolIterator.next(); + try { + pool.getResource(); + fail("JedisCluster's internal pools should be already destroyed"); + } catch (JedisConnectionException e) { + // ok to go... + } + } + } + + private static String getNodeServingSlotRange(String infoOutput) { + // f4f3dc4befda352a4e0beccf29f5e8828438705d 127.0.0.1:7380 master - 0 1394372400827 0 connected 5461-10922 + for (String infoLine : infoOutput.split("\n")) { + if (infoLine.contains("myself")) { + try { + return infoLine.split(" ")[8]; + } catch (ArrayIndexOutOfBoundsException e) { + return null; + } + } + } + return null; + } + + private List getInconsistentSlots(String infoOuput) { + for (String infoLine : infoOuput.split("\n")) { + if (infoLine.contains("myself")) { + return getSlotsBeingMigrated(infoLine); + } + } + + return null; + } + + private List getSlotsBeingMigrated(String infoLine) { + List inconsistentSlots = new ArrayList(); + + String[] splitted = infoLine.split(" "); + + if (splitted.length > 8) { + for (int index = 8 ; index < splitted.length ; index++) { + String info = splitted[index]; + Integer slot = getSlotFromMigrationInfo(info); + if (slot != null) { + inconsistentSlots.add(slot); + } + } + } + + return inconsistentSlots; + } + + private Integer getSlotFromMigrationInfo(String info) { + if (info.startsWith("[")) { + if (info.contains("-<-")) { + return Integer.parseInt(info.split("-<-")[0].substring(1)); + } else if (info.contains("->-")) { + return Integer.parseInt(info.split("->-")[0].substring(1)); + } + } + + return null; + } + + private void assertNodeHandshakeEnded(Jedis node, int timeoutMs) { + int sleepInterval = 100; + for (int sleepTime = 0 ; sleepTime <= timeoutMs ; sleepTime += sleepInterval) { + boolean isHandshaking = isAnyNodeHandshaking(node); + if (!isHandshaking) + return; + + try { + Thread.sleep(sleepInterval); + } catch (InterruptedException e) { + } + } + + throw new JedisException("Node handshaking is not ended"); + } + + private boolean isAnyNodeHandshaking(Jedis node) { + String infoOutput = node.clusterNodes(); + for (String infoLine : infoOutput.split("\n")) { + if (infoLine.contains("handshake")) { + return true; + } + } + return false; + } + } diff --git a/src/test/java/redis/clients/jedis/tests/JedisPoolTest.java b/src/test/java/redis/clients/jedis/tests/JedisPoolTest.java index 514d3b5..9904c1e 100644 --- a/src/test/java/redis/clients/jedis/tests/JedisPoolTest.java +++ b/src/test/java/redis/clients/jedis/tests/JedisPoolTest.java @@ -187,18 +187,48 @@ public class JedisPoolTest extends Assert { 2000, "foobared"); Jedis jedis = pool.getResource(); - jedis.set("hello", "jedis"); - Transaction t = jedis.multi(); - t.set("hello", "world"); - pool.returnResource(jedis); + try { + jedis.set("hello", "jedis"); + Transaction t = jedis.multi(); + t.set("hello", "world"); + } finally { + jedis.close(); + } Jedis jedis2 = pool.getResource(); - assertTrue(jedis == jedis2); - assertEquals("jedis", jedis2.get("hello")); - pool.returnResource(jedis2); + try { + assertTrue(jedis == jedis2); + assertEquals("jedis", jedis2.get("hello")); + } finally { + jedis2.close(); + } + pool.destroy(); } - + + @Test + public void checkResourceIsCloseable() { + GenericObjectPoolConfig config = new GenericObjectPoolConfig(); + config.setMaxTotal(1); + config.setBlockWhenExhausted(false); + JedisPool pool = new JedisPool(config, hnp.getHost(), hnp.getPort(), + 2000, "foobared"); + + Jedis jedis = pool.getResource(); + try { + jedis.set("hello", "jedis"); + } finally { + jedis.close(); + } + + Jedis jedis2 = pool.getResource(); + try { + assertEquals(jedis, jedis2); + } finally { + jedis2.close(); + } + } + @Test public void returnNullObjectShouldNotFail() { JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), @@ -208,4 +238,40 @@ public class JedisPoolTest extends Assert { pool.returnResource(null); pool.returnResourceObject(null); } + + @Test + public void getNumActiveIsNegativeWhenPoolIsClosed() { + JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), + hnp.getPort(), 2000, "foobared", 0, "my_shiny_client_name"); + + pool.destroy(); + assertTrue(pool.getNumActive() < 0); + } + + @Test + public void getNumActiveReturnsTheCorrectNumber() { + JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), + hnp.getPort(), 2000); + Jedis jedis = pool.getResource(); + jedis.auth("foobared"); + jedis.set("foo", "bar"); + assertEquals("bar", jedis.get("foo")); + + assertEquals(1, pool.getNumActive()); + + Jedis jedis2 = pool.getResource(); + jedis.auth("foobared"); + jedis.set("foo", "bar"); + + assertEquals(2, pool.getNumActive()); + + pool.returnResource(jedis); + assertEquals(1, pool.getNumActive()); + + pool.returnResource(jedis2); + + assertEquals(0, pool.getNumActive()); + + pool.destroy(); + } } diff --git a/src/test/java/redis/clients/jedis/tests/JedisSentinelPoolTest.java b/src/test/java/redis/clients/jedis/tests/JedisSentinelPoolTest.java index 1dd2896..205d90a 100644 --- a/src/test/java/redis/clients/jedis/tests/JedisSentinelPoolTest.java +++ b/src/test/java/redis/clients/jedis/tests/JedisSentinelPoolTest.java @@ -79,6 +79,29 @@ public class JedisSentinelPoolTest extends JedisTestBase { pool.destroy(); } } + + @Test + public void checkResourceIsCloseable() { + GenericObjectPoolConfig config = new GenericObjectPoolConfig(); + config.setMaxTotal(1); + config.setBlockWhenExhausted(false); + JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, + config, 1000, "foobared", 2); + + Jedis jedis = pool.getResource(); + try { + jedis.set("hello", "jedis"); + } finally { + jedis.close(); + } + + Jedis jedis2 = pool.getResource(); + try { + assertEquals(jedis, jedis2); + } finally { + jedis2.close(); + } + } @Test public void returnResourceWithNullResource() { diff --git a/src/test/java/redis/clients/jedis/tests/PipeliningTest.java b/src/test/java/redis/clients/jedis/tests/PipeliningTest.java index d3cddd0..94ccb13 100755 --- a/src/test/java/redis/clients/jedis/tests/PipeliningTest.java +++ b/src/test/java/redis/clients/jedis/tests/PipeliningTest.java @@ -52,6 +52,9 @@ public class PipeliningTest extends Assert { jedis.hset("hash", "foo", "bar"); jedis.zadd("zset", 1, "foo"); jedis.sadd("set", "foo"); + jedis.setrange("setrange", 0, "0123456789"); + byte[] bytesForSetRange = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + jedis.setrange("setrangebytes".getBytes(), 0, bytesForSetRange); Pipeline p = jedis.pipelined(); Response string = p.get("string"); @@ -68,8 +71,10 @@ public class PipeliningTest extends Assert { p.sadd("set", "foo"); Response> smembers = p.smembers("set"); Response> zrangeWithScores = p.zrangeWithScores("zset", 0, - -1); - p.sync(); + -1); + Response getrange = p.getrange("setrange", 1, 3); + Response getrangeBytes = p.getrange("setrangebytes".getBytes(), 6, 8); + p.sync(); assertEquals("foo", string.get()); assertEquals("foo", list.get()); @@ -83,6 +88,9 @@ public class PipeliningTest extends Assert { assertNotNull(hgetAll.get().get("foo")); assertEquals(1, smembers.get().size()); assertEquals(1, zrangeWithScores.get().size()); + assertEquals("123", getrange.get()); + byte[] expectedGetRangeBytes = {6, 7, 8}; + assertArrayEquals(expectedGetRangeBytes, getrangeBytes.get()); } @Test @@ -272,6 +280,26 @@ public class PipeliningTest extends Assert { assertEquals("world", r3.get()); } + @Test(expected = JedisDataException.class) + public void pipelineExecShoudThrowJedisDataExceptionWhenNotInMulti() { + Pipeline pipeline = jedis.pipelined(); + pipeline.exec(); + } + + @Test(expected = JedisDataException.class) + public void pipelineDiscardShoudThrowJedisDataExceptionWhenNotInMulti() { + Pipeline pipeline = jedis.pipelined(); + pipeline.discard(); + } + + @Test(expected = JedisDataException.class) + public void pipelineMultiShoudThrowJedisDataExceptionWhenAlreadyInMulti() { + Pipeline pipeline = jedis.pipelined(); + pipeline.multi(); + pipeline.set("foo", "3"); + pipeline.multi(); + } + @Test public void testDiscardInPipeline() { Pipeline pipeline = jedis.pipelined(); diff --git a/src/test/java/redis/clients/jedis/tests/ProtocolTest.java b/src/test/java/redis/clients/jedis/tests/ProtocolTest.java index 504a66a..4245aae 100644 --- a/src/test/java/redis/clients/jedis/tests/ProtocolTest.java +++ b/src/test/java/redis/clients/jedis/tests/ProtocolTest.java @@ -4,6 +4,7 @@ import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.util.ArrayList; @@ -38,6 +39,28 @@ public class ProtocolTest extends JedisTestBase { assertEquals(expectedCommand, sb.toString()); } + + @Test(expected=IOException.class) + public void writeOverflow() throws IOException { + RedisOutputStream ros = new RedisOutputStream(new OutputStream() { + + @Override + public void write(int b) throws IOException { + throw new IOException("thrown exception"); + + } + }); + + ros.write(new byte[8191]); + + try { + ros.write((byte)'*'); + } catch (IOException ioe) {} + + + ros.write((byte)'*'); + + } @Test public void bulkReply() { diff --git a/src/test/java/redis/clients/jedis/tests/ShardedJedisPoolTest.java b/src/test/java/redis/clients/jedis/tests/ShardedJedisPoolTest.java index ee7951b..b1350e9 100644 --- a/src/test/java/redis/clients/jedis/tests/ShardedJedisPoolTest.java +++ b/src/test/java/redis/clients/jedis/tests/ShardedJedisPoolTest.java @@ -14,6 +14,7 @@ import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisShardInfo; import redis.clients.jedis.ShardedJedis; +import redis.clients.jedis.ShardedJedisPipeline; import redis.clients.jedis.ShardedJedisPool; import redis.clients.jedis.exceptions.JedisConnectionException; @@ -233,4 +234,69 @@ public class ShardedJedisPoolTest extends Assert { assertEquals("PONG", jedis.ping()); assertEquals("bar", jedis.get("foo")); } + + @Test + public void returnResourceShouldResetState() throws URISyntaxException { + GenericObjectPoolConfig config = new GenericObjectPoolConfig(); + config.setMaxTotal(1); + config.setBlockWhenExhausted(false); + + List shards = new ArrayList(); + shards.add(new JedisShardInfo(new URI( + "redis://:foobared@localhost:6380"))); + shards.add(new JedisShardInfo(new URI( + "redis://:foobared@localhost:6379"))); + + ShardedJedisPool pool = new ShardedJedisPool(config, shards); + + ShardedJedis jedis = pool.getResource(); + jedis.set("pipelined", String.valueOf(0)); + jedis.set("pipelined2", String.valueOf(0)); + + ShardedJedisPipeline pipeline = jedis.pipelined(); + + pipeline.incr("pipelined"); + pipeline.incr("pipelined2"); + + jedis.resetState(); + + pipeline = jedis.pipelined(); + pipeline.incr("pipelined"); + pipeline.incr("pipelined2"); + List results = pipeline.syncAndReturnAll(); + + assertEquals(2, results.size()); + pool.returnResource(jedis); + pool.destroy(); + } + + @Test + public void checkResourceIsCloseable() throws URISyntaxException { + GenericObjectPoolConfig config = new GenericObjectPoolConfig(); + config.setMaxTotal(1); + config.setBlockWhenExhausted(false); + + List shards = new ArrayList(); + shards.add(new JedisShardInfo(new URI( + "redis://:foobared@localhost:6380"))); + shards.add(new JedisShardInfo(new URI( + "redis://:foobared@localhost:6379"))); + + ShardedJedisPool pool = new ShardedJedisPool(config, shards); + + ShardedJedis jedis = pool.getResource(); + try { + jedis.set("hello", "jedis"); + } finally { + jedis.close(); + } + + ShardedJedis jedis2 = pool.getResource(); + try { + assertEquals(jedis, jedis2); + } finally { + jedis2.close(); + } + } + } diff --git a/src/test/java/redis/clients/jedis/tests/ShardedJedisTest.java b/src/test/java/redis/clients/jedis/tests/ShardedJedisTest.java index 5a71391..2413952 100644 --- a/src/test/java/redis/clients/jedis/tests/ShardedJedisTest.java +++ b/src/test/java/redis/clients/jedis/tests/ShardedJedisTest.java @@ -302,4 +302,25 @@ public class ShardedJedisTest extends Assert { assertEquals(jedisShardInfo.getName(), jedisShardInfo2.getName()); } } + + @Test + public void checkCloseable() { + List shards = new ArrayList(); + shards.add(new JedisShardInfo(redis1.getHost(), redis1.getPort())); + shards.add(new JedisShardInfo(redis2.getHost(), redis2.getPort())); + shards.get(0).setPassword("foobared"); + shards.get(1).setPassword("foobared"); + + ShardedJedis jedisShard = new ShardedJedis(shards); + try { + jedisShard.set("shard_closeable", "true"); + } finally { + jedisShard.close(); + } + + for (Jedis jedis : jedisShard.getAllShards()) { + assertTrue(!jedis.isConnected()); + } + } + } \ No newline at end of file diff --git a/src/test/java/redis/clients/jedis/tests/commands/AllKindOfValuesCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/AllKindOfValuesCommandsTest.java index 057537c..dfcf490 100644 --- a/src/test/java/redis/clients/jedis/tests/commands/AllKindOfValuesCommandsTest.java +++ b/src/test/java/redis/clients/jedis/tests/commands/AllKindOfValuesCommandsTest.java @@ -11,6 +11,7 @@ import redis.clients.jedis.ScanResult; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.util.SafeEncoder; import static redis.clients.jedis.ScanParams.SCAN_POINTER_START; +import static redis.clients.jedis.ScanParams.SCAN_POINTER_START_BINARY; public class AllKindOfValuesCommandsTest extends JedisCommandTestBase { final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; @@ -474,9 +475,16 @@ public class AllKindOfValuesCommandsTest extends JedisCommandTestBase { long status = jedis.pexpire("foo", 10000); assertEquals(0, status); - jedis.set("foo", "bar"); - status = jedis.pexpire("foo", 10000); + jedis.set("foo1", "bar1"); + status = jedis.pexpire("foo1", 10000); assertEquals(1, status); + + jedis.set("foo2", "bar2"); + status = jedis.pexpire("foo2", 200000000000L); + assertEquals(1, status); + + long pttl = jedis.pttl("foo2"); + assertTrue(pttl > 100000000000L); } @Test @@ -513,10 +521,16 @@ public class AllKindOfValuesCommandsTest extends JedisCommandTestBase { ScanResult result = jedis.scan(SCAN_POINTER_START); - assertEquals(SCAN_POINTER_START, result.getStringCursor()); + assertEquals(SCAN_POINTER_START, result.getCursor()); assertFalse(result.getResult().isEmpty()); + + // binary + ScanResult bResult = jedis.scan(SCAN_POINTER_START_BINARY); + + assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); + assertFalse(bResult.getResult().isEmpty()); } - + @Test public void scanMatch() { ScanParams params = new ScanParams(); @@ -527,8 +541,21 @@ public class AllKindOfValuesCommandsTest extends JedisCommandTestBase { jedis.set("aa", "aa"); ScanResult result = jedis.scan(SCAN_POINTER_START, params); - assertEquals(SCAN_POINTER_START, result.getStringCursor()); + assertEquals(SCAN_POINTER_START, result.getCursor()); assertFalse(result.getResult().isEmpty()); + + // binary + params = new ScanParams(); + params.match(bfoostar); + + jedis.set(bfoo1, bbar); + jedis.set(bfoo2, bbar); + jedis.set(bfoo3, bbar); + + ScanResult bResult = jedis.scan(SCAN_POINTER_START_BINARY, params); + + assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); + assertFalse(bResult.getResult().isEmpty()); } @Test @@ -543,5 +570,17 @@ public class AllKindOfValuesCommandsTest extends JedisCommandTestBase { ScanResult result = jedis.scan(SCAN_POINTER_START, params); assertFalse(result.getResult().isEmpty()); + + // binary + params = new ScanParams(); + params.count(2); + + jedis.set(bfoo1, bbar); + jedis.set(bfoo2, bbar); + jedis.set(bfoo3, bbar); + + ScanResult bResult = jedis.scan(SCAN_POINTER_START_BINARY, params); + + assertFalse(bResult.getResult().isEmpty()); } } diff --git a/src/test/java/redis/clients/jedis/tests/commands/BitCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/BitCommandsTest.java index fb14f21..b54ca95 100644 --- a/src/test/java/redis/clients/jedis/tests/commands/BitCommandsTest.java +++ b/src/test/java/redis/clients/jedis/tests/commands/BitCommandsTest.java @@ -3,6 +3,8 @@ package redis.clients.jedis.tests.commands; import org.junit.Test; import redis.clients.jedis.BitOP; +import redis.clients.jedis.BitPosParams; +import redis.clients.jedis.Protocol; public class BitCommandsTest extends JedisCommandTestBase { @Test @@ -20,6 +22,111 @@ public class BitCommandsTest extends JedisCommandTestBase { assertTrue(bbit); } + @Test + public void bitpos() { + String foo = "foo"; + + jedis.set(foo, String.valueOf(0)); + + jedis.setbit(foo, 3, true); + jedis.setbit(foo, 7, true); + jedis.setbit(foo, 13, true); + jedis.setbit(foo, 39, true); + + /* + * byte: 0 1 2 3 4 + * bit: 00010001 / 00000100 / 00000000 / 00000000 / 00000001 + */ + long offset = jedis.bitpos(foo, true); + assertEquals(2, offset); + offset = jedis.bitpos(foo, false); + assertEquals(0, offset); + + offset = jedis.bitpos(foo, true, new BitPosParams(1)); + assertEquals(13, offset); + offset = jedis.bitpos(foo, false, new BitPosParams(1)); + assertEquals(8, offset); + + offset = jedis.bitpos(foo, true, new BitPosParams(2, 3)); + assertEquals(-1, offset); + offset = jedis.bitpos(foo, false, new BitPosParams(2, 3)); + assertEquals(16, offset); + + offset = jedis.bitpos(foo, true, new BitPosParams(3, 4)); + assertEquals(39, offset); + } + + @Test + public void bitposBinary() { + // binary + byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; + + jedis.set(bfoo, Protocol.toByteArray(0)); + + jedis.setbit(bfoo, 3, true); + jedis.setbit(bfoo, 7, true); + jedis.setbit(bfoo, 13, true); + jedis.setbit(bfoo, 39, true); + + /* + * byte: 0 1 2 3 4 + * bit: 00010001 / 00000100 / 00000000 / 00000000 / 00000001 + */ + long offset = jedis.bitpos(bfoo, true); + assertEquals(2, offset); + offset = jedis.bitpos(bfoo, false); + assertEquals(0, offset); + + offset = jedis.bitpos(bfoo, true, new BitPosParams(1)); + assertEquals(13, offset); + offset = jedis.bitpos(bfoo, false, new BitPosParams(1)); + assertEquals(8, offset); + + offset = jedis.bitpos(bfoo, true, new BitPosParams(2, 3)); + assertEquals(-1, offset); + offset = jedis.bitpos(bfoo, false, new BitPosParams(2, 3)); + assertEquals(16, offset); + + offset = jedis.bitpos(bfoo, true, new BitPosParams(3, 4)); + assertEquals(39, offset); + } + + @Test + public void bitposWithNoMatchingBitExist() { + String foo = "foo"; + + jedis.set(foo, String.valueOf(0)); + for (int idx = 0; idx < 8; idx++) { + jedis.setbit(foo, idx, true); + } + + /* + * byte: 0 + * bit: 11111111 + */ + long offset = jedis.bitpos(foo, false); + // offset should be last index + 1 + assertEquals(8, offset); + } + + @Test + public void bitposWithNoMatchingBitExistWithinRange() { + String foo = "foo"; + + jedis.set(foo, String.valueOf(0)); + for (int idx = 0; idx < 8 * 5; idx++) { + jedis.setbit(foo, idx, true); + } + + /* + * byte: 0 1 2 3 4 + * bit: 11111111 / 11111111 / 11111111 / 11111111 / 11111111 + */ + long offset = jedis.bitpos(foo, false, new BitPosParams(2, 3)); + // offset should be -1 + assertEquals(-1, offset); + } + @Test public void setAndgetrange() { jedis.set("key1", "Hello World"); diff --git a/src/test/java/redis/clients/jedis/tests/commands/ClusterCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/ClusterCommandsTest.java index 03ebc89..f4b532f 100644 --- a/src/test/java/redis/clients/jedis/tests/commands/ClusterCommandsTest.java +++ b/src/test/java/redis/clients/jedis/tests/commands/ClusterCommandsTest.java @@ -45,6 +45,8 @@ public class ClusterCommandsTest extends JedisTestBase { node1.clusterDelSlots(1, 2, 3, 4, 5, 500); node1.clusterSetSlotNode(5000, node1Id); node1.clusterDelSlots(5000, 10000); + node1.clusterDelSlots(3000, 3001, 3002); + node2.clusterDelSlots(4000, 4001, 4002); node1.clusterAddSlots(6000); node1.clusterDelSlots(6000); waitForGossip(); @@ -134,5 +136,34 @@ public class ClusterCommandsTest extends JedisTestBase { String status = node1.clusterSetSlotMigrating(5000, nodeId); assertEquals("OK", status); } + + @Test + public void clusterSlots() { + // please see cluster slot output format from below commit + // @see: + // https://github.com/antirez/redis/commit/e14829de3025ffb0d3294e5e5a1553afd9f10b60 + String status = node1.clusterAddSlots(3000, 3001, 3002); + assertEquals("OK", status); + status = node2.clusterAddSlots(4000, 4001, 4002); + assertEquals("OK", status); + + List slots = node1.clusterSlots(); + assertNotNull(slots); + assertTrue(slots.size() > 0); + + for (Object slotInfoObj : slots) { + List slotInfo = (List) slotInfoObj; + assertNotNull(slots); + assertTrue(slots.size() >= 2); + + assertTrue(slotInfo.get(0) instanceof Long); + assertTrue(slotInfo.get(1) instanceof Long); + + if (slots.size() > 2) { + // assigned slots + assertTrue(slotInfo.get(2) instanceof List); + } + } + } } \ No newline at end of file diff --git a/src/test/java/redis/clients/jedis/tests/commands/HashesCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/HashesCommandsTest.java index 5f9ac4e..a2da021 100644 --- a/src/test/java/redis/clients/jedis/tests/commands/HashesCommandsTest.java +++ b/src/test/java/redis/clients/jedis/tests/commands/HashesCommandsTest.java @@ -9,16 +9,24 @@ import java.util.Map; import java.util.Set; import org.junit.Test; - +import redis.clients.jedis.Pipeline; +import redis.clients.jedis.Response; import redis.clients.jedis.ScanParams; import redis.clients.jedis.ScanResult; + import static redis.clients.jedis.ScanParams.SCAN_POINTER_START; +import static redis.clients.jedis.ScanParams.SCAN_POINTER_START_BINARY; public class HashesCommandsTest extends JedisCommandTestBase { final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; final byte[] bbar = { 0x05, 0x06, 0x07, 0x08 }; final byte[] bcar = { 0x09, 0x0A, 0x0B, 0x0C }; - + + final byte[] bbar1 = { 0x05, 0x06, 0x07, 0x08, 0x0A }; + final byte[] bbar2 = { 0x05, 0x06, 0x07, 0x08, 0x0B }; + final byte[] bbar3 = { 0x05, 0x06, 0x07, 0x08, 0x0C }; + final byte[] bbarstar = { 0x05, 0x06, 0x07, 0x08, '*' }; + @Test public void hset() { long status = jedis.hset("foo", "bar", "car"); @@ -147,6 +155,25 @@ public class HashesCommandsTest extends JedisCommandTestBase { } + @Test + public void hincrByFloat() { + Double value = jedis.hincrByFloat("foo", "bar", 1.5d); + assertEquals((Double) 1.5d, value); + value = jedis.hincrByFloat("foo", "bar", -1.5d); + assertEquals((Double) 0d, value); + value = jedis.hincrByFloat("foo", "bar", -10.7d); + assertEquals(Double.compare(-10.7d, value), 0); + + // Binary + double bvalue = jedis.hincrByFloat(bfoo, bbar, 1.5d); + assertEquals(Double.compare(1.5d, bvalue), 0); + bvalue = jedis.hincrByFloat(bfoo, bbar, -1.5d); + assertEquals(Double.compare(0d, bvalue), 0); + bvalue = jedis.hincrByFloat(bfoo, bbar, -10.7d); + assertEquals(Double.compare(-10.7d, value), 0); + + } + @Test public void hexists() { Map hash = new HashMap(); @@ -291,6 +318,22 @@ public class HashesCommandsTest extends JedisCommandTestBase { assertArrayEquals(bbar, bhash.get(bcar)); } + @Test + public void hgetAllPipeline() { + Map bh = new HashMap(); + bh.put(bbar, bcar); + bh.put(bcar, bbar); + jedis.hmset(bfoo, bh); + Pipeline pipeline = jedis.pipelined(); + Response> bhashResponse = pipeline.hgetAll(bfoo); + pipeline.sync(); + Map bhash = bhashResponse.get(); + + assertEquals(2, bhash.size()); + assertArrayEquals(bcar, bhash.get(bbar)); + assertArrayEquals(bbar, bhash.get(bcar)); + } + @Test public void hscan() { jedis.hset("foo", "b", "b"); @@ -298,8 +341,16 @@ public class HashesCommandsTest extends JedisCommandTestBase { ScanResult> result = jedis.hscan("foo", SCAN_POINTER_START); - assertEquals(SCAN_POINTER_START, result.getStringCursor()); + assertEquals(SCAN_POINTER_START, result.getCursor()); assertFalse(result.getResult().isEmpty()); + + // binary + jedis.hset(bfoo, bbar, bcar); + + ScanResult> bResult = jedis.hscan(bfoo, SCAN_POINTER_START_BINARY); + + assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); + assertFalse(bResult.getResult().isEmpty()); } @Test @@ -313,8 +364,22 @@ public class HashesCommandsTest extends JedisCommandTestBase { ScanResult> result = jedis.hscan("foo", SCAN_POINTER_START, params); - assertEquals(SCAN_POINTER_START, result.getStringCursor()); + assertEquals(SCAN_POINTER_START, result.getCursor()); assertFalse(result.getResult().isEmpty()); + + // binary + params = new ScanParams(); + params.match(bbarstar); + + jedis.hset(bfoo, bbar, bcar); + jedis.hset(bfoo, bbar1, bcar); + jedis.hset(bfoo, bbar2, bcar); + jedis.hset(bfoo, bbar3, bcar); + + ScanResult> bResult = jedis.hscan(bfoo, SCAN_POINTER_START_BINARY, params); + + assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); + assertFalse(bResult.getResult().isEmpty()); } @Test @@ -330,5 +395,18 @@ public class HashesCommandsTest extends JedisCommandTestBase { SCAN_POINTER_START, params); assertFalse(result.getResult().isEmpty()); + + // binary + params = new ScanParams(); + params.count(2); + + jedis.hset(bfoo, bbar, bcar); + jedis.hset(bfoo, bbar1, bcar); + jedis.hset(bfoo, bbar2, bcar); + jedis.hset(bfoo, bbar3, bcar); + + ScanResult> bResult = jedis.hscan(bfoo, SCAN_POINTER_START_BINARY, params); + + assertFalse(bResult.getResult().isEmpty()); } } diff --git a/src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java new file mode 100644 index 0000000..0b25366 --- /dev/null +++ b/src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java @@ -0,0 +1,130 @@ +package redis.clients.jedis.tests.commands; + +import org.junit.Test; + +import redis.clients.util.SafeEncoder; + +public class HyperLogLogCommandsTest extends JedisCommandTestBase { + + @Test + public void pfadd() { + long status = jedis.pfadd("foo", "a"); + assertEquals(1, status); + + status = jedis.pfadd("foo", "a"); + assertEquals(0, status); + } + + @Test + public void pfaddBinary() { + byte[] bFoo = SafeEncoder.encode("foo"); + byte[] bBar = SafeEncoder.encode("bar"); + byte[] bBar2 = SafeEncoder.encode("bar2"); + + long status = jedis.pfadd(bFoo, bBar, bBar2); + assertEquals(1, status); + + status = jedis.pfadd(bFoo, bBar, bBar2); + assertEquals(0, status); + } + + @Test + public void pfcount() { + long status = jedis.pfadd("hll", "foo", "bar", "zap"); + assertEquals(1, status); + + status = jedis.pfadd("hll", "zap", "zap", "zap"); + assertEquals(0, status); + + status = jedis.pfadd("hll", "foo", "bar"); + assertEquals(0, status); + + status = jedis.pfcount("hll"); + assertEquals(3, status); + } + + @Test + public void pfcounts() { + long status = jedis.pfadd("hll_1", "foo", "bar", "zap"); + assertEquals(1, status); + status = jedis.pfadd("hll_2", "foo", "bar", "zap"); + assertEquals(1, status); + + status = jedis.pfadd("hll_3", "foo", "bar", "baz"); + assertEquals(1, status); + status = jedis.pfcount("hll_1"); + assertEquals(3, status); + status = jedis.pfcount("hll_2"); + assertEquals(3, status); + status = jedis.pfcount("hll_3"); + assertEquals(3, status); + + status = jedis.pfcount("hll_1", "hll_2"); + assertEquals(3, status); + + status = jedis.pfcount("hll_1", "hll_2", "hll_3"); + assertEquals(4, status); + + + } + + @Test + public void pfcountBinary() { + byte[] bHll = SafeEncoder.encode("hll"); + byte[] bFoo = SafeEncoder.encode("foo"); + byte[] bBar = SafeEncoder.encode("bar"); + byte[] bZap = SafeEncoder.encode("zap"); + + long status = jedis.pfadd(bHll, bFoo, bBar, bZap); + assertEquals(1, status); + + status = jedis.pfadd(bHll, bZap, bZap, bZap); + assertEquals(0, status); + + status = jedis.pfadd(bHll, bFoo, bBar); + assertEquals(0, status); + + status = jedis.pfcount(bHll); + assertEquals(3, status); + } + + @Test + public void pfmerge() { + long status = jedis.pfadd("hll1", "foo", "bar", "zap", "a"); + assertEquals(1, status); + + status = jedis.pfadd("hll2", "a", "b", "c", "foo"); + assertEquals(1, status); + + String mergeStatus = jedis.pfmerge("hll3", "hll1", "hll2"); + assertEquals("OK", mergeStatus); + + status = jedis.pfcount("hll3"); + assertEquals(6, status); + } + + @Test + public void pfmergeBinary() { + byte[] bHll1 = SafeEncoder.encode("hll1"); + byte[] bHll2 = SafeEncoder.encode("hll2"); + byte[] bHll3 = SafeEncoder.encode("hll3"); + byte[] bFoo = SafeEncoder.encode("foo"); + byte[] bBar = SafeEncoder.encode("bar"); + byte[] bZap = SafeEncoder.encode("zap"); + byte[] bA = SafeEncoder.encode("a"); + byte[] bB = SafeEncoder.encode("b"); + byte[] bC = SafeEncoder.encode("c"); + + long status = jedis.pfadd(bHll1, bFoo, bBar, bZap, bA); + assertEquals(1, status); + + status = jedis.pfadd(bHll2, bA, bB, bC, bFoo); + assertEquals(1, status); + + String mergeStatus = jedis.pfmerge(bHll3, bHll1, bHll2); + assertEquals("OK", mergeStatus); + + status = jedis.pfcount("hll3"); + assertEquals(6, status); + } +} diff --git a/src/test/java/redis/clients/jedis/tests/commands/SetCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/SetCommandsTest.java index 2149feb..c10572e 100644 --- a/src/test/java/redis/clients/jedis/tests/commands/SetCommandsTest.java +++ b/src/test/java/redis/clients/jedis/tests/commands/SetCommandsTest.java @@ -9,6 +9,7 @@ import org.junit.Test; import redis.clients.jedis.ScanParams; import redis.clients.jedis.ScanResult; import static redis.clients.jedis.ScanParams.SCAN_POINTER_START; +import static redis.clients.jedis.ScanParams.SCAN_POINTER_START_BINARY; public class SetCommandsTest extends JedisCommandTestBase { final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; @@ -19,6 +20,11 @@ public class SetCommandsTest extends JedisCommandTestBase { final byte[] bc = { 0x0C }; final byte[] bd = { 0x0D }; final byte[] bx = { 0x42 }; + + final byte[] bbar1 = { 0x05, 0x06, 0x07, 0x08, 0x0A }; + final byte[] bbar2 = { 0x05, 0x06, 0x07, 0x08, 0x0B }; + final byte[] bbar3 = { 0x05, 0x06, 0x07, 0x08, 0x0C }; + final byte[] bbarstar = { 0x05, 0x06, 0x07, 0x08, '*' }; @Test public void sadd() { @@ -460,8 +466,16 @@ public class SetCommandsTest extends JedisCommandTestBase { ScanResult result = jedis.sscan("foo", SCAN_POINTER_START); - assertEquals(SCAN_POINTER_START, result.getStringCursor()); + assertEquals(SCAN_POINTER_START, result.getCursor()); assertFalse(result.getResult().isEmpty()); + + // binary + jedis.sadd(bfoo, ba, bb); + + ScanResult bResult = jedis.sscan(bfoo, SCAN_POINTER_START_BINARY); + + assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); + assertFalse(bResult.getResult().isEmpty()); } @Test @@ -472,8 +486,18 @@ public class SetCommandsTest extends JedisCommandTestBase { jedis.sadd("foo", "b", "a", "aa"); ScanResult result = jedis.sscan("foo", SCAN_POINTER_START, params); - assertEquals(SCAN_POINTER_START, result.getStringCursor()); + assertEquals(SCAN_POINTER_START, result.getCursor()); assertFalse(result.getResult().isEmpty()); + + // binary + params = new ScanParams(); + params.match(bbarstar); + + jedis.sadd(bfoo, bbar1, bbar2, bbar3); + ScanResult bResult = jedis.sscan(bfoo, SCAN_POINTER_START_BINARY, params); + + assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); + assertFalse(bResult.getResult().isEmpty()); } @Test @@ -486,5 +510,14 @@ public class SetCommandsTest extends JedisCommandTestBase { ScanResult result = jedis.sscan("foo", SCAN_POINTER_START, params); assertFalse(result.getResult().isEmpty()); + + // binary + params = new ScanParams(); + params.count(2); + + jedis.sadd(bfoo, bbar1, bbar2, bbar3); + ScanResult bResult = jedis.sscan(bfoo, SCAN_POINTER_START_BINARY, params); + + assertFalse(bResult.getResult().isEmpty()); } } \ No newline at end of file diff --git a/src/test/java/redis/clients/jedis/tests/commands/SortedSetCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/SortedSetCommandsTest.java index 3b046a2..248427c 100644 --- a/src/test/java/redis/clients/jedis/tests/commands/SortedSetCommandsTest.java +++ b/src/test/java/redis/clients/jedis/tests/commands/SortedSetCommandsTest.java @@ -13,6 +13,7 @@ import redis.clients.jedis.Tuple; import redis.clients.jedis.ZParams; import redis.clients.util.SafeEncoder; import static redis.clients.jedis.ScanParams.SCAN_POINTER_START; +import static redis.clients.jedis.ScanParams.SCAN_POINTER_START_BINARY; public class SortedSetCommandsTest extends JedisCommandTestBase { final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 }; @@ -25,6 +26,11 @@ public class SortedSetCommandsTest extends JedisCommandTestBase { final byte[] bExclusiveC = { 0x28, 0x0C }; final byte[] bLexMinusInf = { 0x2D }; final byte[] bLexPlusInf = { 0x2B }; + + final byte[] bbar1 = { 0x05, 0x06, 0x07, 0x08, 0x0A }; + final byte[] bbar2 = { 0x05, 0x06, 0x07, 0x08, 0x0B }; + final byte[] bbar3 = { 0x05, 0x06, 0x07, 0x08, 0x0C }; + final byte[] bbarstar = { 0x05, 0x06, 0x07, 0x08, '*' }; @Test public void zadd() { @@ -894,15 +900,15 @@ public class SortedSetCommandsTest extends JedisCommandTestBase { jedis.zadd("bar", 2, "b"); ZParams params = new ZParams(); - params.weights(2, 2); + params.weights(2, 2.5); params.aggregate(ZParams.Aggregate.SUM); long result = jedis.zunionstore("dst", params, "foo", "bar"); assertEquals(2, result); Set expected = new LinkedHashSet(); - expected.add(new Tuple("b", new Double(8))); - expected.add(new Tuple("a", new Double(6))); + expected.add(new Tuple("b", new Double(9))); + expected.add(new Tuple("a", new Double(7))); assertEquals(expected, jedis.zrangeWithScores("dst", 0, 100)); @@ -913,7 +919,7 @@ public class SortedSetCommandsTest extends JedisCommandTestBase { jedis.zadd(bbar, 2, bb); ZParams bparams = new ZParams(); - bparams.weights(2, 2); + bparams.weights(2, 2.5); bparams.aggregate(ZParams.Aggregate.SUM); long bresult = jedis.zunionstore(SafeEncoder.encode("dst"), bparams, bfoo, bbar); @@ -921,8 +927,8 @@ public class SortedSetCommandsTest extends JedisCommandTestBase { assertEquals(2, bresult); Set bexpected = new LinkedHashSet(); - bexpected.add(new Tuple(bb, new Double(8))); - bexpected.add(new Tuple(ba, new Double(6))); + bexpected.add(new Tuple(bb, new Double(9))); + bexpected.add(new Tuple(ba, new Double(7))); assertEquals(bexpected, jedis.zrangeWithScores(SafeEncoder.encode("dst"), 0, 100)); @@ -966,14 +972,14 @@ public class SortedSetCommandsTest extends JedisCommandTestBase { jedis.zadd("bar", 2, "a"); ZParams params = new ZParams(); - params.weights(2, 2); + params.weights(2, 2.5); params.aggregate(ZParams.Aggregate.SUM); long result = jedis.zinterstore("dst", params, "foo", "bar"); assertEquals(1, result); Set expected = new LinkedHashSet(); - expected.add(new Tuple("a", new Double(6))); + expected.add(new Tuple("a", new Double(7))); assertEquals(expected, jedis.zrangeWithScores("dst", 0, 100)); @@ -983,7 +989,7 @@ public class SortedSetCommandsTest extends JedisCommandTestBase { jedis.zadd(bbar, 2, ba); ZParams bparams = new ZParams(); - bparams.weights(2, 2); + bparams.weights(2, 2.5); bparams.aggregate(ZParams.Aggregate.SUM); long bresult = jedis.zinterstore(SafeEncoder.encode("dst"), bparams, bfoo, bbar); @@ -991,7 +997,7 @@ public class SortedSetCommandsTest extends JedisCommandTestBase { assertEquals(1, bresult); Set bexpected = new LinkedHashSet(); - bexpected.add(new Tuple(ba, new Double(6))); + bexpected.add(new Tuple(ba, new Double(7))); assertEquals(bexpected, jedis.zrangeWithScores(SafeEncoder.encode("dst"), 0, 100)); @@ -1014,8 +1020,17 @@ public class SortedSetCommandsTest extends JedisCommandTestBase { ScanResult result = jedis.zscan("foo", SCAN_POINTER_START); - assertEquals(SCAN_POINTER_START, result.getStringCursor()); + assertEquals(SCAN_POINTER_START, result.getCursor()); assertFalse(result.getResult().isEmpty()); + + // binary + jedis.zadd(bfoo, 1, ba); + jedis.zadd(bfoo, 1, bb); + + ScanResult bResult = jedis.zscan(bfoo, SCAN_POINTER_START_BINARY); + + assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); + assertFalse(bResult.getResult().isEmpty()); } @Test @@ -1028,8 +1043,21 @@ public class SortedSetCommandsTest extends JedisCommandTestBase { jedis.zadd("foo", 11, "aa"); ScanResult result = jedis.zscan("foo", SCAN_POINTER_START, params); - assertEquals(SCAN_POINTER_START, result.getStringCursor()); + assertEquals(SCAN_POINTER_START, result.getCursor()); assertFalse(result.getResult().isEmpty()); + + // binary + params = new ScanParams(); + params.match(bbarstar); + + jedis.zadd(bfoo, 2, bbar1); + jedis.zadd(bfoo, 1, bbar2); + jedis.zadd(bfoo, 11, bbar3); + ScanResult bResult = jedis.zscan(bfoo, SCAN_POINTER_START_BINARY, params); + + assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); + assertFalse(bResult.getResult().isEmpty()); + } @Test @@ -1046,5 +1074,17 @@ public class SortedSetCommandsTest extends JedisCommandTestBase { ScanResult result = jedis.zscan("foo", SCAN_POINTER_START, params); assertFalse(result.getResult().isEmpty()); + + // binary + params = new ScanParams(); + params.count(2); + + jedis.zadd(bfoo, 2, bbar1); + jedis.zadd(bfoo, 1, bbar2); + jedis.zadd(bfoo, 11, bbar3); + + ScanResult bResult = jedis.zscan(bfoo, SCAN_POINTER_START_BINARY, params); + + assertFalse(bResult.getResult().isEmpty()); } } \ No newline at end of file diff --git a/src/test/java/redis/clients/jedis/tests/commands/StringValuesCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/StringValuesCommandsTest.java index 625fcc9..2876abe 100644 --- a/src/test/java/redis/clients/jedis/tests/commands/StringValuesCommandsTest.java +++ b/src/test/java/redis/clients/jedis/tests/commands/StringValuesCommandsTest.java @@ -123,6 +123,12 @@ public class StringValuesCommandsTest extends JedisCommandTestBase { assertEquals(4, value); } + @Test(expected = JedisDataException.class) + public void incrByFloatWrongValue() { + jedis.set("foo", "bar"); + jedis.incrByFloat("foo", 2d); + } + @Test(expected = JedisDataException.class) public void decrWrongValue() { jedis.set("foo", "bar"); diff --git a/src/test/java/redis/clients/jedis/tests/utils/JedisClusterTestUtil.java b/src/test/java/redis/clients/jedis/tests/utils/JedisClusterTestUtil.java new file mode 100644 index 0000000..c7f7928 --- /dev/null +++ b/src/test/java/redis/clients/jedis/tests/utils/JedisClusterTestUtil.java @@ -0,0 +1,70 @@ +package redis.clients.jedis.tests.utils; + +import redis.clients.jedis.Jedis; +import redis.clients.jedis.exceptions.JedisException; + +public class JedisClusterTestUtil { + public static void waitForClusterReady(Jedis...nodes) throws InterruptedException { + boolean clusterOk = false; + while (!clusterOk) { + boolean isOk = true; + for (Jedis node : nodes) { + if (!node.clusterInfo().split("\n")[0].contains("ok")) { + isOk = false; + break; + } + } + + if (isOk) { + clusterOk = true; + } + + Thread.sleep(50); + } + } + + public static String getNodeId(String infoOutput) { + for (String infoLine : infoOutput.split("\n")) { + if (infoLine.contains("myself")) { + return infoLine.split(" ")[0]; + } + } + return ""; + } + + public static void assertNodeIsKnown(Jedis node, String targetNodeId, int timeoutMs) { + assertNodeRecognizedStatus(node, targetNodeId, true, timeoutMs); + } + + public static void assertNodeIsUnknown(Jedis node, String targetNodeId, int timeoutMs) { + assertNodeRecognizedStatus(node, targetNodeId, false, timeoutMs); + } + + private static void assertNodeRecognizedStatus(Jedis node, String targetNodeId, boolean shouldRecognized, int timeoutMs) { + int sleepInterval = 100; + for (int sleepTime = 0 ; sleepTime <= timeoutMs ; sleepTime += sleepInterval) { + boolean known = isKnownNode(node, targetNodeId); + if (shouldRecognized == known) + return; + + try { + Thread.sleep(sleepInterval); + } catch (InterruptedException e) { + } + } + + throw new JedisException("Node recognize check error"); + } + + private static boolean isKnownNode(Jedis node, String nodeId) { + String infoOutput = node.clusterNodes(); + for (String infoLine : infoOutput.split("\n")) { + if (infoLine.contains(nodeId)) { + return true; + } + } + return false; + } + + +}