();
+ 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);
}
@@ -1091,7 +1099,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));
}
@@ -1103,10 +1116,6 @@ public class BinaryClient extends Connection {
sendCommand(PTTL, key);
}
- public void incrByFloat(final byte[] key, final double increment) {
- sendCommand(INCRBYFLOAT, key, toByteArray(increment));
- }
-
public void psetex(final byte[] key, final int milliseconds,
final byte[] value) {
sendCommand(PSETEX, key, toByteArray(milliseconds), value);
@@ -1253,4 +1262,20 @@ public class BinaryClient extends Connection {
public void asking() {
sendCommand(Command.ASKING);
}
+
+ public void pfadd(final byte[] key, final byte[]... elements) {
+ sendCommand(PFADD, joinParameters(key, elements));
+ }
+
+ public void pfcount(final byte[] key) {
+ sendCommand(PFCOUNT, key);
+ }
+
+ public void pfcount(final byte[]...keys) {
+ sendCommand(PFCOUNT, keys);
+ }
+
+ public void pfmerge(final byte[] destkey, final byte[]... sourcekeys) {
+ sendCommand(PFMERGE, joinParameters(destkey, sourcekeys));
+ }
}
diff --git a/src/main/java/redis/clients/jedis/BinaryJedis.java b/src/main/java/redis/clients/jedis/BinaryJedis.java
index 9095712..781f17f 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;
@@ -633,6 +634,37 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands,
return client.getIntegerReply();
}
+ /**
+ * INCRBYFLOAT work just like {@link #incrBy(byte[]) INCRBY} but increments
+ * by floats instead of integers.
+ *
+ * INCRBYFLOAT commands are limited to double precision floating point
+ * values.
+ *
+ * Note: this is actually a string operation, that is, in Redis there are
+ * not "double" types. Simply the string stored at the key is parsed as a
+ * base double precision floating point value, incremented, and then
+ * converted back as a string. There is no DECRYBYFLOAT but providing a
+ * negative value will work as expected.
+ *
+ * Time complexity: O(1)
+ *
+ * @see #incr(byte[])
+ * @see #decr(byte[])
+ * @see #decrBy(byte[], long)
+ *
+ * @param key
+ * @param integer
+ * @return Integer reply, this commands will reply with the new value of key
+ * after the increment.
+ */
+ public Double incrByFloat(final byte[] key, final double integer) {
+ checkIsInMulti();
+ client.incrByFloat(key, integer);
+ String dval = client.getBulkReply();
+ return (dval != null ? new Double(dval) : null);
+ }
+
/**
* Increment the number stored at key by one. If the key does not exist or
* contains a value of a wrong type, set the key to the value of "0" before
@@ -825,6 +857,33 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands,
return client.getIntegerReply();
}
+ /**
+ * Increment the number stored at field in the hash at key by a double
+ * precision floating point value. If key does not exist, a new key holding
+ * a hash is created. If field does not exist or holds a string, the value
+ * is set to 0 before applying the operation. Since the value argument is
+ * signed you can use this command to perform both increments and
+ * decrements.
+ *
+ * The range of values supported by HINCRBYFLOAT is limited to double
+ * precision floating point values.
+ *
+ * Time complexity: O(1)
+ *
+ * @param key
+ * @param field
+ * @param value
+ * @return Double precision floating point reply The new value at field
+ * after the increment operation.
+ */
+ public Double hincrByFloat(final byte[] key, final byte[] field,
+ final double value) {
+ checkIsInMulti();
+ client.hincrByFloat(key, field, value);
+ final String dval = client.getBulkReply();
+ return (dval != null ? new Double(dval) : null);
+ }
+
/**
* Test for existence of a specified field in a hash.
*
@@ -3128,6 +3187,16 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands,
return client.getIntegerReply() == 1;
}
+ public Long bitpos(final byte[] key, final boolean value) {
+ return bitpos(key, value, new BitPosParams());
+ }
+
+ public Long bitpos(final byte[] key, final boolean value,
+ final BitPosParams params) {
+ client.bitpos(key, value, params);
+ return client.getIntegerReply();
+ }
+
public Long setrange(byte[] key, long offset, byte[] value) {
client.setrange(key, offset, value);
return client.getIntegerReply();
@@ -3316,7 +3385,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();
@@ -3334,13 +3408,6 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands,
return client.getIntegerReply();
}
- public Double incrByFloat(final byte[] key, final double increment) {
- checkIsInMulti();
- client.incrByFloat(key, increment);
- String relpy = client.getBulkReply();
- return (relpy != null ? new Double(relpy) : null);
- }
-
public String psetex(final byte[] key, final int milliseconds,
final byte[] value) {
checkIsInMulti();
@@ -3398,14 +3465,6 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands,
return client.getStatusCodeReply();
}
- public Double hincrByFloat(final byte[] key, final byte[] field,
- double increment) {
- checkIsInMulti();
- client.hincrByFloat(key, field, increment);
- String relpy = client.getBulkReply();
- return (relpy != null ? new Double(relpy) : null);
- }
-
/**
* Syncrhonous replication of Redis as described here:
* http://antirez.com/news/66
@@ -3419,4 +3478,99 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands,
return client.getIntegerReply();
}
+ @Override
+ public Long pfadd(final byte[] key, final byte[]... elements) {
+ checkIsInMulti();
+ client.pfadd(key, elements);
+ return client.getIntegerReply();
+ }
+
+ @Override
+ public long pfcount(final byte[] key) {
+ checkIsInMulti();
+ client.pfcount(key);
+ return client.getIntegerReply();
+ }
+
+ @Override
+ public String pfmerge(final byte[] destkey, final byte[]... sourcekeys) {
+ checkIsInMulti();
+ client.pfmerge(destkey, sourcekeys);
+ return client.getStatusCodeReply();
+ }
+
+ @Override
+ public Long pfcount(byte[]... keys) {
+ checkIsInMulti();
+ client.pfcount(keys);
+ return client.getIntegerReply();
+ }
+
+ public ScanResult scan(final byte[] cursor) {
+ return scan(cursor, new ScanParams());
+ }
+
+ public ScanResult scan(final byte[] cursor, final ScanParams params) {
+ checkIsInMulti();
+ client.scan(cursor, params);
+ List result = client.getObjectMultiBulkReply();
+ byte[] newcursor = (byte[]) result.get(0);
+ List rawResults = (List) result.get(1);
+ return new ScanResult(newcursor, rawResults);
+ }
+
+ public ScanResult> hscan(final byte[] key,
+ final byte[] cursor) {
+ return hscan(key, cursor, new ScanParams());
+ }
+
+ public ScanResult> hscan(final byte[] key,
+ final byte[] cursor, final ScanParams params) {
+ checkIsInMulti();
+ client.hscan(key, cursor, params);
+ List result = client.getObjectMultiBulkReply();
+ byte[] newcursor = (byte[]) result.get(0);
+ List> results = new ArrayList>();
+ List rawResults = (List) result.get(1);
+ Iterator iterator = rawResults.iterator();
+ while (iterator.hasNext()) {
+ results.add(new AbstractMap.SimpleEntry(iterator
+ .next(), iterator.next()));
+ }
+ return new ScanResult>(newcursor, results);
+ }
+
+ public ScanResult sscan(final byte[] key, final byte[] cursor) {
+ return sscan(key, cursor, new ScanParams());
+ }
+
+ public ScanResult sscan(final byte[] key, final byte[] cursor,
+ final ScanParams params) {
+ checkIsInMulti();
+ client.sscan(key, cursor, params);
+ List result = client.getObjectMultiBulkReply();
+ byte[] newcursor = (byte[]) result.get(0);
+ List rawResults = (List) result.get(1);
+ return new ScanResult(newcursor, rawResults);
+ }
+
+ public ScanResult zscan(final byte[] key, final byte[] cursor) {
+ return zscan(key, cursor, new ScanParams());
+ }
+
+ public ScanResult zscan(final byte[] key, final byte[] cursor,
+ final ScanParams params) {
+ checkIsInMulti();
+ client.zscan(key, cursor, params);
+ List result = client.getObjectMultiBulkReply();
+ byte[] newcursor = (byte[]) result.get(0);
+ List results = new ArrayList();
+ List rawResults = (List) result.get(1);
+ Iterator iterator = rawResults.iterator();
+ while (iterator.hasNext()) {
+ results.add(new Tuple(iterator.next(), Double.valueOf(SafeEncoder
+ .encode(iterator.next()))));
+ }
+ return new ScanResult(newcursor, results);
+ }
}
diff --git a/src/main/java/redis/clients/jedis/BinaryJedisCommands.java b/src/main/java/redis/clients/jedis/BinaryJedisCommands.java
index b229f97..ada68c2 100644
--- a/src/main/java/redis/clients/jedis/BinaryJedisCommands.java
+++ b/src/main/java/redis/clients/jedis/BinaryJedisCommands.java
@@ -47,6 +47,8 @@ public interface BinaryJedisCommands {
Long incrBy(byte[] key, long integer);
+ Double incrByFloat(byte[] key, double value);
+
Long incr(byte[] key);
Long append(byte[] key, byte[] value);
@@ -65,6 +67,8 @@ public interface BinaryJedisCommands {
Long hincrBy(byte[] key, byte[] field, long value);
+ Double hincrByFloat(byte[] key, byte[] field, double value);
+
Boolean hexists(byte[] key, byte[] field);
Long hdel(byte[] key, byte[]... field);
@@ -211,4 +215,8 @@ public interface BinaryJedisCommands {
Long bitcount(final byte[] key);
Long bitcount(final byte[] key, long start, long end);
+
+ Long pfadd(final byte[] key, final byte[]... elements);
+
+ long pfcount(final byte[] key);
}
diff --git a/src/main/java/redis/clients/jedis/BinaryRedisPipeline.java b/src/main/java/redis/clients/jedis/BinaryRedisPipeline.java
index 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/BinaryShardedJedis.java b/src/main/java/redis/clients/jedis/BinaryShardedJedis.java
index 5895f20..77695e7 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);
@@ -569,4 +579,17 @@ public class BinaryShardedJedis extends Sharded
Jedis j = getShard(key);
return j.bitcount(key, start, end);
}
+
+ @Override
+ public Long pfadd(final byte[] key, final byte[]... elements) {
+ Jedis j = getShard(key);
+ return j.pfadd(key, elements);
+ }
+
+ @Override
+ public long pfcount(final byte[] key) {
+ Jedis j = getShard(key);
+ return j.pfcount(key);
+ }
+
}
\ No newline at end of file
diff --git a/src/main/java/redis/clients/jedis/BitPosParams.java b/src/main/java/redis/clients/jedis/BitPosParams.java
new file mode 100644
index 0000000..e480686
--- /dev/null
+++ b/src/main/java/redis/clients/jedis/BitPosParams.java
@@ -0,0 +1,27 @@
+package redis.clients.jedis;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+public class BitPosParams {
+ private List params = new ArrayList();
+
+ protected BitPosParams() {
+ }
+
+ public BitPosParams(long start) {
+ params.add(Protocol.toByteArray(start));
+ }
+
+ public BitPosParams(long start, long end) {
+ this(start);
+
+ params.add(Protocol.toByteArray(end));
+ }
+
+ public Collection getParams() {
+ return Collections.unmodifiableCollection(params);
+ }
+}
diff --git a/src/main/java/redis/clients/jedis/Client.java b/src/main/java/redis/clients/jedis/Client.java
index 64f6f5c..69054ef 100644
--- a/src/main/java/redis/clients/jedis/Client.java
+++ b/src/main/java/redis/clients/jedis/Client.java
@@ -629,6 +629,9 @@ public class Client extends BinaryClient implements Commands {
getbit(SafeEncoder.encode(key), offset);
}
+ public void bitpos(final String key, final boolean value, final BitPosParams params) {
+ bitpos(SafeEncoder.encode(key), value, params);
+ }
public void setrange(String key, long offset, String value) {
setrange(SafeEncoder.encode(key), offset, SafeEncoder.encode(value));
}
@@ -777,7 +780,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);
}
@@ -951,4 +959,56 @@ public class Client extends BinaryClient implements Commands {
cluster(Protocol.CLUSTER_SETSLOT, String.valueOf(slot),
Protocol.CLUSTER_SETSLOT_IMPORTING, nodeId);
}
+
+ public void pfadd(String key, final String... elements) {
+ pfadd(SafeEncoder.encode(key), SafeEncoder.encodeMany(elements));
+ }
+
+ public void pfcount(final String key) {
+ pfcount(SafeEncoder.encode(key));
+ }
+
+ public void pfcount(final String...keys) {
+ pfcount(SafeEncoder.encodeMany(keys));
+ }
+
+ public void pfmerge(final String destkey, final String... sourcekeys) {
+ pfmerge(SafeEncoder.encode(destkey), SafeEncoder.encodeMany(sourcekeys));
+ }
+public void clusterSetSlotStable(final int slot) {
+ cluster(Protocol.CLUSTER_SETSLOT, String.valueOf(slot),
+ Protocol.CLUSTER_SETSLOT_STABLE);
+ }
+
+ public void clusterForget(final String nodeId) {
+ cluster(Protocol.CLUSTER_FORGET, nodeId);
+ }
+
+ public void clusterFlushSlots() {
+ cluster(Protocol.CLUSTER_FLUSHSLOT);
+ }
+
+ public void clusterKeySlot(final String key) {
+ cluster(Protocol.CLUSTER_KEYSLOT, key);
+ }
+
+ public void clusterCountKeysInSlot(final int slot) {
+ cluster(Protocol.CLUSTER_COUNTKEYINSLOT, String.valueOf(slot));
+ }
+
+ public void clusterSaveConfig() {
+ cluster(Protocol.CLUSTER_SAVECONFIG);
+ }
+
+ public void clusterReplicate(final String nodeId) {
+ cluster(Protocol.CLUSTER_REPLICATE, nodeId);
+ }
+
+ public void clusterSlaves(final String nodeId) {
+ cluster(Protocol.CLUSTER_SLAVES, nodeId);
+ }
+
+ public void clusterFailover() {
+ cluster(Protocol.CLUSTER_FAILOVER);
+ }
}
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/Commands.java b/src/main/java/redis/clients/jedis/Commands.java
index a104ca9..93fc55f 100644
--- a/src/main/java/redis/clients/jedis/Commands.java
+++ b/src/main/java/redis/clients/jedis/Commands.java
@@ -61,6 +61,8 @@ public interface Commands {
public void incrBy(final String key, final long integer);
+ public void incrByFloat(final String key, final double value);
+
public void incr(final String key);
public void append(final String key, final String value);
@@ -79,6 +81,8 @@ public interface Commands {
public void hincrBy(final String key, final String field, final long value);
+ public void hincrByFloat(final String key, final String field, final double value);
+
public void hexists(final String key, final String field);
public void hdel(final String key, final String... fields);
diff --git a/src/main/java/redis/clients/jedis/Jedis.java b/src/main/java/redis/clients/jedis/Jedis.java
index a8aa286..fbf430c 100644
--- a/src/main/java/redis/clients/jedis/Jedis.java
+++ b/src/main/java/redis/clients/jedis/Jedis.java
@@ -1,21 +1,14 @@
package redis.clients.jedis;
-import java.net.URI;
-import java.util.AbstractMap;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
import redis.clients.jedis.BinaryClient.LIST_POSITION;
import redis.clients.util.Pool;
import redis.clients.util.SafeEncoder;
import redis.clients.util.Slowlog;
+import java.net.URI;
+import java.util.*;
+import java.util.Map.Entry;
+
public class Jedis extends BinaryJedis implements JedisCommands,
MultiKeyCommands, AdvancedJedisCommands, ScriptingCommands,
BasicCommands, ClusterCommands {
@@ -551,6 +544,31 @@ public class Jedis extends BinaryJedis implements JedisCommands,
return client.getIntegerReply();
}
+ /**
+ * INCRBYFLOAT
+ *
+ * INCRBYFLOAT commands are limited to double precision floating point values.
+ *
+ * Note: this is actually a string operation, that is, in Redis there are
+ * not "double" types. Simply the string stored at the key is parsed as a
+ * base double precision floating point value, incremented, and then
+ * converted back as a string. There is no DECRYBYFLOAT but providing a
+ * negative value will work as expected.
+ *
+ * Time complexity: O(1)
+ *
+ * @param key
+ * @param value
+ * @return Double reply, this commands will reply with the new value of key
+ * after the increment.
+ */
+ public Double incrByFloat(final String key, final double value) {
+ checkIsInMulti();
+ client.incrByFloat(key, value);
+ String dval = client.getBulkReply();
+ return (dval != null ? new Double(dval) : null);
+ }
+
/**
* Increment the number stored at key by one. If the key does not exist or
* contains a value of a wrong type, set the key to the value of "0" before
@@ -743,6 +761,32 @@ public class Jedis extends BinaryJedis implements JedisCommands,
return client.getIntegerReply();
}
+ /**
+ * Increment the number stored at field in the hash at key by a double
+ * precision floating point value. If key does not exist,
+ * a new key holding a hash is created. If field does not
+ * exist or holds a string, the value is set to 0 before applying the
+ * operation. Since the value argument is signed you can use this command to
+ * perform both increments and decrements.
+ *
+ * The range of values supported by HINCRBYFLOAT is limited to
+ * double precision floating point values.
+ *
+ * Time complexity: O(1)
+ *
+ * @param key
+ * @param field
+ * @param value
+ * @return Double precision floating point reply The new value at field after the increment
+ * operation.
+ */
+ public Double hincrByFloat(final String key, final String field, final double value) {
+ checkIsInMulti();
+ client.hincrByFloat(key, field, value);
+ final String dval = client.getBulkReply();
+ return (dval != null ? new Double(dval) : null);
+ }
+
/**
* Test for existence of a specified field in a hash.
*
@@ -2663,6 +2707,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
@@ -3064,7 +3117,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();
@@ -3082,12 +3140,6 @@ public class Jedis extends BinaryJedis implements JedisCommands,
return client.getIntegerReply();
}
- public Double incrByFloat(final String key, final double increment) {
- checkIsInMulti();
- client.incrByFloat(key, increment);
- String relpy = client.getBulkReply();
- return (relpy != null ? new Double(relpy) : null);
- }
public String psetex(final String key, final int milliseconds,
final String value) {
@@ -3128,14 +3180,6 @@ public class Jedis extends BinaryJedis implements JedisCommands,
return client.getStatusCodeReply();
}
- public Double hincrByFloat(final String key, final String field,
- double increment) {
- checkIsInMulti();
- client.hincrByFloat(key, field, increment);
- String relpy = client.getBulkReply();
- return (relpy != null ? new Double(relpy) : null);
- }
-
@Deprecated
/**
* This method is deprecated due to bug (scan cursor should be unsigned long)
@@ -3391,6 +3435,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();
@@ -3431,6 +3529,31 @@ public class Jedis extends BinaryJedis implements JedisCommands,
}
public void setDataSource(Pool jedisPool) {
- this.dataSource = jedisPool;
+ this.dataSource = jedisPool;
+ }
+
+ public Long pfadd(final String key, final String... elements) {
+ checkIsInMulti();
+ client.pfadd(key, elements);
+ return client.getIntegerReply();
+ }
+
+ public long pfcount(final String key) {
+ checkIsInMulti();
+ client.pfcount(key);
+ return client.getIntegerReply();
+ }
+
+ @Override
+ public long pfcount(String... keys) {
+ checkIsInMulti();
+ client.pfcount(keys);
+ return client.getIntegerReply();
+ }
+
+ public String pfmerge(final String destkey, final String... sourcekeys) {
+ checkIsInMulti();
+ client.pfmerge(destkey, sourcekeys);
+ return client.getStatusCodeReply();
}
}
diff --git a/src/main/java/redis/clients/jedis/JedisCluster.java b/src/main/java/redis/clients/jedis/JedisCluster.java
index 1f645ea..f6cb6fd 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;
@@ -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,
@@ -1129,7 +1141,7 @@ public class JedisCluster implements JedisCommands, BasicCommands {
public Long execute(Jedis connection) {
return connection.del(key);
}
- }.run(null);
+ }.run(key);
}
@Override
@@ -1481,4 +1493,26 @@ public class JedisCluster implements JedisCommands, BasicCommands {
}
}.run(null);
}
+
+ @Override
+ public Long pfadd(final String key, final String... elements) {
+ return new JedisClusterCommand(connectionHandler,
+ timeout, maxRedirections) {
+ @Override
+ public Long execute(Jedis connection) {
+ return connection.pfadd(key, elements);
+ }
+ }.run(key);
+ }
+
+ @Override
+ public long pfcount(final String key) {
+ return new JedisClusterCommand(connectionHandler,
+ timeout, maxRedirections) {
+ @Override
+ public Long execute(Jedis connection) {
+ return connection.pfcount(key);
+ }
+ }.run(key);
+ }
}
diff --git a/src/main/java/redis/clients/jedis/JedisClusterCommand.java b/src/main/java/redis/clients/jedis/JedisClusterCommand.java
index 6e110bc..051d5cd 100644
--- a/src/main/java/redis/clients/jedis/JedisClusterCommand.java
+++ b/src/main/java/redis/clients/jedis/JedisClusterCommand.java
@@ -3,19 +3,17 @@ 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.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 +24,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..e6eb01f 100644
--- a/src/main/java/redis/clients/jedis/JedisClusterConnectionHandler.java
+++ b/src/main/java/redis/clients/jedis/JedisClusterConnectionHandler.java
@@ -1,21 +1,26 @@
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();
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,71 +34,94 @@ 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);
- populateNodeSlots(nodeInfo, 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(targetNode.getHost()
- + targetNode.getPort());
+ JedisPool targetPool = nodes.get(getNodeKey(targetNode));
+
+ 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);
+ }
+ }
+
protected JedisPool getRandomConnection() {
Object[] nodeArray = nodes.values().toArray();
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();
+ }
+
+ private void setNodeIfNotExist(HostAndPort node) {
+ String nodeKey = getNodeKey(node);
+ if (nodes.containsKey(nodeKey))
+ return;
+
+ JedisPool nodePool = new JedisPool(node.getHost(), node.getPort());
+ nodes.put(nodeKey, nodePool);
+ }
}
diff --git a/src/main/java/redis/clients/jedis/JedisCommands.java b/src/main/java/redis/clients/jedis/JedisCommands.java
index 5952bdb..d89f508 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);
@@ -240,4 +243,9 @@ 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/JedisPool.java b/src/main/java/redis/clients/jedis/JedisPool.java
index 3aaa6ff..0fbfdfe 100644
--- a/src/main/java/redis/clients/jedis/JedisPool.java
+++ b/src/main/java/redis/clients/jedis/JedisPool.java
@@ -87,11 +87,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/jedis/JedisSentinelPool.java b/src/main/java/redis/clients/jedis/JedisSentinelPool.java
index be8bad1..0ff0dff 100644
--- a/src/main/java/redis/clients/jedis/JedisSentinelPool.java
+++ b/src/main/java/redis/clients/jedis/JedisSentinelPool.java
@@ -169,15 +169,17 @@ public class JedisSentinelPool extends Pool {
return jedis;
}
- @Override
public void returnBrokenResource(final Jedis resource) {
- returnBrokenResourceObject(resource);
+ if (resource != null) {
+ returnBrokenResourceObject(resource);
+ }
}
- @Override
public void returnResource(final Jedis resource) {
- resource.resetState();
- returnResourceObject(resource);
+ if (resource != null) {
+ resource.resetState();
+ returnResourceObject(resource);
+ }
}
protected class JedisPubSubAdapter extends JedisPubSub {
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;
}
}
diff --git a/src/main/java/redis/clients/jedis/MultiKeyBinaryCommands.java b/src/main/java/redis/clients/jedis/MultiKeyBinaryCommands.java
index e6ea8fd..186d822 100644
--- a/src/main/java/redis/clients/jedis/MultiKeyBinaryCommands.java
+++ b/src/main/java/redis/clients/jedis/MultiKeyBinaryCommands.java
@@ -69,4 +69,8 @@ public interface MultiKeyBinaryCommands {
byte[] randomBinaryKey();
Long bitop(BitOP op, final byte[] destKey, byte[]... srcKeys);
+
+ String pfmerge(final byte[] destkey, final byte[]... sourcekeys);
+
+ Long pfcount(byte[]... keys);
}
diff --git a/src/main/java/redis/clients/jedis/MultiKeyBinaryRedisPipeline.java b/src/main/java/redis/clients/jedis/MultiKeyBinaryRedisPipeline.java
index fd71016..a3f8716 100644
--- a/src/main/java/redis/clients/jedis/MultiKeyBinaryRedisPipeline.java
+++ b/src/main/java/redis/clients/jedis/MultiKeyBinaryRedisPipeline.java
@@ -65,4 +65,8 @@ public interface MultiKeyBinaryRedisPipeline {
Response randomKeyBinary();
Response bitop(BitOP op, final byte[] destKey, byte[]... srcKeys);
+
+ Response pfmerge(final byte[] destkey, final byte[]... sourcekeys);
+
+ Response pfcount(final byte[] ... keys);
}
diff --git a/src/main/java/redis/clients/jedis/MultiKeyCommands.java b/src/main/java/redis/clients/jedis/MultiKeyCommands.java
index f03f82c..3f91ea9 100644
--- a/src/main/java/redis/clients/jedis/MultiKeyCommands.java
+++ b/src/main/java/redis/clients/jedis/MultiKeyCommands.java
@@ -79,4 +79,8 @@ public interface MultiKeyCommands {
ScanResult scan(int cursor);
ScanResult scan(final String cursor);
+
+ String pfmerge(final String destkey, final String... sourcekeys);
+
+ long pfcount(final String...keys);
}
diff --git a/src/main/java/redis/clients/jedis/MultiKeyCommandsPipeline.java b/src/main/java/redis/clients/jedis/MultiKeyCommandsPipeline.java
index 92c8d5a..e2de238 100644
--- a/src/main/java/redis/clients/jedis/MultiKeyCommandsPipeline.java
+++ b/src/main/java/redis/clients/jedis/MultiKeyCommandsPipeline.java
@@ -64,4 +64,8 @@ public interface MultiKeyCommandsPipeline {
Response randomKey();
Response bitop(BitOP op, final String destKey, String... srcKeys);
+
+ Response pfmerge(final String destkey, final String... sourcekeys);
+
+ Response pfcount(final String...keys);
}
diff --git a/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java b/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java
index fa7ae6e..31054ce 100644
--- a/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java
+++ b/src/main/java/redis/clients/jedis/MultiKeyPipelineBase.java
@@ -367,6 +367,11 @@ abstract class MultiKeyPipelineBase extends PipelineBase implements
client.info();
return getResponse(BuilderFactory.STRING);
}
+
+ public Response> time() {
+ client.time();
+ return getResponse(BuilderFactory.STRING_LIST);
+ }
public Response dbSize() {
client.dbSize();
@@ -446,4 +451,28 @@ abstract class MultiKeyPipelineBase extends PipelineBase implements
client.clusterSetSlotImporting(slot, nodeId);
return getResponse(BuilderFactory.STRING);
}
+
+ @Override
+ public Response pfmerge(byte[] destkey, byte[]... sourcekeys) {
+ client.pfmerge(destkey, sourcekeys);
+ return getResponse(BuilderFactory.STRING);
+ }
+
+ @Override
+ public Response pfmerge(String destkey, String... sourcekeys) {
+ client.pfmerge(destkey, sourcekeys);
+ return getResponse(BuilderFactory.STRING);
+ }
+
+ @Override
+ public Response pfcount(String...keys) {
+ client.pfcount(keys);
+ return getResponse(BuilderFactory.LONG);
+ }
+
+ @Override
+ public Response pfcount(final byte[] ... keys) {
+ client.pfcount(keys);
+ return getResponse(BuilderFactory.LONG);
+ }
}
diff --git a/src/main/java/redis/clients/jedis/Pipeline.java b/src/main/java/redis/clients/jedis/Pipeline.java
index 97f856b..4b19b5b 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);
}
@@ -98,24 +104,34 @@ 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);
currentMulti = null;
return response;
}
public Response multi() {
+ if (currentMulti != null)
+ throw new JedisDataException("MULTI calls can not be nested");
+
client.multi();
Response response = getResponse(BuilderFactory.STRING); // Expecting
// OK
currentMulti = new MultiResponseBuilder();
return response;
}
-
+
}
diff --git a/src/main/java/redis/clients/jedis/PipelineBase.java b/src/main/java/redis/clients/jedis/PipelineBase.java
index 3183ba1..3972ad4 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);
@@ -1080,12 +1098,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);
}
@@ -1204,4 +1232,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/Protocol.java b/src/main/java/redis/clients/jedis/Protocol.java
index a753f96..3f77b9a 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;
@@ -48,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";
@@ -96,6 +107,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);
}
@@ -204,7 +217,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, BITPOS, SETRANGE, GETRANGE, EVAL, EVALSHA, SCRIPT, SLOWLOG, OBJECT, BITCOUNT, BITOP, SENTINEL, DUMP, RESTORE, PEXPIRE, PEXPIREAT, PTTL, INCRBYFLOAT, PSETEX, CLIENT, TIME, MIGRATE, HINCRBYFLOAT, SCAN, HSCAN, SSCAN, ZSCAN, WAIT, CLUSTER, ASKING, PFADD, PFCOUNT, PFMERGE;
public final byte[] raw;
diff --git a/src/main/java/redis/clients/jedis/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);
}
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/main/java/redis/clients/jedis/ScanParams.java b/src/main/java/redis/clients/jedis/ScanParams.java
index 980bb13..a8b8ac7 100644
--- a/src/main/java/redis/clients/jedis/ScanParams.java
+++ b/src/main/java/redis/clients/jedis/ScanParams.java
@@ -13,15 +13,24 @@ import redis.clients.util.SafeEncoder;
public class ScanParams {
private List params = new ArrayList();
public final static String SCAN_POINTER_START = String.valueOf(0);
+ public final static byte[] SCAN_POINTER_START_BINARY = SafeEncoder.encode(SCAN_POINTER_START);
- public void match(final String pattern) {
+ public ScanParams match(final byte[] pattern) {
+ params.add(MATCH.raw);
+ params.add(pattern);
+ return this;
+ }
+
+ public ScanParams match(final String pattern) {
params.add(MATCH.raw);
params.add(SafeEncoder.encode(pattern));
+ return this;
}
- public void count(final int count) {
+ public ScanParams count(final int count) {
params.add(COUNT.raw);
params.add(Protocol.toByteArray(count));
+ return this;
}
public Collection getParams() {
diff --git a/src/main/java/redis/clients/jedis/ScanResult.java b/src/main/java/redis/clients/jedis/ScanResult.java
index 9afe27d..199689e 100644
--- a/src/main/java/redis/clients/jedis/ScanResult.java
+++ b/src/main/java/redis/clients/jedis/ScanResult.java
@@ -2,8 +2,10 @@ package redis.clients.jedis;
import java.util.List;
+import redis.clients.util.SafeEncoder;
+
public class ScanResult {
- private String cursor;
+ private byte[] cursor;
private List results;
@Deprecated
@@ -13,15 +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 = cursor;
this.results = results;
}
-
+
@Deprecated
/**
* This method is deprecated due to bug (scan cursor should be unsigned long)
@@ -30,13 +35,17 @@ 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 SafeEncoder.encode(cursor);
+ }
+
+ public byte[] getCursorAsBytes() {
return cursor;
}
diff --git a/src/main/java/redis/clients/jedis/ShardedJedis.java b/src/main/java/redis/clients/jedis/ShardedJedis.java
index 0ebc9f0..9691448 100644
--- a/src/main/java/redis/clients/jedis/ShardedJedis.java
+++ b/src/main/java/redis/clients/jedis/ShardedJedis.java
@@ -1,14 +1,15 @@
package redis.clients.jedis;
import java.io.Closeable;
+import redis.clients.jedis.BinaryClient.LIST_POSITION;
+import redis.clients.util.Hashing;
+
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Pattern;
-import redis.clients.jedis.BinaryClient.LIST_POSITION;
-import redis.clients.util.Hashing;
import redis.clients.util.Pool;
public class ShardedJedis extends BinaryShardedJedis implements JedisCommands,
@@ -38,6 +39,13 @@ public class ShardedJedis extends BinaryShardedJedis implements JedisCommands,
return j.set(key, value);
}
+ @Override
+ public String set(String key, String value, String nxxx, String expx,
+ long time) {
+ Jedis j = getShard(key);
+ return j.set(key, value, nxxx, expx, time);
+ }
+
public String get(String key) {
Jedis j = getShard(key);
return j.get(key);
@@ -138,6 +146,11 @@ public class ShardedJedis extends BinaryShardedJedis implements JedisCommands,
return j.incrBy(key, integer);
}
+ public Double incrByFloat(String key, double integer) {
+ Jedis j = getShard(key);
+ return j.incrByFloat(key, integer);
+ }
+
public Long incr(String key) {
Jedis j = getShard(key);
return j.incr(key);
@@ -183,6 +196,11 @@ public class ShardedJedis extends BinaryShardedJedis implements JedisCommands,
return j.hincrBy(key, field, value);
}
+ public Double hincrByFloat(String key, String field, double value) {
+ Jedis j = getShard(key);
+ return j.hincrByFloat(key, field, value);
+ }
+
public Boolean hexists(String key, String field) {
Jedis j = getShard(key);
return j.hexists(key, field);
@@ -610,4 +628,15 @@ public class ShardedJedis extends BinaryShardedJedis implements JedisCommands,
jedis.resetState();
}
}
+
+ public Long pfadd(String key, String... elements) {
+ Jedis j = getShard(key);
+ return j.pfadd(key, elements);
+ }
+
+ @Override
+ public long pfcount(String key) {
+ Jedis j = getShard(key);
+ return j.pfcount(key);
+ }
}
diff --git a/src/main/java/redis/clients/jedis/ShardedJedisPool.java b/src/main/java/redis/clients/jedis/ShardedJedisPool.java
index 5cdfd06..f50f3e7 100644
--- a/src/main/java/redis/clients/jedis/ShardedJedisPool.java
+++ b/src/main/java/redis/clients/jedis/ShardedJedisPool.java
@@ -31,26 +31,29 @@ public class ShardedJedisPool extends Pool {
List shards, Hashing algo, Pattern keyTagPattern) {
super(poolConfig, new ShardedJedisFactory(shards, algo, keyTagPattern));
}
-
+
@Override
public ShardedJedis getResource() {
ShardedJedis jedis = super.getResource();
jedis.setDataSource(this);
return jedis;
}
-
+
@Override
public void returnBrokenResource(final ShardedJedis resource) {
- returnBrokenResourceObject(resource);
+ if (resource != null) {
+ returnBrokenResourceObject(resource);
+ }
}
@Override
public void returnResource(final ShardedJedis resource) {
- resource.resetState();
- returnResourceObject(resource);
+ if (resource != null) {
+ resource.resetState();
+ returnResourceObject(resource);
+ }
}
-
/**
* PoolableObjectFactory custom impl.
*/
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/main/java/redis/clients/util/Pool.java b/src/main/java/redis/clients/util/Pool.java
index 09d8ebb..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) {
@@ -54,11 +57,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 +88,4 @@ public abstract class Pool {
throw new JedisException("Could not destroy the pool", e);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/test/java/redis/clients/jedis/tests/HostAndPortUtil.java b/src/test/java/redis/clients/jedis/tests/HostAndPortUtil.java
index cb7a58b..b2c9cf0 100644
--- a/src/test/java/redis/clients/jedis/tests/HostAndPortUtil.java
+++ b/src/test/java/redis/clients/jedis/tests/HostAndPortUtil.java
@@ -27,6 +27,10 @@ public class HostAndPortUtil {
clusterHostAndPortList.add(new HostAndPort("localhost", 7379));
clusterHostAndPortList.add(new HostAndPort("localhost", 7380));
clusterHostAndPortList.add(new HostAndPort("localhost", 7381));
+ clusterHostAndPortList.add(new HostAndPort("localhost", 7382));
+ clusterHostAndPortList.add(new HostAndPort("localhost", 7383));
+ clusterHostAndPortList.add(new HostAndPort("localhost", 7384));
+ clusterHostAndPortList.add(new HostAndPort("localhost", 7385));
String envRedisHosts = System.getProperty("redis-hosts");
String envSentinelHosts = System.getProperty("sentinel-hosts");
diff --git a/src/test/java/redis/clients/jedis/tests/JedisClusterNodeInformationParserTest.java b/src/test/java/redis/clients/jedis/tests/JedisClusterNodeInformationParserTest.java
new file mode 100644
index 0000000..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));
+ }
+
+}
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/JedisPoolTest.java b/src/test/java/redis/clients/jedis/tests/JedisPoolTest.java
index 7e011c0..7d8e611 100644
--- a/src/test/java/redis/clients/jedis/tests/JedisPoolTest.java
+++ b/src/test/java/redis/clients/jedis/tests/JedisPoolTest.java
@@ -228,4 +228,14 @@ public class JedisPoolTest extends Assert {
jedis2.close();
}
}
-}
\ No newline at end of file
+
+ @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);
+ }
+}
diff --git a/src/test/java/redis/clients/jedis/tests/JedisSentinelPoolTest.java b/src/test/java/redis/clients/jedis/tests/JedisSentinelPoolTest.java
index 46c37a8..205d90a 100644
--- a/src/test/java/redis/clients/jedis/tests/JedisSentinelPoolTest.java
+++ b/src/test/java/redis/clients/jedis/tests/JedisSentinelPoolTest.java
@@ -103,6 +103,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();
diff --git a/src/test/java/redis/clients/jedis/tests/PipeliningTest.java b/src/test/java/redis/clients/jedis/tests/PipeliningTest.java
index aed67dc..29b2f86 100755
--- a/src/test/java/redis/clients/jedis/tests/PipeliningTest.java
+++ b/src/test/java/redis/clients/jedis/tests/PipeliningTest.java
@@ -251,6 +251,47 @@ 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(expected = JedisDataException.class)
+ public void pipelineExecShoudThrowJedisDataExceptionWhenNotInMulti() {
+ Pipeline pipeline = jedis.pipelined();
+ pipeline.exec();
+ }
+
+ @Test(expected = JedisDataException.class)
+ public void pipelineDiscardShoudThrowJedisDataExceptionWhenNotInMulti() {
+ Pipeline pipeline = jedis.pipelined();
+ pipeline.discard();
+ }
+
+ @Test(expected = JedisDataException.class)
+ public void pipelineMultiShoudThrowJedisDataExceptionWhenAlreadyInMulti() {
+ Pipeline pipeline = jedis.pipelined();
+ pipeline.multi();
+ pipeline.set("foo", "3");
+ pipeline.multi();
+ }
+
@Test
public void testDiscardInPipeline() {
Pipeline pipeline = jedis.pipelined();
diff --git a/src/test/java/redis/clients/jedis/tests/commands/AllKindOfValuesCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/AllKindOfValuesCommandsTest.java
index 057537c..2647f07 100644
--- a/src/test/java/redis/clients/jedis/tests/commands/AllKindOfValuesCommandsTest.java
+++ b/src/test/java/redis/clients/jedis/tests/commands/AllKindOfValuesCommandsTest.java
@@ -11,6 +11,7 @@ import redis.clients.jedis.ScanResult;
import redis.clients.jedis.exceptions.JedisDataException;
import redis.clients.util.SafeEncoder;
import static redis.clients.jedis.ScanParams.SCAN_POINTER_START;
+import static redis.clients.jedis.ScanParams.SCAN_POINTER_START_BINARY;
public class AllKindOfValuesCommandsTest extends JedisCommandTestBase {
final byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 };
@@ -474,9 +475,16 @@ public class AllKindOfValuesCommandsTest extends JedisCommandTestBase {
long status = jedis.pexpire("foo", 10000);
assertEquals(0, status);
- jedis.set("foo", "bar");
- status = jedis.pexpire("foo", 10000);
+ jedis.set("foo1", "bar1");
+ status = jedis.pexpire("foo1", 10000);
assertEquals(1, status);
+
+ jedis.set("foo2", "bar2");
+ status = jedis.pexpire("foo2", 200000000000L);
+ assertEquals(1, status);
+
+ long pttl = jedis.pttl("foo2");
+ assertTrue(pttl > 100000000000L);
}
@Test
@@ -515,8 +523,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.getCursorAsBytes());
+ assertFalse(bResult.getResult().isEmpty());
}
-
+
@Test
public void scanMatch() {
ScanParams params = new ScanParams();
@@ -529,6 +543,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.getCursorAsBytes());
+ assertFalse(bResult.getResult().isEmpty());
}
@Test
@@ -543,5 +570,17 @@ public class AllKindOfValuesCommandsTest extends JedisCommandTestBase {
ScanResult result = jedis.scan(SCAN_POINTER_START, params);
assertFalse(result.getResult().isEmpty());
+
+ // binary
+ params = new ScanParams();
+ params.count(2);
+
+ jedis.set(bfoo1, bbar);
+ jedis.set(bfoo2, bbar);
+ jedis.set(bfoo3, bbar);
+
+ ScanResult bResult = jedis.scan(SCAN_POINTER_START_BINARY, params);
+
+ assertFalse(bResult.getResult().isEmpty());
}
}
diff --git a/src/test/java/redis/clients/jedis/tests/commands/BitCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/BitCommandsTest.java
index fb14f21..b54ca95 100644
--- a/src/test/java/redis/clients/jedis/tests/commands/BitCommandsTest.java
+++ b/src/test/java/redis/clients/jedis/tests/commands/BitCommandsTest.java
@@ -3,6 +3,8 @@ package redis.clients.jedis.tests.commands;
import org.junit.Test;
import redis.clients.jedis.BitOP;
+import redis.clients.jedis.BitPosParams;
+import redis.clients.jedis.Protocol;
public class BitCommandsTest extends JedisCommandTestBase {
@Test
@@ -20,6 +22,111 @@ public class BitCommandsTest extends JedisCommandTestBase {
assertTrue(bbit);
}
+ @Test
+ public void bitpos() {
+ String foo = "foo";
+
+ jedis.set(foo, String.valueOf(0));
+
+ jedis.setbit(foo, 3, true);
+ jedis.setbit(foo, 7, true);
+ jedis.setbit(foo, 13, true);
+ jedis.setbit(foo, 39, true);
+
+ /*
+ * byte: 0 1 2 3 4
+ * bit: 00010001 / 00000100 / 00000000 / 00000000 / 00000001
+ */
+ long offset = jedis.bitpos(foo, true);
+ assertEquals(2, offset);
+ offset = jedis.bitpos(foo, false);
+ assertEquals(0, offset);
+
+ offset = jedis.bitpos(foo, true, new BitPosParams(1));
+ assertEquals(13, offset);
+ offset = jedis.bitpos(foo, false, new BitPosParams(1));
+ assertEquals(8, offset);
+
+ offset = jedis.bitpos(foo, true, new BitPosParams(2, 3));
+ assertEquals(-1, offset);
+ offset = jedis.bitpos(foo, false, new BitPosParams(2, 3));
+ assertEquals(16, offset);
+
+ offset = jedis.bitpos(foo, true, new BitPosParams(3, 4));
+ assertEquals(39, offset);
+ }
+
+ @Test
+ public void bitposBinary() {
+ // binary
+ byte[] bfoo = { 0x01, 0x02, 0x03, 0x04 };
+
+ jedis.set(bfoo, Protocol.toByteArray(0));
+
+ jedis.setbit(bfoo, 3, true);
+ jedis.setbit(bfoo, 7, true);
+ jedis.setbit(bfoo, 13, true);
+ jedis.setbit(bfoo, 39, true);
+
+ /*
+ * byte: 0 1 2 3 4
+ * bit: 00010001 / 00000100 / 00000000 / 00000000 / 00000001
+ */
+ long offset = jedis.bitpos(bfoo, true);
+ assertEquals(2, offset);
+ offset = jedis.bitpos(bfoo, false);
+ assertEquals(0, offset);
+
+ offset = jedis.bitpos(bfoo, true, new BitPosParams(1));
+ assertEquals(13, offset);
+ offset = jedis.bitpos(bfoo, false, new BitPosParams(1));
+ assertEquals(8, offset);
+
+ offset = jedis.bitpos(bfoo, true, new BitPosParams(2, 3));
+ assertEquals(-1, offset);
+ offset = jedis.bitpos(bfoo, false, new BitPosParams(2, 3));
+ assertEquals(16, offset);
+
+ offset = jedis.bitpos(bfoo, true, new BitPosParams(3, 4));
+ assertEquals(39, offset);
+ }
+
+ @Test
+ public void bitposWithNoMatchingBitExist() {
+ String foo = "foo";
+
+ jedis.set(foo, String.valueOf(0));
+ for (int idx = 0; idx < 8; idx++) {
+ jedis.setbit(foo, idx, true);
+ }
+
+ /*
+ * byte: 0
+ * bit: 11111111
+ */
+ long offset = jedis.bitpos(foo, false);
+ // offset should be last index + 1
+ assertEquals(8, offset);
+ }
+
+ @Test
+ public void bitposWithNoMatchingBitExistWithinRange() {
+ String foo = "foo";
+
+ jedis.set(foo, String.valueOf(0));
+ for (int idx = 0; idx < 8 * 5; idx++) {
+ jedis.setbit(foo, idx, true);
+ }
+
+ /*
+ * byte: 0 1 2 3 4
+ * bit: 11111111 / 11111111 / 11111111 / 11111111 / 11111111
+ */
+ long offset = jedis.bitpos(foo, false, new BitPosParams(2, 3));
+ // offset should be -1
+ assertEquals(-1, offset);
+ }
+
@Test
public void setAndgetrange() {
jedis.set("key1", "Hello World");
diff --git a/src/test/java/redis/clients/jedis/tests/commands/HashesCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/HashesCommandsTest.java
index 5f9ac4e..aa86993 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");
@@ -147,6 +153,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();
@@ -300,6 +325,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.getCursorAsBytes());
+ assertFalse(bResult.getResult().isEmpty());
}
@Test
@@ -315,6 +348,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.getCursorAsBytes());
+ assertFalse(bResult.getResult().isEmpty());
}
@Test
@@ -330,5 +377,18 @@ public class HashesCommandsTest extends JedisCommandTestBase {
SCAN_POINTER_START, params);
assertFalse(result.getResult().isEmpty());
+
+ // binary
+ params = new ScanParams();
+ params.count(2);
+
+ jedis.hset(bfoo, bbar, bcar);
+ jedis.hset(bfoo, bbar1, bcar);
+ jedis.hset(bfoo, bbar2, bcar);
+ jedis.hset(bfoo, bbar3, bcar);
+
+ ScanResult> bResult = jedis.hscan(bfoo, SCAN_POINTER_START_BINARY, params);
+
+ assertFalse(bResult.getResult().isEmpty());
}
}
diff --git a/src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java
new file mode 100644
index 0000000..0b25366
--- /dev/null
+++ b/src/test/java/redis/clients/jedis/tests/commands/HyperLogLogCommandsTest.java
@@ -0,0 +1,130 @@
+package redis.clients.jedis.tests.commands;
+
+import org.junit.Test;
+
+import redis.clients.util.SafeEncoder;
+
+public class HyperLogLogCommandsTest extends JedisCommandTestBase {
+
+ @Test
+ public void pfadd() {
+ long status = jedis.pfadd("foo", "a");
+ assertEquals(1, status);
+
+ status = jedis.pfadd("foo", "a");
+ assertEquals(0, status);
+ }
+
+ @Test
+ public void pfaddBinary() {
+ byte[] bFoo = SafeEncoder.encode("foo");
+ byte[] bBar = SafeEncoder.encode("bar");
+ byte[] bBar2 = SafeEncoder.encode("bar2");
+
+ long status = jedis.pfadd(bFoo, bBar, bBar2);
+ assertEquals(1, status);
+
+ status = jedis.pfadd(bFoo, bBar, bBar2);
+ assertEquals(0, status);
+ }
+
+ @Test
+ public void pfcount() {
+ long status = jedis.pfadd("hll", "foo", "bar", "zap");
+ assertEquals(1, status);
+
+ status = jedis.pfadd("hll", "zap", "zap", "zap");
+ assertEquals(0, status);
+
+ status = jedis.pfadd("hll", "foo", "bar");
+ assertEquals(0, status);
+
+ status = jedis.pfcount("hll");
+ assertEquals(3, status);
+ }
+
+ @Test
+ public void pfcounts() {
+ long status = jedis.pfadd("hll_1", "foo", "bar", "zap");
+ assertEquals(1, status);
+ status = jedis.pfadd("hll_2", "foo", "bar", "zap");
+ assertEquals(1, status);
+
+ status = jedis.pfadd("hll_3", "foo", "bar", "baz");
+ assertEquals(1, status);
+ status = jedis.pfcount("hll_1");
+ assertEquals(3, status);
+ status = jedis.pfcount("hll_2");
+ assertEquals(3, status);
+ status = jedis.pfcount("hll_3");
+ assertEquals(3, status);
+
+ status = jedis.pfcount("hll_1", "hll_2");
+ assertEquals(3, status);
+
+ status = jedis.pfcount("hll_1", "hll_2", "hll_3");
+ assertEquals(4, status);
+
+
+ }
+
+ @Test
+ public void pfcountBinary() {
+ byte[] bHll = SafeEncoder.encode("hll");
+ byte[] bFoo = SafeEncoder.encode("foo");
+ byte[] bBar = SafeEncoder.encode("bar");
+ byte[] bZap = SafeEncoder.encode("zap");
+
+ long status = jedis.pfadd(bHll, bFoo, bBar, bZap);
+ assertEquals(1, status);
+
+ status = jedis.pfadd(bHll, bZap, bZap, bZap);
+ assertEquals(0, status);
+
+ status = jedis.pfadd(bHll, bFoo, bBar);
+ assertEquals(0, status);
+
+ status = jedis.pfcount(bHll);
+ assertEquals(3, status);
+ }
+
+ @Test
+ public void pfmerge() {
+ long status = jedis.pfadd("hll1", "foo", "bar", "zap", "a");
+ assertEquals(1, status);
+
+ status = jedis.pfadd("hll2", "a", "b", "c", "foo");
+ assertEquals(1, status);
+
+ String mergeStatus = jedis.pfmerge("hll3", "hll1", "hll2");
+ assertEquals("OK", mergeStatus);
+
+ status = jedis.pfcount("hll3");
+ assertEquals(6, status);
+ }
+
+ @Test
+ public void pfmergeBinary() {
+ byte[] bHll1 = SafeEncoder.encode("hll1");
+ byte[] bHll2 = SafeEncoder.encode("hll2");
+ byte[] bHll3 = SafeEncoder.encode("hll3");
+ byte[] bFoo = SafeEncoder.encode("foo");
+ byte[] bBar = SafeEncoder.encode("bar");
+ byte[] bZap = SafeEncoder.encode("zap");
+ byte[] bA = SafeEncoder.encode("a");
+ byte[] bB = SafeEncoder.encode("b");
+ byte[] bC = SafeEncoder.encode("c");
+
+ long status = jedis.pfadd(bHll1, bFoo, bBar, bZap, bA);
+ assertEquals(1, status);
+
+ status = jedis.pfadd(bHll2, bA, bB, bC, bFoo);
+ assertEquals(1, status);
+
+ String mergeStatus = jedis.pfmerge(bHll3, bHll1, bHll2);
+ assertEquals("OK", mergeStatus);
+
+ status = jedis.pfcount("hll3");
+ assertEquals(6, status);
+ }
+}
diff --git a/src/test/java/redis/clients/jedis/tests/commands/SetCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/SetCommandsTest.java
index 2149feb..8687e67 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.getCursorAsBytes());
+ 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.getCursorAsBytes());
+ assertFalse(bResult.getResult().isEmpty());
}
@Test
@@ -486,5 +510,14 @@ public class SetCommandsTest extends JedisCommandTestBase {
ScanResult result = jedis.sscan("foo", SCAN_POINTER_START, params);
assertFalse(result.getResult().isEmpty());
+
+ // binary
+ params = new ScanParams();
+ params.count(2);
+
+ jedis.sadd(bfoo, bbar1, bbar2, bbar3);
+ ScanResult bResult = jedis.sscan(bfoo, SCAN_POINTER_START_BINARY, params);
+
+ assertFalse(bResult.getResult().isEmpty());
}
}
\ No newline at end of file
diff --git a/src/test/java/redis/clients/jedis/tests/commands/SortedSetCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/SortedSetCommandsTest.java
index 90b4c20..3e66642 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.getCursorAsBytes());
+ 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.getCursorAsBytes());
+ 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
diff --git a/src/test/java/redis/clients/jedis/tests/commands/StringValuesCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/StringValuesCommandsTest.java
index 625fcc9..2876abe 100644
--- a/src/test/java/redis/clients/jedis/tests/commands/StringValuesCommandsTest.java
+++ b/src/test/java/redis/clients/jedis/tests/commands/StringValuesCommandsTest.java
@@ -123,6 +123,12 @@ public class StringValuesCommandsTest extends JedisCommandTestBase {
assertEquals(4, value);
}
+ @Test(expected = JedisDataException.class)
+ public void incrByFloatWrongValue() {
+ jedis.set("foo", "bar");
+ jedis.incrByFloat("foo", 2d);
+ }
+
@Test(expected = JedisDataException.class)
public void decrWrongValue() {
jedis.set("foo", "bar");
diff --git a/src/test/java/redis/clients/jedis/tests/utils/JedisClusterTestUtil.java b/src/test/java/redis/clients/jedis/tests/utils/JedisClusterTestUtil.java
new file mode 100644
index 0000000..c7f7928
--- /dev/null
+++ b/src/test/java/redis/clients/jedis/tests/utils/JedisClusterTestUtil.java
@@ -0,0 +1,70 @@
+package redis.clients.jedis.tests.utils;
+
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.exceptions.JedisException;
+
+public class JedisClusterTestUtil {
+ public static void waitForClusterReady(Jedis...nodes) throws InterruptedException {
+ boolean clusterOk = false;
+ while (!clusterOk) {
+ boolean isOk = true;
+ for (Jedis node : nodes) {
+ if (!node.clusterInfo().split("\n")[0].contains("ok")) {
+ isOk = false;
+ break;
+ }
+ }
+
+ if (isOk) {
+ clusterOk = true;
+ }
+
+ Thread.sleep(50);
+ }
+ }
+
+ public static String getNodeId(String infoOutput) {
+ for (String infoLine : infoOutput.split("\n")) {
+ if (infoLine.contains("myself")) {
+ return infoLine.split(" ")[0];
+ }
+ }
+ return "";
+ }
+
+ public static void assertNodeIsKnown(Jedis node, String targetNodeId, int timeoutMs) {
+ assertNodeRecognizedStatus(node, targetNodeId, true, timeoutMs);
+ }
+
+ public static void assertNodeIsUnknown(Jedis node, String targetNodeId, int timeoutMs) {
+ assertNodeRecognizedStatus(node, targetNodeId, false, timeoutMs);
+ }
+
+ private static void assertNodeRecognizedStatus(Jedis node, String targetNodeId, boolean shouldRecognized, int timeoutMs) {
+ int sleepInterval = 100;
+ for (int sleepTime = 0 ; sleepTime <= timeoutMs ; sleepTime += sleepInterval) {
+ boolean known = isKnownNode(node, targetNodeId);
+ if (shouldRecognized == known)
+ return;
+
+ try {
+ Thread.sleep(sleepInterval);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ throw new JedisException("Node recognize check error");
+ }
+
+ private static boolean isKnownNode(Jedis node, String nodeId) {
+ String infoOutput = node.clusterNodes();
+ for (String infoLine : infoOutput.split("\n")) {
+ if (infoLine.contains(nodeId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+}