Added a JUnit test for JedisSentinelPool.

This test will set up and get a master from a Redis master slave pair being
watched by 2 Sentinels. It pings the master, segfaults it, asks the pool for
another connection and makes sure it can ping it.

This commit also restores the pom.xml file's scm information back to
xetorthio and adds the default Sentinel port to the Procotol.
This commit is contained in:
Hisham Mardam-Bey
2013-08-28 23:10:50 -04:00
parent e17679f568
commit 39d81d8f1f
7 changed files with 209 additions and 37 deletions

View File

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

15
pom.xml
View File

@@ -13,7 +13,7 @@
<version>2.2.2-SNAPSHOT</version>
<name>Jedis</name>
<description>Jedis is a blazingly small and sane Redis java client.</description>
<url>https://github.com/mardambey/jedis</url>
<url>https://github.com/xetorthio/jedis</url>
<mailingLists>
<mailingList>
@@ -28,24 +28,25 @@
<licenses>
<license>
<name>MIT</name>
<url>http://github.com/mardambey/jedis/raw/master/LICENSE.txt</url>
<url>http://github.com/xetorthio/jedis/raw/master/LICENSE.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<issueManagement>
<system>github</system>
<url>http://github.com/mardambey/jedis/issues</url>
<url>http://github.com/xetorthio/jedis/issues</url>
</issueManagement>
<scm>
<connection>scm:git:git@github.com:mardambey/jedis.git</connection>
<url>scm:git:git@github.com:mardambey/jedis.git</url>
<developerConnection>scm:git:git@github.com:mardambey/jedis.git</developerConnection>
<connection>scm:git:git@github.com:xetorthio/jedis.git</connection>
<url>scm:git:git@github.com:xetorthio/jedis.git</url>
<developerConnection>scm:git:git@github.com:xetorthio/jedis.git</developerConnection>
</scm>
<properties>
<redis-hosts>localhost:6379,localhost:6380</redis-hosts>
<redis-hosts>localhost:6379,localhost:6380,localhost:6381,localhost:6382</redis-hosts>
<sentinel-hosts>localhost:26379,localhost:26380</sentinel-hosts>
</properties>
<dependencies>

View File

@@ -231,9 +231,13 @@ public class JedisSentinelPool extends Pool<Jedis> {
}, "+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);
}
}
}
}

View File

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

View File

@@ -6,56 +6,104 @@ import java.util.List;
import redis.clients.jedis.Protocol;
public class HostAndPortUtil {
private static List<HostAndPort> hostAndPortList = new ArrayList<HostAndPortUtil.HostAndPort>(
2);
private static List<HostAndPort> redisHostAndPortList = new ArrayList<HostAndPortUtil.HostAndPort>();
private static List<HostAndPort> sentinelHostAndPortList = new ArrayList<HostAndPortUtil.HostAndPort>();
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<HostAndPort> parseHosts(String envHosts, List<HostAndPort> existingHostsAndPorts) {
if (null != envHosts && 0 < envHosts.length()) {
String[] hostDefs = envHosts.split(",");
if (null != hostDefs && 2 <= hostDefs.length) {
hostAndPortList = new ArrayList<HostAndPortUtil.HostAndPort>(
hostDefs.length);
List<HostAndPort> envHostsAndPorts = new ArrayList<HostAndPortUtil.HostAndPort>(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<HostAndPort> getRedisServers() {
return hostAndPortList;
return redisHostAndPortList;
}
public static List<HostAndPort> getSentinelServers() {
return sentinelHostAndPortList;
}
public static class HostAndPort {
public String host;
public int port;
@Override
public String toString() {
return host + ":" + port;
}
}
}

View File

@@ -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<String> sentinels = new HashSet<String>();
@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());
}
}

View File

@@ -49,7 +49,7 @@ public class JedisSentinelTest {
assertEquals("6379", masterHostAndPort.get(1));
List<Map<String, String>> slaves = j.sentinelSlaves(masterName);
assertEquals(1, slaves.size());
assertTrue(slaves.size() > 0);
assertEquals("6379", slaves.get(0).get("master-port"));
List<? extends Object> isMasterDownByAddr = j