diff --git a/README.md b/README.md
index 6979a298..18a8faf5 100644
--- a/README.md
+++ b/README.md
@@ -3,44 +3,24 @@ embedded-redis
Redis embedded server for Java integration testing
-
-Maven dependency
-==============
-
-Currently embedded-redis is available in clojars repository:
-```
-
- clojars.org
- http://clojars.org/repo
-
-```
-
-Dependency configuration:
-```
-
- redis.embedded
- embedded-redis
- 0.3
-
-```
-More at https://clojars.org/redis.embedded/embedded-redis
-
-Usage example
+Usage
==============
Running RedisServer is as simple as:
-```
+```java
RedisServer redisServer = new RedisServer(6379);
redisServer.start();
// do some work
redisServer.stop();
```
+
You can also provide RedisServer with your own redis executable to run:
-```
+```java
RedisServer redisServer = new RedisServer("/path/to/your/redis", 6379);
```
+
You can also use fluent API to create RedisServer:
-```
+```java
RedisServer redisServer = RedisServer.builder()
.executable("/path/to/your/redis")
.port(6379)
@@ -48,8 +28,9 @@ RedisServer redisServer = RedisServer.builder()
.configFile("/path/to/your/redis.conf")
.build();
```
+
Or even create simple redis.conf file from scratch:
-```
+```java
RedisServer redisServer = RedisServer.builder()
.executable("/path/to/your/redis")
.port(6379)
@@ -58,15 +39,23 @@ RedisServer redisServer = RedisServer.builder()
.setting("appendonly no")
.build();
```
-A simple redis integration test would look like this:
-```
+
+Our Embedded Redis has support for HA Redis clusters with Sentinels and master-slave replication
+
+A simple redis integration test with Redis cluster setup similar to that from production would look like this:
+```java
public class SomeIntegrationTestThatRequiresRedis {
- private RedisServer redisServer;
+ private RedisCluster cluster;
@Before
public void setup() throws Exception {
- redisServer = new RedisServer(6379); // or new RedisServer("/path/to/your/redis", 6379);
- redisServer.start();
+ //creates a cluster with 3 sentinels, quorum size of 2 and 3 replication groups, each with one master and one slave
+ cluster = RedisCluster.builder().sentinelCount(3).quorumSize(2)
+ .replicationGroup("master1", 1)
+ .replicationGroup("master2", 1)
+ .replicationGroup("master3", 1)
+ .build();
+ cluster.start();
}
@Test
@@ -76,7 +65,7 @@ public class SomeIntegrationTestThatRequiresRedis {
@After
public void tearDown() throws Exception {
- redisServer.stop();
+ cluster.stop();
}
}
```
@@ -86,7 +75,8 @@ Redis version
==============
When not provided with the desired redis executable, RedisServer runs os-dependent executable enclosed in jar. Currently is uses:
-- Redis 2.6.14 in case of Linux/Unix
+- Redis 2.8.19 in case of Linux/Unix
+- Redis 2.8.19 in case of OSX
- unofficial Win32/64 port from https://github.com/MSOpenTech/redis (branch 2.6) in case of Windows
However, you should provide RedisServer with redis executable if you need specific version.
diff --git a/src/main/java/redis/embedded/RedisClusterBuilder.java b/src/main/java/redis/embedded/RedisClusterBuilder.java
index 7bda5ceb..acc8b5d5 100644
--- a/src/main/java/redis/embedded/RedisClusterBuilder.java
+++ b/src/main/java/redis/embedded/RedisClusterBuilder.java
@@ -32,6 +32,11 @@ public RedisClusterBuilder sentinelCount(int sentinelCount) {
return this;
}
+ public RedisClusterBuilder sentinelStartingPort(int startingPort) {
+ this.currentSentinelPort = startingPort;
+ return this;
+ }
+
public RedisClusterBuilder quorumSize(int quorumSize) {
this.quorumSize = quorumSize;
return this;
diff --git a/src/main/java/redis/embedded/RedisSentinelBuilder.java b/src/main/java/redis/embedded/RedisSentinelBuilder.java
index e1fe821c..41c73770 100644
--- a/src/main/java/redis/embedded/RedisSentinelBuilder.java
+++ b/src/main/java/redis/embedded/RedisSentinelBuilder.java
@@ -82,7 +82,7 @@ public RedisSentinelBuilder parallelSyncs(int parallelSyncs) {
public RedisSentinelBuilder configFile(String redisConf) {
if (redisConfigBuilder != null) {
- throw new RuntimeException("Redis configuration is already partially build using setting(String) method!");
+ throw new RedisBuildingException("Redis configuration is already partially build using setting(String) method!");
}
this.sentinelConf = redisConf;
return this;
@@ -90,7 +90,7 @@ public RedisSentinelBuilder configFile(String redisConf) {
public RedisSentinelBuilder setting(String configLine) {
if (sentinelConf != null) {
- throw new RuntimeException("Redis configuration is already set using redis conf file!");
+ throw new RedisBuildingException("Redis configuration is already set using redis conf file!");
}
if (redisConfigBuilder == null) {
@@ -123,6 +123,7 @@ private void tryResolveConfAndExec() {
public void reset() {
this.redisConfigBuilder = null;
+ this.sentinelConf = null;
}
public void addDefaultReplicationGroup() {
diff --git a/src/main/java/redis/embedded/RedisServerBuilder.java b/src/main/java/redis/embedded/RedisServerBuilder.java
index da8b0cf6..39cb1ce5 100644
--- a/src/main/java/redis/embedded/RedisServerBuilder.java
+++ b/src/main/java/redis/embedded/RedisServerBuilder.java
@@ -53,7 +53,7 @@ public RedisServerBuilder slaveOf(InetSocketAddress slaveOf) {
public RedisServerBuilder configFile(String redisConf) {
if (redisConfigBuilder != null) {
- throw new RuntimeException("Redis configuration is already partially build using setting(String) method!");
+ throw new RedisBuildingException("Redis configuration is already partially build using setting(String) method!");
}
this.redisConf = redisConf;
return this;
@@ -61,7 +61,7 @@ public RedisServerBuilder configFile(String redisConf) {
public RedisServerBuilder setting(String configLine) {
if (redisConf != null) {
- throw new RuntimeException("Redis configuration is already set using redis conf file!");
+ throw new RedisBuildingException("Redis configuration is already set using redis conf file!");
}
if (redisConfigBuilder == null) {
@@ -82,6 +82,7 @@ public RedisServer build() {
public void reset() {
this.redisConfigBuilder = null;
this.slaveOf = null;
+ this.redisConf = null;
}
private void tryResolveConfAndExec() {
diff --git a/src/main/java/redis/embedded/exceptions/RedisBuildingException.java b/src/main/java/redis/embedded/exceptions/RedisBuildingException.java
index 03493d63..29fa76c6 100644
--- a/src/main/java/redis/embedded/exceptions/RedisBuildingException.java
+++ b/src/main/java/redis/embedded/exceptions/RedisBuildingException.java
@@ -7,4 +7,8 @@ public class RedisBuildingException extends RuntimeException {
public RedisBuildingException(String message, Throwable cause) {
super(message, cause);
}
+
+ public RedisBuildingException(String message) {
+ super(message);
+ }
}
diff --git a/src/test/java/redis/embedded/RedisClusterTest.java b/src/test/java/redis/embedded/RedisClusterTest.java
index 994ecc3f..0217e420 100644
--- a/src/test/java/redis/embedded/RedisClusterTest.java
+++ b/src/test/java/redis/embedded/RedisClusterTest.java
@@ -1,11 +1,15 @@
package redis.embedded;
+import com.google.common.collect.Sets;
import org.junit.Before;
import org.junit.Test;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisSentinelPool;
import java.util.Arrays;
import java.util.List;
+import static org.junit.Assert.assertEquals;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -75,4 +79,130 @@ public void isActiveShouldCheckEntireClusterIfAllActive() throws Exception {
sentinels.stream().forEach(s -> verify(s).isActive());
servers.stream().forEach(m -> verify(m).isActive());
}
+
+ @Test
+ public void testSimpleOperationsAfterRunWithSingleMasterNoSlavesCluster() throws Exception {
+ //given
+ final RedisCluster cluster = RedisCluster.builder().sentinelCount(1).replicationGroup("ourmaster", 0).build();
+ cluster.start();
+
+ //when
+ JedisSentinelPool pool = null;
+ Jedis jedis = null;
+ try {
+ pool = new JedisSentinelPool("ourmaster", Sets.newHashSet("localhost:26379"));
+ jedis = testPool(pool);
+ } finally {
+ if (jedis != null)
+ pool.returnResource(jedis);
+ cluster.stop();
+ }
+ }
+
+ @Test
+ public void testSimpleOperationsAfterRunWithSingleMasterAndOneSlave() throws Exception {
+ //given
+ final RedisCluster cluster = RedisCluster.builder().sentinelCount(1).replicationGroup("ourmaster", 1).build();
+ cluster.start();
+
+ //when
+ JedisSentinelPool pool = null;
+ Jedis jedis = null;
+ try {
+ pool = new JedisSentinelPool("ourmaster", Sets.newHashSet("localhost:26379"));
+ jedis = testPool(pool);
+ } finally {
+ if (jedis != null)
+ pool.returnResource(jedis);
+ cluster.stop();
+ }
+ }
+
+ @Test
+ public void testSimpleOperationsAfterRunWithSingleMasterMultipleSlaves() throws Exception {
+ //given
+ final RedisCluster cluster = RedisCluster.builder().sentinelCount(1).replicationGroup("ourmaster", 2).build();
+ cluster.start();
+
+ //when
+ JedisSentinelPool pool = null;
+ Jedis jedis = null;
+ try {
+ pool = new JedisSentinelPool("ourmaster", Sets.newHashSet("localhost:26379"));
+ jedis = testPool(pool);
+ } finally {
+ if (jedis != null)
+ pool.returnResource(jedis);
+ cluster.stop();
+ }
+ }
+
+ @Test
+ public void testSimpleOperationsAfterRunWithTwoSentinelsSingleMasterMultipleSlaves() throws Exception {
+ //given
+ final RedisCluster cluster = RedisCluster.builder().sentinelCount(2).replicationGroup("ourmaster", 2).build();
+ cluster.start();
+
+ //when
+ JedisSentinelPool pool = null;
+ Jedis jedis = null;
+ try {
+ pool = new JedisSentinelPool("ourmaster", Sets.newHashSet("localhost:26379", "localhost:26380"));
+ jedis = testPool(pool);
+ } finally {
+ if (jedis != null)
+ pool.returnResource(jedis);
+ cluster.stop();
+ }
+ }
+
+ @Test
+ public void testSimpleOperationsAfterRunWithThreeSentinelsThreeMastersOneSlavePerMasterCluster() throws Exception {
+ //given
+ final String master1 = "master1";
+ final String master2 = "master2";
+ final String master3 = "master3";
+ final RedisCluster cluster = RedisCluster.builder().sentinelCount(3).quorumSize(2)
+ .replicationGroup(master1, 1)
+ .replicationGroup(master2, 1)
+ .replicationGroup(master3, 1)
+ .build();
+ cluster.start();
+
+ //when
+ JedisSentinelPool pool1 = null;
+ JedisSentinelPool pool2 = null;
+ JedisSentinelPool pool3 = null;
+ Jedis jedis1 = null;
+ Jedis jedis2 = null;
+ Jedis jedis3 = null;
+ try {
+ pool1 = new JedisSentinelPool(master1, Sets.newHashSet("localhost:26379", "localhost:26380", "localhost:26381"));
+ pool2 = new JedisSentinelPool(master2, Sets.newHashSet("localhost:26379", "localhost:26380", "localhost:26381"));
+ pool3 = new JedisSentinelPool(master3, Sets.newHashSet("localhost:26379", "localhost:26380", "localhost:26381"));
+ jedis1 = testPool(pool1);
+ jedis2 = testPool(pool2);
+ jedis3 = testPool(pool3);
+ } finally {
+ if (jedis1 != null)
+ pool1.returnResource(jedis1);
+ if (jedis2 != null)
+ pool2.returnResource(jedis2);
+ if (jedis3 != null)
+ pool3.returnResource(jedis3);
+ cluster.stop();
+ }
+ }
+
+ private Jedis testPool(JedisSentinelPool pool) {
+ Jedis jedis;
+ jedis = pool.getResource();
+ jedis.mset("abc", "1", "def", "2");
+
+ //then
+ assertEquals("1", jedis.mget("abc").get(0));
+ assertEquals("2", jedis.mget("def").get(0));
+ assertEquals(null, jedis.mget("xyz").get(0));
+ return jedis;
+ }
}
\ No newline at end of file
diff --git a/src/test/java/redis/embedded/RedisSentinelTest.java b/src/test/java/redis/embedded/RedisSentinelTest.java
index 913299c8..5dbc2204 100644
--- a/src/test/java/redis/embedded/RedisSentinelTest.java
+++ b/src/test/java/redis/embedded/RedisSentinelTest.java
@@ -1,9 +1,14 @@
package redis.embedded;
+import com.google.common.collect.Sets;
import org.junit.Test;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisSentinelPool;
import java.util.concurrent.TimeUnit;
+import static org.junit.Assert.assertEquals;
+
public class RedisSentinelTest {
private RedisSentinel sentinel;
private RedisServer server;
@@ -19,4 +24,46 @@ public void testSimpleRun() throws Exception {
sentinel.stop();
}
+ @Test
+ public void shouldAllowSubsequentRuns() throws Exception {
+ sentinel = RedisSentinel.builder().build();
+ sentinel.start();
+ sentinel.stop();
+
+ sentinel.start();
+ sentinel.stop();
+
+ sentinel.start();
+ sentinel.stop();
+ }
+
+ @Test
+ public void testSimpleOperationsAfterRun() throws Exception {
+ //given
+ server = new RedisServer();
+ sentinel = RedisSentinel.builder().build();
+ server.start();
+ sentinel.start();
+ TimeUnit.SECONDS.sleep(1);
+
+ //when
+ JedisSentinelPool pool = null;
+ Jedis jedis = null;
+ try {
+ pool = new JedisSentinelPool("mymaster", Sets.newHashSet("localhost:26379"));
+ jedis = pool.getResource();
+ jedis.mset("abc", "1", "def", "2");
+
+ //then
+ assertEquals("1", jedis.mget("abc").get(0));
+ assertEquals("2", jedis.mget("def").get(0));
+ assertEquals(null, jedis.mget("xyz").get(0));
+ } finally {
+ if (jedis != null)
+ pool.returnResource(jedis);
+ sentinel.stop();
+ server.stop();
+ }
+ }
+
}
\ No newline at end of file