Merge branch 'master' into implements-closeable-to-pooled-jedis-shardedjedis

Conflicts:
	src/main/java/redis/clients/jedis/Jedis.java
	src/main/java/redis/clients/jedis/JedisSentinelPool.java
	src/main/java/redis/clients/jedis/ShardedJedis.java
	src/test/java/redis/clients/jedis/tests/JedisPoolTest.java
This commit is contained in:
Jungtaek Lim
2014-05-26 11:18:45 +09:00
53 changed files with 2195 additions and 216 deletions

View File

@@ -1,5 +1,7 @@
package redis.clients.jedis;
import java.util.List;
/**
* Pipelined responses for all of the low level, non key related commands
*/
@@ -24,6 +26,8 @@ public interface BasicRedisPipeline {
Response<String> flushAll();
Response<String> info();
Response<List<String>> time();
Response<Long> dbSize();

View File

@@ -1,26 +1,22 @@
package redis.clients.jedis;
import static redis.clients.jedis.Protocol.toByteArray;
import static redis.clients.jedis.Protocol.Command.*;
import static redis.clients.jedis.Protocol.Keyword.ENCODING;
import static redis.clients.jedis.Protocol.Keyword.IDLETIME;
import static redis.clients.jedis.Protocol.Keyword.LEN;
import static redis.clients.jedis.Protocol.Keyword.LIMIT;
import static redis.clients.jedis.Protocol.Keyword.NO;
import static redis.clients.jedis.Protocol.Keyword.ONE;
import static redis.clients.jedis.Protocol.Keyword.REFCOUNT;
import static redis.clients.jedis.Protocol.Keyword.RESET;
import static redis.clients.jedis.Protocol.Keyword.STORE;
import static redis.clients.jedis.Protocol.Keyword.WITHSCORES;
import redis.clients.jedis.Protocol.Command;
import redis.clients.jedis.Protocol.Keyword;
import redis.clients.util.SafeEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import redis.clients.jedis.Protocol.Command;
import redis.clients.jedis.Protocol.Keyword;
import redis.clients.util.SafeEncoder;
import static redis.clients.jedis.Protocol.Command.*;
import static redis.clients.jedis.Protocol.Command.EXISTS;
import static redis.clients.jedis.Protocol.Command.PSUBSCRIBE;
import static redis.clients.jedis.Protocol.Command.PUNSUBSCRIBE;
import static redis.clients.jedis.Protocol.Command.SUBSCRIBE;
import static redis.clients.jedis.Protocol.Command.UNSUBSCRIBE;
import static redis.clients.jedis.Protocol.Keyword.*;
import static redis.clients.jedis.Protocol.toByteArray;
public class BinaryClient extends Connection {
public enum LIST_POSITION {
@@ -203,6 +199,10 @@ public class BinaryClient extends Connection {
sendCommand(INCRBY, key, toByteArray(integer));
}
public void incrByFloat(final byte[] key, final double value) {
sendCommand(INCRBYFLOAT, key, toByteArray(value));
}
public void incr(final byte[] key) {
sendCommand(INCR, key);
}
@@ -320,7 +320,7 @@ public class BinaryClient extends Connection {
public void sadd(final byte[] key, final byte[]... members) {
sendCommand(SADD, joinParameters(key, members));
}
public void smembers(final byte[] key) {
sendCommand(SMEMBERS, key);
}
@@ -929,7 +929,15 @@ public class BinaryClient extends Connection {
public void getbit(byte[] key, long offset) {
sendCommand(GETBIT, key, toByteArray(offset));
}
public void bitpos(final byte[] key, final boolean value, final BitPosParams params) {
final List<byte[]> args = new ArrayList<byte[]>();
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));
}
}

View File

@@ -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.
* <p>
* INCRBYFLOAT commands are limited to double precision floating point
* values.
* <p>
* 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.
* <p>
* 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.
* <p>
* The range of values supported by HINCRBYFLOAT is limited to double
* precision floating point values.
* <p>
* <b>Time complexity:</b> 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<byte[]> scan(final byte[] cursor) {
return scan(cursor, new ScanParams());
}
public ScanResult<byte[]> scan(final byte[] cursor, final ScanParams params) {
checkIsInMulti();
client.scan(cursor, params);
List<Object> result = client.getObjectMultiBulkReply();
byte[] newcursor = (byte[]) result.get(0);
List<byte[]> rawResults = (List<byte[]>) result.get(1);
return new ScanResult<byte[]>(newcursor, rawResults);
}
public ScanResult<Map.Entry<byte[], byte[]>> hscan(final byte[] key,
final byte[] cursor) {
return hscan(key, cursor, new ScanParams());
}
public ScanResult<Map.Entry<byte[], byte[]>> hscan(final byte[] key,
final byte[] cursor, final ScanParams params) {
checkIsInMulti();
client.hscan(key, cursor, params);
List<Object> result = client.getObjectMultiBulkReply();
byte[] newcursor = (byte[]) result.get(0);
List<Map.Entry<byte[], byte[]>> results = new ArrayList<Map.Entry<byte[], byte[]>>();
List<byte[]> rawResults = (List<byte[]>) result.get(1);
Iterator<byte[]> iterator = rawResults.iterator();
while (iterator.hasNext()) {
results.add(new AbstractMap.SimpleEntry<byte[], byte[]>(iterator
.next(), iterator.next()));
}
return new ScanResult<Map.Entry<byte[], byte[]>>(newcursor, results);
}
public ScanResult<byte[]> sscan(final byte[] key, final byte[] cursor) {
return sscan(key, cursor, new ScanParams());
}
public ScanResult<byte[]> sscan(final byte[] key, final byte[] cursor,
final ScanParams params) {
checkIsInMulti();
client.sscan(key, cursor, params);
List<Object> result = client.getObjectMultiBulkReply();
byte[] newcursor = (byte[]) result.get(0);
List<byte[]> rawResults = (List<byte[]>) result.get(1);
return new ScanResult<byte[]>(newcursor, rawResults);
}
public ScanResult<Tuple> zscan(final byte[] key, final byte[] cursor) {
return zscan(key, cursor, new ScanParams());
}
public ScanResult<Tuple> zscan(final byte[] key, final byte[] cursor,
final ScanParams params) {
checkIsInMulti();
client.zscan(key, cursor, params);
List<Object> result = client.getObjectMultiBulkReply();
byte[] newcursor = (byte[]) result.get(0);
List<Tuple> results = new ArrayList<Tuple>();
List<byte[]> rawResults = (List<byte[]>) result.get(1);
Iterator<byte[]> iterator = rawResults.iterator();
while (iterator.hasNext()) {
results.add(new Tuple(iterator.next(), Double.valueOf(SafeEncoder
.encode(iterator.next()))));
}
return new ScanResult<Tuple>(newcursor, results);
}
}

View File

@@ -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);
}

View File

@@ -210,4 +210,8 @@ public interface BinaryRedisPipeline {
Response<Long> bitcount(byte[] key);
Response<Long> bitcount(byte[] key, long start, long end);
Response<Long> pfadd(final byte[] key, final byte[]... elements);
Response<Long> pfcount(final byte[] key);
}

View File

@@ -110,6 +110,11 @@ public class BinaryShardedJedis extends Sharded<Jedis, JedisShardInfo>
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<Jedis, JedisShardInfo>
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, JedisShardInfo>
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);
}
}

View File

@@ -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<byte[]> params = new ArrayList<byte[]>();
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<byte[]> getParams() {
return Collections.unmodifiableCollection(params);
}
}

View File

@@ -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);
}
}

View File

@@ -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<String> clusterSlaves(final String nodeId);
String clusterFailover();
}

View File

@@ -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);

View File

@@ -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
* <p>
* INCRBYFLOAT commands are limited to double precision floating point values.
* <p>
* 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.
* <p>
* 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.
* <p>
* The range of values supported by HINCRBYFLOAT is limited to
* double precision floating point values.
* <p>
* <b>Time complexity:</b> 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<String> 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<Jedis> 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();
}
}

View File

@@ -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<String>(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<String>(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<Long>(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<Long>(connectionHandler,
timeout, maxRedirections) {
@Override
public Long execute(Jedis connection) {
return connection.pfcount(key);
}
}.run(key);
}
}

View File

@@ -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<T> {
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<T> {
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);
}
}

View File

@@ -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<String, JedisPool> nodes = new HashMap<String, JedisPool>();
protected Map<Integer, JedisPool> slots = new HashMap<Integer, JedisPool>();
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<HostAndPort> nodes) {
for (HostAndPort hostAndPort : nodes) {
private void initializeSlotsCache(Set<HostAndPort> 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<Integer> 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);
}
}

View File

@@ -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<String> sscan(final String key, final String cursor);
ScanResult<Tuple> zscan(final String key, final String cursor);
Long pfadd(final String key, final String... elements);
long pfcount(final String key);
}

View File

@@ -87,11 +87,15 @@ public class JedisPool extends Pool<Jedis> {
}
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);
}
}
}

View File

@@ -169,15 +169,17 @@ public class JedisSentinelPool extends Pool<Jedis> {
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 {

View File

@@ -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<JedisPool> 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<JedisPool> getShuffledNodesPool() {
List<JedisPool> pools = new ArrayList<JedisPool>();
pools.addAll(nodes.values());
Collections.shuffle(pools);
return pools;
}
}

View File

@@ -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);
}

View File

@@ -65,4 +65,8 @@ public interface MultiKeyBinaryRedisPipeline {
Response<byte[]> randomKeyBinary();
Response<Long> bitop(BitOP op, final byte[] destKey, byte[]... srcKeys);
Response<String> pfmerge(final byte[] destkey, final byte[]... sourcekeys);
Response<Long> pfcount(final byte[] ... keys);
}

View File

@@ -79,4 +79,8 @@ public interface MultiKeyCommands {
ScanResult<String> scan(int cursor);
ScanResult<String> scan(final String cursor);
String pfmerge(final String destkey, final String... sourcekeys);
long pfcount(final String...keys);
}

View File

@@ -64,4 +64,8 @@ public interface MultiKeyCommandsPipeline {
Response<String> randomKey();
Response<Long> bitop(BitOP op, final String destKey, String... srcKeys);
Response<String> pfmerge(final String destkey, final String... sourcekeys);
Response<Long> pfcount(final String...keys);
}

View File

@@ -367,6 +367,11 @@ abstract class MultiKeyPipelineBase extends PipelineBase implements
client.info();
return getResponse(BuilderFactory.STRING);
}
public Response<List<String>> time() {
client.time();
return getResponse(BuilderFactory.STRING_LIST);
}
public Response<Long> dbSize() {
client.dbSize();
@@ -446,4 +451,28 @@ abstract class MultiKeyPipelineBase extends PipelineBase implements
client.clusterSetSlotImporting(slot, nodeId);
return getResponse(BuilderFactory.STRING);
}
@Override
public Response<String> pfmerge(byte[] destkey, byte[]... sourcekeys) {
client.pfmerge(destkey, sourcekeys);
return getResponse(BuilderFactory.STRING);
}
@Override
public Response<String> pfmerge(String destkey, String... sourcekeys) {
client.pfmerge(destkey, sourcekeys);
return getResponse(BuilderFactory.STRING);
}
@Override
public Response<Long> pfcount(String...keys) {
client.pfcount(keys);
return getResponse(BuilderFactory.LONG);
}
@Override
public Response<Long> pfcount(final byte[] ... keys) {
client.pfcount(keys);
return getResponse(BuilderFactory.LONG);
}
}

View File

@@ -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<String> discard() {
if (currentMulti == null)
throw new JedisDataException("DISCARD without MULTI");
client.discard();
currentMulti = null;
return getResponse(BuilderFactory.STRING);
}
public Response<List<Object>> exec() {
if (currentMulti == null)
throw new JedisDataException("EXEC without MULTI");
client.exec();
Response<List<Object>> response = super.getResponse(currentMulti);
currentMulti.setResponseDependency(response);
currentMulti = null;
return response;
}
public Response<String> multi() {
if (currentMulti != null)
throw new JedisDataException("MULTI calls can not be nested");
client.multi();
Response<String> response = getResponse(BuilderFactory.STRING); // Expecting
// OK
currentMulti = new MultiResponseBuilder();
return response;
}
}

View File

@@ -142,7 +142,25 @@ abstract class PipelineBase extends Queable implements BinaryRedisPipeline,
getClient(key).getbit(key, offset);
return getResponse(BuilderFactory.BOOLEAN);
}
public Response<Long> bitpos(final String key, final boolean value) {
return bitpos(key, value, new BitPosParams());
}
public Response<Long> bitpos(final String key, final boolean value, final BitPosParams params) {
getClient(key).bitpos(key, value, params);
return getResponse(BuilderFactory.LONG);
}
public Response<Long> bitpos(final byte[] key, final boolean value) {
return bitpos(key, value, new BitPosParams());
}
public Response<Long> bitpos(final byte[] key, final boolean value, final BitPosParams params) {
getClient(key).bitpos(key, value, params);
return getResponse(BuilderFactory.LONG);
}
public Response<String> 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<Long> pexpire(String key, int milliseconds) {
return pexpire(key, (long) milliseconds);
}
@Deprecated
public Response<Long> pexpire(byte[] key, int milliseconds) {
return pexpire(key, (long) milliseconds);
}
public Response<Long> pexpire(String key, long milliseconds) {
getClient(key).pexpire(key, milliseconds);
return getResponse(BuilderFactory.LONG);
}
public Response<Long> pexpire(byte[] key, int milliseconds) {
public Response<Long> 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<Long> pfadd(byte[] key, byte[]... elements) {
getClient(key).pfadd(key, elements);
return getResponse(BuilderFactory.LONG);
}
@Override
public Response<Long> pfcount(byte[] key) {
getClient(key).pfcount(key);
return getResponse(BuilderFactory.LONG);
}
@Override
public Response<Long> pfadd(String key, String... elements) {
getClient(key).pfadd(key, elements);
return getResponse(BuilderFactory.LONG);
}
@Override
public Response<Long> pfcount(String key) {
getClient(key).pfcount(key);
return getResponse(BuilderFactory.LONG);
}
}

View File

@@ -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;

View File

@@ -188,4 +188,8 @@ public interface RedisPipeline {
Response<Long> bitcount(String key);
Response<Long> bitcount(String key, long start, long end);
Response<Long> pfadd(final String key, final String... elements);
Response<Long> pfcount(final String key);
}

View File

@@ -8,6 +8,8 @@ public class Response<T> {
private boolean set = false;
private Builder<T> builder;
private Object data;
private Response<?> dependency = null;
private boolean requestDependencyBuild = false;
public Response(Builder<T> b) {
this.builder = b;
@@ -19,23 +21,39 @@ public class Response<T> {
}
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();
}

View File

@@ -13,15 +13,24 @@ import redis.clients.util.SafeEncoder;
public class ScanParams {
private List<byte[]> params = new ArrayList<byte[]>();
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<byte[]> getParams() {

View File

@@ -2,8 +2,10 @@ package redis.clients.jedis;
import java.util.List;
import redis.clients.util.SafeEncoder;
public class ScanResult<T> {
private String cursor;
private byte[] cursor;
private List<T> results;
@Deprecated
@@ -13,15 +15,18 @@ public class ScanResult<T> {
* @see https://github.com/xetorthio/jedis/issues/531
*/
public ScanResult(int cursor, List<T> results) {
this.cursor = String.valueOf(cursor);
this.results = results;
this(Protocol.toByteArray(cursor), results);
}
public ScanResult(String cursor, List<T> results) {
this(SafeEncoder.encode(cursor), results);
}
public ScanResult(byte[] cursor, List<T> 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<T> {
* @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;
}

View File

@@ -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);
}
}

View File

@@ -31,26 +31,29 @@ public class ShardedJedisPool extends Pool<ShardedJedis> {
List<JedisShardInfo> 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.
*/

View File

@@ -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<Integer> availableSlots;
private List<Integer> slotsBeingImported;
private List<Integer> slotsBeingMigrated;
public ClusterNodeInformation(HostAndPort node) {
this.node = node;
this.availableSlots = new ArrayList<Integer>();
this.slotsBeingImported = new ArrayList<Integer>();
this.slotsBeingMigrated = new ArrayList<Integer>();
}
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<Integer> getAvailableSlots() {
return availableSlots;
}
public List<Integer> getSlotsBeingImported() {
return slotsBeingImported;
}
public List<Integer> getSlotsBeingMigrated() {
return slotsBeingMigrated;
}
}

View File

@@ -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));
}
}
}

View File

@@ -45,6 +45,9 @@ public abstract class Pool<T> {
}
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<T> {
}
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<T> {
throw new JedisException("Could not destroy the pool", e);
}
}
}
}

View File

@@ -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");

View File

@@ -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));
}
}

View File

@@ -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<Jedis, Jedis> replMap = new HashMap<Jedis, Jedis>();
replMap.put(node5, node6);
waitForReplicateReady(replMap, TIMEOUT);
JedisClusterTestUtil.waitForClusterReady(node5, node6);
}
private void waitForReplicateReady(Map<Jedis, Jedis> replMap, int timeoutMs) {
int interval = 100;
for (int timeout = 0; timeout <= timeoutMs; timeout += interval) {
for (Entry<Jedis, Jedis> 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<String> 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<String> 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<HostAndPort> jedisClusterNode = new HashSet<HostAndPort>();
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<Jedis, Jedis> replMap = new HashMap<Jedis, Jedis>();
replMap.put(node6, node5);
waitForReplicateReady(replMap, TIMEOUT);
JedisClusterTestUtil.waitForClusterReady(node5, node6);
List<String> 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<Jedis, Jedis> replMap = new HashMap<Jedis, Jedis>();
replMap.put(node5, node6);
waitForReplicateReady(replMap, TIMEOUT);
JedisClusterTestUtil.waitForClusterReady(node5, node6);
}
}
}

View File

@@ -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<Integer> 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<HostAndPort> jedisClusterNode = new HashSet<HostAndPort>();
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<HostAndPort> jedisClusterNode = new HashSet<HostAndPort>();
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<HostAndPort> jedisClusterNode = new HashSet<HostAndPort>();
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<Integer> getInconsistentSlots(String infoOuput) {
for (String infoLine : infoOuput.split("\n")) {
if (infoLine.contains("myself")) {
return getSlotsBeingMigrated(infoLine);
}
}
return null;
}
private List<Integer> getSlotsBeingMigrated(String infoLine) {
List<Integer> inconsistentSlots = new ArrayList<Integer>();
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;
}
}

View File

@@ -228,4 +228,14 @@ public class JedisPoolTest extends Assert {
jedis2.close();
}
}
}
@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);
}
}

View File

@@ -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();

View File

@@ -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<String> r1 = p.get("bar");
p.multi();
Response<String> r2 = p.get("foo");
p.exec();
Response<String> 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();

View File

@@ -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<byte[]> 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<byte[]> 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<String> 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<byte[]> bResult = jedis.scan(SCAN_POINTER_START_BINARY, params);
assertFalse(bResult.getResult().isEmpty());
}
}

View File

@@ -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");

View File

@@ -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<String, String> hash = new HashMap<String, String>();
@@ -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<Map.Entry<byte[], byte[]>> 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<Map.Entry<byte[], byte[]>> 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<Map.Entry<byte[], byte[]>> bResult = jedis.hscan(bfoo, SCAN_POINTER_START_BINARY, params);
assertFalse(bResult.getResult().isEmpty());
}
}

View File

@@ -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);
}
}

View File

@@ -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<byte[]> 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<byte[]> 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<String> 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<byte[]> bResult = jedis.sscan(bfoo, SCAN_POINTER_START_BINARY, params);
assertFalse(bResult.getResult().isEmpty());
}
}

View File

@@ -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<Tuple> 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<Tuple> 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<Tuple> 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<Tuple> bResult = jedis.zscan(bfoo, SCAN_POINTER_START_BINARY, params);
assertFalse(bResult.getResult().isEmpty());
}
}

View File

@@ -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");

View File

@@ -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;
}
}