From a8987ed865e1034f83d1e6a3ef681582ccb8a614 Mon Sep 17 00:00:00 2001 From: Marcos Nils Date: Sat, 7 Dec 2013 17:55:17 -0300 Subject: [PATCH] Add first test to thorw MovedDataException when receiving MOVED from a cluster node --- Makefile | 2 +- .../java/redis/clients/jedis/BinaryJedis.java | 2 +- .../redis/clients/jedis/JedisCluster.java | 738 +++++++++++++++++- .../java/redis/clients/jedis/JedisPool.java | 2 +- .../java/redis/clients/jedis/Protocol.java | 12 +- .../exceptions/JedisMovedDataException.java | 17 + .../clients/jedis/tests/JedisClusterTest.java | 15 +- 7 files changed, 775 insertions(+), 13 deletions(-) create mode 100644 src/main/java/redis/clients/jedis/exceptions/JedisMovedDataException.java diff --git a/Makefile b/Makefile index e0189f2..8e45570 100644 --- a/Makefile +++ b/Makefile @@ -181,7 +181,7 @@ stop: test: make start - mvn clean compile test + mvn -Dtest=${TEST} clean compile test make stop deploy: diff --git a/src/main/java/redis/clients/jedis/BinaryJedis.java b/src/main/java/redis/clients/jedis/BinaryJedis.java index 385b1c0..2d8dead 100644 --- a/src/main/java/redis/clients/jedis/BinaryJedis.java +++ b/src/main/java/redis/clients/jedis/BinaryJedis.java @@ -1697,7 +1697,7 @@ public class BinaryJedis implements BasicCommands, BinaryJedisCommands, MultiKey protected void checkIsInMulti() { if (client.isInMulti()) { - throw new JedisDataException( + throw new JedisDataException( "Cannot use Jedis when in Multi. Please use JedisTransaction instead."); } } diff --git a/src/main/java/redis/clients/jedis/JedisCluster.java b/src/main/java/redis/clients/jedis/JedisCluster.java index 7b5613b..4586b8f 100644 --- a/src/main/java/redis/clients/jedis/JedisCluster.java +++ b/src/main/java/redis/clients/jedis/JedisCluster.java @@ -1,5 +1,739 @@ package redis.clients.jedis; -public class JedisCluster { - public static final int HASH_SLOTS = 16384; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; + +import redis.clients.jedis.BinaryClient.LIST_POSITION; +import redis.clients.util.Pool; + +public class JedisCluster implements JedisCommands, BasicCommands { + + public static final short HASHSLOTS = 16384; + private static final int DEFAULT_TIMEOUT = 1; + + private Map nodes = new HashMap(); + + public JedisCluster(Set nodes, int timeout) { + initializeSlotsCache(nodes); + } + + private void initializeSlotsCache(Set nodes) { + for (HostAndPort hostAndPort : nodes) { + JedisPool jp = new JedisPool(hostAndPort.getHost(), hostAndPort.getPort()); + this.nodes.put(hostAndPort.getHost()+hostAndPort.getPort(), jp); + } + + } + + public JedisCluster(Set nodes) { + this(nodes, DEFAULT_TIMEOUT); + } + + + @Override + public String set(String key, String value) { + return getRandomConnection().set(key, value); + } + + @Override + public String get(String key) { + return getRandomConnection().get(key); + } + + @Override + public Boolean exists(String key) { + return getRandomConnection().exists(key); + } + + @Override + public Long persist(String key) { + return getRandomConnection().persist(key); + } + + @Override + public String type(String key) { + return getRandomConnection().type(key); + } + + @Override + public Long expire(String key, int seconds) { + return getRandomConnection().expire(key, seconds); + } + + @Override + public Long expireAt(String key, long unixTime) { + return getRandomConnection().expireAt(key, unixTime); + } + + @Override + public Long ttl(String key) { + return getRandomConnection().ttl(key); + } + + @Override + public Boolean setbit(String key, long offset, boolean value) { + return getRandomConnection().setbit(key, offset, value); + } + + @Override + public Boolean setbit(String key, long offset, String value) { + return getRandomConnection().setbit(key, offset, value); + } + + @Override + public Boolean getbit(String key, long offset) { + return getRandomConnection().getbit(key, offset); + } + + @Override + public Long setrange(String key, long offset, String value) { + return getRandomConnection().setrange(key, offset, value); + } + + @Override + public String getrange(String key, long startOffset, long endOffset) { + return getRandomConnection().getrange(key, startOffset, endOffset); + } + + @Override + public String getSet(String key, String value) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long setnx(String key, String value) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String setex(String key, int seconds, String value) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long decrBy(String key, long integer) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long decr(String key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long incrBy(String key, long integer) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long incr(String key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long append(String key, String value) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String substr(String key, int start, int end) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long hset(String key, String field, String value) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String hget(String key, String field) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long hsetnx(String key, String field, String value) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String hmset(String key, Map hash) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List hmget(String key, String... fields) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long hincrBy(String key, String field, long value) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Boolean hexists(String key, String field) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long hdel(String key, String... field) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long hlen(String key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set hkeys(String key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List hvals(String key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Map hgetAll(String key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long rpush(String key, String... string) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long lpush(String key, String... string) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long llen(String key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List lrange(String key, long start, long end) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String ltrim(String key, long start, long end) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String lindex(String key, long index) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String lset(String key, long index, String value) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long lrem(String key, long count, String value) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String lpop(String key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String rpop(String key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long sadd(String key, String... member) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set smembers(String key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long srem(String key, String... member) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String spop(String key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long scard(String key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Boolean sismember(String key, String member) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String srandmember(String key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long strlen(String key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long zadd(String key, double score, String member) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long zadd(String key, Map scoreMembers) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set zrange(String key, long start, long end) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long zrem(String key, String... member) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Double zincrby(String key, double score, String member) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long zrank(String key, String member) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long zrevrank(String key, String member) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set zrevrange(String key, long start, long end) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set zrangeWithScores(String key, long start, long end) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set zrevrangeWithScores(String key, long start, long end) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long zcard(String key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Double zscore(String key, String member) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List sort(String key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List sort(String key, SortingParams sortingParameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long zcount(String key, double min, double max) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long zcount(String key, String min, String max) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set zrangeByScore(String key, double min, double max) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set zrangeByScore(String key, String min, String max) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set zrevrangeByScore(String key, double max, double min) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set zrangeByScore(String key, double min, double max, + int offset, int count) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set zrevrangeByScore(String key, String max, String min) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set zrangeByScore(String key, String min, String max, + int offset, int count) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set zrevrangeByScore(String key, double max, double min, + int offset, int count) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set zrangeByScoreWithScores(String key, double min, double max) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set zrevrangeByScoreWithScores(String key, double max, + double min) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set zrangeByScoreWithScores(String key, double min, + double max, int offset, int count) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set zrevrangeByScore(String key, String max, String min, + int offset, int count) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set zrangeByScoreWithScores(String key, String min, String max) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set zrevrangeByScoreWithScores(String key, String max, + String min) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set zrangeByScoreWithScores(String key, String min, + String max, int offset, int count) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set zrevrangeByScoreWithScores(String key, double max, + double min, int offset, int count) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set zrevrangeByScoreWithScores(String key, String max, + String min, int offset, int count) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long zremrangeByRank(String key, long start, long end) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long zremrangeByScore(String key, double start, double end) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long zremrangeByScore(String key, String start, String end) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long linsert(String key, LIST_POSITION where, String pivot, + String value) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long lpushx(String key, String... string) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long rpushx(String key, String... string) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List blpop(String arg) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List brpop(String arg) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long del(String key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String echo(String string) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long move(String key, int dbIndex) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long bitcount(String key) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long bitcount(String key, long start, long end) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String ping() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String quit() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String flushDB() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long dbSize() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String select(int index) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String flushAll() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String auth(String password) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String save() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String bgsave() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String bgrewriteaof() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long lastsave() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String shutdown() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String info() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String info(String section) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String slaveof(String host, int port) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String slaveofNoOne() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Long getDB() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String debug(DebugParams params) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String configResetStat() { + // TODO Auto-generated method stub + return null; + } + + + @SuppressWarnings("unchecked") + private Jedis getRandomConnection() { + Object[] nodeArray = nodes.values().toArray(); + return ((Pool) nodeArray[new Random().nextInt(nodeArray.length)]).getResource(); + } + + + } diff --git a/src/main/java/redis/clients/jedis/JedisPool.java b/src/main/java/redis/clients/jedis/JedisPool.java index 58212ba..6511207 100644 --- a/src/main/java/redis/clients/jedis/JedisPool.java +++ b/src/main/java/redis/clients/jedis/JedisPool.java @@ -18,7 +18,7 @@ public class JedisPool extends Pool { this(new GenericObjectPoolConfig(), host, port, Protocol.DEFAULT_TIMEOUT, null, Protocol.DEFAULT_DATABASE, null); } - + public JedisPool(final String host) { URI uri = URI.create(host); if (uri.getScheme() != null && uri.getScheme().equals("redis")) { diff --git a/src/main/java/redis/clients/jedis/Protocol.java b/src/main/java/redis/clients/jedis/Protocol.java index a3eacd6..123edc4 100644 --- a/src/main/java/redis/clients/jedis/Protocol.java +++ b/src/main/java/redis/clients/jedis/Protocol.java @@ -6,13 +6,15 @@ import java.util.List; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisDataException; +import redis.clients.jedis.exceptions.JedisMovedDataException; import redis.clients.util.RedisInputStream; import redis.clients.util.RedisOutputStream; import redis.clients.util.SafeEncoder; public final class Protocol { - public static final int DEFAULT_PORT = 6379; + private static final String MOVED_RESPONSE = "MOVED"; + public static final int DEFAULT_PORT = 6379; public static final int DEFAULT_SENTINEL_PORT = 26379; public static final int DEFAULT_TIMEOUT = 2000; public static final int DEFAULT_DATABASE = 0; @@ -72,8 +74,12 @@ public final class Protocol { } private static void processError(final RedisInputStream is) { - String message = is.readLine(); - throw new JedisDataException(message); + String message = is.readLine(); + //TODO: Read only first 5 bytes? + if (message.contains(MOVED_RESPONSE)) { + throw new JedisMovedDataException(message); + } + throw new JedisDataException(message); } private static Object process(final RedisInputStream is) { diff --git a/src/main/java/redis/clients/jedis/exceptions/JedisMovedDataException.java b/src/main/java/redis/clients/jedis/exceptions/JedisMovedDataException.java new file mode 100644 index 0000000..78e0a4b --- /dev/null +++ b/src/main/java/redis/clients/jedis/exceptions/JedisMovedDataException.java @@ -0,0 +1,17 @@ +package redis.clients.jedis.exceptions; + +public class JedisMovedDataException extends JedisDataException { + private static final long serialVersionUID = 3878126572474819403L; + + public JedisMovedDataException(String message) { + super(message); + } + + public JedisMovedDataException(Throwable cause) { + super(cause); + } + + public JedisMovedDataException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/test/java/redis/clients/jedis/tests/JedisClusterTest.java b/src/test/java/redis/clients/jedis/tests/JedisClusterTest.java index ca90362..68ad4eb 100644 --- a/src/test/java/redis/clients/jedis/tests/JedisClusterTest.java +++ b/src/test/java/redis/clients/jedis/tests/JedisClusterTest.java @@ -1,5 +1,7 @@ package redis.clients.jedis.tests; +import java.util.HashSet; + import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -9,6 +11,7 @@ import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisCluster; import redis.clients.jedis.Pipeline; +import redis.clients.jedis.exceptions.JedisMovedDataException; public class JedisClusterTest extends Assert { private Jedis node1; @@ -42,7 +45,7 @@ public class JedisClusterTest extends Assert { // add all slots to node1 Pipeline pipelined = node1.pipelined(); - for (int i = 0; i < JedisCluster.HASH_SLOTS; i++) { + for (int i = 0; i < JedisCluster.HASHSLOTS; i++) { pipelined.clusterAddSlots(i); } pipelined.sync(); @@ -52,15 +55,17 @@ public class JedisClusterTest extends Assert { public void tearDown() { // clear all slots of node1 Pipeline pipelined = node1.pipelined(); - for (int i = 0; i < JedisCluster.HASH_SLOTS; i++) { + for (int i = 0; i < JedisCluster.HASHSLOTS; i++) { pipelined.clusterDelSlots(i); } pipelined.sync(); } - @Test - public void moved() { - //TODO: needs to implement + @Test(expected=JedisMovedDataException.class) + public void throwMovedExceptionTest() { + JedisCluster jc = new JedisCluster(new HashSet(HostAndPortUtil.getClusterServers())); + jc.set("foo", "bar"); + jc.get("foo"); } @Test