diff --git a/.gitignore b/.gitignore index 982e6b6..181df84 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,6 @@ target/ build/ bin/ tags +.idea +*.aof +*.rdb diff --git a/Makefile b/Makefile index 3485047..1ac2bb9 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,7 @@ define REDIS3_CONF daemonize yes port 6381 requirepass foobared +masterauth foobared pidfile /tmp/redis3.pid logfile /tmp/redis3.log save "" @@ -52,7 +53,7 @@ pidfile /tmp/redis5.pid logfile /tmp/redis5.log save "" appendonly no -slaveof localhost 6381 +slaveof localhost 6379 endef define REDIS6_CONF @@ -64,7 +65,6 @@ pidfile /tmp/redis6.pid logfile /tmp/redis6.log save "" appendonly no -slaveof localhost 6379 endef define REDIS7_CONF @@ -76,18 +76,7 @@ pidfile /tmp/redis7.pid logfile /tmp/redis7.log save "" appendonly no -endef - -define REDIS8_CONF -daemonize yes -port 6386 -requirepass foobared -masterauth foobared -pidfile /tmp/redis8.pid -logfile /tmp/redis8.log -save "" -appendonly no -slaveof localhost 6385 +slaveof localhost 6384 endef # SENTINELS @@ -97,7 +86,7 @@ daemonize yes sentinel monitor mymaster 127.0.0.1 6379 1 sentinel auth-pass mymaster foobared sentinel down-after-milliseconds mymaster 2000 -sentinel failover-timeout mymaster 180000 +sentinel failover-timeout mymaster 120000 sentinel parallel-syncs mymaster 1 pidfile /tmp/sentinel1.pid logfile /tmp/sentinel1.log @@ -106,11 +95,11 @@ endef define REDIS_SENTINEL2 port 26380 daemonize yes -sentinel monitor mymaster 127.0.0.1 6381 2 +sentinel monitor mymaster 127.0.0.1 6381 1 sentinel auth-pass mymaster foobared sentinel down-after-milliseconds mymaster 2000 sentinel parallel-syncs mymaster 1 -sentinel failover-timeout mymaster 180000 +sentinel failover-timeout mymaster 120000 pidfile /tmp/sentinel2.pid logfile /tmp/sentinel2.log endef @@ -118,25 +107,13 @@ endef define REDIS_SENTINEL3 port 26381 daemonize yes -sentinel monitor mymaster 127.0.0.1 6381 2 -sentinel auth-pass mymaster foobared -sentinel down-after-milliseconds mymaster 2000 -sentinel parallel-syncs mymaster 1 -sentinel failover-timeout mymaster 180000 -pidfile /tmp/sentinel3.pid -logfile /tmp/sentinel3.log -endef - -define REDIS_SENTINEL4 -port 26382 -daemonize yes -sentinel monitor mymasterfailover 127.0.0.1 6385 1 +sentinel monitor mymasterfailover 127.0.0.1 6384 1 sentinel auth-pass mymasterfailover foobared sentinel down-after-milliseconds mymasterfailover 2000 -sentinel failover-timeout mymasterfailover 180000 +sentinel failover-timeout mymasterfailover 120000 sentinel parallel-syncs mymasterfailover 1 -pidfile /tmp/sentinel4.pid -logfile /tmp/sentinel4.log +pidfile /tmp/sentinel3.pid +logfile /tmp/sentinel3.log endef # CLUSTER REDIS NODES @@ -183,11 +160,9 @@ export REDIS4_CONF export REDIS5_CONF export REDIS6_CONF export REDIS7_CONF -export REDIS8_CONF export REDIS_SENTINEL1 export REDIS_SENTINEL2 export REDIS_SENTINEL3 -export REDIS_SENTINEL4 export REDIS_CLUSTER_NODE1_CONF export REDIS_CLUSTER_NODE2_CONF export REDIS_CLUSTER_NODE3_CONF @@ -200,14 +175,11 @@ start: cleanup echo "$$REDIS5_CONF" | redis-server - echo "$$REDIS6_CONF" | redis-server - echo "$$REDIS7_CONF" | redis-server - - echo "$$REDIS8_CONF" | redis-server - echo "$$REDIS_SENTINEL1" > /tmp/sentinel1.conf && redis-server /tmp/sentinel1.conf --sentinel @sleep 0.5 echo "$$REDIS_SENTINEL2" > /tmp/sentinel2.conf && redis-server /tmp/sentinel2.conf --sentinel @sleep 0.5 echo "$$REDIS_SENTINEL3" > /tmp/sentinel3.conf && redis-server /tmp/sentinel3.conf --sentinel - @sleep 0.5 - echo "$$REDIS_SENTINEL4" > /tmp/sentinel4.conf && redis-server /tmp/sentinel4.conf --sentinel echo "$$REDIS_CLUSTER_NODE1_CONF" | redis-server - echo "$$REDIS_CLUSTER_NODE2_CONF" | redis-server - echo "$$REDIS_CLUSTER_NODE3_CONF" | redis-server - @@ -219,24 +191,20 @@ cleanup: stop: 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` || true - kill `cat /tmp/redis5.pid` || true - kill `cat /tmp/redis6.pid` || true + kill `cat /tmp/redis3.pid` + kill `cat /tmp/redis4.pid` + kill `cat /tmp/redis5.pid` + kill `cat /tmp/redis6.pid` kill `cat /tmp/redis7.pid` - kill `cat /tmp/redis8.pid` kill `cat /tmp/sentinel1.pid` kill `cat /tmp/sentinel2.pid` kill `cat /tmp/sentinel3.pid` - kill `cat /tmp/sentinel4.pid` kill `cat /tmp/redis_cluster_node1.pid` || true kill `cat /tmp/redis_cluster_node2.pid` || true kill `cat /tmp/redis_cluster_node3.pid` || true rm -f /tmp/sentinel1.conf rm -f /tmp/sentinel2.conf rm -f /tmp/sentinel3.conf - rm -f /tmp/sentinel4.conf rm -f /tmp/redis_cluster_node1.conf rm -f /tmp/redis_cluster_node2.conf rm -f /tmp/redis_cluster_node3.conf diff --git a/pom.xml b/pom.xml index 064af01..a55806e 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ jar redis.clients jedis - 2.4.1-SNAPSHOT + 2.4.2-SNAPSHOT Jedis Jedis is a blazingly small and sane Redis java client. https://github.com/xetorthio/jedis @@ -45,8 +45,8 @@ - localhost:6379,localhost:6380,localhost:6381,localhost:6382,localhost:6383,localhost:6384,localhost:6385,localhost:6386 - localhost:26379,localhost:26380,localhost:26381,localhost:26382 + localhost:6379,localhost:6380,localhost:6381,localhost:6382,localhost:6383,localhost:6384,localhost:6385 + localhost:26379,localhost:26380,localhost:26381 localhost:7379,localhost:7380,localhost:7381 github diff --git a/src/test/java/redis/clients/jedis/tests/HostAndPortUtil.java b/src/test/java/redis/clients/jedis/tests/HostAndPortUtil.java index 491f0a3..cb7a58b 100644 --- a/src/test/java/redis/clients/jedis/tests/HostAndPortUtil.java +++ b/src/test/java/redis/clients/jedis/tests/HostAndPortUtil.java @@ -23,7 +23,6 @@ public class HostAndPortUtil { sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT)); sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 1)); sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 2)); - sentinelHostAndPortList.add(new HostAndPort("localhost", Protocol.DEFAULT_SENTINEL_PORT + 3)); clusterHostAndPortList.add(new HostAndPort("localhost", 7379)); clusterHostAndPortList.add(new HostAndPort("localhost", 7380)); diff --git a/src/test/java/redis/clients/jedis/tests/JedisSentinelPoolTest.java b/src/test/java/redis/clients/jedis/tests/JedisSentinelPoolTest.java index f8a3ee2..c8df9c5 100644 --- a/src/test/java/redis/clients/jedis/tests/JedisSentinelPoolTest.java +++ b/src/test/java/redis/clients/jedis/tests/JedisSentinelPoolTest.java @@ -2,18 +2,17 @@ package redis.clients.jedis.tests; import java.util.HashSet; import java.util.Set; -import java.util.concurrent.atomic.AtomicReference; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.Before; import org.junit.Test; -import redis.clients.jedis.DebugParams; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPubSub; import redis.clients.jedis.JedisSentinelPool; import redis.clients.jedis.Transaction; +import redis.clients.jedis.exceptions.JedisConnectionException; +import redis.clients.jedis.tests.utils.JedisSentinelTestUtil; public class JedisSentinelPoolTest extends JedisTestBase { private static final String MASTER_NAME = "mymaster"; @@ -22,12 +21,8 @@ public class JedisSentinelPoolTest extends JedisTestBase { .get(2); protected static HostAndPort slave1 = HostAndPortUtil.getRedisServers() .get(3); - protected static HostAndPort slave2 = HostAndPortUtil.getRedisServers() - .get(4); protected static HostAndPort sentinel1 = HostAndPortUtil .getSentinelServers().get(1); - protected static HostAndPort sentinel2 = HostAndPortUtil - .getSentinelServers().get(2); protected static Jedis sentinelJedis1; @@ -36,7 +31,6 @@ public class JedisSentinelPoolTest extends JedisTestBase { @Before public void setUp() throws Exception { sentinels.add(sentinel1.toString()); - sentinels.add(sentinel2.toString()); sentinelJedis1 = new Jedis(sentinel1.getHost(), sentinel1.getPort()); } @@ -46,17 +40,47 @@ public class JedisSentinelPoolTest extends JedisTestBase { JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, new GenericObjectPoolConfig(), 1000, "foobared", 2); - // perform failover - doSegFaultMaster(pool); - - // perform failover once again - doSegFaultMaster(pool); + forceFailover(pool); + forceFailover(pool); // you can test failover as much as possible - // but you need to prepare additional slave per failover + } + + @Test + public void returnResourceShouldResetState() { + GenericObjectPoolConfig config = new GenericObjectPoolConfig(); + config.setMaxTotal(1); + config.setBlockWhenExhausted(false); + JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, + config, 1000, "foobared", 2); + + Jedis jedis = pool.getResource(); + Jedis jedis2 = null; + + try { + jedis.set("hello", "jedis"); + Transaction t = jedis.multi(); + t.set("hello", "world"); + pool.returnResource(jedis); + + jedis2 = pool.getResource(); + + assertTrue(jedis == jedis2); + assertEquals("jedis", jedis2.get("hello")); + } catch (JedisConnectionException e) { + if (jedis2 != null) { + pool.returnBrokenResource(jedis2); + jedis2 = null; + } + } finally { + if (jedis2 != null) + pool.returnResource(jedis2); + + pool.destroy(); + } } - private void doSegFaultMaster(JedisSentinelPool pool) + private void forceFailover(JedisSentinelPool pool) throws InterruptedException { HostAndPort oldMaster = pool.getCurrentHostMaster(); @@ -64,14 +88,16 @@ public class JedisSentinelPoolTest extends JedisTestBase { Jedis jedis = pool.getResource(); assertEquals("PONG", jedis.ping()); - try { - jedis.debug(DebugParams.SEGFAULT()); - } catch (Exception e) { - } - + // It can throw JedisDataException while there's no slave to promote + // There's nothing we can do, so we just pass Exception to make test + // fail fast + sentinelJedis1.sentinelFailover(MASTER_NAME); + waitForFailover(pool, oldMaster); + // JedisSentinelPool recognize master but may not changed internal pool + // yet Thread.sleep(100); - + jedis = pool.getResource(); assertEquals("PONG", jedis.ping()); assertEquals("foobared", jedis.configGet("requirepass").get(1)); @@ -80,62 +106,15 @@ public class JedisSentinelPoolTest extends JedisTestBase { private void waitForFailover(JedisSentinelPool pool, HostAndPort oldMaster) throws InterruptedException { - waitForJedisSentinelPoolRecognizeNewMaster(pool); + HostAndPort newMaster = JedisSentinelTestUtil + .waitForNewPromotedMaster(sentinelJedis1); + + waitForJedisSentinelPoolRecognizeNewMaster(pool, newMaster); } private void waitForJedisSentinelPoolRecognizeNewMaster( - JedisSentinelPool pool) throws InterruptedException { - - final AtomicReference newmaster = new AtomicReference( - ""); - - sentinelJedis1.psubscribe(new JedisPubSub() { - - @Override - public void onMessage(String channel, String message) { - // TODO Auto-generated method stub - - } - - @Override - public void onPMessage(String pattern, String channel, - String message) { - if (channel.equals("+switch-master")) { - newmaster.set(message); - punsubscribe(); - } - // TODO Auto-generated method stub - - } - - @Override - public void onSubscribe(String channel, int subscribedChannels) { - // TODO Auto-generated method stub - - } - - @Override - public void onUnsubscribe(String channel, int subscribedChannels) { - // TODO Auto-generated method stub - - } - - @Override - public void onPUnsubscribe(String pattern, int subscribedChannels) { - // TODO Auto-generated method stub - - } - - @Override - public void onPSubscribe(String pattern, int subscribedChannels) { - // TODO Auto-generated method stub - - } - }, "*"); - - String[] chunks = newmaster.get().split(" "); - HostAndPort newMaster = new HostAndPort(chunks[3], - Integer.parseInt(chunks[4])); + JedisSentinelPool pool, HostAndPort newMaster) + throws InterruptedException { while (true) { String host = pool.getCurrentHostMaster().getHost(); @@ -150,25 +129,5 @@ public class JedisSentinelPoolTest extends JedisTestBase { Thread.sleep(100); } } - - @Test - public void returnResourceShouldResetState() { - GenericObjectPoolConfig config = new GenericObjectPoolConfig(); - config.setMaxTotal(1); - config.setBlockWhenExhausted(false); - JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, - config, 1000, "foobared", 2); - - Jedis jedis = pool.getResource(); - jedis.set("hello", "jedis"); - Transaction t = jedis.multi(); - t.set("hello", "world"); - pool.returnResource(jedis); - - Jedis jedis2 = pool.getResource(); - assertTrue(jedis == jedis2); - assertEquals("jedis", jedis2.get("hello")); - pool.returnResource(jedis2); - pool.destroy(); - } + } diff --git a/src/test/java/redis/clients/jedis/tests/JedisSentinelTest.java b/src/test/java/redis/clients/jedis/tests/JedisSentinelTest.java index 4445072..822c659 100644 --- a/src/test/java/redis/clients/jedis/tests/JedisSentinelTest.java +++ b/src/test/java/redis/clients/jedis/tests/JedisSentinelTest.java @@ -22,15 +22,15 @@ public class JedisSentinelTest extends JedisTestBase { protected static HostAndPort master = HostAndPortUtil.getRedisServers() .get(0); - protected static HostAndPort slave = HostAndPortUtil.getRedisServers().get( - 5); + protected static HostAndPort slave = HostAndPortUtil.getRedisServers() + .get(4); protected static HostAndPort sentinel = HostAndPortUtil .getSentinelServers().get(0); protected static HostAndPort sentinelForFailover = HostAndPortUtil - .getSentinelServers().get(3); + .getSentinelServers().get(2); protected static HostAndPort masterForFailover = HostAndPortUtil - .getRedisServers().get(6); + .getRedisServers().get(5); @Before public void setup() throws InterruptedException { @@ -50,30 +50,35 @@ public class JedisSentinelTest extends JedisTestBase { @Test public void sentinel() { Jedis j = new Jedis(sentinel.getHost(), sentinel.getPort()); - List> masters = j.sentinelMasters(); - boolean inMasters = false; - for (Map master : masters) - if (MASTER_NAME.equals(master.get("name"))) - inMasters = true; + try { + List> masters = j.sentinelMasters(); - assertTrue(inMasters); + boolean inMasters = false; + for (Map master : masters) + if (MASTER_NAME.equals(master.get("name"))) + inMasters = true; - List masterHostAndPort = j - .sentinelGetMasterAddrByName(MASTER_NAME); - HostAndPort masterFromSentinel = new HostAndPort( - masterHostAndPort.get(0), Integer.parseInt(masterHostAndPort - .get(1))); - assertEquals(master, masterFromSentinel); + assertTrue(inMasters); - List> slaves = j.sentinelSlaves(MASTER_NAME); - assertTrue(slaves.size() > 0); - assertEquals(master.getPort(), - Integer.parseInt(slaves.get(0).get("master-port"))); + List masterHostAndPort = j + .sentinelGetMasterAddrByName(MASTER_NAME); + HostAndPort masterFromSentinel = new HostAndPort( + masterHostAndPort.get(0), + Integer.parseInt(masterHostAndPort.get(1))); + assertEquals(master, masterFromSentinel); - // DO NOT RE-RUN TEST TOO FAST, RESET TAKES SOME TIME TO... RESET - assertEquals(Long.valueOf(1), j.sentinelReset(MASTER_NAME)); - assertEquals(Long.valueOf(0), j.sentinelReset("woof" + MASTER_NAME)); + List> slaves = j.sentinelSlaves(MASTER_NAME); + assertTrue(slaves.size() > 0); + assertEquals(master.getPort(), + Integer.parseInt(slaves.get(0).get("master-port"))); + + // DO NOT RE-RUN TEST TOO FAST, RESET TAKES SOME TIME TO... RESET + assertEquals(Long.valueOf(1), j.sentinelReset(MASTER_NAME)); + assertEquals(Long.valueOf(0), j.sentinelReset("woof" + MASTER_NAME)); + } finally { + j.close(); + } } @Test @@ -81,40 +86,48 @@ public class JedisSentinelTest extends JedisTestBase { Jedis j = new Jedis(sentinelForFailover.getHost(), sentinelForFailover.getPort()); - HostAndPort currentMaster = new HostAndPort( - masterForFailover.getHost(), masterForFailover.getPort()); + try { + List masterHostAndPort = j + .sentinelGetMasterAddrByName(FAILOVER_MASTER_NAME); + HostAndPort currentMaster = new HostAndPort(masterHostAndPort.get(0), + Integer.parseInt(masterHostAndPort.get(1))); + String result = j.sentinelFailover(FAILOVER_MASTER_NAME); + assertEquals("OK", result); - List masterHostAndPort = j - .sentinelGetMasterAddrByName(FAILOVER_MASTER_NAME); - String result = j.sentinelFailover(FAILOVER_MASTER_NAME); - assertEquals("OK", result); + JedisSentinelTestUtil.waitForNewPromotedMaster(j); - JedisSentinelTestUtil.waitForNewPromotedMaster(sentinelForFailover, - FAILOVER_MASTER_NAME, currentMaster); + masterHostAndPort = j + .sentinelGetMasterAddrByName(FAILOVER_MASTER_NAME); + HostAndPort newMaster = new HostAndPort(masterHostAndPort.get(0), + Integer.parseInt(masterHostAndPort.get(1))); - masterHostAndPort = j.sentinelGetMasterAddrByName(FAILOVER_MASTER_NAME); - HostAndPort newMaster = new HostAndPort(masterHostAndPort.get(0), - Integer.parseInt(masterHostAndPort.get(1))); + assertNotEquals(newMaster, currentMaster); + } finally { + j.close(); + } - assertNotEquals(newMaster, currentMaster); } @Test public void sentinelMonitor() { Jedis j = new Jedis(sentinel.getHost(), sentinel.getPort()); - // monitor new master - String result = j.sentinelMonitor(MONITOR_MASTER_NAME, MASTER_IP, - master.getPort(), 1); - assertEquals("OK", result); - - // already monitored try { - j.sentinelMonitor(MONITOR_MASTER_NAME, MASTER_IP, master.getPort(), - 1); - fail(); - } catch (JedisDataException e) { - // pass + // monitor new master + String result = j.sentinelMonitor(MONITOR_MASTER_NAME, MASTER_IP, + master.getPort(), 1); + assertEquals("OK", result); + + // already monitored + try { + j.sentinelMonitor(MONITOR_MASTER_NAME, MASTER_IP, + master.getPort(), 1); + fail(); + } catch (JedisDataException e) { + // pass + } + } finally { + j.close(); } } @@ -122,19 +135,23 @@ public class JedisSentinelTest extends JedisTestBase { public void sentinelRemove() { Jedis j = new Jedis(sentinel.getHost(), sentinel.getPort()); - ensureMonitored(sentinel, REMOVE_MASTER_NAME, MASTER_IP, - master.getPort(), 1); - - String result = j.sentinelRemove(REMOVE_MASTER_NAME); - assertEquals("OK", result); - - // not exist try { - result = j.sentinelRemove(REMOVE_MASTER_NAME); - assertNotEquals("OK", result); - fail(); - } catch (JedisDataException e) { - // pass + ensureMonitored(sentinel, REMOVE_MASTER_NAME, MASTER_IP, + master.getPort(), 1); + + String result = j.sentinelRemove(REMOVE_MASTER_NAME); + assertEquals("OK", result); + + // not exist + try { + result = j.sentinelRemove(REMOVE_MASTER_NAME); + assertNotEquals("OK", result); + fail(); + } catch (JedisDataException e) { + // pass + } + } finally { + j.close(); } } @@ -142,24 +159,29 @@ public class JedisSentinelTest extends JedisTestBase { public void sentinelSet() { Jedis j = new Jedis(sentinel.getHost(), sentinel.getPort()); - Map parameterMap = new HashMap(); - parameterMap.put("down-after-milliseconds", String.valueOf(1234)); - parameterMap.put("parallel-syncs", String.valueOf(3)); - parameterMap.put("quorum", String.valueOf(2)); - j.sentinelSet(MASTER_NAME, parameterMap); + try { + Map parameterMap = new HashMap(); + parameterMap.put("down-after-milliseconds", String.valueOf(1234)); + parameterMap.put("parallel-syncs", String.valueOf(3)); + parameterMap.put("quorum", String.valueOf(2)); + j.sentinelSet(MASTER_NAME, parameterMap); - List> masters = j.sentinelMasters(); - for (Map master : masters) { - if (master.get("name").equals(MASTER_NAME)) { - assertEquals(1234, - Integer.parseInt(master.get("down-after-milliseconds"))); - assertEquals(3, Integer.parseInt(master.get("parallel-syncs"))); - assertEquals(2, Integer.parseInt(master.get("quorum"))); + List> masters = j.sentinelMasters(); + for (Map master : masters) { + if (master.get("name").equals(MASTER_NAME)) { + assertEquals(1234, Integer.parseInt(master + .get("down-after-milliseconds"))); + assertEquals(3, + Integer.parseInt(master.get("parallel-syncs"))); + assertEquals(2, Integer.parseInt(master.get("quorum"))); + } } - } - parameterMap.put("quorum", String.valueOf(1)); - j.sentinelSet(MASTER_NAME, parameterMap); + parameterMap.put("quorum", String.valueOf(1)); + j.sentinelSet(MASTER_NAME, parameterMap); + } finally { + j.close(); + } } private void ensureMonitored(HostAndPort sentinel, String masterName, @@ -168,6 +190,8 @@ public class JedisSentinelTest extends JedisTestBase { try { j.sentinelMonitor(masterName, ip, port, quorum); } catch (JedisDataException e) { + } finally { + j.close(); } } @@ -176,6 +200,9 @@ public class JedisSentinelTest extends JedisTestBase { try { j.sentinelRemove(masterName); } catch (JedisDataException e) { + } finally { + j.close(); } } + } diff --git a/src/test/java/redis/clients/jedis/tests/ShardedJedisPoolTest.java b/src/test/java/redis/clients/jedis/tests/ShardedJedisPoolTest.java index 35d7c54..ee7951b 100644 --- a/src/test/java/redis/clients/jedis/tests/ShardedJedisPoolTest.java +++ b/src/test/java/redis/clients/jedis/tests/ShardedJedisPoolTest.java @@ -155,7 +155,7 @@ public class ShardedJedisPoolTest extends Assert { // items on one shard // alter shard 1 and recreate pool pool.destroy(); - shards.set(1, new JedisShardInfo("nohost", 1234)); + shards.set(1, new JedisShardInfo("localhost", 1234)); pool = new ShardedJedisPool(redisConfig, shards); jedis = pool.getResource(); Long actual = Long.valueOf(0); diff --git a/src/test/java/redis/clients/jedis/tests/utils/FailoverAbortedException.java b/src/test/java/redis/clients/jedis/tests/utils/FailoverAbortedException.java new file mode 100644 index 0000000..e711087 --- /dev/null +++ b/src/test/java/redis/clients/jedis/tests/utils/FailoverAbortedException.java @@ -0,0 +1,17 @@ +package redis.clients.jedis.tests.utils; + +public class FailoverAbortedException extends RuntimeException { + private static final long serialVersionUID = 1925110762858409954L; + + public FailoverAbortedException(String message) { + super(message); + } + + public FailoverAbortedException(Throwable cause) { + super(cause); + } + + public FailoverAbortedException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/test/java/redis/clients/jedis/tests/utils/JedisSentinelTestUtil.java b/src/test/java/redis/clients/jedis/tests/utils/JedisSentinelTestUtil.java index 9ac4a85..dcb9334 100644 --- a/src/test/java/redis/clients/jedis/tests/utils/JedisSentinelTestUtil.java +++ b/src/test/java/redis/clients/jedis/tests/utils/JedisSentinelTestUtil.java @@ -1,131 +1,60 @@ package redis.clients.jedis.tests.utils; -import java.util.List; -import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPubSub; +import redis.clients.jedis.tests.utils.FailoverAbortedException; public class JedisSentinelTestUtil { - - public static void waitForSentinelRecognizeRedisReplication( - HostAndPort sentinel, String masterName, HostAndPort master, - List slaves) throws InterruptedException { - Jedis sentinelJedis = new Jedis(sentinel.getHost(), sentinel.getPort()); - while (true) { - Thread.sleep(1000); - - if (!isMasterRecognized(sentinelJedis, masterName, master)) { - System.out.println("Master not recognized by Sentinel " - + sentinel.getHost() + ":" + sentinel.getPort() - + ", sleep..."); - continue; - } - - if (!isSlavesRecognized(sentinelJedis, masterName, slaves)) { - System.out.println("Slaves not recognized by Sentinel " - + sentinel.getHost() + ":" + sentinel.getPort() - + ", sleep..."); - continue; - } - - // all recognized - break; - } - - } - - public static HostAndPort waitForNewPromotedMaster(HostAndPort sentinel, - String masterName, HostAndPort oldMaster) + public static HostAndPort waitForNewPromotedMaster(Jedis sentinelJedis) throws InterruptedException { - Jedis sentinelJedis = new Jedis(sentinel.getHost(), sentinel.getPort()); + + final AtomicReference newmaster = new AtomicReference( + ""); - HostAndPort newMaster = null; - while (true) { - Thread.sleep(1000); + sentinelJedis.psubscribe(new JedisPubSub() { - List sentinelMasterInfos = sentinelJedis - .sentinelGetMasterAddrByName(masterName); - if (sentinelMasterInfos == null) { - System.out - .println("Cannot retrieve Sentinel's master address info, sleep..."); - continue; + @Override + public void onMessage(String channel, String message) { } - newMaster = new HostAndPort(sentinelMasterInfos.get(0), - Integer.parseInt(sentinelMasterInfos.get(1))); + @Override + public void onPMessage(String pattern, String channel, + String message) { + if (channel.equals("+switch-master")) { + newmaster.set(message); + punsubscribe(); + } else if (channel.startsWith("-failover-abort")) { + punsubscribe(); + throw new FailoverAbortedException("Unfortunately sentinel cannot failover... reason(channel) : " + + channel + " / message : " + message); + } + } - if (!newMaster.equals(oldMaster)) - break; + @Override + public void onSubscribe(String channel, int subscribedChannels) { + } - System.out - .println("Sentinel's master is not yet changed, sleep..."); - } + @Override + public void onUnsubscribe(String channel, int subscribedChannels) { + } + + @Override + public void onPUnsubscribe(String pattern, int subscribedChannels) { + } + + @Override + public void onPSubscribe(String pattern, int subscribedChannels) { + } + }, "*"); + + String[] chunks = newmaster.get().split(" "); + HostAndPort newMaster = new HostAndPort(chunks[3], + Integer.parseInt(chunks[4])); return newMaster; } - public static void waitForSentinelsRecognizeEachOthers() - throws InterruptedException { - // During failover, master has been changed - // It means that sentinels need to recognize other sentinels from new - // master's hello channel - // Without recognizing, Sentinels cannot run failover - - // Sentinels need to take some time to recognize each other... - // http://redis.io/topics/sentinel - // Sentinel Rule #8: Every Sentinel publishes a message to every - // monitored master - // Pub/Sub channel __sentinel__:hello, every five seconds, blabla... - - // FIXME There're no command for sentinel to list recognized sentinels - // so sleep wisely (channel's hello message interval + margin) - Thread.sleep(5000 + 500); - } - - private static boolean isMasterRecognized(Jedis sentinelJedis, - String masterName, HostAndPort master) { - List sentinelMasterInfos = sentinelJedis - .sentinelGetMasterAddrByName(masterName); - if (sentinelMasterInfos == null) - return false; - - HostAndPort sentinelMaster = new HostAndPort( - sentinelMasterInfos.get(0), - Integer.parseInt(sentinelMasterInfos.get(1))); - - return sentinelMaster.equals(master); - } - - private static boolean isSlavesRecognized(Jedis sentinelJedis, - String masterName, List slaves) { - List> slavesMap = sentinelJedis - .sentinelSlaves(masterName); - - if (slavesMap.size() != slaves.size()) - return false; - - int slavesRecognized = 0; - - for (HostAndPort slave : slaves) { - if (isSlaveFoundInSlavesMap(slavesMap, slave)) - slavesRecognized++; - } - - return slavesRecognized == slaves.size(); - } - - private static boolean isSlaveFoundInSlavesMap( - List> slavesMap, HostAndPort slave) { - for (Map slaveMap : slavesMap) { - HostAndPort sentinelSlave = new HostAndPort(slaveMap.get("ip"), - Integer.parseInt(slaveMap.get("port"))); - - if (sentinelSlave.equals(slave)) - return true; - } - - return false; - } - }