diff --git a/Makefile b/Makefile index 00f29bf..56f1910 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ daemonize yes port 6379 requirepass foobared pidfile /tmp/redis1.pid +logfile /tmp/redis1.log endef define REDIS2_CONF @@ -10,33 +11,86 @@ daemonize yes port 6380 requirepass foobared pidfile /tmp/redis2.pid +logfile /tmp/redis2.log endef +define REDIS3_CONF +daemonize yes +port 6381 +pidfile /tmp/redis3.pid +logfile /tmp/redis3.log +endef + +define REDIS4_CONF +daemonize yes +port 6382 +pidfile /tmp/redis4.pid +logfile /tmp/redis4.log +endef define REDIS_SENTINEL1 port 26379 daemonize yes -sentinel monitor mymaster 127.0.0.1 6379 2 +sentinel monitor mymaster 127.0.0.1 6379 1 sentinel auth-pass mymaster foobared -sentinel down-after-milliseconds mymaster 5000 +sentinel down-after-milliseconds mymaster 1000 sentinel failover-timeout mymaster 900000 sentinel can-failover mymaster yes sentinel parallel-syncs mymaster 1 pidfile /tmp/sentinel1.pid +logfile /tmp/sentinel1.log +endef + +define REDIS_SENTINEL2 +port 26380 +daemonize yes +sentinel monitor mymaster 127.0.0.1 6381 2 +sentinel down-after-milliseconds mymaster 3000 +sentinel can-failover mymaster yes +sentinel parallel-syncs mymaster 1 +sentinel failover-timeout mymaster 900000 +pidfile /tmp/sentinel2.pid +logfile /tmp/sentinel2.log +endef + +define REDIS_SENTINEL3 +port 26381 +daemonize yes +sentinel monitor mymaster 127.0.0.1 6381 2 +sentinel down-after-milliseconds mymaster 3000 +sentinel can-failover mymaster yes +sentinel parallel-syncs mymaster 1 +sentinel failover-timeout mymaster 900000 +pidfile /tmp/sentinel3.pid +logfile /tmp/sentinel3.log endef export REDIS1_CONF export REDIS2_CONF +export REDIS3_CONF +export REDIS4_CONF export REDIS_SENTINEL1 +export REDIS_SENTINEL2 +export REDIS_SENTINEL3 + test: echo "$$REDIS1_CONF" | redis-server - echo "$$REDIS2_CONF" | redis-server - + echo "$$REDIS3_CONF" | redis-server - + echo "$$REDIS4_CONF" | redis-server - echo "$$REDIS_SENTINEL1" | redis-sentinel - + echo "$$REDIS_SENTINEL2" | redis-sentinel - + echo "$$REDIS_SENTINEL3" | redis-sentinel - mvn clean compile test kill `cat /tmp/redis1.pid` kill `cat /tmp/redis2.pid` + # this get's segfaulted by the tests + kill `cat /tmp/redis3.pid` || true + kill `cat /tmp/redis4.pid` kill `cat /tmp/sentinel1.pid` + kill `cat /tmp/sentinel2.pid` + kill `cat /tmp/sentinel3.pid` .PHONY: test diff --git a/pom.xml b/pom.xml index a42dc27..a724a9d 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ 2.2.2-SNAPSHOT Jedis Jedis is a blazingly small and sane Redis java client. - https://github.com/mardambey/jedis + https://github.com/xetorthio/jedis @@ -28,24 +28,25 @@ MIT - http://github.com/mardambey/jedis/raw/master/LICENSE.txt + http://github.com/xetorthio/jedis/raw/master/LICENSE.txt repo github - http://github.com/mardambey/jedis/issues + http://github.com/xetorthio/jedis/issues - scm:git:git@github.com:mardambey/jedis.git - scm:git:git@github.com:mardambey/jedis.git - scm:git:git@github.com:mardambey/jedis.git + scm:git:git@github.com:xetorthio/jedis.git + scm:git:git@github.com:xetorthio/jedis.git + scm:git:git@github.com:xetorthio/jedis.git - localhost:6379,localhost:6380 + localhost:6379,localhost:6380,localhost:6381,localhost:6382 + localhost:26379,localhost:26380 diff --git a/src/main/java/redis/clients/jedis/JedisSentinelPool.java b/src/main/java/redis/clients/jedis/JedisSentinelPool.java index 9bee0e9..609c742 100644 --- a/src/main/java/redis/clients/jedis/JedisSentinelPool.java +++ b/src/main/java/redis/clients/jedis/JedisSentinelPool.java @@ -231,9 +231,13 @@ public class JedisSentinelPool extends Pool { }, "+switch-master"); } catch (JedisConnectionException e) { - log.severe("Lost connection to Sentinel at " + host + ":" + port + ". Sleeping 5000ms and retrying."); - try { Thread.sleep(subscribeRetryWaitTimeMillis); } catch (InterruptedException e1) { e1.printStackTrace(); } + if (running.get()) { + log.severe("Lost connection to Sentinel at " + host + ":" + port + ". Sleeping 5000ms and retrying."); + try { Thread.sleep(subscribeRetryWaitTimeMillis); } catch (InterruptedException e1) { e1.printStackTrace(); } + } else { + log.fine("Unsubscribing from Sentinel at " + host + ":" + port); + } } } } diff --git a/src/main/java/redis/clients/jedis/Protocol.java b/src/main/java/redis/clients/jedis/Protocol.java index d167b84..db3d915 100644 --- a/src/main/java/redis/clients/jedis/Protocol.java +++ b/src/main/java/redis/clients/jedis/Protocol.java @@ -13,6 +13,7 @@ import redis.clients.util.SafeEncoder; public final class Protocol { 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; diff --git a/src/test/java/redis/clients/jedis/tests/HostAndPortUtil.java b/src/test/java/redis/clients/jedis/tests/HostAndPortUtil.java index 215cfc4..991cf35 100644 --- a/src/test/java/redis/clients/jedis/tests/HostAndPortUtil.java +++ b/src/test/java/redis/clients/jedis/tests/HostAndPortUtil.java @@ -6,56 +6,104 @@ import java.util.List; import redis.clients.jedis.Protocol; public class HostAndPortUtil { - private static List hostAndPortList = new ArrayList( - 2); + private static List redisHostAndPortList = new ArrayList(); + private static List sentinelHostAndPortList = new ArrayList(); static { - final HostAndPort defaulthnp1 = new HostAndPort(); + + HostAndPort defaulthnp1 = new HostAndPort(); defaulthnp1.host = "localhost"; - defaulthnp1.port = Protocol.DEFAULT_PORT; - hostAndPortList.add(defaulthnp1); + defaulthnp1.port = Protocol.DEFAULT_PORT; + redisHostAndPortList.add(defaulthnp1); - final HostAndPort defaulthnp2 = new HostAndPort(); + HostAndPort defaulthnp2 = new HostAndPort(); defaulthnp2.host = "localhost"; defaulthnp2.port = Protocol.DEFAULT_PORT + 1; - hostAndPortList.add(defaulthnp2); + redisHostAndPortList.add(defaulthnp2); + + HostAndPort defaulthnp3 = new HostAndPort(); + defaulthnp3.host = "localhost"; + defaulthnp3.port = Protocol.DEFAULT_PORT + 2; + redisHostAndPortList.add(defaulthnp3); + + HostAndPort defaulthnp4 = new HostAndPort(); + defaulthnp4.host = "localhost"; + defaulthnp4.port = Protocol.DEFAULT_PORT + 3; + redisHostAndPortList.add(defaulthnp4); + + HostAndPort defaulthnp5 = new HostAndPort(); + defaulthnp5.host = "localhost"; + defaulthnp5.port = Protocol.DEFAULT_SENTINEL_PORT; + sentinelHostAndPortList.add(defaulthnp5); + + HostAndPort defaulthnp6 = new HostAndPort(); + defaulthnp6.host = "localhost"; + defaulthnp6.port = Protocol.DEFAULT_SENTINEL_PORT + 1; + sentinelHostAndPortList.add(defaulthnp6); + + HostAndPort defaulthnp7 = new HostAndPort(); + defaulthnp7.host = "localhost"; + defaulthnp7.port = Protocol.DEFAULT_SENTINEL_PORT + 2; + sentinelHostAndPortList.add(defaulthnp7); - final String envHosts = System.getProperty("redis-hosts"); - if (null != envHosts && 0 < envHosts.length()) { - final String[] hostDefs = envHosts.split(","); + String envRedisHosts = System.getProperty("redis-hosts"); + String envSentinelHosts = System.getProperty("sentinel-hosts"); + + redisHostAndPortList = parseHosts(envRedisHosts, redisHostAndPortList); + sentinelHostAndPortList = parseHosts(envSentinelHosts, sentinelHostAndPortList); + } + + public static List parseHosts(String envHosts, List existingHostsAndPorts) { + + if (null != envHosts && 0 < envHosts.length()) { + + String[] hostDefs = envHosts.split(","); + if (null != hostDefs && 2 <= hostDefs.length) { - hostAndPortList = new ArrayList( - hostDefs.length); + + List envHostsAndPorts = new ArrayList(hostDefs.length); + for (String hostDef : hostDefs) { - final String[] hostAndPort = hostDef.split(":"); + + String[] hostAndPort = hostDef.split(":"); + if (null != hostAndPort && 2 == hostAndPort.length) { - final HostAndPort hnp = new HostAndPort(); + + HostAndPort hnp = new HostAndPort(); hnp.host = hostAndPort[0]; + try { hnp.port = Integer.parseInt(hostAndPort[1]); } catch (final NumberFormatException nfe) { hnp.port = Protocol.DEFAULT_PORT; - } - hostAndPortList.add(hnp); + } + + envHostsAndPorts.add(hnp); } } + + return envHostsAndPorts; } } - final StringBuilder strb = new StringBuilder( - "Redis hosts to be used : "); - for (HostAndPort hnp : hostAndPortList) { - strb.append('[').append(hnp.host).append(':').append(hnp.port) - .append(']').append(' '); - } - System.out.println(strb); + + return existingHostsAndPorts; } - + public static List getRedisServers() { - return hostAndPortList; + return redisHostAndPortList; + } + + public static List getSentinelServers() { + return sentinelHostAndPortList; } public static class HostAndPort { public String host; public int port; + + @Override + public String toString() { + return host + ":" + port; + } } } diff --git a/src/test/java/redis/clients/jedis/tests/JedisSentinelPoolTest.java b/src/test/java/redis/clients/jedis/tests/JedisSentinelPoolTest.java new file mode 100644 index 0000000..5c8affe --- /dev/null +++ b/src/test/java/redis/clients/jedis/tests/JedisSentinelPoolTest.java @@ -0,0 +1,64 @@ +package redis.clients.jedis.tests; + +import java.util.HashSet; +import java.util.Set; + +import org.junit.Before; +import org.junit.Test; + +import redis.clients.jedis.DebugParams; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisSentinelPool; +import redis.clients.jedis.tests.HostAndPortUtil.HostAndPort; + +public class JedisSentinelPoolTest extends JedisTestBase { + + protected static HostAndPort master = HostAndPortUtil.getRedisServers().get(2); + protected static HostAndPort slave1 = HostAndPortUtil.getRedisServers().get(3); + protected static HostAndPort sentinel1 = HostAndPortUtil.getSentinelServers().get(1); + protected static HostAndPort sentinel2 = HostAndPortUtil.getSentinelServers().get(2); + + protected static Jedis masterJedis; + protected static Jedis slaveJedis1; + protected static Jedis sentinelJedis1; + + protected Set sentinels = new HashSet(); + + @Before + public void setUp() throws Exception { + + // set up master and slaves + masterJedis = new Jedis(master.host, master.port); + masterJedis.slaveofNoOne(); + + slaveJedis1 = new Jedis(slave1.host, slave1.port); + slaveJedis1.slaveof(master.host, master.port); + + sentinelJedis1 = new Jedis(sentinel1.host, sentinel1.port); + sentinels.add(sentinel1.toString()); + sentinels.add(sentinel2.toString()); + + // FIXME: The following allows the master/slave relationship to + // be established. We can do this more elegantly. + Thread.sleep(10000); + } + + @Test + public void segfaultMaster() throws InterruptedException { + + JedisSentinelPool pool = new JedisSentinelPool("mymaster", sentinels); + + Jedis jedis = pool.getResource(); + assertEquals("PONG", jedis.ping()); + + try { masterJedis.debug(DebugParams.SEGFAULT()); } catch (Exception e) {} + + // wait for the sentinel to promote a master + // FIXME: we can query the sentinel and sleep + // right until the master is promoted + Thread.sleep(35000); + + jedis = pool.getResource(); + assertEquals("PONG", jedis.ping()); + } +} diff --git a/src/test/java/redis/clients/jedis/tests/JedisSentinelTest.java b/src/test/java/redis/clients/jedis/tests/JedisSentinelTest.java index 5d5d48d..a3dc68a 100644 --- a/src/test/java/redis/clients/jedis/tests/JedisSentinelTest.java +++ b/src/test/java/redis/clients/jedis/tests/JedisSentinelTest.java @@ -49,7 +49,7 @@ public class JedisSentinelTest { assertEquals("6379", masterHostAndPort.get(1)); List> slaves = j.sentinelSlaves(masterName); - assertEquals(1, slaves.size()); + assertTrue(slaves.size() > 0); assertEquals("6379", slaves.get(0).get("master-port")); List isMasterDownByAddr = j