From 610f7d454615db23cb56089bff076feddd12910a Mon Sep 17 00:00:00 2001 From: Eric Treworgy Date: Mon, 7 Jan 2013 12:33:19 -0800 Subject: [PATCH 01/44] Added incrByFloat and hincrByFloat commands (binary and standard) + support for pipelining and sharding --- .../redis/clients/jedis/BinaryClient.java | 8 +++ .../java/redis/clients/jedis/BinaryJedis.java | 56 +++++++++++++++++++ .../clients/jedis/BinaryJedisCommands.java | 4 ++ .../clients/jedis/BinaryShardedJedis.java | 10 ++++ src/main/java/redis/clients/jedis/Client.java | 8 +++ .../java/redis/clients/jedis/Commands.java | 4 ++ src/main/java/redis/clients/jedis/Jedis.java | 51 +++++++++++++++++ .../java/redis/clients/jedis/Pipeline.java | 20 +++++++ .../java/redis/clients/jedis/Protocol.java | 2 +- .../redis/clients/jedis/ShardedJedis.java | 10 ++++ .../clients/jedis/ShardedJedisPipeline.java | 14 +++++ .../tests/commands/HashesCommandsTest.java | 19 +++++++ .../tests/commands/ScriptingCommandsTest.java | 2 +- .../commands/StringValuesCommandsTest.java | 16 ++++++ 14 files changed, 222 insertions(+), 2 deletions(-) diff --git a/src/main/java/redis/clients/jedis/BinaryClient.java b/src/main/java/redis/clients/jedis/BinaryClient.java index a5323d2..56be470 100644 --- a/src/main/java/redis/clients/jedis/BinaryClient.java +++ b/src/main/java/redis/clients/jedis/BinaryClient.java @@ -192,6 +192,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); } @@ -238,6 +242,10 @@ public class BinaryClient extends Connection { sendCommand(HINCRBY, key, field, toByteArray(value)); } + public void hincrByFloat(final byte[] key, final byte[] field, final double value) { + sendCommand(HINCRBYFLOAT, key, field, toByteArray(value)); + } + public void hexists(final byte[] key, final byte[] field) { sendCommand(HEXISTS, key, field); } diff --git a/src/main/java/redis/clients/jedis/BinaryJedis.java b/src/main/java/redis/clients/jedis/BinaryJedis.java index 5403e70..c959f47 100644 --- a/src/main/java/redis/clients/jedis/BinaryJedis.java +++ b/src/main/java/redis/clients/jedis/BinaryJedis.java @@ -602,6 +602,36 @@ public class BinaryJedis implements 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 @@ -794,6 +824,32 @@ public class BinaryJedis implements 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. * diff --git a/src/main/java/redis/clients/jedis/BinaryJedisCommands.java b/src/main/java/redis/clients/jedis/BinaryJedisCommands.java index 77189f7..1a5f079 100644 --- a/src/main/java/redis/clients/jedis/BinaryJedisCommands.java +++ b/src/main/java/redis/clients/jedis/BinaryJedisCommands.java @@ -37,6 +37,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); @@ -55,6 +57,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); diff --git a/src/main/java/redis/clients/jedis/BinaryShardedJedis.java b/src/main/java/redis/clients/jedis/BinaryShardedJedis.java index 23fd6d6..1b6ad9a 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); diff --git a/src/main/java/redis/clients/jedis/Client.java b/src/main/java/redis/clients/jedis/Client.java index 39aae12..992d10d 100644 --- a/src/main/java/redis/clients/jedis/Client.java +++ b/src/main/java/redis/clients/jedis/Client.java @@ -117,6 +117,10 @@ public class Client extends BinaryClient implements Commands { incrBy(SafeEncoder.encode(key), integer); } + public void incrByFloat(final String key, final double value) { + incrByFloat(SafeEncoder.encode(key), value); + } + public void incr(final String key) { incr(SafeEncoder.encode(key)); } @@ -165,6 +169,10 @@ public class Client extends BinaryClient implements Commands { hincrBy(SafeEncoder.encode(key), SafeEncoder.encode(field), value); } + public void hincrByFloat(final String key, final String field, final double value) { + hincrByFloat(SafeEncoder.encode(key), SafeEncoder.encode(field), value); + } + public void hexists(final String key, final String field) { hexists(SafeEncoder.encode(key), SafeEncoder.encode(field)); } diff --git a/src/main/java/redis/clients/jedis/Commands.java b/src/main/java/redis/clients/jedis/Commands.java index 7881909..3776342 100644 --- a/src/main/java/redis/clients/jedis/Commands.java +++ b/src/main/java/redis/clients/jedis/Commands.java @@ -56,6 +56,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); @@ -74,6 +76,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); diff --git a/src/main/java/redis/clients/jedis/Jedis.java b/src/main/java/redis/clients/jedis/Jedis.java index f27c2bc..62040f9 100644 --- a/src/main/java/redis/clients/jedis/Jedis.java +++ b/src/main/java/redis/clients/jedis/Jedis.java @@ -572,6 +572,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 @@ -764,6 +789,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. * diff --git a/src/main/java/redis/clients/jedis/Pipeline.java b/src/main/java/redis/clients/jedis/Pipeline.java index d89efea..cd1ecaa 100644 --- a/src/main/java/redis/clients/jedis/Pipeline.java +++ b/src/main/java/redis/clients/jedis/Pipeline.java @@ -275,6 +275,16 @@ public class Pipeline extends Queable { return getResponse(BuilderFactory.LONG); } + public Response hincrByFloat(String key, String field, double value) { + client.hincrByFloat(key, field, value); + return getResponse(BuilderFactory.DOUBLE); + } + + public Response hincrByFloat(byte[] key, byte[] field, double value) { + client.hincrByFloat(key, field, value); + return getResponse(BuilderFactory.DOUBLE); + } + public Response> hkeys(String key) { client.hkeys(key); return getResponse(BuilderFactory.STRING_SET); @@ -365,6 +375,16 @@ public class Pipeline extends Queable { return getResponse(BuilderFactory.LONG); } + public Response incrByFloat(String key, double value) { + client.incrByFloat(key, value); + return getResponse(BuilderFactory.DOUBLE); + } + + public Response incrByFloat(byte[] key, double value) { + client.incrByFloat(key, value); + return getResponse(BuilderFactory.DOUBLE); + } + public Response> keys(String pattern) { client.keys(pattern); return getResponse(BuilderFactory.STRING_SET); diff --git a/src/main/java/redis/clients/jedis/Protocol.java b/src/main/java/redis/clients/jedis/Protocol.java index b1e391d..e061ae5 100644 --- a/src/main/java/redis/clients/jedis/Protocol.java +++ b/src/main/java/redis/clients/jedis/Protocol.java @@ -144,7 +144,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, ZCOUNT, ZRANGEBYSCORE, ZREVRANGEBYSCORE, ZREMRANGEBYRANK, ZREMRANGEBYSCORE, ZUNIONSTORE, ZINTERSTORE, 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; + 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, INCRBYFLOAT, INCR, APPEND, SUBSTR, HSET, HGET, HSETNX, HMSET, HMGET, HINCRBY, HINCRBYFLOAT, 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, ZCOUNT, ZRANGEBYSCORE, ZREVRANGEBYSCORE, ZREMRANGEBYRANK, ZREMRANGEBYSCORE, ZUNIONSTORE, ZINTERSTORE, 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; public final byte[] raw; diff --git a/src/main/java/redis/clients/jedis/ShardedJedis.java b/src/main/java/redis/clients/jedis/ShardedJedis.java index ea2f11e..6e45e26 100644 --- a/src/main/java/redis/clients/jedis/ShardedJedis.java +++ b/src/main/java/redis/clients/jedis/ShardedJedis.java @@ -118,6 +118,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); @@ -163,6 +168,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); diff --git a/src/main/java/redis/clients/jedis/ShardedJedisPipeline.java b/src/main/java/redis/clients/jedis/ShardedJedisPipeline.java index af416c0..e8eeed7 100644 --- a/src/main/java/redis/clients/jedis/ShardedJedisPipeline.java +++ b/src/main/java/redis/clients/jedis/ShardedJedisPipeline.java @@ -123,6 +123,13 @@ public class ShardedJedisPipeline extends Queable { return getResponse(BuilderFactory.LONG); } + public Response incrByFloat(String key, double value) { + Client c = getClient(key); + c.incrByFloat(key, value); + results.add(new FutureResult(c)); + return getResponse(BuilderFactory.DOUBLE); + } + public Response incr(String key) { Client c = getClient(key); c.incr(key); @@ -186,6 +193,13 @@ public class ShardedJedisPipeline extends Queable { return getResponse(BuilderFactory.LONG); } + public Response hincrByFloat(String key, String field, double value) { + Client c = getClient(key); + c.hincrByFloat(key, field, value); + results.add(new FutureResult(c)); + return getResponse(BuilderFactory.DOUBLE); + } + public Response hexists(String key, String field) { Client c = getClient(key); c.hexists(key, field); 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 fbcbca3..aa303dd 100644 --- a/src/test/java/redis/clients/jedis/tests/commands/HashesCommandsTest.java +++ b/src/test/java/redis/clients/jedis/tests/commands/HashesCommandsTest.java @@ -143,6 +143,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(); diff --git a/src/test/java/redis/clients/jedis/tests/commands/ScriptingCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/ScriptingCommandsTest.java index acee2cf..0a059cd 100644 --- a/src/test/java/redis/clients/jedis/tests/commands/ScriptingCommandsTest.java +++ b/src/test/java/redis/clients/jedis/tests/commands/ScriptingCommandsTest.java @@ -132,7 +132,7 @@ public class ScriptingCommandsTest extends JedisCommandTestBase { jedis.scriptKill(); } catch(JedisDataException e) { - assertEquals("ERR No scripts in execution right now.", e.getMessage()); + assertTrue(e.getMessage().contains("No scripts in execution right now.")); } } } \ 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 fcf9f99..6b19d32 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,22 @@ public class StringValuesCommandsTest extends JedisCommandTestBase { assertEquals(4, value); } + @Test(expected = JedisDataException.class) + public void incrByFloatWrongValue() { + jedis.set("foo", "bar"); + jedis.incrByFloat("foo", 2d); + } + + @Test + public void incrByFloat() { + Double value = jedis.incrByFloat("foo", 2d); + assertEquals((Double)2d, value); + value = jedis.incrByFloat("foo", 2.5d); + assertEquals((Double)4.5d, value); + value = jedis.incrByFloat("foo", -6.5d); + assertEquals(Double.compare(-2d, value), 0); + } + @Test(expected = JedisDataException.class) public void decrWrongValue() { jedis.set("foo", "bar"); From 68ee4e49d061ad66352e146612c2ab9bdc0fd566 Mon Sep 17 00:00:00 2001 From: Jungtaek Lim Date: Mon, 17 Feb 2014 13:37:06 +0900 Subject: [PATCH 02/44] Set dependency to Response when multi in pipeline and build dependency first if Response's dependency found and not built * there's some dependency with exec response and command responses within multi * if command responses's get() called before exec response's build(), it calls exec response's build() first * unit test included --- .../java/redis/clients/jedis/Pipeline.java | 7 ++++ .../java/redis/clients/jedis/Response.java | 34 ++++++++++++++----- .../clients/jedis/tests/PipeliningTest.java | 21 ++++++++++++ 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/src/main/java/redis/clients/jedis/Pipeline.java b/src/main/java/redis/clients/jedis/Pipeline.java index 97f856b..2687f84 100755 --- a/src/main/java/redis/clients/jedis/Pipeline.java +++ b/src/main/java/redis/clients/jedis/Pipeline.java @@ -31,6 +31,12 @@ public class Pipeline extends MultiKeyPipelineBase { return values; } + public void setResponseDependency(Response dependency) { + for (Response response : responses) { + response.setDependency(dependency); + } + } + public void addResponse(Response response) { responses.add(response); } @@ -106,6 +112,7 @@ public class Pipeline extends MultiKeyPipelineBase { public Response> exec() { client.exec(); Response> response = super.getResponse(currentMulti); + currentMulti.setResponseDependency(response); currentMulti = null; return response; } diff --git a/src/main/java/redis/clients/jedis/Response.java b/src/main/java/redis/clients/jedis/Response.java index b17f314..955277a 100644 --- a/src/main/java/redis/clients/jedis/Response.java +++ b/src/main/java/redis/clients/jedis/Response.java @@ -8,6 +8,8 @@ public class Response { private boolean set = false; private Builder builder; private Object data; + private Response dependency = null; + private boolean requestDependencyBuild = false; public Response(Builder b) { this.builder = b; @@ -19,23 +21,39 @@ public class Response { } public T get() { + // if response has dependency response and dependency is not built, + // build it first and no more!! + if (!requestDependencyBuild && dependency != null && dependency.set + && !dependency.built) { + requestDependencyBuild = true; + dependency.build(); + } if (!set) { throw new JedisDataException( "Please close pipeline or multi block before calling this method."); } if (!built) { - if (data != null) { - if (data instanceof JedisDataException) { - throw new JedisDataException((JedisDataException) data); - } - response = builder.build(data); - } - this.data = null; - built = true; + build(); } return response; } + public void setDependency(Response dependency) { + this.dependency = dependency; + this.requestDependencyBuild = false; + } + + private void build() { + if (data != null) { + if (data instanceof JedisDataException) { + throw new JedisDataException((JedisDataException) data); + } + response = builder.build(data); + } + data = null; + built = true; + } + public String toString() { return "Response " + builder.toString(); } diff --git a/src/test/java/redis/clients/jedis/tests/PipeliningTest.java b/src/test/java/redis/clients/jedis/tests/PipeliningTest.java index aed67dc..d3cddd0 100755 --- a/src/test/java/redis/clients/jedis/tests/PipeliningTest.java +++ b/src/test/java/redis/clients/jedis/tests/PipeliningTest.java @@ -251,6 +251,27 @@ public class PipeliningTest extends Assert { } + @Test + public void multiWithSync() { + jedis.set("foo", "314"); + jedis.set("bar", "foo"); + jedis.set("hello", "world"); + Pipeline p = jedis.pipelined(); + Response r1 = p.get("bar"); + p.multi(); + Response r2 = p.get("foo"); + p.exec(); + Response r3 = p.get("hello"); + p.sync(); + + // before multi + assertEquals("foo", r1.get()); + // It should be readable whether exec's response was built or not + assertEquals("314", r2.get()); + // after multi + assertEquals("world", r3.get()); + } + @Test public void testDiscardInPipeline() { Pipeline pipeline = jedis.pipelined(); From 882d662470351d08d106006821c837d76b5ddaac Mon Sep 17 00:00:00 2001 From: Jungtaek Lim Date: Tue, 25 Feb 2014 18:29:09 +0900 Subject: [PATCH 03/44] Make Jedis Cluster more likely to antirez's redis-rb-cluster JedisClusterCommand * improvements on connection error handling ** if based on slot connection throws connection related exception, retry to random node ** if we retry with random node, but all nodes are unreachable, throw JedisConnectionException without retry ** try to release connection whether connection is broken or not * bug fix : if asking flag is on, and success this time, set asking flag to off JedisClusterConnectionHandler * have flexibility on initializing slots cache ** allow some nodes connection failure - skip ** if current node is success initializing slots cache, skip other nodes ** if current node failed to initialize slots cache, discard all discovered nodes and slots * set nodes if node does not exist in nodes ** it restricts JedisPool to replace - prevent IllegalStateException : Returned object not currently part of this pool JedisSlotBasedConnectionGuaranteedConnectionHandler * getConnection (random connection) ** check all connections by random sequence ** always return valid connection (able to ping-pong) ** throw exception if all connections are invalid * some refactoring --- .../redis/clients/jedis/JedisCluster.java | 2 +- .../clients/jedis/JedisClusterCommand.java | 87 ++++++++++++++----- .../jedis/JedisClusterConnectionHandler.java | 80 +++++++++++++---- ...ConnectionGuaranteedConnectionHandler.java | 68 +++++++++++++++ 4 files changed, 196 insertions(+), 41 deletions(-) create mode 100644 src/main/java/redis/clients/jedis/JedisSlotBasedConnectionGuaranteedConnectionHandler.java diff --git a/src/main/java/redis/clients/jedis/JedisCluster.java b/src/main/java/redis/clients/jedis/JedisCluster.java index 1f645ea..afa0109 100644 --- a/src/main/java/redis/clients/jedis/JedisCluster.java +++ b/src/main/java/redis/clients/jedis/JedisCluster.java @@ -27,7 +27,7 @@ public class JedisCluster implements JedisCommands, BasicCommands { public JedisCluster(Set jedisClusterNode, int timeout, int maxRedirections) { - this.connectionHandler = new JedisSlotBasedConnectionHandler( + this.connectionHandler = new JedisSlotBasedConnectionGuaranteedConnectionHandler( jedisClusterNode); this.timeout = timeout; this.maxRedirections = maxRedirections; diff --git a/src/main/java/redis/clients/jedis/JedisClusterCommand.java b/src/main/java/redis/clients/jedis/JedisClusterCommand.java index 6e110bc..c1be912 100644 --- a/src/main/java/redis/clients/jedis/JedisClusterCommand.java +++ b/src/main/java/redis/clients/jedis/JedisClusterCommand.java @@ -3,19 +3,18 @@ package redis.clients.jedis; 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; public abstract class JedisClusterCommand { - private boolean asking = false; - private JedisClusterConnectionHandler connectionHandler; private int commandTimeout; private int redirections; - // private boolean asking = false; - public JedisClusterCommand(JedisClusterConnectionHandler connectionHandler, int timeout, int maxRedirections) { this.connectionHandler = connectionHandler; @@ -26,40 +25,80 @@ public abstract class JedisClusterCommand { public abstract T execute(Jedis connection); public T run(String key) { + if (key == null) { + throw new JedisClusterException( + "No way to dispatch this command to Redis Cluster."); + } + + return runWithRetries(key, this.redirections, false, false); + } + + private T runWithRetries(String key, int redirections, + boolean tryRandomNode, boolean asking) { + if (redirections <= 0) { + throw new JedisClusterMaxRedirectionsException( + "Too many Cluster redirections?"); + } + Jedis connection = null; try { - - if (key == null) { - throw new JedisClusterException( - "No way to dispatch this command to Redis Cluster."); - } else if (redirections == 0) { - throw new JedisClusterMaxRedirectionsException( - "Too many Cluster redirections?"); + if (tryRandomNode) { + connection = connectionHandler.getConnection(); + } else { + connection = connectionHandler + .getConnectionFromSlot(JedisClusterCRC16.getSlot(key)); } - connection = connectionHandler.getConnectionFromSlot(JedisClusterCRC16 - .getSlot(key)); + if (asking) { // TODO: Pipeline asking with the original command to make it // faster.... connection.asking(); + + // if asking success, reset asking flag + asking = false; } + return execute(connection); + } catch (JedisConnectionException jce) { + if (tryRandomNode) { + // maybe all connection is down + throw jce; + } + + releaseConnection(connection, true); + connection = null; + + // retry with random connection + return runWithRetries(key, redirections--, true, asking); } catch (JedisRedirectionException jre) { - return handleRedirection(jre, key); + if (jre instanceof JedisAskDataException) { + asking = true; + } else if (jre instanceof JedisMovedDataException) { + // TODO : In antirez's redis-rb-cluster implementation, + // it rebuilds cluster's slot and node cache + } + + this.connectionHandler.assignSlotToNode(jre.getSlot(), + jre.getTargetNode()); + + releaseConnection(connection, false); + connection = null; + + return runWithRetries(key, redirections - 1, false, asking); } finally { - if (connection != null) { + releaseConnection(connection, false); + } + + } + + private void releaseConnection(Jedis connection, boolean broken) { + if (connection != null) { + if (broken) { + connectionHandler.returnBrokenConnection(connection); + } else { connectionHandler.returnConnection(connection); } } } - private T handleRedirection(JedisRedirectionException jre, String key) { - if (jre instanceof JedisAskDataException) { - asking = true; - } - redirections--; - this.connectionHandler.assignSlotToNode(jre.getSlot(), - jre.getTargetNode()); - return run(key); - } } \ No newline at end of file diff --git a/src/main/java/redis/clients/jedis/JedisClusterConnectionHandler.java b/src/main/java/redis/clients/jedis/JedisClusterConnectionHandler.java index 28e22f9..94f4c75 100644 --- a/src/main/java/redis/clients/jedis/JedisClusterConnectionHandler.java +++ b/src/main/java/redis/clients/jedis/JedisClusterConnectionHandler.java @@ -5,17 +5,22 @@ import java.util.Map; import java.util.Random; import java.util.Set; +import redis.clients.jedis.exceptions.JedisConnectionException; + public abstract class JedisClusterConnectionHandler { protected Map nodes = new HashMap(); protected Map slots = new HashMap(); abstract Jedis getConnection(); - + protected void returnConnection(Jedis connection) { - nodes.get( - connection.getClient().getHost() - + connection.getClient().getPort()).returnResource( + nodes.get(getNodeKey(connection.getClient())) + .returnResource(connection); + } + + public void returnBrokenConnection(Jedis connection) { + nodes.get(getNodeKey(connection.getClient())).returnBrokenResource( connection); } @@ -29,29 +34,57 @@ public abstract class JedisClusterConnectionHandler { return nodes; } - private void initializeSlotsCache(Set nodes) { - for (HostAndPort hostAndPort : nodes) { + private void initializeSlotsCache(Set startNodes) { + for (HostAndPort hostAndPort : startNodes) { JedisPool jp = new JedisPool(hostAndPort.getHost(), hostAndPort.getPort()); - this.nodes.put(hostAndPort.getHost() + hostAndPort.getPort(), jp); - Jedis jedis = jp.getResource(); + + this.nodes.clear(); + this.slots.clear(); + + Jedis jedis = null; try { + jedis = jp.getResource(); discoverClusterNodesAndSlots(jedis); + break; + } catch (JedisConnectionException e) { + if (jedis != null) { + jp.returnBrokenResource(jedis); + jedis = null; + } + + // try next nodes } finally { - jp.returnResource(jedis); + if (jedis != null) { + jp.returnResource(jedis); + } } } - } + for (HostAndPort node : startNodes) { + setNodeIfNotExist(node); + } + } + private void discoverClusterNodesAndSlots(Jedis jedis) { String localNodes = jedis.clusterNodes(); for (String nodeInfo : localNodes.split("\n")) { HostAndPort node = getHostAndPortFromNodeLine(nodeInfo, jedis); - JedisPool nodePool = new JedisPool(node.getHost(), node.getPort()); - this.nodes.put(node.getHost() + node.getPort(), nodePool); + 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); + } private void populateNodeSlots(String nodeInfo, JedisPool nodePool) { String[] nodeInfoArray = nodeInfo.split(" "); @@ -74,7 +107,8 @@ public abstract class JedisClusterConnectionHandler { } } - private HostAndPort getHostAndPortFromNodeLine(String nodeInfo, Jedis currentConnection) { + private HostAndPort getHostAndPortFromNodeLine(String nodeInfo, + Jedis currentConnection) { String stringHostAndPort = nodeInfo.split(" ", 3)[1]; if (":0".equals(stringHostAndPort)) { return new HostAndPort(currentConnection.getClient().getHost(), @@ -86,9 +120,16 @@ public abstract class JedisClusterConnectionHandler { } public void assignSlotToNode(int slot, HostAndPort targetNode) { - JedisPool targetPool = nodes.get(targetNode.getHost() - + targetNode.getPort()); - slots.put(slot, targetPool); + JedisPool targetPool = nodes.get(getNodeKey(targetNode)); + + if (targetPool != null) { + slots.put(slot, targetPool); + } else { + setNodeIfNotExist(targetNode); + + targetPool = nodes.get(getNodeKey(targetNode)); + slots.put(slot, targetPool); + } } protected JedisPool getRandomConnection() { @@ -96,4 +137,11 @@ public abstract class JedisClusterConnectionHandler { return (JedisPool) (nodeArray[new Random().nextInt(nodeArray.length)]); } + protected String getNodeKey(HostAndPort hnp) { + return hnp.getHost() + ":" + hnp.getPort(); + } + + protected String getNodeKey(Client client) { + return client.getHost() + ":" + client.getPort(); + } } diff --git a/src/main/java/redis/clients/jedis/JedisSlotBasedConnectionGuaranteedConnectionHandler.java b/src/main/java/redis/clients/jedis/JedisSlotBasedConnectionGuaranteedConnectionHandler.java new file mode 100644 index 0000000..0fe2cec --- /dev/null +++ b/src/main/java/redis/clients/jedis/JedisSlotBasedConnectionGuaranteedConnectionHandler.java @@ -0,0 +1,68 @@ +package redis.clients.jedis; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import redis.clients.jedis.exceptions.JedisConnectionException; + +public class JedisSlotBasedConnectionGuaranteedConnectionHandler extends + JedisSlotBasedConnectionHandler { + + public JedisSlotBasedConnectionGuaranteedConnectionHandler( + Set nodes) { + super(nodes); + } + + public Jedis getConnection() { + // In antirez's redis-rb-cluster implementation, + // getRandomConnection always return valid connection (able to ping-pong) + // or exception if all connections are invalid + + List pools = getShuffledNodesPool(); + + for (JedisPool pool : pools) { + Jedis jedis = null; + try { + jedis = pool.getResource(); + + if (jedis == null) { + continue; + } + + String result = jedis.ping(); + + if (result.equalsIgnoreCase("pong")) + return jedis; + + pool.returnBrokenResource(jedis); + } catch (JedisConnectionException ex) { + if (jedis != null) { + pool.returnBrokenResource(jedis); + } + } + } + + throw new JedisConnectionException("no reachable node in cluster"); + } + + @Override + public Jedis getConnectionFromSlot(int slot) { + JedisPool connectionPool = slots.get(slot); + if (connectionPool != null) { + // It can't guaranteed to get valid connection because of node assignment + return connectionPool.getResource(); + } else { + return getConnection(); + } + } + + private List getShuffledNodesPool() { + List pools = new ArrayList(); + pools.addAll(nodes.values()); + Collections.shuffle(pools); + return pools; + } + +} From fcea0fe0feca0d2b4160a377ecf79c90c9dcda7d Mon Sep 17 00:00:00 2001 From: Jungtaek Lim Date: Wed, 26 Feb 2014 07:54:08 +0900 Subject: [PATCH 04/44] CLUSTERDOWN : JedisClusterException --- src/main/java/redis/clients/jedis/Protocol.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/redis/clients/jedis/Protocol.java b/src/main/java/redis/clients/jedis/Protocol.java index a753f96..681bc56 100644 --- a/src/main/java/redis/clients/jedis/Protocol.java +++ b/src/main/java/redis/clients/jedis/Protocol.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.List; import redis.clients.jedis.exceptions.JedisAskDataException; +import redis.clients.jedis.exceptions.JedisClusterException; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.exceptions.JedisMovedDataException; @@ -16,6 +17,7 @@ public final class Protocol { private static final String ASK_RESPONSE = "ASK"; private static final String MOVED_RESPONSE = "MOVED"; + private static final String CLUSTERDOWN_RESPONSE = "CLUSTERDOWN"; public static final int DEFAULT_PORT = 6379; public static final int DEFAULT_SENTINEL_PORT = 26379; public static final int DEFAULT_TIMEOUT = 2000; @@ -96,6 +98,8 @@ public final class Protocol { throw new JedisAskDataException(message, new HostAndPort( askInfo[1], Integer.valueOf(askInfo[2])), Integer.valueOf(askInfo[0])); + } else if (message.startsWith(CLUSTERDOWN_RESPONSE)) { + throw new JedisClusterException(message); } throw new JedisDataException(message); } From ddb1870a5f6781f909d9cd803bc0ac17e8846457 Mon Sep 17 00:00:00 2001 From: Marcos Nils Date: Thu, 27 Feb 2014 10:48:46 -0300 Subject: [PATCH 05/44] Fix bug in JedisCluster del command. Fix #568 --- src/main/java/redis/clients/jedis/JedisCluster.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/redis/clients/jedis/JedisCluster.java b/src/main/java/redis/clients/jedis/JedisCluster.java index 1f645ea..121bde8 100644 --- a/src/main/java/redis/clients/jedis/JedisCluster.java +++ b/src/main/java/redis/clients/jedis/JedisCluster.java @@ -1129,7 +1129,7 @@ public class JedisCluster implements JedisCommands, BasicCommands { public Long execute(Jedis connection) { return connection.del(key); } - }.run(null); + }.run(key); } @Override From 46eef9530b4a6f493ed1ab1e677213467461bfbe Mon Sep 17 00:00:00 2001 From: Henning Schmiedehausen Date: Thu, 27 Feb 2014 10:58:46 -0800 Subject: [PATCH 06/44] add a number of null check to return methods. This allows calling these methods on error cleanup paths without having to surround them with if checks all the time. --- src/main/java/redis/clients/jedis/JedisPool.java | 10 +++++++--- src/main/java/redis/clients/util/Pool.java | 10 +++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/main/java/redis/clients/jedis/JedisPool.java b/src/main/java/redis/clients/jedis/JedisPool.java index 6b2c80c..8e34d19 100644 --- a/src/main/java/redis/clients/jedis/JedisPool.java +++ b/src/main/java/redis/clients/jedis/JedisPool.java @@ -80,11 +80,15 @@ public class JedisPool extends Pool { } public void returnBrokenResource(final Jedis resource) { - returnBrokenResourceObject(resource); + if (resource != null) { + returnBrokenResourceObject(resource); + } } public void returnResource(final Jedis resource) { - resource.resetState(); - returnResourceObject(resource); + if (resource != null) { + resource.resetState(); + returnResourceObject(resource); + } } } diff --git a/src/main/java/redis/clients/util/Pool.java b/src/main/java/redis/clients/util/Pool.java index 09d8ebb..2b0d91f 100644 --- a/src/main/java/redis/clients/util/Pool.java +++ b/src/main/java/redis/clients/util/Pool.java @@ -54,11 +54,15 @@ public abstract class Pool { } public void returnBrokenResource(final T resource) { - returnBrokenResourceObject(resource); + if (resource != null) { + returnBrokenResourceObject(resource); + } } public void returnResource(final T resource) { - returnResourceObject(resource); + if (resource != null) { + returnResourceObject(resource); + } } public void destroy() { @@ -81,4 +85,4 @@ public abstract class Pool { throw new JedisException("Could not destroy the pool", e); } } -} \ No newline at end of file +} From b2eb5d6d1c1e15c32bb702bf60b638044f79e510 Mon Sep 17 00:00:00 2001 From: Roland von Herget Date: Fri, 28 Feb 2014 14:36:53 +0100 Subject: [PATCH 07/44] add a returnBrokenResource method, this way we can throw away broken ShardedJedis objects (e.g. due to timeouts on one shard) --- src/main/java/redis/clients/jedis/ShardedJedisPool.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/redis/clients/jedis/ShardedJedisPool.java b/src/main/java/redis/clients/jedis/ShardedJedisPool.java index dd56ac1..7816c1e 100644 --- a/src/main/java/redis/clients/jedis/ShardedJedisPool.java +++ b/src/main/java/redis/clients/jedis/ShardedJedisPool.java @@ -32,6 +32,10 @@ public class ShardedJedisPool extends Pool { super(poolConfig, new ShardedJedisFactory(shards, algo, keyTagPattern)); } + public void returnBrokenResource(final ShardedJedis resource) { + returnBrokenResourceObject(resource); + } + /** * PoolableObjectFactory custom impl. */ From 4e78b811be01b3b859433fa173c2e7ba305518bc Mon Sep 17 00:00:00 2001 From: Marcos Nils Date: Mon, 3 Mar 2014 18:54:24 -0300 Subject: [PATCH 08/44] Merge JedisSlotBasedConnectionGuaranteedConnectionHandler to JedisSlotBasedConnectionHandler --- .../redis/clients/jedis/JedisCluster.java | 2 +- ...ConnectionGuaranteedConnectionHandler.java | 68 ------------------- .../JedisSlotBasedConnectionHandler.java | 50 ++++++++++++-- 3 files changed, 47 insertions(+), 73 deletions(-) delete mode 100644 src/main/java/redis/clients/jedis/JedisSlotBasedConnectionGuaranteedConnectionHandler.java diff --git a/src/main/java/redis/clients/jedis/JedisCluster.java b/src/main/java/redis/clients/jedis/JedisCluster.java index c1a34ce..121bde8 100644 --- a/src/main/java/redis/clients/jedis/JedisCluster.java +++ b/src/main/java/redis/clients/jedis/JedisCluster.java @@ -27,7 +27,7 @@ public class JedisCluster implements JedisCommands, BasicCommands { public JedisCluster(Set jedisClusterNode, int timeout, int maxRedirections) { - this.connectionHandler = new JedisSlotBasedConnectionGuaranteedConnectionHandler( + this.connectionHandler = new JedisSlotBasedConnectionHandler( jedisClusterNode); this.timeout = timeout; this.maxRedirections = maxRedirections; diff --git a/src/main/java/redis/clients/jedis/JedisSlotBasedConnectionGuaranteedConnectionHandler.java b/src/main/java/redis/clients/jedis/JedisSlotBasedConnectionGuaranteedConnectionHandler.java deleted file mode 100644 index 0fe2cec..0000000 --- a/src/main/java/redis/clients/jedis/JedisSlotBasedConnectionGuaranteedConnectionHandler.java +++ /dev/null @@ -1,68 +0,0 @@ -package redis.clients.jedis; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -import redis.clients.jedis.exceptions.JedisConnectionException; - -public class JedisSlotBasedConnectionGuaranteedConnectionHandler extends - JedisSlotBasedConnectionHandler { - - public JedisSlotBasedConnectionGuaranteedConnectionHandler( - Set nodes) { - super(nodes); - } - - public Jedis getConnection() { - // In antirez's redis-rb-cluster implementation, - // getRandomConnection always return valid connection (able to ping-pong) - // or exception if all connections are invalid - - List pools = getShuffledNodesPool(); - - for (JedisPool pool : pools) { - Jedis jedis = null; - try { - jedis = pool.getResource(); - - if (jedis == null) { - continue; - } - - String result = jedis.ping(); - - if (result.equalsIgnoreCase("pong")) - return jedis; - - pool.returnBrokenResource(jedis); - } catch (JedisConnectionException ex) { - if (jedis != null) { - pool.returnBrokenResource(jedis); - } - } - } - - throw new JedisConnectionException("no reachable node in cluster"); - } - - @Override - public Jedis getConnectionFromSlot(int slot) { - JedisPool connectionPool = slots.get(slot); - if (connectionPool != null) { - // It can't guaranteed to get valid connection because of node assignment - return connectionPool.getResource(); - } else { - return getConnection(); - } - } - - private List getShuffledNodesPool() { - List pools = new ArrayList(); - pools.addAll(nodes.values()); - Collections.shuffle(pools); - return pools; - } - -} diff --git a/src/main/java/redis/clients/jedis/JedisSlotBasedConnectionHandler.java b/src/main/java/redis/clients/jedis/JedisSlotBasedConnectionHandler.java index 18aa424..4cd4fc7 100644 --- a/src/main/java/redis/clients/jedis/JedisSlotBasedConnectionHandler.java +++ b/src/main/java/redis/clients/jedis/JedisSlotBasedConnectionHandler.java @@ -1,7 +1,12 @@ package redis.clients.jedis; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Set; +import redis.clients.jedis.exceptions.JedisConnectionException; + public class JedisSlotBasedConnectionHandler extends JedisClusterConnectionHandler { @@ -10,7 +15,35 @@ public class JedisSlotBasedConnectionHandler extends } public Jedis getConnection() { - return getRandomConnection().getResource(); + // In antirez's redis-rb-cluster implementation, + // getRandomConnection always return valid connection (able to ping-pong) + // or exception if all connections are invalid + + List pools = getShuffledNodesPool(); + + for (JedisPool pool : pools) { + Jedis jedis = null; + try { + jedis = pool.getResource(); + + if (jedis == null) { + continue; + } + + String result = jedis.ping(); + + if (result.equalsIgnoreCase("pong")) + return jedis; + + pool.returnBrokenResource(jedis); + } catch (JedisConnectionException ex) { + if (jedis != null) { + pool.returnBrokenResource(jedis); + } + } + } + + throw new JedisConnectionException("no reachable node in cluster"); } @Override @@ -21,10 +54,19 @@ public class JedisSlotBasedConnectionHandler extends @Override public Jedis getConnectionFromSlot(int slot) { JedisPool connectionPool = slots.get(slot); - if (connectionPool == null) { - connectionPool = getRandomConnection(); + if (connectionPool != null) { + // It can't guaranteed to get valid connection because of node assignment + return connectionPool.getResource(); + } else { + return getConnection(); } - return connectionPool.getResource(); + } + + private List getShuffledNodesPool() { + List pools = new ArrayList(); + pools.addAll(nodes.values()); + Collections.shuffle(pools); + return pools; } } From 0cd32a61037eaaef58b7f6a64b38b0f408b5505b Mon Sep 17 00:00:00 2001 From: Jungtaek Lim Date: Tue, 4 Mar 2014 23:54:44 +0900 Subject: [PATCH 09/44] Expose *SCAN commands to BinaryJedis * method signatures are a bit similar to Jedis's *SCAN ** but it takes parameters to byte[] instead of String * ScanParams : allow match pattern with byte[] * ScanResult : add method to get cursor with byte[] type * *SCAN for BinaryJedis unit tests included --- .../java/redis/clients/jedis/BinaryJedis.java | 68 +++++++++++++++++++ .../java/redis/clients/jedis/ScanParams.java | 6 ++ .../java/redis/clients/jedis/ScanResult.java | 11 +++ .../commands/AllKindOfValuesCommandsTest.java | 34 +++++++++- .../tests/commands/HashesCommandsTest.java | 43 +++++++++++- .../jedis/tests/commands/SetCommandsTest.java | 33 +++++++++ .../tests/commands/SortedSetCommandsTest.java | 40 +++++++++++ 7 files changed, 233 insertions(+), 2 deletions(-) diff --git a/src/main/java/redis/clients/jedis/BinaryJedis.java b/src/main/java/redis/clients/jedis/BinaryJedis.java index eba466f..a2cf153 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; @@ -3417,4 +3418,71 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands, 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/ScanParams.java b/src/main/java/redis/clients/jedis/ScanParams.java index 980bb13..49317b8 100644 --- a/src/main/java/redis/clients/jedis/ScanParams.java +++ b/src/main/java/redis/clients/jedis/ScanParams.java @@ -13,7 +13,13 @@ 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 byte[] pattern) { + params.add(MATCH.raw); + params.add(pattern); + } + public void match(final String pattern) { params.add(MATCH.raw); params.add(SafeEncoder.encode(pattern)); diff --git a/src/main/java/redis/clients/jedis/ScanResult.java b/src/main/java/redis/clients/jedis/ScanResult.java index 9afe27d..bf1bbd4 100644 --- a/src/main/java/redis/clients/jedis/ScanResult.java +++ b/src/main/java/redis/clients/jedis/ScanResult.java @@ -2,6 +2,8 @@ package redis.clients.jedis; import java.util.List; +import redis.clients.util.SafeEncoder; + public class ScanResult { private String cursor; private List results; @@ -17,6 +19,11 @@ public class ScanResult { this.results = results; } + public ScanResult(byte[] cursor, List results) { + this.cursor = SafeEncoder.encode(cursor); + this.results = results; + } + public ScanResult(String cursor, List results) { this.cursor = cursor; this.results = results; @@ -40,6 +47,10 @@ public class ScanResult { return cursor; } + public byte[] getBinaryCursor() { + return SafeEncoder.encode(cursor); + } + public List getResult() { return results; } 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..c0a9670 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 }; @@ -515,8 +516,14 @@ public class AllKindOfValuesCommandsTest extends JedisCommandTestBase { assertEquals(SCAN_POINTER_START, result.getStringCursor()); assertFalse(result.getResult().isEmpty()); + + // binary + ScanResult bResult = jedis.scan(SCAN_POINTER_START_BINARY); + + assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getBinaryCursor()); + assertFalse(bResult.getResult().isEmpty()); } - + @Test public void scanMatch() { ScanParams params = new ScanParams(); @@ -529,6 +536,19 @@ public class AllKindOfValuesCommandsTest extends JedisCommandTestBase { assertEquals(SCAN_POINTER_START, result.getStringCursor()); 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.getBinaryCursor()); + assertFalse(bResult.getResult().isEmpty()); } @Test @@ -543,5 +563,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/HashesCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/HashesCommandsTest.java index 5f9ac4e..8883f1f 100644 --- a/src/test/java/redis/clients/jedis/tests/commands/HashesCommandsTest.java +++ b/src/test/java/redis/clients/jedis/tests/commands/HashesCommandsTest.java @@ -13,12 +13,18 @@ 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 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"); @@ -300,6 +306,14 @@ public class HashesCommandsTest extends JedisCommandTestBase { assertEquals(SCAN_POINTER_START, result.getStringCursor()); 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.getBinaryCursor()); + assertFalse(bResult.getResult().isEmpty()); } @Test @@ -315,6 +329,20 @@ public class HashesCommandsTest extends JedisCommandTestBase { assertEquals(SCAN_POINTER_START, result.getStringCursor()); 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.getBinaryCursor()); + assertFalse(bResult.getResult().isEmpty()); } @Test @@ -330,5 +358,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/SetCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/SetCommandsTest.java index 2149feb..abdfc2b 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() { @@ -462,6 +468,14 @@ public class SetCommandsTest extends JedisCommandTestBase { assertEquals(SCAN_POINTER_START, result.getStringCursor()); 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.getBinaryCursor()); + assertFalse(bResult.getResult().isEmpty()); } @Test @@ -474,6 +488,16 @@ public class SetCommandsTest extends JedisCommandTestBase { assertEquals(SCAN_POINTER_START, result.getStringCursor()); 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.getBinaryCursor()); + 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 90b4c20..6b73cb7 100644 --- a/src/test/java/redis/clients/jedis/tests/commands/SortedSetCommandsTest.java +++ b/src/test/java/redis/clients/jedis/tests/commands/SortedSetCommandsTest.java @@ -11,6 +11,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 }; @@ -19,6 +20,11 @@ public class SortedSetCommandsTest extends JedisCommandTestBase { final byte[] ba = { 0x0A }; final byte[] bb = { 0x0B }; final byte[] bc = { 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 zadd() { @@ -899,6 +905,15 @@ public class SortedSetCommandsTest extends JedisCommandTestBase { assertEquals(SCAN_POINTER_START, result.getStringCursor()); 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.getBinaryCursor()); + assertFalse(bResult.getResult().isEmpty()); } @Test @@ -913,6 +928,19 @@ public class SortedSetCommandsTest extends JedisCommandTestBase { assertEquals(SCAN_POINTER_START, result.getStringCursor()); 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.getBinaryCursor()); + assertFalse(bResult.getResult().isEmpty()); + } @Test @@ -929,5 +957,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 From d9813a09dca3343c00991e7160801dc0f27b595a Mon Sep 17 00:00:00 2001 From: Jungtaek Lim Date: Wed, 5 Mar 2014 08:30:09 +0900 Subject: [PATCH 10/44] Change ScanResult's cursor type to byte[] * It's less probabilities to conversion with byte[] <-> ? --- .../java/redis/clients/jedis/ScanResult.java | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/main/java/redis/clients/jedis/ScanResult.java b/src/main/java/redis/clients/jedis/ScanResult.java index bf1bbd4..d864ee5 100644 --- a/src/main/java/redis/clients/jedis/ScanResult.java +++ b/src/main/java/redis/clients/jedis/ScanResult.java @@ -5,7 +5,7 @@ import java.util.List; import redis.clients.util.SafeEncoder; public class ScanResult { - private String cursor; + private byte[] cursor; private List results; @Deprecated @@ -15,20 +15,18 @@ public class ScanResult { * @see https://github.com/xetorthio/jedis/issues/531 */ public ScanResult(int cursor, List results) { - this.cursor = String.valueOf(cursor); - this.results = results; + this(Protocol.toByteArray(cursor), results); + } + + public ScanResult(String cursor, List results) { + this(SafeEncoder.encode(cursor), results); } public ScanResult(byte[] cursor, List results) { - this.cursor = SafeEncoder.encode(cursor); - this.results = results; - } - - public ScanResult(String cursor, List results) { this.cursor = cursor; this.results = results; } - + @Deprecated /** * This method is deprecated due to bug (scan cursor should be unsigned long) @@ -37,18 +35,18 @@ public class ScanResult { * @return int(currently), but will be changed to String, so be careful to prepare! */ public int getCursor() { - return Integer.parseInt(cursor); + return Integer.parseInt(getStringCursor()); } /** * FIXME: This method should be changed to getCursor() on next major release */ public String getStringCursor() { - return cursor; + return SafeEncoder.encode(cursor); } public byte[] getBinaryCursor() { - return SafeEncoder.encode(cursor); + return cursor; } public List getResult() { From e1f50b5fae9288a8327e8a9238481eee9405dfc6 Mon Sep 17 00:00:00 2001 From: Jungtaek Lim Date: Wed, 5 Mar 2014 21:30:36 +0900 Subject: [PATCH 11/44] Change method naming : ScanResult.getBinaryCursor to ScanResult.getCursorAsBytes --- src/main/java/redis/clients/jedis/ScanResult.java | 2 +- .../jedis/tests/commands/AllKindOfValuesCommandsTest.java | 4 ++-- .../clients/jedis/tests/commands/HashesCommandsTest.java | 4 ++-- .../redis/clients/jedis/tests/commands/SetCommandsTest.java | 4 ++-- .../clients/jedis/tests/commands/SortedSetCommandsTest.java | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/redis/clients/jedis/ScanResult.java b/src/main/java/redis/clients/jedis/ScanResult.java index d864ee5..199689e 100644 --- a/src/main/java/redis/clients/jedis/ScanResult.java +++ b/src/main/java/redis/clients/jedis/ScanResult.java @@ -45,7 +45,7 @@ public class ScanResult { return SafeEncoder.encode(cursor); } - public byte[] getBinaryCursor() { + public byte[] getCursorAsBytes() { return cursor; } 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 c0a9670..261bc3f 100644 --- a/src/test/java/redis/clients/jedis/tests/commands/AllKindOfValuesCommandsTest.java +++ b/src/test/java/redis/clients/jedis/tests/commands/AllKindOfValuesCommandsTest.java @@ -520,7 +520,7 @@ public class AllKindOfValuesCommandsTest extends JedisCommandTestBase { // binary ScanResult bResult = jedis.scan(SCAN_POINTER_START_BINARY); - assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getBinaryCursor()); + assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertFalse(bResult.getResult().isEmpty()); } @@ -547,7 +547,7 @@ public class AllKindOfValuesCommandsTest extends JedisCommandTestBase { ScanResult bResult = jedis.scan(SCAN_POINTER_START_BINARY, params); - assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getBinaryCursor()); + assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertFalse(bResult.getResult().isEmpty()); } 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 8883f1f..7ecfbf6 100644 --- a/src/test/java/redis/clients/jedis/tests/commands/HashesCommandsTest.java +++ b/src/test/java/redis/clients/jedis/tests/commands/HashesCommandsTest.java @@ -312,7 +312,7 @@ public class HashesCommandsTest extends JedisCommandTestBase { ScanResult> bResult = jedis.hscan(bfoo, SCAN_POINTER_START_BINARY); - assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getBinaryCursor()); + assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertFalse(bResult.getResult().isEmpty()); } @@ -341,7 +341,7 @@ public class HashesCommandsTest extends JedisCommandTestBase { ScanResult> bResult = jedis.hscan(bfoo, SCAN_POINTER_START_BINARY, params); - assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getBinaryCursor()); + assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertFalse(bResult.getResult().isEmpty()); } 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 abdfc2b..8687e67 100644 --- a/src/test/java/redis/clients/jedis/tests/commands/SetCommandsTest.java +++ b/src/test/java/redis/clients/jedis/tests/commands/SetCommandsTest.java @@ -474,7 +474,7 @@ public class SetCommandsTest extends JedisCommandTestBase { ScanResult bResult = jedis.sscan(bfoo, SCAN_POINTER_START_BINARY); - assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getBinaryCursor()); + assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertFalse(bResult.getResult().isEmpty()); } @@ -496,7 +496,7 @@ public class SetCommandsTest extends JedisCommandTestBase { jedis.sadd(bfoo, bbar1, bbar2, bbar3); ScanResult bResult = jedis.sscan(bfoo, SCAN_POINTER_START_BINARY, params); - assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getBinaryCursor()); + assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertFalse(bResult.getResult().isEmpty()); } 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 6b73cb7..3e66642 100644 --- a/src/test/java/redis/clients/jedis/tests/commands/SortedSetCommandsTest.java +++ b/src/test/java/redis/clients/jedis/tests/commands/SortedSetCommandsTest.java @@ -912,7 +912,7 @@ public class SortedSetCommandsTest extends JedisCommandTestBase { ScanResult bResult = jedis.zscan(bfoo, SCAN_POINTER_START_BINARY); - assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getBinaryCursor()); + assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertFalse(bResult.getResult().isEmpty()); } @@ -938,7 +938,7 @@ public class SortedSetCommandsTest extends JedisCommandTestBase { jedis.zadd(bfoo, 11, bbar3); ScanResult bResult = jedis.zscan(bfoo, SCAN_POINTER_START_BINARY, params); - assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getBinaryCursor()); + assertArrayEquals(SCAN_POINTER_START_BINARY, bResult.getCursorAsBytes()); assertFalse(bResult.getResult().isEmpty()); } From e7285ade5c10a74e15680158bcfd60ffbad83f06 Mon Sep 17 00:00:00 2001 From: Aniket Schneider Date: Tue, 4 Mar 2014 18:14:41 -0500 Subject: [PATCH 12/44] Accept long parameter for pexpire --- src/main/java/redis/clients/jedis/BinaryClient.java | 5 +++++ src/main/java/redis/clients/jedis/BinaryJedis.java | 5 +++++ src/main/java/redis/clients/jedis/Client.java | 5 +++++ src/main/java/redis/clients/jedis/Jedis.java | 5 +++++ src/main/java/redis/clients/jedis/PipelineBase.java | 12 +++++++++++- .../tests/commands/AllKindOfValuesCommandsTest.java | 11 +++++++++-- 6 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/main/java/redis/clients/jedis/BinaryClient.java b/src/main/java/redis/clients/jedis/BinaryClient.java index 2377665..10d980c 100644 --- a/src/main/java/redis/clients/jedis/BinaryClient.java +++ b/src/main/java/redis/clients/jedis/BinaryClient.java @@ -1091,7 +1091,12 @@ public class BinaryClient extends Connection { sendCommand(RESTORE, key, toByteArray(ttl), serializedValue); } + @Deprecated public void pexpire(final byte[] key, final int milliseconds) { + pexpire(key, (long) milliseconds); + } + + public void pexpire(final byte[] key, final long milliseconds) { sendCommand(PEXPIRE, key, toByteArray(milliseconds)); } diff --git a/src/main/java/redis/clients/jedis/BinaryJedis.java b/src/main/java/redis/clients/jedis/BinaryJedis.java index eba466f..a08cb09 100644 --- a/src/main/java/redis/clients/jedis/BinaryJedis.java +++ b/src/main/java/redis/clients/jedis/BinaryJedis.java @@ -3314,7 +3314,12 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands, return client.getStatusCodeReply(); } + @Deprecated public Long pexpire(final byte[] key, final int milliseconds) { + return pexpire(key, (long) milliseconds); + } + + public Long pexpire(final byte[] key, final long milliseconds) { checkIsInMulti(); client.pexpire(key, milliseconds); return client.getIntegerReply(); diff --git a/src/main/java/redis/clients/jedis/Client.java b/src/main/java/redis/clients/jedis/Client.java index 64f6f5c..98b33b4 100644 --- a/src/main/java/redis/clients/jedis/Client.java +++ b/src/main/java/redis/clients/jedis/Client.java @@ -777,7 +777,12 @@ public class Client extends BinaryClient implements Commands { restore(SafeEncoder.encode(key), ttl, serializedValue); } + @Deprecated public void pexpire(final String key, final int milliseconds) { + pexpire(key, (long) milliseconds); + } + + public void pexpire(final String key, final long milliseconds) { pexpire(SafeEncoder.encode(key), milliseconds); } diff --git a/src/main/java/redis/clients/jedis/Jedis.java b/src/main/java/redis/clients/jedis/Jedis.java index 4961f42..43e0de9 100644 --- a/src/main/java/redis/clients/jedis/Jedis.java +++ b/src/main/java/redis/clients/jedis/Jedis.java @@ -3060,7 +3060,12 @@ public class Jedis extends BinaryJedis implements JedisCommands, return client.getStatusCodeReply(); } + @Deprecated public Long pexpire(final String key, final int milliseconds) { + return pexpire(key, (long) milliseconds); + } + + public Long pexpire(final String key, final long milliseconds) { checkIsInMulti(); client.pexpire(key, milliseconds); return client.getIntegerReply(); diff --git a/src/main/java/redis/clients/jedis/PipelineBase.java b/src/main/java/redis/clients/jedis/PipelineBase.java index 3183ba1..9e237bd 100644 --- a/src/main/java/redis/clients/jedis/PipelineBase.java +++ b/src/main/java/redis/clients/jedis/PipelineBase.java @@ -1080,12 +1080,22 @@ abstract class PipelineBase extends Queable implements BinaryRedisPipeline, return getResponse(BuilderFactory.LONG); } + @Deprecated public Response pexpire(String key, int milliseconds) { + return pexpire(key, (long) milliseconds); + } + + @Deprecated + public Response pexpire(byte[] key, int milliseconds) { + return pexpire(key, (long) 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); } 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..8c75c27 100644 --- a/src/test/java/redis/clients/jedis/tests/commands/AllKindOfValuesCommandsTest.java +++ b/src/test/java/redis/clients/jedis/tests/commands/AllKindOfValuesCommandsTest.java @@ -474,9 +474,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 From 62b98a3e633434e4f635d4ef1ca0076d4404df42 Mon Sep 17 00:00:00 2001 From: Jonathan Leibiusky Date: Wed, 12 Mar 2014 10:31:22 -0400 Subject: [PATCH 13/44] Add tests to check returning null to pool --- src/main/java/redis/clients/util/Pool.java | 3 +++ .../java/redis/clients/jedis/tests/JedisPoolTest.java | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/main/java/redis/clients/util/Pool.java b/src/main/java/redis/clients/util/Pool.java index 2b0d91f..659c731 100644 --- a/src/main/java/redis/clients/util/Pool.java +++ b/src/main/java/redis/clients/util/Pool.java @@ -45,6 +45,9 @@ public abstract class Pool { } public void returnResourceObject(final T resource) { + if (resource == null) { + return; + } try { internalPool.returnObject(resource); } catch (Exception e) { diff --git a/src/test/java/redis/clients/jedis/tests/JedisPoolTest.java b/src/test/java/redis/clients/jedis/tests/JedisPoolTest.java index a501024..514d3b5 100644 --- a/src/test/java/redis/clients/jedis/tests/JedisPoolTest.java +++ b/src/test/java/redis/clients/jedis/tests/JedisPoolTest.java @@ -198,4 +198,14 @@ public class JedisPoolTest extends Assert { pool.returnResource(jedis2); pool.destroy(); } + + @Test + public void returnNullObjectShouldNotFail() { + JedisPool pool = new JedisPool(new JedisPoolConfig(), hnp.getHost(), + hnp.getPort(), 2000, "foobared", 0, "my_shiny_client_name"); + + pool.returnBrokenResource(null); + pool.returnResource(null); + pool.returnResourceObject(null); + } } From e449923ec01846e7063a3ad468ab5d63fba8709b Mon Sep 17 00:00:00 2001 From: Jonathan Leibiusky Date: Wed, 12 Mar 2014 10:44:00 -0400 Subject: [PATCH 14/44] [maven-release-plugin] prepare release jedis-2.4.2 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a55806e..2cf1640 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ jar redis.clients jedis - 2.4.2-SNAPSHOT + 2.4.2 Jedis Jedis is a blazingly small and sane Redis java client. https://github.com/xetorthio/jedis @@ -41,7 +41,7 @@ scm:git:git@github.com:xetorthio/jedis.git scm:git:git@github.com:xetorthio/jedis.git scm:git:git@github.com:xetorthio/jedis.git - jedis-2.2.0 + jedis-2.4.2 From ec03c0940e9a40e31ecce2b6912822c8f2bec64d Mon Sep 17 00:00:00 2001 From: Jonathan Leibiusky Date: Wed, 12 Mar 2014 10:44:02 -0400 Subject: [PATCH 15/44] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2cf1640..1be71dd 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ jar redis.clients jedis - 2.4.2 + 2.5.0-SNAPSHOT Jedis Jedis is a blazingly small and sane Redis java client. https://github.com/xetorthio/jedis @@ -41,7 +41,7 @@ scm:git:git@github.com:xetorthio/jedis.git scm:git:git@github.com:xetorthio/jedis.git scm:git:git@github.com:xetorthio/jedis.git - jedis-2.4.2 + jedis-2.2.0 From d00e8b6444b6e4d79e8d0efa2fb20cd0fc915e69 Mon Sep 17 00:00:00 2001 From: Jonathan Leibiusky Date: Wed, 12 Mar 2014 13:13:48 -0400 Subject: [PATCH 16/44] revert back version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1be71dd..a55806e 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ jar redis.clients jedis - 2.5.0-SNAPSHOT + 2.4.2-SNAPSHOT Jedis Jedis is a blazingly small and sane Redis java client. https://github.com/xetorthio/jedis From 70fa35f3ba13b90d74cf98095e7525d10e1cf80a Mon Sep 17 00:00:00 2001 From: Jonathan Leibiusky Date: Wed, 12 Mar 2014 13:14:49 -0400 Subject: [PATCH 17/44] [maven-release-plugin] prepare release jedis-2.4.2 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index a55806e..2cf1640 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ jar redis.clients jedis - 2.4.2-SNAPSHOT + 2.4.2 Jedis Jedis is a blazingly small and sane Redis java client. https://github.com/xetorthio/jedis @@ -41,7 +41,7 @@ scm:git:git@github.com:xetorthio/jedis.git scm:git:git@github.com:xetorthio/jedis.git scm:git:git@github.com:xetorthio/jedis.git - jedis-2.2.0 + jedis-2.4.2 From bcd40b4e34d367cdbcc35c66bd210283f0608ee0 Mon Sep 17 00:00:00 2001 From: Jonathan Leibiusky Date: Wed, 12 Mar 2014 13:14:51 -0400 Subject: [PATCH 18/44] [maven-release-plugin] prepare for next development iteration --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 2cf1640..1be71dd 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ jar redis.clients jedis - 2.4.2 + 2.5.0-SNAPSHOT Jedis Jedis is a blazingly small and sane Redis java client. https://github.com/xetorthio/jedis @@ -41,7 +41,7 @@ scm:git:git@github.com:xetorthio/jedis.git scm:git:git@github.com:xetorthio/jedis.git scm:git:git@github.com:xetorthio/jedis.git - jedis-2.4.2 + jedis-2.2.0 From d7f88789eacd27b50ab01de0d26caaf3b4edc843 Mon Sep 17 00:00:00 2001 From: Jungtaek Lim Date: Sat, 22 Mar 2014 18:00:34 +0900 Subject: [PATCH 19/44] Reflect recent version to maven dependency explain --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bbefbe1..450c574 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Or use it as a maven dependency: redis.clients jedis - 2.2.1 + 2.4.2 jar compile From b7db408a67019ba2232041ac2d643697a00d2b00 Mon Sep 17 00:00:00 2001 From: Jungtaek Lim Date: Thu, 27 Mar 2014 23:33:00 +0900 Subject: [PATCH 20/44] Fix invalid link to download (it's deprecated) - replace to releases --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 450c574..70664a1 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ All of the following redis features are supported: ## How do I use it? You can download the latest build at: - http://github.com/xetorthio/jedis/downloads + http://github.com/xetorthio/jedis/releases Or use it as a maven dependency: From 7a8e8f994d742b32fe3680760be82f9b49901747 Mon Sep 17 00:00:00 2001 From: Jungtaek Lim Date: Fri, 28 Mar 2014 23:16:30 +0900 Subject: [PATCH 21/44] Append 'package' command in Makefile --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index 1ac2bb9..f3bc013 100644 --- a/Makefile +++ b/Makefile @@ -215,6 +215,11 @@ test: mvn -Dtest=${TEST} clean compile test make stop +package: + make start + mvn clean package + make stop + deploy: make start mvn clean deploy From ce1156be44ea2b17073942ad5c60a78b566ed8e8 Mon Sep 17 00:00:00 2001 From: Vijay Ramesh Date: Thu, 3 Apr 2014 16:27:52 -0700 Subject: [PATCH 22/44] JedisSentinelPool#return[Broken]Resource should handle nulls the same was as JedisPool --- .../clients/jedis/JedisSentinelPool.java | 10 ++++--- .../jedis/tests/JedisSentinelPoolTest.java | 26 +++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/main/java/redis/clients/jedis/JedisSentinelPool.java b/src/main/java/redis/clients/jedis/JedisSentinelPool.java index 4fe5433..224ca99 100644 --- a/src/main/java/redis/clients/jedis/JedisSentinelPool.java +++ b/src/main/java/redis/clients/jedis/JedisSentinelPool.java @@ -75,12 +75,16 @@ public class JedisSentinelPool extends Pool { } public void returnBrokenResource(final Jedis resource) { - returnBrokenResourceObject(resource); + if (resource != null) { + returnBrokenResourceObject(resource); + } } public void returnResource(final Jedis resource) { - resource.resetState(); - returnResourceObject(resource); + if (resource != null) { + resource.resetState(); + returnResourceObject(resource); + } } private volatile HostAndPort currentHostMaster; diff --git a/src/test/java/redis/clients/jedis/tests/JedisSentinelPoolTest.java b/src/test/java/redis/clients/jedis/tests/JedisSentinelPoolTest.java index c8df9c5..1dd2896 100644 --- a/src/test/java/redis/clients/jedis/tests/JedisSentinelPoolTest.java +++ b/src/test/java/redis/clients/jedis/tests/JedisSentinelPoolTest.java @@ -80,6 +80,32 @@ public class JedisSentinelPoolTest extends JedisTestBase { } } + @Test + public void returnResourceWithNullResource() { + GenericObjectPoolConfig config = new GenericObjectPoolConfig(); + config.setMaxTotal(1); + config.setBlockWhenExhausted(false); + JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, + config, 1000, "foobared", 2); + + Jedis nullJedis = null; + pool.returnResource(nullJedis); + pool.destroy(); + } + + @Test + public void returnBrokenResourceWithNullResource() { + GenericObjectPoolConfig config = new GenericObjectPoolConfig(); + config.setMaxTotal(1); + config.setBlockWhenExhausted(false); + JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, + config, 1000, "foobared", 2); + + Jedis nullJedis = null; + pool.returnBrokenResource(nullJedis); + pool.destroy(); + } + private void forceFailover(JedisSentinelPool pool) throws InterruptedException { HostAndPort oldMaster = pool.getCurrentHostMaster(); From afefb71e57b0258a52b465b2b4e57c132aef4db9 Mon Sep 17 00:00:00 2001 From: Marcos Nils Date: Thu, 3 Apr 2014 22:01:15 -0300 Subject: [PATCH 23/44] Add support for HLL --- src/main/java/RedisTimeout.java | 50 +++++++++++++++++++ .../redis/clients/jedis/BinaryClient.java | 13 ++++- src/main/java/redis/clients/jedis/Client.java | 12 +++++ src/main/java/redis/clients/jedis/Jedis.java | 18 +++++++ .../java/redis/clients/jedis/Protocol.java | 2 +- .../commands/HyperLogLogCommandsTest.java | 46 +++++++++++++++++ 6 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 src/main/java/RedisTimeout.java create mode 100644 src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java diff --git a/src/main/java/RedisTimeout.java b/src/main/java/RedisTimeout.java new file mode 100644 index 0000000..2311080 --- /dev/null +++ b/src/main/java/RedisTimeout.java @@ -0,0 +1,50 @@ +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; + +public class RedisTimeout { + + public static void main(String[] args) { + // set k,v pairs where k == v + Jedis edis = new Jedis("127.0.0.1", 6379, 1000); + for (int i = 0; i < 100000; i++) { + edis.set("" + i, "" + i); + } + edis.close(); + JedisPoolConfig conf = new JedisPoolConfig(); + conf.setMaxTotal(1); + final JedisPool pool = new JedisPool(new JedisPoolConfig(), "127.0.0.1", 6379, 1); + for (int t = 0; t < 100; t++) { + new Thread() { + public void run() { + + final Jedis edis = pool.getResource(); + // timeout + // = 1 + // here. + for (int i = 0; i < 1000; i++) { + try { + String key = "" + i; + String value = edis.get(key); + if (!key.equals(value)) { + System.err.println(Thread.currentThread() + .getName() + + "\t\t\t" + + key + + "<>" + + value); + } + } catch (Exception e) { + System.err.println(">>>>>>>>>>" + e.toString()); + e.printStackTrace(); + } + } + System.out.println("Done"); + pool.returnResource(edis); + }; + }.start(); + } + + } + +} \ No newline at end of file diff --git a/src/main/java/redis/clients/jedis/BinaryClient.java b/src/main/java/redis/clients/jedis/BinaryClient.java index 2377665..bbd0f8f 100644 --- a/src/main/java/redis/clients/jedis/BinaryClient.java +++ b/src/main/java/redis/clients/jedis/BinaryClient.java @@ -320,7 +320,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); } @@ -1253,4 +1253,15 @@ 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 pfmerge(final byte[] deskey, final byte[]... sourcekeys) { + sendCommand(PFMERGE, joinParameters(deskey, sourcekeys)); + } } diff --git a/src/main/java/redis/clients/jedis/Client.java b/src/main/java/redis/clients/jedis/Client.java index 64f6f5c..f6b9529 100644 --- a/src/main/java/redis/clients/jedis/Client.java +++ b/src/main/java/redis/clients/jedis/Client.java @@ -951,4 +951,16 @@ 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 pfmerge(final String destkey, final String... sourcekeys) { + pfmerge(SafeEncoder.encode(destkey), SafeEncoder.encodeMany(sourcekeys)); + } } diff --git a/src/main/java/redis/clients/jedis/Jedis.java b/src/main/java/redis/clients/jedis/Jedis.java index 4961f42..188140f 100644 --- a/src/main/java/redis/clients/jedis/Jedis.java +++ b/src/main/java/redis/clients/jedis/Jedis.java @@ -3412,4 +3412,22 @@ public class Jedis extends BinaryJedis implements JedisCommands, return BuilderFactory.STRING_MAP .build(client.getBinaryMultiBulkReply()); } + + public Long pfadd(final String key, String... elements) { + checkIsInMulti(); + client.pfadd(key, elements); + return client.getIntegerReply(); + } + + public long pfcount(final String key) { + checkIsInMulti(); + client.pfcount(key); + 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/Protocol.java b/src/main/java/redis/clients/jedis/Protocol.java index 681bc56..5ceccaa 100644 --- a/src/main/java/redis/clients/jedis/Protocol.java +++ b/src/main/java/redis/clients/jedis/Protocol.java @@ -208,7 +208,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, 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, 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, PFADD, PFCOUNT, PFMERGE; public final byte[] raw; 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..e14c59b --- /dev/null +++ b/src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java @@ -0,0 +1,46 @@ +package redis.clients.jedis.tests.commands; + +import org.junit.Test; + +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 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 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); + } +} \ No newline at end of file From a6b76ae6651e840c0f4317548ff79223cc82b4ab Mon Sep 17 00:00:00 2001 From: Marcos Nils Date: Thu, 3 Apr 2014 22:05:40 -0300 Subject: [PATCH 24/44] Remove RedisTimeout that was added accidentally --- src/main/java/RedisTimeout.java | 50 --------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 src/main/java/RedisTimeout.java diff --git a/src/main/java/RedisTimeout.java b/src/main/java/RedisTimeout.java deleted file mode 100644 index 2311080..0000000 --- a/src/main/java/RedisTimeout.java +++ /dev/null @@ -1,50 +0,0 @@ -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPool; -import redis.clients.jedis.JedisPoolConfig; - -public class RedisTimeout { - - public static void main(String[] args) { - // set k,v pairs where k == v - Jedis edis = new Jedis("127.0.0.1", 6379, 1000); - for (int i = 0; i < 100000; i++) { - edis.set("" + i, "" + i); - } - edis.close(); - JedisPoolConfig conf = new JedisPoolConfig(); - conf.setMaxTotal(1); - final JedisPool pool = new JedisPool(new JedisPoolConfig(), "127.0.0.1", 6379, 1); - for (int t = 0; t < 100; t++) { - new Thread() { - public void run() { - - final Jedis edis = pool.getResource(); - // timeout - // = 1 - // here. - for (int i = 0; i < 1000; i++) { - try { - String key = "" + i; - String value = edis.get(key); - if (!key.equals(value)) { - System.err.println(Thread.currentThread() - .getName() - + "\t\t\t" - + key - + "<>" - + value); - } - } catch (Exception e) { - System.err.println(">>>>>>>>>>" + e.toString()); - e.printStackTrace(); - } - } - System.out.println("Done"); - pool.returnResource(edis); - }; - }.start(); - } - - } - -} \ No newline at end of file From ac53759f9706b52184963e98af11d2e44bfb1297 Mon Sep 17 00:00:00 2001 From: Jungtaek Lim Date: Sat, 5 Apr 2014 18:46:06 +0900 Subject: [PATCH 25/44] Revert "Add support for HLL" This reverts commit afefb71e57b0258a52b465b2b4e57c132aef4db9. --- .../redis/clients/jedis/BinaryClient.java | 13 +----- src/main/java/redis/clients/jedis/Client.java | 12 ----- src/main/java/redis/clients/jedis/Jedis.java | 18 -------- .../java/redis/clients/jedis/Protocol.java | 2 +- .../commands/HyperLogLogCommandsTest.java | 46 ------------------- 5 files changed, 2 insertions(+), 89 deletions(-) delete mode 100644 src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java diff --git a/src/main/java/redis/clients/jedis/BinaryClient.java b/src/main/java/redis/clients/jedis/BinaryClient.java index bbd0f8f..2377665 100644 --- a/src/main/java/redis/clients/jedis/BinaryClient.java +++ b/src/main/java/redis/clients/jedis/BinaryClient.java @@ -320,7 +320,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); } @@ -1253,15 +1253,4 @@ 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 pfmerge(final byte[] deskey, final byte[]... sourcekeys) { - sendCommand(PFMERGE, joinParameters(deskey, sourcekeys)); - } } diff --git a/src/main/java/redis/clients/jedis/Client.java b/src/main/java/redis/clients/jedis/Client.java index f6b9529..64f6f5c 100644 --- a/src/main/java/redis/clients/jedis/Client.java +++ b/src/main/java/redis/clients/jedis/Client.java @@ -951,16 +951,4 @@ 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 pfmerge(final String destkey, final String... sourcekeys) { - pfmerge(SafeEncoder.encode(destkey), SafeEncoder.encodeMany(sourcekeys)); - } } diff --git a/src/main/java/redis/clients/jedis/Jedis.java b/src/main/java/redis/clients/jedis/Jedis.java index 188140f..4961f42 100644 --- a/src/main/java/redis/clients/jedis/Jedis.java +++ b/src/main/java/redis/clients/jedis/Jedis.java @@ -3412,22 +3412,4 @@ public class Jedis extends BinaryJedis implements JedisCommands, return BuilderFactory.STRING_MAP .build(client.getBinaryMultiBulkReply()); } - - public Long pfadd(final String key, String... elements) { - checkIsInMulti(); - client.pfadd(key, elements); - return client.getIntegerReply(); - } - - public long pfcount(final String key) { - checkIsInMulti(); - client.pfcount(key); - 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/Protocol.java b/src/main/java/redis/clients/jedis/Protocol.java index 5ceccaa..681bc56 100644 --- a/src/main/java/redis/clients/jedis/Protocol.java +++ b/src/main/java/redis/clients/jedis/Protocol.java @@ -208,7 +208,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, 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, PFADD, PFCOUNT, PFMERGE; + 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, 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; public final byte[] raw; diff --git a/src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java deleted file mode 100644 index e14c59b..0000000 --- a/src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package redis.clients.jedis.tests.commands; - -import org.junit.Test; - -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 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 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); - } -} \ No newline at end of file From 1345b5c1da180631fdf9d3bf98813e1897dbd9e8 Mon Sep 17 00:00:00 2001 From: Jungtaek Lim Date: Sat, 5 Apr 2014 22:19:11 +0900 Subject: [PATCH 26/44] Apply PF* commands to JedisCluster, ShardedJedis * Apply PF* commands to JedisCluster, ShardedJedis * PF* commands to interface ** pfadd / pfcount : JedisCommands ** pfmerge : MultiKeyCommands --- src/main/java/redis/clients/jedis/Jedis.java | 2 +- .../redis/clients/jedis/JedisCluster.java | 23 +++++++++++++++++++ .../redis/clients/jedis/JedisCommands.java | 4 ++++ .../redis/clients/jedis/MultiKeyCommands.java | 2 ++ .../redis/clients/jedis/ShardedJedis.java | 12 ++++++++++ 5 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/main/java/redis/clients/jedis/Jedis.java b/src/main/java/redis/clients/jedis/Jedis.java index 188140f..8782fc9 100644 --- a/src/main/java/redis/clients/jedis/Jedis.java +++ b/src/main/java/redis/clients/jedis/Jedis.java @@ -3413,7 +3413,7 @@ public class Jedis extends BinaryJedis implements JedisCommands, .build(client.getBinaryMultiBulkReply()); } - public Long pfadd(final String key, String... elements) { + public Long pfadd(final String key, final String... elements) { checkIsInMulti(); client.pfadd(key, elements); return client.getIntegerReply(); diff --git a/src/main/java/redis/clients/jedis/JedisCluster.java b/src/main/java/redis/clients/jedis/JedisCluster.java index 121bde8..a359226 100644 --- a/src/main/java/redis/clients/jedis/JedisCluster.java +++ b/src/main/java/redis/clients/jedis/JedisCluster.java @@ -1481,4 +1481,27 @@ 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/JedisCommands.java b/src/main/java/redis/clients/jedis/JedisCommands.java index 5952bdb..5ce0121 100644 --- a/src/main/java/redis/clients/jedis/JedisCommands.java +++ b/src/main/java/redis/clients/jedis/JedisCommands.java @@ -240,4 +240,8 @@ public interface JedisCommands { 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/MultiKeyCommands.java b/src/main/java/redis/clients/jedis/MultiKeyCommands.java index f03f82c..b829fca 100644 --- a/src/main/java/redis/clients/jedis/MultiKeyCommands.java +++ b/src/main/java/redis/clients/jedis/MultiKeyCommands.java @@ -79,4 +79,6 @@ public interface MultiKeyCommands { ScanResult scan(int cursor); ScanResult scan(final String cursor); + + String pfmerge(final String destkey, final String... sourcekeys); } diff --git a/src/main/java/redis/clients/jedis/ShardedJedis.java b/src/main/java/redis/clients/jedis/ShardedJedis.java index 7235cfe..46110ad 100644 --- a/src/main/java/redis/clients/jedis/ShardedJedis.java +++ b/src/main/java/redis/clients/jedis/ShardedJedis.java @@ -570,4 +570,16 @@ public class ShardedJedis extends BinaryShardedJedis implements JedisCommands { Jedis j = getShard(key); return j.zscan(key, cursor); } + + @Override + 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); + } } From 11f05ec161d25ea37338410789d6683a79c91e7b Mon Sep 17 00:00:00 2001 From: Jungtaek Lim Date: Sat, 5 Apr 2014 23:21:47 +0900 Subject: [PATCH 27/44] Apply binary PF* commands to BinaryJedis, BinaryShardedJedis * Apply binary PF* commands to BinaryJedis, BinaryShardedJedis * binary PF* commands to interface ** pfadd / pfcount : BinaryJedisCommands ** pfmerge : MultiKeyBinaryCommands --- .../java/redis/clients/jedis/BinaryJedis.java | 21 +++++++ .../clients/jedis/BinaryJedisCommands.java | 4 ++ .../clients/jedis/BinaryShardedJedis.java | 13 ++++ .../clients/jedis/MultiKeyBinaryCommands.java | 2 + .../commands/HyperLogLogCommandsTest.java | 60 +++++++++++++++++++ 5 files changed, 100 insertions(+) diff --git a/src/main/java/redis/clients/jedis/BinaryJedis.java b/src/main/java/redis/clients/jedis/BinaryJedis.java index eba466f..bed597f 100644 --- a/src/main/java/redis/clients/jedis/BinaryJedis.java +++ b/src/main/java/redis/clients/jedis/BinaryJedis.java @@ -3417,4 +3417,25 @@ 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(); + } + } diff --git a/src/main/java/redis/clients/jedis/BinaryJedisCommands.java b/src/main/java/redis/clients/jedis/BinaryJedisCommands.java index b229f97..f22428b 100644 --- a/src/main/java/redis/clients/jedis/BinaryJedisCommands.java +++ b/src/main/java/redis/clients/jedis/BinaryJedisCommands.java @@ -211,4 +211,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/BinaryShardedJedis.java b/src/main/java/redis/clients/jedis/BinaryShardedJedis.java index 5895f20..b0dd05c 100644 --- a/src/main/java/redis/clients/jedis/BinaryShardedJedis.java +++ b/src/main/java/redis/clients/jedis/BinaryShardedJedis.java @@ -569,4 +569,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/MultiKeyBinaryCommands.java b/src/main/java/redis/clients/jedis/MultiKeyBinaryCommands.java index e6ea8fd..8b54d90 100644 --- a/src/main/java/redis/clients/jedis/MultiKeyBinaryCommands.java +++ b/src/main/java/redis/clients/jedis/MultiKeyBinaryCommands.java @@ -69,4 +69,6 @@ public interface MultiKeyBinaryCommands { byte[] randomBinaryKey(); Long bitop(BitOP op, final byte[] destKey, byte[]... srcKeys); + + String pfmerge(final byte[] destkey, final byte[]... sourcekeys); } diff --git a/src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java index e14c59b..4eb7ed4 100644 --- a/src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java +++ b/src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java @@ -2,6 +2,8 @@ package redis.clients.jedis.tests.commands; import org.junit.Test; +import redis.clients.util.SafeEncoder; + public class HyperLogLogCommandsTest extends JedisCommandTestBase { @@ -14,6 +16,19 @@ public class HyperLogLogCommandsTest extends JedisCommandTestBase { 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"); @@ -29,6 +44,26 @@ public class HyperLogLogCommandsTest extends JedisCommandTestBase { assertEquals(3, 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"); @@ -43,4 +78,29 @@ public class HyperLogLogCommandsTest extends JedisCommandTestBase { 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); + } } \ No newline at end of file From c2cf79c97273470c76a493d8e53aae6b0962b5ad Mon Sep 17 00:00:00 2001 From: Jungtaek Lim Date: Sat, 5 Apr 2014 23:48:49 +0900 Subject: [PATCH 28/44] Apply PF* (string, binary) commands to Pipeline * Apply PF* (string, binary) commands to Pipeline * PF* Pipeline (string, binary) commands to interface ** pfadd / pfcount : BinaryRedisPipeline, RedisPipeline ** pfmerge : MultiKeyBinaryRedisPipeline, MultiKeyCommandsPipeline --- .../redis/clients/jedis/BinaryClient.java | 4 ++-- .../clients/jedis/BinaryRedisPipeline.java | 4 ++++ .../clients/jedis/JedisClusterCommand.java | 1 - .../jedis/MultiKeyBinaryRedisPipeline.java | 2 ++ .../jedis/MultiKeyCommandsPipeline.java | 2 ++ .../clients/jedis/MultiKeyPipelineBase.java | 12 ++++++++++ .../java/redis/clients/jedis/Pipeline.java | 2 +- .../redis/clients/jedis/PipelineBase.java | 24 +++++++++++++++++++ .../redis/clients/jedis/RedisPipeline.java | 4 ++++ 9 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/main/java/redis/clients/jedis/BinaryClient.java b/src/main/java/redis/clients/jedis/BinaryClient.java index bbd0f8f..b1aaa96 100644 --- a/src/main/java/redis/clients/jedis/BinaryClient.java +++ b/src/main/java/redis/clients/jedis/BinaryClient.java @@ -1261,7 +1261,7 @@ public class BinaryClient extends Connection { public void pfcount(final byte[] key) { sendCommand(PFCOUNT, key); } - public void pfmerge(final byte[] deskey, final byte[]... sourcekeys) { - sendCommand(PFMERGE, joinParameters(deskey, sourcekeys)); + public void pfmerge(final byte[] destkey, final byte[]... sourcekeys) { + sendCommand(PFMERGE, joinParameters(destkey, sourcekeys)); } } diff --git a/src/main/java/redis/clients/jedis/BinaryRedisPipeline.java b/src/main/java/redis/clients/jedis/BinaryRedisPipeline.java index 73037b7..5ce2ca5 100644 --- a/src/main/java/redis/clients/jedis/BinaryRedisPipeline.java +++ b/src/main/java/redis/clients/jedis/BinaryRedisPipeline.java @@ -210,4 +210,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/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/MultiKeyBinaryRedisPipeline.java b/src/main/java/redis/clients/jedis/MultiKeyBinaryRedisPipeline.java index fd71016..90bd8ca 100644 --- a/src/main/java/redis/clients/jedis/MultiKeyBinaryRedisPipeline.java +++ b/src/main/java/redis/clients/jedis/MultiKeyBinaryRedisPipeline.java @@ -65,4 +65,6 @@ public interface MultiKeyBinaryRedisPipeline { Response randomKeyBinary(); Response bitop(BitOP op, final byte[] destKey, byte[]... srcKeys); + + Response pfmerge(final byte[] destkey, final byte[]... sourcekeys); } diff --git a/src/main/java/redis/clients/jedis/MultiKeyCommandsPipeline.java b/src/main/java/redis/clients/jedis/MultiKeyCommandsPipeline.java index 92c8d5a..71f00d1 100644 --- a/src/main/java/redis/clients/jedis/MultiKeyCommandsPipeline.java +++ b/src/main/java/redis/clients/jedis/MultiKeyCommandsPipeline.java @@ -64,4 +64,6 @@ public interface MultiKeyCommandsPipeline { Response randomKey(); Response bitop(BitOP op, final String destKey, String... srcKeys); + + Response pfmerge(final String destkey, final String... sourcekeys); } diff --git a/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java b/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java index fa7ae6e..f5a953f 100644 --- a/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java +++ b/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java @@ -446,4 +446,16 @@ 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); + } } diff --git a/src/main/java/redis/clients/jedis/Pipeline.java b/src/main/java/redis/clients/jedis/Pipeline.java index 2687f84..678f6a6 100755 --- a/src/main/java/redis/clients/jedis/Pipeline.java +++ b/src/main/java/redis/clients/jedis/Pipeline.java @@ -124,5 +124,5 @@ public class Pipeline extends MultiKeyPipelineBase { 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 3183ba1..24046eb 100644 --- a/src/main/java/redis/clients/jedis/PipelineBase.java +++ b/src/main/java/redis/clients/jedis/PipelineBase.java @@ -1204,4 +1204,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/RedisPipeline.java b/src/main/java/redis/clients/jedis/RedisPipeline.java index bb5226c..6e36d10 100644 --- a/src/main/java/redis/clients/jedis/RedisPipeline.java +++ b/src/main/java/redis/clients/jedis/RedisPipeline.java @@ -188,4 +188,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); } From 986acc8c9e334bc8d0b36215d4eb1c6ef015a77c Mon Sep 17 00:00:00 2001 From: Jungtaek Lim Date: Sun, 6 Apr 2014 00:19:16 +0900 Subject: [PATCH 29/44] Revert "Revert "Add support for HLL"" This reverts commit ac53759f9706b52184963e98af11d2e44bfb1297. Sorry for double reverting, I should revert "merged commit", not origin commit Conflicts: src/main/java/redis/clients/jedis/BinaryClient.java src/main/java/redis/clients/jedis/Jedis.java src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java --- src/main/java/redis/clients/jedis/BinaryClient.java | 3 ++- src/main/java/redis/clients/jedis/Client.java | 12 ++++++++++++ src/main/java/redis/clients/jedis/Protocol.java | 2 +- .../tests/commands/HyperLogLogCommandsTest.java | 3 +-- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/main/java/redis/clients/jedis/BinaryClient.java b/src/main/java/redis/clients/jedis/BinaryClient.java index 9b2cce6..1537293 100644 --- a/src/main/java/redis/clients/jedis/BinaryClient.java +++ b/src/main/java/redis/clients/jedis/BinaryClient.java @@ -320,7 +320,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); } @@ -1261,6 +1261,7 @@ public class BinaryClient extends Connection { public void pfcount(final byte[] key) { sendCommand(PFCOUNT, key); } + public void pfmerge(final byte[] destkey, final byte[]... sourcekeys) { sendCommand(PFMERGE, joinParameters(destkey, sourcekeys)); } diff --git a/src/main/java/redis/clients/jedis/Client.java b/src/main/java/redis/clients/jedis/Client.java index 64f6f5c..f6b9529 100644 --- a/src/main/java/redis/clients/jedis/Client.java +++ b/src/main/java/redis/clients/jedis/Client.java @@ -951,4 +951,16 @@ 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 pfmerge(final String destkey, final String... sourcekeys) { + pfmerge(SafeEncoder.encode(destkey), SafeEncoder.encodeMany(sourcekeys)); + } } diff --git a/src/main/java/redis/clients/jedis/Protocol.java b/src/main/java/redis/clients/jedis/Protocol.java index 681bc56..5ceccaa 100644 --- a/src/main/java/redis/clients/jedis/Protocol.java +++ b/src/main/java/redis/clients/jedis/Protocol.java @@ -208,7 +208,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, 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, 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, PFADD, PFCOUNT, PFMERGE; public final byte[] raw; diff --git a/src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java index 4eb7ed4..07a5021 100644 --- a/src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java +++ b/src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java @@ -6,7 +6,6 @@ import redis.clients.util.SafeEncoder; public class HyperLogLogCommandsTest extends JedisCommandTestBase { - @Test public void pfadd() { long status = jedis.pfadd("foo", "a"); @@ -103,4 +102,4 @@ public class HyperLogLogCommandsTest extends JedisCommandTestBase { status = jedis.pfcount("hll3"); assertEquals(6, status); } -} \ No newline at end of file +} From 69de8d84d5c80330dfc3c9d759d92792940f5b14 Mon Sep 17 00:00:00 2001 From: mindwind Date: Thu, 24 Apr 2014 10:58:57 +0800 Subject: [PATCH 30/44] fix - ScanParams should be return this reference build pattern for chain code style --- src/main/java/redis/clients/jedis/ScanParams.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/redis/clients/jedis/ScanParams.java b/src/main/java/redis/clients/jedis/ScanParams.java index 980bb13..c9b1ca6 100644 --- a/src/main/java/redis/clients/jedis/ScanParams.java +++ b/src/main/java/redis/clients/jedis/ScanParams.java @@ -14,14 +14,16 @@ public class ScanParams { private List params = new ArrayList(); public final static String SCAN_POINTER_START = String.valueOf(0); - public void match(final String pattern) { + 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() { From bbc9078c3f848fe478fad575c607c5de35893f52 Mon Sep 17 00:00:00 2001 From: Jungtaek Lim Date: Tue, 29 Apr 2014 00:05:49 +0900 Subject: [PATCH 31/44] Fix Pipeline NPE or sth with multi * followings are now throwing JedisDataException: it was uncontrolled or controlled by Redis itself ** exec without multi ** discard without multi ** multi within multi * updates unit test actually Redis returns ERR and we can pick, but Pipeline + multi has some complex sequence so it can just throw NPE without ERR --- .../java/redis/clients/jedis/Pipeline.java | 9 +++++++++ .../clients/jedis/tests/PipeliningTest.java | 20 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/main/java/redis/clients/jedis/Pipeline.java b/src/main/java/redis/clients/jedis/Pipeline.java index 2687f84..50fbe3f 100755 --- a/src/main/java/redis/clients/jedis/Pipeline.java +++ b/src/main/java/redis/clients/jedis/Pipeline.java @@ -104,12 +104,18 @@ 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,6 +124,9 @@ 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 diff --git a/src/test/java/redis/clients/jedis/tests/PipeliningTest.java b/src/test/java/redis/clients/jedis/tests/PipeliningTest.java index d3cddd0..29b2f86 100755 --- a/src/test/java/redis/clients/jedis/tests/PipeliningTest.java +++ b/src/test/java/redis/clients/jedis/tests/PipeliningTest.java @@ -272,6 +272,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(); From cd722e5a8283ff9f6c89bef232ee58010e82d218 Mon Sep 17 00:00:00 2001 From: Alok Singh Date: Mon, 28 Apr 2014 14:17:46 -0700 Subject: [PATCH 32/44] Add support for pfcount with multiple keys --- .../redis/clients/jedis/BinaryClient.java | 30 +++++++++---------- src/main/java/redis/clients/jedis/Client.java | 8 +++-- src/main/java/redis/clients/jedis/Jedis.java | 22 +++++++------- .../redis/clients/jedis/JedisCluster.java | 15 ++++++++-- .../redis/clients/jedis/JedisCommands.java | 2 ++ .../redis/clients/jedis/ShardedJedis.java | 13 ++++++-- .../commands/HyperLogLogCommandsTest.java | 25 ++++++++++++++++ 7 files changed, 82 insertions(+), 33 deletions(-) diff --git a/src/main/java/redis/clients/jedis/BinaryClient.java b/src/main/java/redis/clients/jedis/BinaryClient.java index 1537293..edb9274 100644 --- a/src/main/java/redis/clients/jedis/BinaryClient.java +++ b/src/main/java/redis/clients/jedis/BinaryClient.java @@ -1,26 +1,22 @@ 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; import java.util.Map; 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 { @@ -1262,6 +1258,10 @@ public class BinaryClient extends Connection { 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/Client.java b/src/main/java/redis/clients/jedis/Client.java index f6b9529..a424528 100644 --- a/src/main/java/redis/clients/jedis/Client.java +++ b/src/main/java/redis/clients/jedis/Client.java @@ -1,6 +1,6 @@ package redis.clients.jedis; -import static redis.clients.jedis.Protocol.toByteArray; +import redis.clients.util.SafeEncoder; import java.util.ArrayList; import java.util.HashMap; @@ -8,7 +8,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import redis.clients.util.SafeEncoder; +import static redis.clients.jedis.Protocol.toByteArray; public class Client extends BinaryClient implements Commands { public Client(final String host) { @@ -960,6 +960,10 @@ public class Client extends BinaryClient implements Commands { 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)); } diff --git a/src/main/java/redis/clients/jedis/Jedis.java b/src/main/java/redis/clients/jedis/Jedis.java index 8782fc9..29e53a2 100644 --- a/src/main/java/redis/clients/jedis/Jedis.java +++ b/src/main/java/redis/clients/jedis/Jedis.java @@ -1,20 +1,13 @@ 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.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 { @@ -3425,6 +3418,13 @@ public class Jedis extends BinaryJedis implements JedisCommands, 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); diff --git a/src/main/java/redis/clients/jedis/JedisCluster.java b/src/main/java/redis/clients/jedis/JedisCluster.java index a359226..163f033 100644 --- a/src/main/java/redis/clients/jedis/JedisCluster.java +++ b/src/main/java/redis/clients/jedis/JedisCluster.java @@ -1,12 +1,12 @@ package redis.clients.jedis; +import redis.clients.jedis.BinaryClient.LIST_POSITION; + 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 static final short HASHSLOTS = 16384; private static final int DEFAULT_TIMEOUT = 1; @@ -1504,4 +1504,15 @@ public class JedisCluster implements JedisCommands, BasicCommands { }.run(key); } + @Override + public long pfcount(final String... keys) { + return new JedisClusterCommand(connectionHandler, + timeout, maxRedirections) { + @Override + public Long execute(Jedis connection) { + return connection.pfcount(keys); + } + }.run(keys[0]); + } + } diff --git a/src/main/java/redis/clients/jedis/JedisCommands.java b/src/main/java/redis/clients/jedis/JedisCommands.java index 5ce0121..52c1798 100644 --- a/src/main/java/redis/clients/jedis/JedisCommands.java +++ b/src/main/java/redis/clients/jedis/JedisCommands.java @@ -244,4 +244,6 @@ public interface JedisCommands { Long pfadd(final String key, final String... elements); long pfcount(final String key); + + long pfcount(final String...keys); } diff --git a/src/main/java/redis/clients/jedis/ShardedJedis.java b/src/main/java/redis/clients/jedis/ShardedJedis.java index 46110ad..fc78ddc 100644 --- a/src/main/java/redis/clients/jedis/ShardedJedis.java +++ b/src/main/java/redis/clients/jedis/ShardedJedis.java @@ -1,14 +1,14 @@ package redis.clients.jedis; +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; - public class ShardedJedis extends BinaryShardedJedis implements JedisCommands { public ShardedJedis(List shards) { super(shards); @@ -582,4 +582,11 @@ public class ShardedJedis extends BinaryShardedJedis implements JedisCommands { Jedis j = getShard(key); return j.pfcount(key); } + + @Override + public long pfcount(String... keys) { + //The command will be sent to the first shard. + Jedis j = getShard(keys[0]); + return j.pfcount(keys); + } } diff --git a/src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java index 07a5021..0b25366 100644 --- a/src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java +++ b/src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java @@ -43,6 +43,31 @@ public class HyperLogLogCommandsTest extends JedisCommandTestBase { 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"); From b633024c112accec48c0fb485e2fa3124ff8db48 Mon Sep 17 00:00:00 2001 From: Alok Singh Date: Mon, 28 Apr 2014 16:41:09 -0700 Subject: [PATCH 33/44] Move pfcount(keys) method to multikey interfaces --- src/main/java/redis/clients/jedis/JedisCluster.java | 12 ------------ src/main/java/redis/clients/jedis/JedisCommands.java | 1 - .../java/redis/clients/jedis/MultiKeyCommands.java | 2 ++ .../clients/jedis/MultiKeyCommandsPipeline.java | 2 ++ .../redis/clients/jedis/MultiKeyPipelineBase.java | 6 ++++++ src/main/java/redis/clients/jedis/ShardedJedis.java | 7 ------- 6 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/main/java/redis/clients/jedis/JedisCluster.java b/src/main/java/redis/clients/jedis/JedisCluster.java index 163f033..5440e56 100644 --- a/src/main/java/redis/clients/jedis/JedisCluster.java +++ b/src/main/java/redis/clients/jedis/JedisCluster.java @@ -1503,16 +1503,4 @@ public class JedisCluster implements JedisCommands, BasicCommands { } }.run(key); } - - @Override - public long pfcount(final String... keys) { - return new JedisClusterCommand(connectionHandler, - timeout, maxRedirections) { - @Override - public Long execute(Jedis connection) { - return connection.pfcount(keys); - } - }.run(keys[0]); - } - } diff --git a/src/main/java/redis/clients/jedis/JedisCommands.java b/src/main/java/redis/clients/jedis/JedisCommands.java index 52c1798..dd6d89c 100644 --- a/src/main/java/redis/clients/jedis/JedisCommands.java +++ b/src/main/java/redis/clients/jedis/JedisCommands.java @@ -245,5 +245,4 @@ public interface JedisCommands { long pfcount(final String key); - long pfcount(final String...keys); } diff --git a/src/main/java/redis/clients/jedis/MultiKeyCommands.java b/src/main/java/redis/clients/jedis/MultiKeyCommands.java index b829fca..3f91ea9 100644 --- a/src/main/java/redis/clients/jedis/MultiKeyCommands.java +++ b/src/main/java/redis/clients/jedis/MultiKeyCommands.java @@ -81,4 +81,6 @@ public interface MultiKeyCommands { 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 71f00d1..e2de238 100644 --- a/src/main/java/redis/clients/jedis/MultiKeyCommandsPipeline.java +++ b/src/main/java/redis/clients/jedis/MultiKeyCommandsPipeline.java @@ -66,4 +66,6 @@ public interface MultiKeyCommandsPipeline { 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 f5a953f..7bffd38 100644 --- a/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java +++ b/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java @@ -458,4 +458,10 @@ abstract class MultiKeyPipelineBase extends PipelineBase implements client.pfmerge(destkey, sourcekeys); return getResponse(BuilderFactory.STRING); } + + @Override + public Response pfcount(String...keys) { + client.pfcount(keys); + return getResponse(BuilderFactory.LONG); + } } diff --git a/src/main/java/redis/clients/jedis/ShardedJedis.java b/src/main/java/redis/clients/jedis/ShardedJedis.java index fc78ddc..62a22da 100644 --- a/src/main/java/redis/clients/jedis/ShardedJedis.java +++ b/src/main/java/redis/clients/jedis/ShardedJedis.java @@ -582,11 +582,4 @@ public class ShardedJedis extends BinaryShardedJedis implements JedisCommands { Jedis j = getShard(key); return j.pfcount(key); } - - @Override - public long pfcount(String... keys) { - //The command will be sent to the first shard. - Jedis j = getShard(keys[0]); - return j.pfcount(keys); - } } From d9d039d06087c19c68aa8c0e64bc902d150260d2 Mon Sep 17 00:00:00 2001 From: Alok Singh Date: Tue, 29 Apr 2014 08:58:15 -0700 Subject: [PATCH 34/44] Added pfcount(keys) to BinaryJedis interfaces --- src/main/java/redis/clients/jedis/BinaryJedis.java | 7 +++++++ .../java/redis/clients/jedis/MultiKeyBinaryCommands.java | 2 ++ 2 files changed, 9 insertions(+) diff --git a/src/main/java/redis/clients/jedis/BinaryJedis.java b/src/main/java/redis/clients/jedis/BinaryJedis.java index bed597f..9066f53 100644 --- a/src/main/java/redis/clients/jedis/BinaryJedis.java +++ b/src/main/java/redis/clients/jedis/BinaryJedis.java @@ -3438,4 +3438,11 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands, return client.getStatusCodeReply(); } + @Override + public Long pfcount(byte[]... keys) { + checkIsInMulti(); + client.pfcount(keys); + return client.getIntegerReply(); + } + } diff --git a/src/main/java/redis/clients/jedis/MultiKeyBinaryCommands.java b/src/main/java/redis/clients/jedis/MultiKeyBinaryCommands.java index 8b54d90..186d822 100644 --- a/src/main/java/redis/clients/jedis/MultiKeyBinaryCommands.java +++ b/src/main/java/redis/clients/jedis/MultiKeyBinaryCommands.java @@ -71,4 +71,6 @@ public interface MultiKeyBinaryCommands { Long bitop(BitOP op, final byte[] destKey, byte[]... srcKeys); String pfmerge(final byte[] destkey, final byte[]... sourcekeys); + + Long pfcount(byte[]... keys); } From 6485a7ec08c5293c5c0e0ddb256213d122798b88 Mon Sep 17 00:00:00 2001 From: Jungtaek Lim Date: Wed, 30 Apr 2014 08:17:59 +0900 Subject: [PATCH 35/44] Add Binary Pipeline version of pfcount --- .../redis/clients/jedis/MultiKeyBinaryRedisPipeline.java | 2 ++ src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/src/main/java/redis/clients/jedis/MultiKeyBinaryRedisPipeline.java b/src/main/java/redis/clients/jedis/MultiKeyBinaryRedisPipeline.java index 90bd8ca..a3f8716 100644 --- a/src/main/java/redis/clients/jedis/MultiKeyBinaryRedisPipeline.java +++ b/src/main/java/redis/clients/jedis/MultiKeyBinaryRedisPipeline.java @@ -67,4 +67,6 @@ public interface MultiKeyBinaryRedisPipeline { 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/MultiKeyPipelineBase.java b/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java index 7bffd38..9a14285 100644 --- a/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java +++ b/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java @@ -464,4 +464,10 @@ abstract class MultiKeyPipelineBase extends PipelineBase implements client.pfcount(keys); return getResponse(BuilderFactory.LONG); } + + @Override + public Response pfcount(final byte[] ... keys) { + client.pfcount(keys); + return getResponse(BuilderFactory.LONG); + } } From c63e901232a2c407cd5269c78b356ce5dc49503d Mon Sep 17 00:00:00 2001 From: PumpkinJack Date: Sat, 3 May 2014 17:25:10 +0800 Subject: [PATCH 36/44] add 'time()' method to MultiKeyPipellineBase --- src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java b/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java index fa7ae6e..45fe51e 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(); From e17a312d92df250ed4bf24e4afeff9374be5aa09 Mon Sep 17 00:00:00 2001 From: PumpkinJack Date: Sun, 4 May 2014 15:03:18 +0800 Subject: [PATCH 37/44] To solving the error (at Pool.java 35 : The type org.apache.commons.pool2.impl.AbandonedConfig cannot be resolved. It is indirectly referenced from required .class files), we update common-pool2 (2.0 -> 2.2) --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1be71dd..7ce7b4b 100644 --- a/pom.xml +++ b/pom.xml @@ -62,7 +62,7 @@ org.apache.commons commons-pool2 - 2.0 + 2.2 jar compile From 942fc1d77bec65837e5b9b4becb8d13765f523b3 Mon Sep 17 00:00:00 2001 From: PumpkinJack Date: Sun, 4 May 2014 15:15:21 +0800 Subject: [PATCH 38/44] add the time() method --- src/main/java/redis/clients/jedis/BasicRedisPipeline.java | 4 ++++ 1 file changed, 4 insertions(+) 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(); From 7255a8cae173dc19f8250cca85bfec7e92306c02 Mon Sep 17 00:00:00 2001 From: Jungtaek Lim Date: Wed, 21 May 2014 08:08:12 +0900 Subject: [PATCH 39/44] fix "cluster nodes" parse error when slot is in transition * extract cluster nodes info. parser from JedisClusterConnectionHandler * unit test for migrating slot included --- .../jedis/JedisClusterConnectionHandler.java | 90 ++++++++----------- .../clients/util/ClusterNodeInformation.java | 48 ++++++++++ .../util/ClusterNodeInformationParser.java | 81 +++++++++++++++++ ...JedisClusterNodeInformationParserTest.java | 63 +++++++++++++ 4 files changed, 227 insertions(+), 55 deletions(-) create mode 100644 src/main/java/redis/clients/util/ClusterNodeInformation.java create mode 100644 src/main/java/redis/clients/util/ClusterNodeInformationParser.java create mode 100644 src/test/java/redis/clients/jedis/tests/JedisClusterNodeInformationParserTest.java 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/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..995df6f --- /dev/null +++ b/src/main/java/redis/clients/util/ClusterNodeInformationParser.java @@ -0,0 +1,81 @@ +package redis.clients.util; + +import redis.clients.jedis.HostAndPort; + +public class ClusterNodeInformationParser { + private static final String HOST_MYSELF_IDENTIFIER = ":0"; + 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]; + if (HOST_MYSELF_IDENTIFIER.equals(stringHostAndPort)) { + return current; + } + + String[] arrayHostAndPort = stringHostAndPort.split(":"); + return new HostAndPort(arrayHostAndPort[0], + 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/test/java/redis/clients/jedis/tests/JedisClusterNodeInformationParserTest.java b/src/test/java/redis/clients/jedis/tests/JedisClusterNodeInformationParserTest.java new file mode 100644 index 0000000..bc0fd42 --- /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 :0 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 :0 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)); + } + +} From 51de5f72abacbe544429fa24139ddfe8f57da71f Mon Sep 17 00:00:00 2001 From: Steve Parrington Date: Wed, 21 May 2014 16:09:47 +0100 Subject: [PATCH 40/44] Added Set NXXX and EXPX options for JedisCluster and ShardedJedis --- src/main/java/redis/clients/jedis/JedisCluster.java | 12 ++++++++++++ src/main/java/redis/clients/jedis/JedisCommands.java | 3 +++ src/main/java/redis/clients/jedis/ShardedJedis.java | 5 +++++ 3 files changed, 20 insertions(+) diff --git a/src/main/java/redis/clients/jedis/JedisCluster.java b/src/main/java/redis/clients/jedis/JedisCluster.java index 121bde8..def62ae 100644 --- a/src/main/java/redis/clients/jedis/JedisCluster.java +++ b/src/main/java/redis/clients/jedis/JedisCluster.java @@ -44,6 +44,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, diff --git a/src/main/java/redis/clients/jedis/JedisCommands.java b/src/main/java/redis/clients/jedis/JedisCommands.java index 5952bdb..7d1984d 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); diff --git a/src/main/java/redis/clients/jedis/ShardedJedis.java b/src/main/java/redis/clients/jedis/ShardedJedis.java index 7235cfe..e485c6c 100644 --- a/src/main/java/redis/clients/jedis/ShardedJedis.java +++ b/src/main/java/redis/clients/jedis/ShardedJedis.java @@ -32,6 +32,11 @@ public class ShardedJedis extends BinaryShardedJedis implements JedisCommands { return j.set(key, value); } + 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); From 742e83b1e030706a56c02f61aaa11d65a91d7bdc Mon Sep 17 00:00:00 2001 From: Steve Parrington Date: Thu, 22 May 2014 11:29:55 +0100 Subject: [PATCH 41/44] Added override annotation to ShardedJedis.set() --- src/main/java/redis/clients/jedis/ShardedJedis.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/redis/clients/jedis/ShardedJedis.java b/src/main/java/redis/clients/jedis/ShardedJedis.java index e485c6c..a44d330 100644 --- a/src/main/java/redis/clients/jedis/ShardedJedis.java +++ b/src/main/java/redis/clients/jedis/ShardedJedis.java @@ -32,11 +32,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); From 6106f5bbe63e986b9c1ed92fdd8c8835e852e2af Mon Sep 17 00:00:00 2001 From: Marcos Nils Date: Sun, 25 May 2014 15:01:24 -0300 Subject: [PATCH 42/44] Manually merge #578 to master --- .../redis/clients/jedis/BinaryClient.java | 10 +- .../java/redis/clients/jedis/BinaryJedis.java | 9 ++ .../redis/clients/jedis/BitPosParams.java | 27 +++++ src/main/java/redis/clients/jedis/Client.java | 4 + src/main/java/redis/clients/jedis/Jedis.java | 9 ++ .../redis/clients/jedis/PipelineBase.java | 20 +++- .../java/redis/clients/jedis/Protocol.java | 2 +- .../jedis/tests/commands/BitCommandsTest.java | 107 ++++++++++++++++++ 8 files changed, 185 insertions(+), 3 deletions(-) create mode 100644 src/main/java/redis/clients/jedis/BitPosParams.java diff --git a/src/main/java/redis/clients/jedis/BinaryClient.java b/src/main/java/redis/clients/jedis/BinaryClient.java index edb9274..4c615f3 100644 --- a/src/main/java/redis/clients/jedis/BinaryClient.java +++ b/src/main/java/redis/clients/jedis/BinaryClient.java @@ -925,7 +925,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); } diff --git a/src/main/java/redis/clients/jedis/BinaryJedis.java b/src/main/java/redis/clients/jedis/BinaryJedis.java index 9066f53..4984990 100644 --- a/src/main/java/redis/clients/jedis/BinaryJedis.java +++ b/src/main/java/redis/clients/jedis/BinaryJedis.java @@ -3125,6 +3125,15 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands, client.getbit(key, offset); 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); 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/Client.java b/src/main/java/redis/clients/jedis/Client.java index a424528..3a7a845 100644 --- a/src/main/java/redis/clients/jedis/Client.java +++ b/src/main/java/redis/clients/jedis/Client.java @@ -628,6 +628,10 @@ public class Client extends BinaryClient implements Commands { public void getbit(String key, long offset) { 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)); diff --git a/src/main/java/redis/clients/jedis/Jedis.java b/src/main/java/redis/clients/jedis/Jedis.java index 29e53a2..e6614d8 100644 --- a/src/main/java/redis/clients/jedis/Jedis.java +++ b/src/main/java/redis/clients/jedis/Jedis.java @@ -2652,6 +2652,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 diff --git a/src/main/java/redis/clients/jedis/PipelineBase.java b/src/main/java/redis/clients/jedis/PipelineBase.java index 24046eb..101020e 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); diff --git a/src/main/java/redis/clients/jedis/Protocol.java b/src/main/java/redis/clients/jedis/Protocol.java index 5ceccaa..485c10b 100644 --- a/src/main/java/redis/clients/jedis/Protocol.java +++ b/src/main/java/redis/clients/jedis/Protocol.java @@ -208,7 +208,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, 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, PFADD, PFCOUNT, PFMERGE; + 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, 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/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"); From 1782aaeeb17d872b18e1dc0f06c1dca9492969da Mon Sep 17 00:00:00 2001 From: Marcos Nils Date: Sun, 25 May 2014 16:30:41 -0300 Subject: [PATCH 43/44] Manual merge of #581 --- Makefile | 48 ++++ pom.xml | 2 +- src/main/java/redis/clients/jedis/Client.java | 55 +++- .../redis/clients/jedis/ClusterCommands.java | 18 ++ src/main/java/redis/clients/jedis/Jedis.java | 55 ++++ .../java/redis/clients/jedis/Protocol.java | 9 + .../clients/jedis/tests/HostAndPortUtil.java | 4 + .../tests/JedisClusterReplicateTest.java | 167 ++++++++++++ .../clients/jedis/tests/JedisClusterTest.java | 242 ++++++++++++++++-- .../tests/utils/JedisClusterTestUtil.java | 70 +++++ 10 files changed, 634 insertions(+), 36 deletions(-) create mode 100644 src/test/java/redis/clients/jedis/tests/JedisClusterReplicateTest.java create mode 100644 src/test/java/redis/clients/jedis/tests/utils/JedisClusterTestUtil.java diff --git a/Makefile b/Makefile index 1ac2bb9..467541e 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 diff --git a/pom.xml b/pom.xml index 1be71dd..ddc40c3 100644 --- a/pom.xml +++ b/pom.xml @@ -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/Client.java b/src/main/java/redis/clients/jedis/Client.java index ddc8646..69054ef 100644 --- a/src/main/java/redis/clients/jedis/Client.java +++ b/src/main/java/redis/clients/jedis/Client.java @@ -1,6 +1,6 @@ package redis.clients.jedis; -import redis.clients.util.SafeEncoder; +import static redis.clients.jedis.Protocol.toByteArray; import java.util.ArrayList; import java.util.HashMap; @@ -8,7 +8,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import static redis.clients.jedis.Protocol.toByteArray; +import redis.clients.util.SafeEncoder; public class Client extends BinaryClient implements Commands { public Client(final String host) { @@ -173,10 +173,6 @@ public class Client extends BinaryClient implements Commands { hincrBy(SafeEncoder.encode(key), SafeEncoder.encode(field), value); } - public void hincrByFloat(final String key, final String field, final double value) { - hincrByFloat(SafeEncoder.encode(key), SafeEncoder.encode(field), value); - } - public void hexists(final String key, final String field) { hexists(SafeEncoder.encode(key), SafeEncoder.encode(field)); } @@ -632,11 +628,10 @@ public class Client extends BinaryClient implements Commands { public void getbit(String key, long offset) { 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)); } @@ -789,7 +784,7 @@ public class Client extends BinaryClient implements Commands { public void pexpire(final String key, final int milliseconds) { pexpire(key, (long) milliseconds); } - + public void pexpire(final String key, final long milliseconds) { pexpire(SafeEncoder.encode(key), milliseconds); } @@ -840,6 +835,12 @@ public class Client extends BinaryClient implements Commands { destinationDb, timeout); } + public void hincrByFloat(final String key, final String field, + double increment) { + hincrByFloat(SafeEncoder.encode(key), SafeEncoder.encode(field), + increment); + } + @Deprecated /** * This method is deprecated due to bug (scan cursor should be unsigned long) @@ -974,4 +975,40 @@ public class Client extends BinaryClient implements Commands { 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); + } } diff --git a/src/main/java/redis/clients/jedis/ClusterCommands.java b/src/main/java/redis/clients/jedis/ClusterCommands.java index fff4533..b77069b 100644 --- a/src/main/java/redis/clients/jedis/ClusterCommands.java +++ b/src/main/java/redis/clients/jedis/ClusterCommands.java @@ -20,4 +20,22 @@ 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(); } diff --git a/src/main/java/redis/clients/jedis/Jedis.java b/src/main/java/redis/clients/jedis/Jedis.java index 48968df..8e8a258 100644 --- a/src/main/java/redis/clients/jedis/Jedis.java +++ b/src/main/java/redis/clients/jedis/Jedis.java @@ -3136,6 +3136,7 @@ public class Jedis extends BinaryJedis implements JedisCommands, return client.getIntegerReply(); } + public String psetex(final String key, final int milliseconds, final String value) { checkIsInMulti(); @@ -3430,6 +3431,60 @@ 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(); + } public String asking() { checkIsInMulti(); diff --git a/src/main/java/redis/clients/jedis/Protocol.java b/src/main/java/redis/clients/jedis/Protocol.java index 485c10b..3f77b9a 100644 --- a/src/main/java/redis/clients/jedis/Protocol.java +++ b/src/main/java/redis/clients/jedis/Protocol.java @@ -50,6 +50,15 @@ 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 PUBSUB_CHANNELS= "channels"; public static final String PUBSUB_NUMSUB = "numsub"; public static final String PUBSUB_NUM_PAT = "numpat"; 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/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..28af2a4 100644 --- a/src/test/java/redis/clients/jedis/tests/JedisClusterTest.java +++ b/src/test/java/redis/clients/jedis/tests/JedisClusterTest.java @@ -1,6 +1,8 @@ package redis.clients.jedis.tests; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; import org.junit.After; @@ -15,17 +17,21 @@ import redis.clients.jedis.JedisCluster; import redis.clients.jedis.exceptions.JedisAskDataException; import redis.clients.jedis.exceptions.JedisClusterException; import redis.clients.jedis.exceptions.JedisClusterMaxRedirectionsException; +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 +46,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 +76,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 +146,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 +156,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 +180,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 +191,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 +212,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 +224,181 @@ 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")); + } + + 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/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; + } + + +} From 1fba7de9bc5f60574af28f50327c321bd469d7d5 Mon Sep 17 00:00:00 2001 From: Marcos Nils Date: Sun, 25 May 2014 16:51:47 -0300 Subject: [PATCH 44/44] Change back commons pool to 2.0 as 2.2 isn't fully tested --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 38d94b2..ddc40c3 100644 --- a/pom.xml +++ b/pom.xml @@ -62,7 +62,7 @@ org.apache.commons commons-pool2 - 2.2 + 2.0 jar compile