refactoring

pull/5676/head
Nikita Koksharov 1 year ago
parent e5a70dfc90
commit 91f6db8be4

@ -190,7 +190,7 @@ public class PublishSubscribeService {
return new PubSubPatternStatusListener((PubSubPatternStatusListener) l) { return new PubSubPatternStatusListener((PubSubPatternStatusListener) l) {
@Override @Override
public void onStatus(PubSubType type, CharSequence channel) { public void onStatus(PubSubType type, CharSequence channel) {
if (statusCounter.decrementAndGet() == 0) { if (statusCounter.get() == 0 || statusCounter.decrementAndGet() == 0) {
super.onStatus(type, channel); super.onStatus(type, channel);
} }
} }
@ -224,8 +224,8 @@ public class PublishSubscribeService {
public boolean isMultiEntity(ChannelName channelName) { public boolean isMultiEntity(ChannelName channelName) {
return connectionManager.isClusterMode() return connectionManager.isClusterMode()
&& (channelName.toString().startsWith("__keyspace@") && (channelName.toString().startsWith("__keyspace")
|| channelName.toString().startsWith("__keyevent@")); || channelName.toString().startsWith("__keyevent"));
} }
public CompletableFuture<PubSubConnectionEntry> subscribe(MasterSlaveEntry entry, ClientConnectionsEntry clientEntry, public CompletableFuture<PubSubConnectionEntry> subscribe(MasterSlaveEntry entry, ClientConnectionsEntry clientEntry,
@ -356,7 +356,7 @@ public class PublishSubscribeService {
return new PubSubStatusListener(((PubSubStatusListener) l).getListener(), ((PubSubStatusListener) l).getName()) { return new PubSubStatusListener(((PubSubStatusListener) l).getListener(), ((PubSubStatusListener) l).getName()) {
@Override @Override
public void onStatus(PubSubType type, CharSequence channel) { public void onStatus(PubSubType type, CharSequence channel) {
if (statusCounter.decrementAndGet() == 0) { if (statusCounter.get() == 0 || statusCounter.decrementAndGet() == 0) {
super.onStatus(type, channel); super.onStatus(type, channel);
} }
} }

@ -1,5 +1,10 @@
package org.redisson; package org.redisson;
import com.github.dockerjava.api.command.InspectContainerResponse;
import com.github.dockerjava.api.model.ContainerNetwork;
import com.github.dockerjava.api.model.ExposedPort;
import com.github.dockerjava.api.model.PortBinding;
import com.github.dockerjava.api.model.Ports;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.redisson.api.NatMapper; import org.redisson.api.NatMapper;
@ -7,20 +12,24 @@ import org.redisson.api.RedissonClient;
import org.redisson.config.Config; import org.redisson.config.Config;
import org.redisson.config.Protocol; import org.redisson.config.Protocol;
import org.redisson.misc.RedisURI; import org.redisson.misc.RedisURI;
import org.testcontainers.containers.ContainerState;
import org.testcontainers.containers.DockerComposeContainer;
import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.startupcheck.MinimumDurationRunningStartupCheckStrategy; import org.testcontainers.containers.startupcheck.MinimumDurationRunningStartupCheckStrategy;
import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
import java.io.File;
import java.time.Duration; import java.time.Duration;
import java.util.ArrayList; import java.util.*;
import java.util.Arrays; import java.util.function.BiConsumer;
import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
public class RedisDockerTest { public class RedisDockerTest {
protected static final String NOTIFY_KEYSPACE_EVENTS = "--notify-keyspace-events"; protected static final String NOTIFY_KEYSPACE_EVENTS = "--notify-keyspace-events";
private static final GenericContainer<?> REDIS = createRedis(); protected static final GenericContainer<?> REDIS = createRedis();
protected static final Protocol protocol = Protocol.RESP2; protected static final Protocol protocol = Protocol.RESP2;
@ -35,9 +44,16 @@ public class RedisDockerTest {
} }
protected static GenericContainer<?> createRedis(String version) { protected static GenericContainer<?> createRedis(String version) {
return createRedis(version, "--save", "");
}
protected static GenericContainer<?> createRedis(String version, String... params) {
return new GenericContainer<>("redis:" + version) return new GenericContainer<>("redis:" + version)
.withCreateContainerCmdModifier(cmd -> { .withCreateContainerCmdModifier(cmd -> {
cmd.withCmd("redis-server", "--save", "''"); List<String> args = new ArrayList<>();
args.add("redis-server");
args.addAll(Arrays.asList(params));
cmd.withCmd(args);
}) })
.withExposedPorts(6379); .withExposedPorts(6379);
} }
@ -65,10 +81,14 @@ public class RedisDockerTest {
} }
protected static Config createConfig() { protected static Config createConfig() {
return createConfig(REDIS);
}
protected static Config createConfig(GenericContainer<?> container) {
Config config = new Config(); Config config = new Config();
config.setProtocol(protocol); config.setProtocol(protocol);
config.useSingleServer() config.useSingleServer()
.setAddress("redis://127.0.0.1:" + REDIS.getFirstMappedPort()); .setAddress("redis://127.0.0.1:" + container.getFirstMappedPort());
return config; return config;
} }
@ -77,6 +97,29 @@ public class RedisDockerTest {
return Redisson.create(config); return Redisson.create(config);
} }
protected void withRedisParams(Consumer<Config> redissonCallback, String... params) {
GenericContainer<?> redis =
new GenericContainer<>("redis:7.2")
.withCreateContainerCmdModifier(cmd -> {
List<String> args = new ArrayList<>();
args.add("redis-server");
args.addAll(Arrays.asList(params));
cmd.withCmd(args);
})
.withExposedPorts(6379);
redis.start();
Config config = new Config();
config.setProtocol(protocol);
config.useSingleServer().setAddress("redis://127.0.0.1:" + redis.getFirstMappedPort());
try {
redissonCallback.accept(config);
} finally {
redis.stop();
}
}
protected void testWithParams(Consumer<RedissonClient> redissonCallback, String... params) { protected void testWithParams(Consumer<RedissonClient> redissonCallback, String... params) {
GenericContainer<?> redis = GenericContainer<?> redis =
new GenericContainer<>("redis:7.2") new GenericContainer<>("redis:7.2")
@ -100,7 +143,6 @@ public class RedisDockerTest {
redisson.shutdown(); redisson.shutdown();
redis.stop(); redis.stop();
} }
} }
protected void testInCluster(Consumer<RedissonClient> redissonCallback) { protected void testInCluster(Consumer<RedissonClient> redissonCallback) {
@ -137,4 +179,200 @@ public class RedisDockerTest {
} }
} }
protected void withSentinel(BiConsumer<List<GenericContainer<?>>, Config> callback, int slaves) throws InterruptedException {
Network network = Network.newNetwork();
List<GenericContainer<? extends GenericContainer<?>>> nodes = new ArrayList<>();
GenericContainer<?> master =
new GenericContainer<>("bitnami/redis:7.2.4")
.withNetwork(network)
.withEnv("REDIS_REPLICATION_MODE", "master")
.withEnv("ALLOW_EMPTY_PASSWORD", "yes")
.withNetworkAliases("redis")
.withExposedPorts(6379);
master.start();
assert master.getNetwork() == network;
int masterPort = master.getFirstMappedPort();
master.withCreateContainerCmdModifier(cmd -> {
cmd.getHostConfig().withPortBindings(
new PortBinding(Ports.Binding.bindPort(Integer.valueOf(masterPort)),
cmd.getExposedPorts()[0]));
});
nodes.add(master);
for (int i = 0; i < slaves; i++) {
GenericContainer<?> slave =
new GenericContainer<>("bitnami/redis:7.2.4")
.withNetwork(network)
.withEnv("REDIS_REPLICATION_MODE", "slave")
.withEnv("REDIS_MASTER_HOST", "redis")
.withEnv("ALLOW_EMPTY_PASSWORD", "yes")
.withNetworkAliases("slave" + i)
.withExposedPorts(6379);
slave.start();
int slavePort = slave.getFirstMappedPort();
slave.withCreateContainerCmdModifier(cmd -> {
cmd.getHostConfig().withPortBindings(
new PortBinding(Ports.Binding.bindPort(Integer.valueOf(slavePort)),
cmd.getExposedPorts()[0]));
});
nodes.add(slave);
}
GenericContainer<?> sentinel1 =
new GenericContainer<>("bitnami/redis-sentinel:7.2.4")
.withNetwork(network)
.withEnv("REDIS_SENTINEL_DOWN_AFTER_MILLISECONDS", "5000")
.withEnv("REDIS_SENTINEL_FAILOVER_TIMEOUT", "10000")
.withNetworkAliases("sentinel1")
.withExposedPorts(26379);
sentinel1.start();
int sentinel1Port = sentinel1.getFirstMappedPort();
sentinel1.withCreateContainerCmdModifier(cmd -> {
cmd.getHostConfig().withPortBindings(
new PortBinding(Ports.Binding.bindPort(Integer.valueOf(sentinel1Port)),
cmd.getExposedPorts()[0]));
});
nodes.add(sentinel1);
GenericContainer<?> sentinel2 =
new GenericContainer<>("bitnami/redis-sentinel:7.2.4")
.withNetwork(network)
.withEnv("REDIS_SENTINEL_DOWN_AFTER_MILLISECONDS", "5000")
.withEnv("REDIS_SENTINEL_FAILOVER_TIMEOUT", "10000")
.withNetworkAliases("sentinel2")
.withExposedPorts(26379);
sentinel2.start();
int sentinel2Port = sentinel2.getFirstMappedPort();
sentinel2.withCreateContainerCmdModifier(cmd -> {
cmd.getHostConfig().withPortBindings(
new PortBinding(Ports.Binding.bindPort(Integer.valueOf(sentinel2Port)),
cmd.getExposedPorts()[0]));
});
nodes.add(sentinel2);
GenericContainer<?> sentinel3 =
new GenericContainer<>("bitnami/redis-sentinel:7.2.4")
.withNetwork(network)
.withEnv("REDIS_SENTINEL_DOWN_AFTER_MILLISECONDS", "5000")
.withEnv("REDIS_SENTINEL_FAILOVER_TIMEOUT", "10000")
.withNetworkAliases("sentinel3")
.withExposedPorts(26379);
sentinel3.start();
int sentinel3Port = sentinel3.getFirstMappedPort();
sentinel3.withCreateContainerCmdModifier(cmd -> {
cmd.getHostConfig().withPortBindings(
new PortBinding(Ports.Binding.bindPort(Integer.valueOf(sentinel3Port)),
cmd.getExposedPorts()[0]));
});
nodes.add(sentinel3);
Thread.sleep(5000);
Config config = new Config();
config.setProtocol(protocol);
config.useSentinelServers()
.setNatMapper(new NatMapper() {
@Override
public RedisURI map(RedisURI uri) {
for (GenericContainer<? extends GenericContainer<?>> node : nodes) {
if (node.getContainerInfo() == null) {
continue;
}
Ports.Binding[] mappedPort = node.getContainerInfo().getNetworkSettings()
.getPorts().getBindings().get(new ExposedPort(uri.getPort()));
Map<String, ContainerNetwork> ss = node.getContainerInfo().getNetworkSettings().getNetworks();
ContainerNetwork s = ss.values().iterator().next();
if (uri.getPort() == 6379 && node.getNetworkAliases().contains("slave0")) {
return new RedisURI(uri.getScheme(), "127.0.0.1", Integer.valueOf(mappedPort[0].getHostPortSpec()));
}
if (mappedPort != null
&& s.getIpAddress().equals(uri.getHost())) {
return new RedisURI(uri.getScheme(), "127.0.0.1", Integer.valueOf(mappedPort[0].getHostPortSpec()));
}
}
return uri;
}
})
.addSentinelAddress("redis://127.0.0.1:" + sentinel1.getFirstMappedPort())
.setMasterName("mymaster");
callback.accept(nodes, config);
nodes.forEach(n -> n.stop());
network.close();
}
protected void withNewCluster(Consumer<RedissonClient> callback) {
List<InspectContainerResponse> nodes = new ArrayList<>();
LogMessageWaitStrategy wait2 = new LogMessageWaitStrategy().withRegEx(".*REPLICA\ssync\\:\sFinished\swith\ssuccess.*");
DockerComposeContainer environment =
new DockerComposeContainer(new File("src/test/resources/docker-compose.yml"))
.withExposedService("redis-node-0", 6379)
.withExposedService("redis-node-1", 6379)
.withExposedService("redis-node-2", 6379)
.withExposedService("redis-node-3", 6379)
.withExposedService("redis-node-4", 6379)
.withExposedService("redis-node-5", 6379, wait2);
environment.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
for (int i = 0; i < 6; i++) {
Optional<ContainerState> cc = environment.getContainerByServiceName("redis-node-" + i);
nodes.add(cc.get().getContainerInfo());
}
Optional<ContainerState> cc2 = environment.getContainerByServiceName("redis-node-0");
Config config = new Config();
config.useClusterServers()
.setNatMapper(new NatMapper() {
@Override
public RedisURI map(RedisURI uri) {
for (InspectContainerResponse node : nodes) {
Ports.Binding[] mappedPort = node.getNetworkSettings()
.getPorts().getBindings().get(new ExposedPort(uri.getPort()));
Map<String, ContainerNetwork> ss = node.getNetworkSettings().getNetworks();
ContainerNetwork s = ss.values().iterator().next();
if (mappedPort != null
&& s.getIpAddress().equals(uri.getHost())) {
return new RedisURI(uri.getScheme(), "127.0.0.1", Integer.valueOf(mappedPort[0].getHostPortSpec()));
}
}
return uri;
}
})
.addNodeAddress("redis://127.0.0.1:" + cc2.get().getFirstMappedPort());
RedissonClient redisson = Redisson.create(config);
callback.accept(redisson);
redisson.shutdown();
environment.stop();
}
protected void restart(GenericContainer<?> redis) {
redis.setPortBindings(Arrays.asList(redis.getFirstMappedPort() + ":6379"));
redis.stop();
redis.start();
}
} }

@ -20,9 +20,6 @@ import org.redisson.codec.JsonJacksonCodec;
import org.redisson.command.BatchPromise; import org.redisson.command.BatchPromise;
import org.redisson.config.Config; import org.redisson.config.Config;
import org.redisson.config.SubscriptionMode; import org.redisson.config.SubscriptionMode;
import org.redisson.misc.RedisURI;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.startupcheck.MinimumDurationRunningStartupCheckStrategy;
import java.time.Duration; import java.time.Duration;
import java.util.*; import java.util.*;
@ -45,100 +42,89 @@ public class RedissonBatchTest extends RedisDockerTest {
@ParameterizedTest @ParameterizedTest
@MethodSource("data") @MethodSource("data")
public void testSlotMigrationInCluster(BatchOptions batchOptions) throws Exception { public void testSlotMigrationInCluster(BatchOptions batchOptions) {
RedisRunner master1 = new RedisRunner().randomPort().randomDir().nosave(); withNewCluster(redissonClient -> {
RedisRunner master2 = new RedisRunner().randomPort().randomDir().nosave(); Config config = redissonClient.getConfig();
RedisRunner master3 = new RedisRunner().randomPort().randomDir().nosave(); config.useClusterServers()
RedisRunner slot1 = new RedisRunner().randomPort().randomDir().nosave(); .setScanInterval(1000)
RedisRunner slot2 = new RedisRunner().randomPort().randomDir().nosave(); .setSubscriptionMode(SubscriptionMode.MASTER);
RedisRunner slot3 = new RedisRunner().randomPort().randomDir().nosave(); RedissonClient redisson = Redisson.create(config);
ClusterRunner clusterRunner = new ClusterRunner()
.addNode(master1, slot1)
.addNode(master2, slot2)
.addNode(master3, slot3);
ClusterRunner.ClusterProcesses process = clusterRunner.run();
Config config = new Config();
config.useClusterServers()
.setScanInterval(1000)
.setSubscriptionMode(SubscriptionMode.MASTER)
.addNodeAddress(process.getNodes().stream().findAny().get().getRedisServerAddressAndPort());
RedissonClient redisson = Redisson.create(config);
RedisClientConfig cfg = new RedisClientConfig();
cfg.setAddress(process.getNodes().iterator().next().getRedisServerAddressAndPort());
RedisClient c = RedisClient.create(cfg);
RedisConnection cc = c.connect();
List<ClusterNodeInfo> mastersList = cc.sync(RedisCommands.CLUSTER_NODES);
mastersList = mastersList.stream().filter(i -> i.containsFlag(ClusterNodeInfo.Flag.MASTER)).collect(Collectors.toList());
c.shutdown();
ClusterNodeInfo destination = mastersList.stream().filter(i -> i.getSlotRanges().iterator().next().getStartSlot() != 10922).findAny().get();
ClusterNodeInfo source = mastersList.stream().filter(i -> i.getSlotRanges().iterator().next().getStartSlot() == 10922).findAny().get();
RedisClientConfig sourceCfg = new RedisClientConfig(); RedisClientConfig cfg = new RedisClientConfig();
sourceCfg.setAddress(source.getAddress()); cfg.setAddress(config.useClusterServers().getNodeAddresses().get(0));
RedisClient sourceClient = RedisClient.create(sourceCfg); RedisClient c = RedisClient.create(cfg);
RedisConnection sourceConnection = sourceClient.connect(); RedisConnection cc = c.connect();
List<ClusterNodeInfo> mastersList = cc.sync(RedisCommands.CLUSTER_NODES);
mastersList = mastersList.stream().filter(i -> i.containsFlag(ClusterNodeInfo.Flag.MASTER)).collect(Collectors.toList());
c.shutdown();
RedisClientConfig destinationCfg = new RedisClientConfig(); ClusterNodeInfo destination = mastersList.stream().filter(i -> i.getSlotRanges().stream().noneMatch(s -> s.hasSlot(10922))).findAny().get();
destinationCfg.setAddress(destination.getAddress()); ClusterNodeInfo source = mastersList.stream().filter(i -> i.getSlotRanges().stream().anyMatch(s -> s.hasSlot(10922))).findAny().get();
RedisClient destinationClient = RedisClient.create(destinationCfg);
RedisConnection destinationConnection = destinationClient.connect();
String lockName = "test{kaO}"; RedisClientConfig sourceCfg = new RedisClientConfig();
sourceCfg.setAddress(config.useClusterServers().getNatMapper().map(source.getAddress()));
RedisClient sourceClient = RedisClient.create(sourceCfg);
RedisConnection sourceConnection = sourceClient.connect();
RBatch batch = redisson.createBatch(batchOptions); RedisClientConfig destinationCfg = new RedisClientConfig();
List<RFuture<Boolean>> futures = new ArrayList<>(); destinationCfg.setAddress(config.useClusterServers().getNatMapper().map(destination.getAddress()));
for (int i = 0; i < 5; i++) { RedisClient destinationClient = RedisClient.create(destinationCfg);
RFuture<Boolean> f = batch.getMap(lockName).fastPutAsync("" + i, i); RedisConnection destinationConnection = destinationClient.connect();
futures.add(f);
}
destinationConnection.sync(RedisCommands.CLUSTER_SETSLOT, source.getSlotRanges().iterator().next().getStartSlot(), "IMPORTING", source.getNodeId()); String lockName = "test{kaO}";
sourceConnection.sync(RedisCommands.CLUSTER_SETSLOT, source.getSlotRanges().iterator().next().getStartSlot(), "MIGRATING", destination.getNodeId());
List<String> keys = sourceConnection.sync(RedisCommands.CLUSTER_GETKEYSINSLOT, source.getSlotRanges().iterator().next().getStartSlot(), 100);
List<Object> params = new ArrayList<Object>();
params.add(destination.getAddress().getHost());
params.add(destination.getAddress().getPort());
params.add("");
params.add(0);
params.add(2000);
params.add("KEYS");
params.addAll(keys);
sourceConnection.async(RedisCommands.MIGRATE, params.toArray());
for (ClusterNodeInfo node : mastersList) {
RedisClientConfig cc1 = new RedisClientConfig();
cc1.setAddress(node.getAddress());
RedisClient ccc = RedisClient.create(cc1);
RedisConnection connection = ccc.connect();
connection.sync(RedisCommands.CLUSTER_SETSLOT, source.getSlotRanges().iterator().next().getStartSlot(), "NODE", destination.getNodeId());
ccc.shutdownAsync();
}
Thread.sleep(2000); RBatch batch = redisson.createBatch(batchOptions);
List<RFuture<Boolean>> futures = new ArrayList<>();
for (int i = 0; i < 5; i++) {
RFuture<Boolean> f = batch.getMap(lockName).fastPutAsync("" + i, i);
futures.add(f);
}
batch.execute(); destinationConnection.sync(RedisCommands.CLUSTER_SETSLOT, source.getSlotRanges().iterator().next().getStartSlot(), "IMPORTING", source.getNodeId());
sourceConnection.sync(RedisCommands.CLUSTER_SETSLOT, source.getSlotRanges().iterator().next().getStartSlot(), "MIGRATING", destination.getNodeId());
List<String> keys = sourceConnection.sync(RedisCommands.CLUSTER_GETKEYSINSLOT, source.getSlotRanges().iterator().next().getStartSlot(), 100);
List<Object> params = new ArrayList<Object>();
params.add(destination.getAddress().getHost());
params.add(destination.getAddress().getPort());
params.add("");
params.add(0);
params.add(2000);
params.add("KEYS");
params.addAll(keys);
sourceConnection.async(RedisCommands.MIGRATE, params.toArray());
for (ClusterNodeInfo node : mastersList) {
RedisClientConfig cc1 = new RedisClientConfig();
cc1.setAddress(config.useClusterServers().getNatMapper().map(node.getAddress()));
RedisClient ccc = RedisClient.create(cc1);
RedisConnection connection = ccc.connect();
connection.sync(RedisCommands.CLUSTER_SETSLOT, source.getSlotRanges().iterator().next().getStartSlot(), "NODE", destination.getNodeId());
ccc.shutdownAsync();
}
futures.forEach(f -> {
try { try {
f.toCompletableFuture().get(1, TimeUnit.MILLISECONDS); Thread.sleep(2000);
} catch (TimeoutException e) {
org.junit.jupiter.api.Assertions.fail(e);
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); throw new RuntimeException(e);
} }
});
sourceClient.shutdown(); batch.execute();
destinationClient.shutdown();
redisson.shutdown(); futures.forEach(f -> {
process.shutdown(); try {
f.toCompletableFuture().get(1, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
org.junit.jupiter.api.Assertions.fail(e);
} catch (Exception e) {
// skip
}
});
sourceClient.shutdown();
destinationClient.shutdown();
redisson.shutdown();
});
} }
@ParameterizedTest @ParameterizedTest

@ -2,17 +2,21 @@ package org.redisson;
import org.awaitility.Awaitility; import org.awaitility.Awaitility;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.Timeout;
import org.redisson.ClusterRunner.ClusterProcesses;
import org.redisson.RedisRunner.RedisProcess;
import org.redisson.api.Entry; import org.redisson.api.Entry;
import org.redisson.api.RBlockingQueue; import org.redisson.api.RBlockingQueue;
import org.redisson.api.RFuture; import org.redisson.api.RFuture;
import org.redisson.api.RedissonClient; import org.redisson.api.RedissonClient;
import org.redisson.api.redisnode.RedisCluster;
import org.redisson.api.redisnode.RedisClusterMaster;
import org.redisson.api.redisnode.RedisNodes;
import org.redisson.client.RedisClient;
import org.redisson.client.RedisClientConfig;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.config.Config; import org.redisson.config.Config;
import org.redisson.connection.balancer.RandomLoadBalancer; import org.redisson.connection.balancer.RandomLoadBalancer;
import org.testcontainers.containers.GenericContainer;
import java.io.IOException; import java.io.IOException;
import java.time.Duration; import java.time.Duration;
@ -42,26 +46,19 @@ public class RedissonBlockingQueueTest extends RedissonQueueTest {
} }
@Test @Test
public void testPollWithBrokenConnection() throws IOException, InterruptedException, ExecutionException { public void testPollWithBrokenConnection() throws InterruptedException, ExecutionException {
RedisProcess runner = new RedisRunner() GenericContainer<?> redis = createRedis();
.nosave() redis.start();
.randomDir()
.randomPort() Config config = createConfig(redis);
.run();
Config config = new Config();
config.useSingleServer().setAddress(runner.getRedisServerAddressAndPort());
RedissonClient redisson = Redisson.create(config); RedissonClient redisson = Redisson.create(config);
final RBlockingQueue<Integer> queue1 = getQueue(redisson); RBlockingQueue<Integer> queue1 = getQueue(redisson);
RFuture<Integer> f = queue1.pollAsync(5, TimeUnit.SECONDS); RFuture<Integer> f = queue1.pollAsync(5, TimeUnit.SECONDS);
try { Assertions.assertThrows(TimeoutException.class, () -> {
f.toCompletableFuture().get(1, TimeUnit.SECONDS); f.toCompletableFuture().get(1, TimeUnit.SECONDS);
Assertions.fail(); });
} catch (TimeoutException e) { redis.stop();
// skip
}
runner.stop();
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
assertThat(f.get()).isNull(); assertThat(f.get()).isNull();
@ -79,17 +76,12 @@ public class RedissonBlockingQueueTest extends RedissonQueueTest {
} }
@Test @Test
public void testPollReattach() throws InterruptedException, IOException { public void testPollReattach() throws InterruptedException {
RedisProcess runner = new RedisRunner() GenericContainer<?> redis = createRedis("latest","--requirepass", "1234");
.nosave() redis.start();
.randomDir()
.randomPort() Config config = createConfig(redis);
.requirepass("1234") config.useSingleServer().setPassword("1234");
.run();
Config config = new Config();
config.useSingleServer().setAddress(runner.getRedisServerAddressAndPort())
.setPassword("1234");
RedissonClient redisson = Redisson.create(config); RedissonClient redisson = Redisson.create(config);
final AtomicBoolean executed = new AtomicBoolean(); final AtomicBoolean executed = new AtomicBoolean();
@ -112,15 +104,9 @@ public class RedissonBlockingQueueTest extends RedissonQueueTest {
t.start(); t.start();
t.join(1000); t.join(1000);
runner.stop();
runner = new RedisRunner() restart(redis);
.port(runner.getRedisServerPort())
.nosave()
.randomDir()
.requirepass("1234")
.run();
Thread.sleep(1000); Thread.sleep(1000);
RBlockingQueue<Integer> queue1 = getQueue(redisson); RBlockingQueue<Integer> queue1 = getQueue(redisson);
@ -131,19 +117,15 @@ public class RedissonBlockingQueueTest extends RedissonQueueTest {
await().atMost(7, TimeUnit.SECONDS).untilTrue(executed); await().atMost(7, TimeUnit.SECONDS).untilTrue(executed);
redisson.shutdown(); redisson.shutdown();
runner.stop(); redis.stop();
} }
@Test @Test
public void testPollAsyncReattach() throws InterruptedException, IOException, ExecutionException, TimeoutException { public void testPollAsyncReattach() throws InterruptedException, IOException, ExecutionException, TimeoutException {
RedisProcess runner = new RedisRunner() GenericContainer<?> redis = createRedis();
.nosave() redis.start();
.randomDir()
.randomPort() Config config = createConfig(redis);
.run();
Config config = new Config();
config.useSingleServer().setAddress(runner.getRedisServerAddressAndPort());
RedissonClient redisson = Redisson.create(config); RedissonClient redisson = Redisson.create(config);
RBlockingQueue<Integer> queue1 = getQueue(redisson); RBlockingQueue<Integer> queue1 = getQueue(redisson);
@ -153,13 +135,9 @@ public class RedissonBlockingQueueTest extends RedissonQueueTest {
} catch (ExecutionException | TimeoutException e) { } catch (ExecutionException | TimeoutException e) {
// skip // skip
} }
runner.stop();
runner = new RedisRunner() restart(redis);
.port(runner.getRedisServerPort())
.nosave()
.randomDir()
.run();
queue1.put(123); queue1.put(123);
// check connection rotation // check connection rotation
@ -172,68 +150,63 @@ public class RedissonBlockingQueueTest extends RedissonQueueTest {
assertThat(result).isEqualTo(123); assertThat(result).isEqualTo(123);
redisson.shutdown(); redisson.shutdown();
runner.stop(); redis.stop();
} }
@Test @Test
public void testTakeReattachCluster() throws IOException, InterruptedException { public void testTakeReattachCluster() {
RedisRunner master1 = new RedisRunner().port(6890).randomDir().nosave(); withNewCluster(redisson -> {
RedisRunner master2 = new RedisRunner().port(6891).randomDir().nosave(); List<RFuture<Integer>> futures = new ArrayList<>();
RedisRunner master3 = new RedisRunner().port(6892).randomDir().nosave(); for (int i = 0; i < 10; i++) {
RedisRunner slave1 = new RedisRunner().port(6900).randomDir().nosave(); RBlockingQueue<Integer> queue = redisson.getBlockingQueue("queue" + i);
RedisRunner slave2 = new RedisRunner().port(6901).randomDir().nosave(); RFuture<Integer> f = queue.takeAsync();
RedisRunner slave3 = new RedisRunner().port(6902).randomDir().nosave(); futures.add(f);
}
ClusterRunner clusterRunner = new ClusterRunner()
.addNode(master1, slave1)
.addNode(master2, slave2)
.addNode(master3, slave3);
ClusterProcesses process = clusterRunner.run();
Thread.sleep(1000);
Config config = new Config();
config.useClusterServers()
.setLoadBalancer(new RandomLoadBalancer())
.addNodeAddress(process.getNodes().stream().findAny().get().getRedisServerAddressAndPort());
RedissonClient redisson = Redisson.create(config);
RedisProcess master = process.getNodes().stream().filter(x -> x.getRedisServerPort() == master1.getPort()).findFirst().get();
List<RFuture<Integer>> futures = new ArrayList<>();
for (int i = 0; i < 10; i++) {
RBlockingQueue<Integer> queue = redisson.getBlockingQueue("queue" + i);
RFuture<Integer> f = queue.takeAsync();
try { try {
f.toCompletableFuture().get(1, TimeUnit.SECONDS); TimeUnit.SECONDS.sleep(1);
} catch (ExecutionException | TimeoutException e) { } catch (InterruptedException e) {
// skip throw new RuntimeException(e);
} }
futures.add(f);
}
master.stop(); RedisCluster rnc = redisson.getRedisNodes(RedisNodes.CLUSTER);
Optional<RedisClusterMaster> ff = rnc.getMasters().stream().findFirst();
RedisClusterMaster master = ff.get();
RedisClientConfig cc = new RedisClientConfig();
cc.setAddress("redis://" + master.getAddr().getHostString() + ":" + master.getAddr().getPort());
RedisClient c = RedisClient.create(cc);
c.connect().async(RedisCommands.SHUTDOWN);
c.shutdown();
Thread.sleep(TimeUnit.SECONDS.toMillis(80)); try {
Thread.sleep(TimeUnit.SECONDS.toMillis(30));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
RBlockingQueue<Integer> queue = redisson.getBlockingQueue("queue" + i); RBlockingQueue<Integer> queue = redisson.getBlockingQueue("queue" + i);
queue.put(i*100); try {
} queue.put(i*100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
RFuture<Integer> f = futures.get(i); RFuture<Integer> f = futures.get(i);
try { try {
f.toCompletableFuture().get(20, TimeUnit.SECONDS); f.toCompletableFuture().get(20, TimeUnit.SECONDS);
} catch (ExecutionException | TimeoutException e) { } catch (Exception e) {
// skip // skip
}
Integer result = f.toCompletableFuture().getNow(null);
assertThat(result).isEqualTo(i*100);
} }
Integer result = f.toCompletableFuture().getNow(null);
assertThat(result).isEqualTo(i*100);
}
redisson.shutdown(); redisson.shutdown();
process.shutdown();
});
} }
@Test @Test
@ -319,14 +292,10 @@ public class RedissonBlockingQueueTest extends RedissonQueueTest {
@Test @Test
public void testTakeReattach() throws Exception { public void testTakeReattach() throws Exception {
RedisProcess runner = new RedisRunner() GenericContainer<?> redis = createRedis();
.nosave() redis.start();
.randomDir()
.randomPort() Config config = createConfig(redis);
.run();
Config config = new Config();
config.useSingleServer().setAddress(runner.getRedisServerAddressAndPort());
RedissonClient redisson = Redisson.create(config); RedissonClient redisson = Redisson.create(config);
RBlockingQueue<Integer> queue1 = getQueue(redisson); RBlockingQueue<Integer> queue1 = getQueue(redisson);
@ -336,13 +305,8 @@ public class RedissonBlockingQueueTest extends RedissonQueueTest {
} catch (ExecutionException | TimeoutException e) { } catch (ExecutionException | TimeoutException e) {
e.printStackTrace(); e.printStackTrace();
} }
runner.stop();
runner = new RedisRunner() restart(redis);
.port(runner.getRedisServerPort())
.nosave()
.randomDir()
.run();
queue1.put(123); queue1.put(123);
// check connection rotation // check connection rotation
@ -353,9 +317,9 @@ public class RedissonBlockingQueueTest extends RedissonQueueTest {
Integer result = f.get(1, TimeUnit.SECONDS); Integer result = f.get(1, TimeUnit.SECONDS);
assertThat(result).isEqualTo(123); assertThat(result).isEqualTo(123);
runner.stop();
redisson.shutdown(); redisson.shutdown();
redis.stop();
} }
@Test @Test
@ -444,48 +408,26 @@ public class RedissonBlockingQueueTest extends RedissonQueueTest {
} }
@Test @Test
public void testPollFromAnyInCluster() throws Exception { public void testPollFromAnyInCluster() {
RedisRunner master1 = new RedisRunner().port(6890).randomDir().nosave(); testInCluster(redissonClient -> {
RedisRunner master2 = new RedisRunner().port(6891).randomDir().nosave(); RBlockingQueue<Integer> queue1 = redisson.getBlockingQueue("queue:pollany");
RedisRunner master3 = new RedisRunner().port(6892).randomDir().nosave(); Executors.newSingleThreadScheduledExecutor().schedule(() -> {
RedisRunner slave1 = new RedisRunner().port(6900).randomDir().nosave(); RBlockingQueue<Integer> queue2 = redisson.getBlockingQueue("queue:pollany1");
RedisRunner slave2 = new RedisRunner().port(6901).randomDir().nosave(); RBlockingQueue<Integer> queue3 = redisson.getBlockingQueue("queue:pollany2");
RedisRunner slave3 = new RedisRunner().port(6902).randomDir().nosave(); try {
queue3.put(2);
ClusterRunner clusterRunner = new ClusterRunner() queue1.put(1);
.addNode(master1, slave1) queue2.put(3);
.addNode(master2, slave2) } catch (InterruptedException e) {
.addNode(master3, slave3); Assertions.fail();
ClusterProcesses process = clusterRunner.run(); }
}, 3, TimeUnit.SECONDS);
Thread.sleep(5000);
Config config = new Config();
config.useClusterServers()
.setLoadBalancer(new RandomLoadBalancer())
.addNodeAddress(process.getNodes().stream().findAny().get().getRedisServerAddressAndPort());
RedissonClient redisson = Redisson.create(config);
RBlockingQueue<Integer> queue1 = redisson.getBlockingQueue("queue:pollany");
Executors.newSingleThreadScheduledExecutor().schedule(() -> {
RBlockingQueue<Integer> queue2 = redisson.getBlockingQueue("queue:pollany1");
RBlockingQueue<Integer> queue3 = redisson.getBlockingQueue("queue:pollany2");
try {
queue3.put(2);
queue1.put(1);
queue2.put(3);
} catch (InterruptedException e) {
Assertions.fail();
}
}, 3, TimeUnit.SECONDS);
Awaitility.await().between(Duration.ofSeconds(2), Duration.ofSeconds(4)).untilAsserted(() -> { Awaitility.await().between(Duration.ofSeconds(2), Duration.ofSeconds(4)).untilAsserted(() -> {
int value = queue1.pollFromAny(4, TimeUnit.SECONDS, "queue:pollany1", "queue:pollany2"); int value = queue1.pollFromAny(4, TimeUnit.SECONDS, "queue:pollany1", "queue:pollany2");
assertThat(value).isEqualTo(1); assertThat(value).isEqualTo(1);
});
}); });
redisson.shutdown();
process.shutdown();
} }
@Test @Test
@ -557,22 +499,18 @@ public class RedissonBlockingQueueTest extends RedissonQueueTest {
@Test @Test
public void testPollLastFromAny() throws InterruptedException { public void testPollLastFromAny() throws InterruptedException {
Assumptions.assumeTrue(RedisRunner.getDefaultRedisServerInstance().getRedisVersion().compareTo("7.0.0") > 0);
RBlockingQueue<Integer> queue1 = redisson.getBlockingQueue("queue:pollany"); RBlockingQueue<Integer> queue1 = redisson.getBlockingQueue("queue:pollany");
RBlockingQueue<Integer> queue2 = redisson.getBlockingQueue("queue:pollany1"); RBlockingQueue<Integer> queue2 = redisson.getBlockingQueue("queue:pollany1");
RBlockingQueue<Integer> queue3 = redisson.getBlockingQueue("queue:pollany2"); RBlockingQueue<Integer> queue3 = redisson.getBlockingQueue("queue:pollany2");
Assertions.assertDoesNotThrow(() -> { queue3.put(1);
queue3.put(1); queue3.put(2);
queue3.put(2); queue3.put(3);
queue3.put(3); queue1.put(4);
queue1.put(4); queue1.put(5);
queue1.put(5); queue1.put(6);
queue1.put(6); queue2.put(7);
queue2.put(7); queue2.put(8);
queue2.put(8); queue2.put(9);
queue2.put(9);
});
Map<String, List<Integer>> res = queue1.pollLastFromAny(Duration.ofSeconds(4), 2, "queue:pollany1", "queue:pollany2"); Map<String, List<Integer>> res = queue1.pollLastFromAny(Duration.ofSeconds(4), 2, "queue:pollany1", "queue:pollany2");
assertThat(res.get("queue:pollany")).containsExactly(6, 5); assertThat(res.get("queue:pollany")).containsExactly(6, 5);

@ -1,78 +1,49 @@
package org.redisson; package org.redisson;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.redisson.api.RLock; import org.redisson.api.RLock;
import org.redisson.api.RedissonClient; import org.redisson.api.RedissonClient;
import org.redisson.config.Config; import org.redisson.config.Config;
import org.testcontainers.containers.GenericContainer;
import java.io.IOException;
import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.assertThatThrownBy;
public class RedissonLockExpirationRenewalTest { public class RedissonLockExpirationRenewalTest extends RedisDockerTest {
private static final String LOCK_KEY = "LOCK_KEY"; private static final String LOCK_KEY = "LOCK_KEY";
public static final long LOCK_WATCHDOG_TIMEOUT = 1_000L; public static final long LOCK_WATCHDOG_TIMEOUT = 1_000L;
private RedissonClient redisson;
@BeforeEach
public void before() throws IOException, InterruptedException {
RedisRunner.startDefaultRedisServerInstance();
redisson = createInstance();
}
@AfterEach
public void after() throws InterruptedException {
redisson.shutdown();
RedisRunner.shutDownDefaultRedisServerInstance();
}
@Test @Test
public void testExpirationRenewalIsWorkingAfterTimeout() throws IOException, InterruptedException { public void testExpirationRenewalIsWorkingAfterTimeout() throws InterruptedException {
{ GenericContainer<?> redis = createRedis();
RLock lock = redisson.getLock(LOCK_KEY); redis.start();
lock.lock();
try { Config c = createConfig(redis);
// force expiration renewal error c.setLockWatchdogTimeout(LOCK_WATCHDOG_TIMEOUT);
restartRedisServer(); RedissonClient redisson = Redisson.create(c);
// wait for timeout
Thread.sleep(LOCK_WATCHDOG_TIMEOUT * 2); RLock lock = redisson.getLock(LOCK_KEY);
} finally { lock.lock();
assertThatThrownBy(lock::unlock).isInstanceOf(IllegalMonitorStateException.class); try {
} // force expiration renewal error
restart(redis);
// wait for timeout
Thread.sleep(LOCK_WATCHDOG_TIMEOUT * 2);
} finally {
assertThatThrownBy(lock::unlock).isInstanceOf(IllegalMonitorStateException.class);
} }
{ RLock lock2 = redisson.getLock(LOCK_KEY);
RLock lock = redisson.getLock(LOCK_KEY); lock2.lock();
lock.lock(); try {
try { // wait for timeout
// wait for timeout Thread.sleep(LOCK_WATCHDOG_TIMEOUT * 2);
Thread.sleep(LOCK_WATCHDOG_TIMEOUT * 2); } finally {
} finally { lock2.unlock();
lock.unlock();
}
} }
}
private void restartRedisServer() throws InterruptedException, IOException {
int currentPort = RedisRunner.defaultRedisInstance.getRedisServerPort();
RedisRunner.shutDownDefaultRedisServerInstance();
RedisRunner.defaultRedisInstance = new RedisRunner().nosave().randomDir().port(currentPort).run();
}
public static Config createConfig() { redisson.shutdown();
Config config = new Config(); redis.stop();
config.useSingleServer()
.setAddress(RedisRunner.getDefaultRedisServerBindAddressAndPort());
config.setLockWatchdogTimeout(LOCK_WATCHDOG_TIMEOUT);
return config;
} }
public static RedissonClient createInstance() {
Config config = createConfig();
return Redisson.create(config);
}
} }

@ -14,7 +14,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class RedissonLockHeavyTest extends BaseTest { public class RedissonLockHeavyTest extends RedisDockerTest {
public static Collection<Arguments> data() { public static Collection<Arguments> data() {
return Arrays.asList(Arguments.of(2, 5000), return Arrays.asList(Arguments.of(2, 5000),

@ -1,20 +1,20 @@
package org.redisson; package org.redisson;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.redisson.RedisRunner.RedisProcess;
import org.redisson.api.RBlockingQueue; import org.redisson.api.RBlockingQueue;
import org.redisson.api.RFuture; import org.redisson.api.RFuture;
import org.redisson.api.RedissonClient; import org.redisson.api.RedissonClient;
import org.redisson.config.Config; import org.redisson.config.Config;
import org.testcontainers.containers.GenericContainer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static org.assertj.core.api.Assertions.assertThat;
public class RedissonPriorityBlockingQueueTest extends RedissonBlockingQueueTest { public class RedissonPriorityBlockingQueueTest extends RedissonBlockingQueueTest {
@ -34,15 +34,11 @@ public class RedissonPriorityBlockingQueueTest extends RedissonBlockingQueueTest
} }
@Test @Test
public void testPollAsyncReattach() throws InterruptedException, IOException, ExecutionException, TimeoutException { public void testPollAsyncReattach() throws InterruptedException, ExecutionException {
RedisProcess runner = new RedisRunner() GenericContainer<?> redis = createRedis();
.nosave() redis.start();
.randomDir()
.randomPort() Config config = createConfig(redis);
.run();
Config config = new Config();
config.useSingleServer().setAddress(runner.getRedisServerAddressAndPort());
RedissonClient redisson = Redisson.create(config); RedissonClient redisson = Redisson.create(config);
RBlockingQueue<Integer> queue1 = getQueue(redisson); RBlockingQueue<Integer> queue1 = getQueue(redisson);
@ -52,13 +48,10 @@ public class RedissonPriorityBlockingQueueTest extends RedissonBlockingQueueTest
} catch (ExecutionException | TimeoutException e) { } catch (ExecutionException | TimeoutException e) {
// skip // skip
} }
runner.stop(); redis.setPortBindings(Arrays.asList(redis.getFirstMappedPort() + ":6379"));
redis.stop();
runner = new RedisRunner() redis.start();
.port(runner.getRedisServerPort())
.nosave()
.randomDir()
.run();
queue1.put(123); queue1.put(123);
// check connection rotation // check connection rotation
@ -71,20 +64,17 @@ public class RedissonPriorityBlockingQueueTest extends RedissonBlockingQueueTest
assertThat(result).isEqualTo(123); assertThat(result).isEqualTo(123);
redisson.shutdown(); redisson.shutdown();
runner.stop(); redis.stop();
} }
@Test @Test
public void testTakeReattach() throws Exception { public void testTakeReattach() throws Exception {
RedisProcess runner = new RedisRunner() GenericContainer<?> redis = createRedis();
.nosave() redis.start();
.randomDir()
.randomPort() Config config = createConfig(redis);
.run();
Config config = new Config();
config.useSingleServer().setAddress(runner.getRedisServerAddressAndPort());
RedissonClient redisson = Redisson.create(config); RedissonClient redisson = Redisson.create(config);
RBlockingQueue<Integer> queue1 = getQueue(redisson); RBlockingQueue<Integer> queue1 = getQueue(redisson);
RFuture<Integer> f = queue1.takeAsync(); RFuture<Integer> f = queue1.takeAsync();
try { try {
@ -92,13 +82,10 @@ public class RedissonPriorityBlockingQueueTest extends RedissonBlockingQueueTest
} catch (ExecutionException | TimeoutException e) { } catch (ExecutionException | TimeoutException e) {
// skip // skip
} }
runner.stop(); redis.setPortBindings(Arrays.asList(redis.getFirstMappedPort() + ":6379"));
redis.stop();
runner = new RedisRunner() redis.start();
.port(runner.getRedisServerPort())
.nosave()
.randomDir()
.run();
queue1.put(123); queue1.put(123);
// check connection rotation // check connection rotation
@ -109,9 +96,9 @@ public class RedissonPriorityBlockingQueueTest extends RedissonBlockingQueueTest
Integer result = f.get(); Integer result = f.get();
assertThat(result).isEqualTo(123); assertThat(result).isEqualTo(123);
assertThat(queue1.size()).isEqualTo(10); assertThat(queue1.size()).isEqualTo(10);
runner.stop();
redisson.shutdown(); redisson.shutdown();
redis.stop();
} }

@ -14,7 +14,7 @@ import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.redisson.api.RQueue; import org.redisson.api.RQueue;
public class RedissonQueueTest extends BaseTest { public class RedissonQueueTest extends RedisDockerTest {
<T> RQueue<T> getQueue() { <T> RQueue<T> getQueue() {
return redisson.getQueue("queue"); return redisson.getQueue("queue");

@ -1,5 +1,6 @@
package org.redisson; package org.redisson;
import com.github.dockerjava.api.model.ContainerNetwork;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.redisson.api.RedissonClient; import org.redisson.api.RedissonClient;
@ -7,12 +8,8 @@ import org.redisson.api.redisnode.RedisNodes;
import org.redisson.api.redisnode.*; import org.redisson.api.redisnode.*;
import org.redisson.client.protocol.Time; import org.redisson.client.protocol.Time;
import org.redisson.cluster.ClusterSlotRange; import org.redisson.cluster.ClusterSlotRange;
import org.redisson.config.Config;
import org.redisson.connection.balancer.RandomLoadBalancer;
import org.redisson.misc.RedisURI; import org.redisson.misc.RedisURI;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -24,7 +21,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Nikita Koksharov * @author Nikita Koksharov
* *
*/ */
public class RedissonRedisNodesTest extends BaseTest { public class RedissonRedisNodesTest extends RedisDockerTest {
@Test @Test
public void testNode() { public void testNode() {
@ -100,246 +97,110 @@ public class RedissonRedisNodesTest extends BaseTest {
} }
@Test @Test
public void testSentinelFailover() throws IOException, InterruptedException { public void testSentinelFailover() throws InterruptedException {
RedisRunner.RedisProcess master = new RedisRunner() withSentinel((nns, config) -> {
.nosave() RedissonClient redisson = Redisson.create(config);
.randomDir() RedisSentinelMasterSlave nodes = redisson.getRedisNodes(RedisNodes.SENTINEL_MASTER_SLAVE);
.run(); RedisSentinel sentinel = nodes.getSentinels().iterator().next();
RedisRunner.RedisProcess slave1 = new RedisRunner() sentinel.failover(config.useSentinelServers().getMasterName());
.port(6380)
.nosave() redisson.shutdown();
.randomDir() }, 2);
.slaveof("127.0.0.1", 6379)
.run();
RedisRunner.RedisProcess slave2 = new RedisRunner()
.port(6381)
.nosave()
.randomDir()
.slaveof("127.0.0.1", 6379)
.run();
RedisRunner.RedisProcess sentinel1 = new RedisRunner()
.nosave()
.randomDir()
.port(26379)
.sentinel()
.sentinelMonitor("myMaster", "127.0.0.1", 6379, 2)
.run();
RedisRunner.RedisProcess sentinel2 = new RedisRunner()
.nosave()
.randomDir()
.port(26380)
.sentinel()
.sentinelMonitor("myMaster", "127.0.0.1", 6379, 2)
.run();
RedisRunner.RedisProcess sentinel3 = new RedisRunner()
.nosave()
.randomDir()
.port(26381)
.sentinel()
.sentinelMonitor("myMaster", "127.0.0.1", 6379, 2)
.run();
Config config = new Config();
config.useSentinelServers()
.setLoadBalancer(new RandomLoadBalancer())
.addSentinelAddress(sentinel3.getRedisServerAddressAndPort()).setMasterName("myMaster");
long t = System.currentTimeMillis();
RedissonClient redisson = Redisson.create(config);
RedisSentinelMasterSlave nodes = redisson.getRedisNodes(RedisNodes.SENTINEL_MASTER_SLAVE);
RedisSentinel sentinel = nodes.getSentinels().iterator().next();
sentinel.failover("myMaster");
redisson.shutdown();
sentinel1.stop();
sentinel2.stop();
sentinel3.stop();
master.stop();
slave1.stop();
slave2.stop();
} }
@Test @Test
public void testCluster() throws Exception { public void testCluster() {
RedisRunner master1 = new RedisRunner().port(6890).randomDir().nosave(); testInCluster(redisson -> {
RedisRunner master2 = new RedisRunner().port(6891).randomDir().nosave(); RedisCluster nodes = redisson.getRedisNodes(RedisNodes.CLUSTER);
RedisRunner master3 = new RedisRunner().port(6892).randomDir().nosave(); RedisClusterMaster n = nodes.getMasters().iterator().next();
RedisRunner slave1 = new RedisRunner().port(6900).randomDir().nosave(); assertThat(nodes.getMaster("redis://" + n.getAddr().getHostString() + ":" + n.getAddr().getPort())).isNotNull();
RedisRunner slave2 = new RedisRunner().port(6901).randomDir().nosave(); assertThat(nodes.getMaster("redis://127.0.0.1:6899")).isNull();
RedisRunner slave3 = new RedisRunner().port(6902).randomDir().nosave(); assertThat(nodes.getMasters()).hasSize(3);
RedisRunner slave4 = new RedisRunner().port(6903).randomDir().nosave(); assertThat(nodes.getSlaves()).hasSize(3);
ClusterRunner clusterRunner = new ClusterRunner() for (RedisClusterMaster master : nodes.getMasters()) {
.addNode(master1, slave1, slave4) master.clusterDeleteSlots(1, 2);
.addNode(master2, slave2) master.clusterAddSlots(1, 2);
.addNode(master3, slave3); master.clusterCountKeysInSlot(1);
ClusterRunner.ClusterProcesses process = clusterRunner.run(); List<String> keys = master.clusterGetKeysInSlot(1, 10);
assertThat(keys).isEmpty();;
Thread.sleep(5000); String nodeId = master.clusterId();
assertThat(nodeId).isNotNull();
Config config = new Config();
config.useClusterServers() assertThat(master.clusterCountFailureReports(nodeId)).isZero();
.setLoadBalancer(new RandomLoadBalancer()) Map<ClusterSlotRange, Set<String>> slots = master.clusterSlots();
.addNodeAddress(process.getNodes().stream().findAny().get().getRedisServerAddressAndPort()); assertThat(slots.entrySet().size()).isBetween(3, 5);
RedissonClient redisson = Redisson.create(config); }
for (RedisClusterSlave slave : nodes.getSlaves()) {
RedisCluster nodes = redisson.getRedisNodes(RedisNodes.CLUSTER); slave.clusterDeleteSlots(1, 2);
assertThat(nodes.getMaster("redis://127.0.0.1:6890")).isNotNull(); slave.clusterAddSlots(1, 2);
assertThat(nodes.getMaster("redis://127.0.0.1:6899")).isNull(); slave.clusterCountKeysInSlot(1);
assertThat(nodes.getMasters()).hasSize(3); List<String> keys = slave.clusterGetKeysInSlot(1, 10);
assertThat(nodes.getSlaves()).hasSize(4); assertThat(keys).isEmpty();;
String nodeId = slave.clusterId();
for (RedisClusterMaster master : nodes.getMasters()) { assertThat(nodeId).isNotNull();
master.clusterDeleteSlots(1, 2);
master.clusterAddSlots(1, 2); assertThat(slave.clusterCountFailureReports(nodeId)).isZero();
master.clusterCountKeysInSlot(1); Map<ClusterSlotRange, Set<String>> slots = slave.clusterSlots();
List<String> keys = master.clusterGetKeysInSlot(1, 10); assertThat(slots.entrySet().size()).isBetween(3, 5);
assertThat(keys).isEmpty();; }
String nodeId = master.clusterId(); });
assertThat(nodeId).isNotNull();
assertThat(master.clusterCountFailureReports(nodeId)).isZero();
Map<ClusterSlotRange, Set<String>> slots = master.clusterSlots();
assertThat(slots.entrySet().size()).isBetween(3, 5);
}
for (RedisClusterSlave slave : nodes.getSlaves()) {
slave.clusterDeleteSlots(1, 2);
slave.clusterAddSlots(1, 2);
slave.clusterCountKeysInSlot(1);
List<String> keys = slave.clusterGetKeysInSlot(1, 10);
assertThat(keys).isEmpty();;
String nodeId = slave.clusterId();
assertThat(nodeId).isNotNull();
assertThat(slave.clusterCountFailureReports(nodeId)).isZero();
Map<ClusterSlotRange, Set<String>> slots = slave.clusterSlots();
assertThat(slots.entrySet().size()).isBetween(3, 5);
}
redisson.shutdown();
process.shutdown();
} }
@Test @Test
public void testSentinel() throws IOException, InterruptedException { public void testSentinel() throws InterruptedException {
RedisRunner.RedisProcess master = new RedisRunner() withSentinel((nns, config) -> {
.nosave() RedissonClient redisson = Redisson.create(config);
.randomDir()
.run(); RedisSentinelMasterSlave nodes = redisson.getRedisNodes(RedisNodes.SENTINEL_MASTER_SLAVE);
RedisRunner.RedisProcess slave1 = new RedisRunner() assertThat(nodes.getSentinels()).hasSize(3);
.port(6380) assertThat(nodes.getSlaves()).hasSize(2);
.nosave() assertThat(nodes.getMaster()).isNotNull();
.randomDir()
.slaveof("127.0.0.1", 6379) for (RedisSentinel sentinel : nodes.getSentinels()) {
.run(); Assertions.assertTrue(sentinel.ping());
RedisRunner.RedisProcess slave2 = new RedisRunner() RedisURI addr = sentinel.getMasterAddr(config.useSentinelServers().getMasterName());
.port(6381)
.nosave() Map<String, ContainerNetwork> ss = nns.get(0).getContainerInfo().getNetworkSettings().getNetworks();
.randomDir() ContainerNetwork s = ss.values().iterator().next();
.slaveof("127.0.0.1", 6379) Integer port = nns.get(0).getExposedPorts().get(0);
.run();
RedisRunner.RedisProcess sentinel1 = new RedisRunner() assertThat(addr.getHost()).isEqualTo(s.getIpAddress());
.nosave() assertThat(addr.getPort()).isEqualTo(port);
.randomDir()
.port(26379) Map<String, String> masterMap = sentinel.getMaster(config.useSentinelServers().getMasterName());
.sentinel() assertThat(masterMap).isNotEmpty();
.sentinelMonitor("myMaster", "127.0.0.1", 6379, 2)
.run(); List<Map<String, String>> masters = sentinel.getMasters();
RedisRunner.RedisProcess sentinel2 = new RedisRunner() assertThat(masters).hasSize(1);
.nosave() Map<String, String> m = masters.get(0);
.randomDir() assertThat(m.get("ip")).isEqualTo(s.getIpAddress());
.port(26380) assertThat(Integer.valueOf(m.get("port"))).isEqualTo(port);
.sentinel()
.sentinelMonitor("myMaster", "127.0.0.1", 6379, 2) List<Map<String, String>> slaves = sentinel.getSlaves(config.useSentinelServers().getMasterName());
.run(); assertThat(slaves).hasSize(2);
RedisRunner.RedisProcess sentinel3 = new RedisRunner() }
.nosave() nodes.getSlaves().forEach((node) -> {
.randomDir() Assertions.assertTrue(node.ping());
.port(26381) });
.sentinel()
.sentinelMonitor("myMaster", "127.0.0.1", 6379, 2) redisson.shutdown();
.run(); }, 2);
Config config = new Config();
config.useSentinelServers()
.setLoadBalancer(new RandomLoadBalancer())
.addSentinelAddress(sentinel3.getRedisServerAddressAndPort()).setMasterName("myMaster");
long t = System.currentTimeMillis();
RedissonClient redisson = Redisson.create(config);
RedisSentinelMasterSlave nodes = redisson.getRedisNodes(RedisNodes.SENTINEL_MASTER_SLAVE);
assertThat(nodes.getSentinels()).hasSize(3);
assertThat(nodes.getSlaves()).hasSize(2);
assertThat(nodes.getMaster()).isNotNull();
for (RedisSentinel sentinel : nodes.getSentinels()) {
Assertions.assertTrue(sentinel.ping());
RedisURI addr = sentinel.getMasterAddr("myMaster");
assertThat(addr.getHost()).isEqualTo("127.0.0.1");
assertThat(addr.getPort()).isEqualTo(master.getRedisServerPort());
Map<String, String> masterMap = sentinel.getMaster("myMaster");
assertThat(masterMap).isNotEmpty();
List<Map<String, String>> masters = sentinel.getMasters();
assertThat(masters).hasSize(1);
Map<String, String> m = masters.get(0);
assertThat(m.get("ip")).isEqualTo("127.0.0.1");
assertThat(Integer.valueOf(m.get("port"))).isEqualTo(master.getRedisServerPort());
List<Map<String, String>> slaves = sentinel.getSlaves("myMaster");
assertThat(slaves).hasSize(2);
}
nodes.getSlaves().forEach((node) -> {
Assertions.assertTrue(node.ping());
});
redisson.shutdown();
sentinel1.stop();
sentinel2.stop();
sentinel3.stop();
master.stop();
slave1.stop();
slave2.stop();
} }
@Test @Test
public void testNodesInCluster() throws Exception { public void testNodesInCluster() {
RedisRunner master1 = new RedisRunner().randomPort().randomDir().nosave(); testInCluster(redisson -> {
RedisRunner master2 = new RedisRunner().randomPort().randomDir().nosave(); RedisCluster nodes = redisson.getRedisNodes(RedisNodes.CLUSTER);
RedisRunner master3 = new RedisRunner().randomPort().randomDir().nosave(); assertThat(nodes.getMasters()).hasSize(3);
RedisRunner slot1 = new RedisRunner().randomPort().randomDir().nosave(); for (RedisClusterMaster node : nodes.getMasters()) {
RedisRunner slot2 = new RedisRunner().randomPort().randomDir().nosave(); assertThat(node.info(RedisNode.InfoSection.ALL)).isNotEmpty();
RedisRunner slot3 = new RedisRunner().randomPort().randomDir().nosave(); }
assertThat(nodes.getSlaves()).hasSize(3);
ClusterRunner clusterRunner = new ClusterRunner() for (RedisClusterSlave node : nodes.getSlaves()) {
.addNode(master1, slot1) assertThat(node.info(RedisNode.InfoSection.ALL)).isNotEmpty();
.addNode(master2, slot2) }
.addNode(master3, slot3); });
ClusterRunner.ClusterProcesses process = clusterRunner.run();
Config config = new Config();
config.useClusterServers()
.setLoadBalancer(new RandomLoadBalancer())
.addNodeAddress(process.getNodes().stream().findAny().get().getRedisServerAddressAndPort());
RedissonClient redisson = Redisson.create(config);
RedisCluster nodes = redisson.getRedisNodes(RedisNodes.CLUSTER);
assertThat(nodes.getMasters()).hasSize(3);
for (RedisClusterMaster node : nodes.getMasters()) {
assertThat(node.info(RedisNode.InfoSection.ALL)).isNotEmpty();
}
assertThat(nodes.getSlaves()).hasSize(3);
for (RedisClusterSlave node : nodes.getSlaves()) {
assertThat(node.info(RedisNode.InfoSection.ALL)).isNotEmpty();
}
redisson.shutdown();
process.shutdown();
} }
@Test @Test

@ -1,36 +1,22 @@
package org.redisson; package org.redisson;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.assertj.core.api.ListAssert;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.redisson.ClusterRunner.ClusterProcesses;
import org.redisson.RedisRunner.FailedToStartRedisException; import org.redisson.RedisRunner.FailedToStartRedisException;
import org.redisson.api.RFuture; import org.redisson.api.RFuture;
import org.redisson.api.RList; import org.redisson.api.RList;
import org.redisson.api.RSet; import org.redisson.api.RSet;
import org.redisson.api.RedissonClient;
import org.redisson.api.SortOrder; import org.redisson.api.SortOrder;
import org.redisson.client.codec.IntegerCodec; import org.redisson.client.codec.IntegerCodec;
import org.redisson.client.codec.LongCodec;
import org.redisson.client.codec.StringCodec; import org.redisson.client.codec.StringCodec;
import org.redisson.config.Config;
import org.redisson.connection.balancer.RandomLoadBalancer; import java.io.Serializable;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import static org.assertj.core.api.Assertions.assertThat;
public class RedissonSetTest extends RedisDockerTest { public class RedissonSetTest extends RedisDockerTest {

@ -8,8 +8,6 @@ import org.awaitility.Awaitility;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.redisson.ClusterRunner.ClusterProcesses;
import org.redisson.RedisRunner.RedisProcess;
import org.redisson.api.*; import org.redisson.api.*;
import org.redisson.api.redisnode.RedisClusterMaster; import org.redisson.api.redisnode.RedisClusterMaster;
import org.redisson.api.redisnode.RedisMaster; import org.redisson.api.redisnode.RedisMaster;
@ -27,11 +25,17 @@ import org.redisson.cluster.ClusterNodeInfo;
import org.redisson.cluster.ClusterNodeInfo.Flag; import org.redisson.cluster.ClusterNodeInfo.Flag;
import org.redisson.codec.JsonJacksonCodec; import org.redisson.codec.JsonJacksonCodec;
import org.redisson.codec.SerializationCodec; import org.redisson.codec.SerializationCodec;
import org.redisson.config.*; import org.redisson.config.Config;
import org.redisson.config.ConfigSupport;
import org.redisson.config.Credentials;
import org.redisson.config.CredentialsResolver;
import org.redisson.connection.CRC16; import org.redisson.connection.CRC16;
import org.redisson.connection.ConnectionListener; import org.redisson.connection.ConnectionListener;
import org.redisson.connection.MasterSlaveConnectionManager; import org.redisson.connection.MasterSlaveConnectionManager;
import org.redisson.connection.balancer.RandomLoadBalancer; import org.redisson.connection.balancer.RandomLoadBalancer;
import org.redisson.misc.RedisURI;
import org.testcontainers.containers.FixedHostPortGenericContainer;
import org.testcontainers.containers.GenericContainer;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
@ -46,7 +50,7 @@ import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await; import static org.awaitility.Awaitility.await;
public class RedissonTest extends BaseTest { public class RedissonTest extends RedisDockerTest {
@Test @Test
public void testVirtualThreads() { public void testVirtualThreads() {
@ -110,7 +114,7 @@ public class RedissonTest extends BaseTest {
} }
@Test @Test
public void testLazyInitialization() throws IOException, InterruptedException { public void testLazyInitialization() {
Config config = new Config(); Config config = new Config();
config.setLazyInitialization(true); config.setLazyInitialization(true);
config.useSingleServer() config.useSingleServer()
@ -125,16 +129,14 @@ public class RedissonTest extends BaseTest {
redisson.getStream("test").createGroup(StreamCreateGroupArgs.name("test").makeStream()); redisson.getStream("test").createGroup(StreamCreateGroupArgs.name("test").makeStream());
}); });
RedisProcess pp = new RedisRunner() FixedHostPortGenericContainer c = new FixedHostPortGenericContainer("redis:7.2")
.nosave() .withFixedExposedPort(4431, 6379);
.port(4431) c.start();
.randomDir()
.run();
redisson.getStream("test").createGroup(StreamCreateGroupArgs.name("test").makeStream()); redisson.getStream("test").createGroup(StreamCreateGroupArgs.name("test").makeStream());
redisson.shutdown(); redisson.shutdown();
pp.stop(); c.stop();
} }
@Test @Test
@ -152,20 +154,19 @@ public class RedissonTest extends BaseTest {
} }
ex.shutdown(); ex.shutdown();
assertThat(ex.awaitTermination(8, TimeUnit.SECONDS)).isTrue(); assertThat(ex.awaitTermination(20, TimeUnit.SECONDS)).isTrue();
inst.shutdown(); inst.shutdown();
} }
@Test @Test
public void testResponseHandling2() throws InterruptedException { public void testResponseHandling2() throws InterruptedException {
Config config = new Config(); Config config = createConfig();
config.useSingleServer() config.useSingleServer()
.setTimeout(10) .setTimeout(10)
.setRetryAttempts(0) .setRetryAttempts(0)
.setConnectionPoolSize(1) .setConnectionPoolSize(1)
.setConnectionMinimumIdleSize(1) .setConnectionMinimumIdleSize(1)
.setPingConnectionInterval(0) .setPingConnectionInterval(0);
.setAddress(RedisRunner.getDefaultRedisServerBindAddressAndPort());
RedissonClient redisson = Redisson.create(config); RedissonClient redisson = Redisson.create(config);
@ -225,7 +226,7 @@ public class RedissonTest extends BaseTest {
}); });
} }
e.shutdown(); e.shutdown();
assertThat(e.awaitTermination(40, TimeUnit.SECONDS)).isTrue(); assertThat(e.awaitTermination(70, TimeUnit.SECONDS)).isTrue();
assertThat(counter.get()).isEqualTo(10000 * 100); assertThat(counter.get()).isEqualTo(10000 * 100);
} }
@ -247,11 +248,10 @@ public class RedissonTest extends BaseTest {
@Test @Test
public void testSmallPool() throws InterruptedException { public void testSmallPool() throws InterruptedException {
Config config = new Config(); Config config = createConfig();
config.useSingleServer() config.useSingleServer()
.setConnectionMinimumIdleSize(3) .setConnectionMinimumIdleSize(3)
.setConnectionPoolSize(3) .setConnectionPoolSize(3);
.setAddress(RedisRunner.getDefaultRedisServerBindAddressAndPort());
RedissonClient localRedisson = Redisson.create(config); RedissonClient localRedisson = Redisson.create(config);
@ -283,12 +283,11 @@ public class RedissonTest extends BaseTest {
} }
@Test @Test
public void testNextResponseAfterDecoderError() throws Exception { public void testNextResponseAfterDecoderError() {
Config config = new Config(); Config config = createConfig();
config.useSingleServer() config.useSingleServer()
.setConnectionMinimumIdleSize(1) .setConnectionMinimumIdleSize(1)
.setConnectionPoolSize(1) .setConnectionPoolSize(1);
.setAddress(RedisRunner.getDefaultRedisServerBindAddressAndPort());
RedissonClient redisson = Redisson.create(config); RedissonClient redisson = Redisson.create(config);
@ -299,7 +298,7 @@ public class RedissonTest extends BaseTest {
RBuckets buckets = redisson.getBuckets(new JsonJacksonCodec()); RBuckets buckets = redisson.getBuckets(new JsonJacksonCodec());
buckets.get("test2", "test1"); buckets.get("test2", "test1");
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); // skip
} }
assertThat(getStringValue(redisson, "test3")).isEqualTo("\"test3\""); assertThat(getStringValue(redisson, "test3")).isEqualTo("\"test3\"");
@ -324,81 +323,76 @@ public class RedissonTest extends BaseTest {
@Test @Test
public void testSer() { public void testSer() {
Config config = new Config(); Config config = createConfig();
config.useSingleServer().setAddress(RedisRunner.getDefaultRedisServerBindAddressAndPort());
config.setCodec(new SerializationCodec()); config.setCodec(new SerializationCodec());
RedissonClient r = Redisson.create(config);
Assertions.assertThrows(IllegalArgumentException.class, () -> { Assertions.assertThrows(IllegalArgumentException.class, () -> {
RedissonClient r = Redisson.create(config);
r.getMap("test").put("1", new Dummy()); r.getMap("test").put("1", new Dummy());
}); });
r.shutdown();
} }
@Test @Test
public void testMemoryScript() throws IOException, InterruptedException { public void testMemoryScript() {
RedisProcess p = redisTestSmallMemory(); testWithParams(redissonClient -> {
Config c = redissonClient.getConfig();
c.useSingleServer().setTimeout(100000);
Config config = new Config(); Assertions.assertThrows(RedisOutOfMemoryException.class, () -> {
config.useSingleServer().setAddress(p.getRedisServerAddressAndPort()).setTimeout(100000); RedissonClient r = null;
try {
Assertions.assertThrows(RedisOutOfMemoryException.class, () -> { r = Redisson.create(c);
RedissonClient r = null; for (int i = 0; i < 10000; i++) {
try { r.getMap("test").put("" + i, "" + i);
r = Redisson.create(config); }
r.getKeys().flushall(); } finally {
for (int i = 0; i < 10000; i++) { r.shutdown();
r.getMap("test").put("" + i, "" + i);
} }
} finally { });
r.shutdown(); }, "--maxmemory", "1mb");
p.stop();
}
});
} }
@Test @Test
public void testMemoryCommand() throws IOException, InterruptedException { public void testMemoryCommand() {
RedisProcess p = redisTestSmallMemory(); testWithParams(redissonClient -> {
Config c = redissonClient.getConfig();
c.useSingleServer().setTimeout(100000);
Config config = new Config(); Assertions.assertThrows(RedisOutOfMemoryException.class, () -> {
config.useSingleServer().setAddress(p.getRedisServerAddressAndPort()).setTimeout(100000); RedissonClient r = null;
try {
Assertions.assertThrows(RedisOutOfMemoryException.class, () -> { r = Redisson.create(c);
RedissonClient r = null; for (int i = 0; i < 10000; i++) {
try { r.getMap("test").fastPut("" + i, "" + i);
r = Redisson.create(config); }
r.getKeys().flushall(); } finally {
for (int i = 0; i < 10000; i++) { r.shutdown();
r.getMap("test").fastPut("" + i, "" + i);
} }
} finally { });
r.shutdown(); }, "--maxmemory", "1mb");
p.stop();
}
});
} }
@Test @Test
public void testConfigValidation() { public void testConfigValidation() {
Assertions.assertThrows(IllegalArgumentException.class, () -> { Assertions.assertThrows(IllegalArgumentException.class, () -> {
Config redissonConfig = new Config(); Config redissonConfig = createConfig();
redissonConfig.useSingleServer() redissonConfig.useSingleServer()
.setAddress(RedisRunner.getDefaultRedisServerBindAddressAndPort())
.setConnectionPoolSize(2); .setConnectionPoolSize(2);
Redisson.create(redissonConfig); Redisson.create(redissonConfig);
}); });
} }
@Test @Test
public void testConnectionListener() throws IOException, InterruptedException, TimeoutException { public void testConnectionListener() {
GenericContainer<?> redis = createRedis();
final RedisProcess p = redisTestConnection(); redis.start();
final AtomicInteger connectCounter = new AtomicInteger(); final AtomicInteger connectCounter = new AtomicInteger();
final AtomicInteger disconnectCounter = new AtomicInteger(); final AtomicInteger disconnectCounter = new AtomicInteger();
Config config = new Config(); Config config = createConfig(redis);
config.useSingleServer().setAddress(p.getRedisServerAddressAndPort());
config.setConnectionListener(new ConnectionListener() { config.setConnectionListener(new ConnectionListener() {
@Override @Override
@ -407,7 +401,7 @@ public class RedissonTest extends BaseTest {
@Override @Override
public void onDisconnect(InetSocketAddress addr, NodeType nodeType) { public void onDisconnect(InetSocketAddress addr, NodeType nodeType) {
assertThat(addr).isEqualTo(new InetSocketAddress(p.getRedisServerBindAddress(), p.getRedisServerPort())); assertThat(addr).isEqualTo(new InetSocketAddress(redis.getHost(), redis.getFirstMappedPort()));
assertThat(nodeType).isEqualTo(NodeType.MASTER); assertThat(nodeType).isEqualTo(NodeType.MASTER);
disconnectCounter.incrementAndGet(); disconnectCounter.incrementAndGet();
} }
@ -418,7 +412,7 @@ public class RedissonTest extends BaseTest {
@Override @Override
public void onConnect(InetSocketAddress addr, NodeType nodeType) { public void onConnect(InetSocketAddress addr, NodeType nodeType) {
assertThat(addr).isEqualTo(new InetSocketAddress(p.getRedisServerBindAddress(), p.getRedisServerPort())); assertThat(addr).isEqualTo(new InetSocketAddress(redis.getHost(), redis.getFirstMappedPort()));
assertThat(nodeType).isEqualTo(NodeType.MASTER); assertThat(nodeType).isEqualTo(NodeType.MASTER);
connectCounter.incrementAndGet(); connectCounter.incrementAndGet();
} }
@ -427,23 +421,21 @@ public class RedissonTest extends BaseTest {
RedissonClient r = Redisson.create(config); RedissonClient r = Redisson.create(config);
r.getBucket("1").get(); r.getBucket("1").get();
Assertions.assertEquals(0, p.stop()); redis.setPortBindings(Arrays.asList(redis.getFirstMappedPort() + ":6379"));
redis.stop();
await().atMost(2, TimeUnit.SECONDS).until(() -> disconnectCounter.get() == 1); await().atMost(2, TimeUnit.SECONDS).until(() -> disconnectCounter.get() == 1);
try { try {
r.getBucket("1").get(); r.getBucket("1").get();
} catch (Exception e) { } catch (Exception e) {
// skip
} }
assertThat(connectCounter.get()).isEqualTo(1); assertThat(connectCounter.get()).isEqualTo(1);
assertThat(disconnectCounter.get()).isEqualTo(1); assertThat(disconnectCounter.get()).isEqualTo(1);
RedisProcess pp = new RedisRunner() redis.start();
.nosave()
.port(p.getRedisServerPort())
.randomDir()
.run();
r.getBucket("1").get(); r.getBucket("1").get();
@ -451,7 +443,7 @@ public class RedissonTest extends BaseTest {
assertThat(disconnectCounter.get()).isEqualTo(1); assertThat(disconnectCounter.get()).isEqualTo(1);
r.shutdown(); r.shutdown();
Assertions.assertEquals(0, pp.stop()); redis.stop();
} }
public static class SlowCodec extends BaseCodec { public static class SlowCodec extends BaseCodec {
@ -501,28 +493,21 @@ public class RedissonTest extends BaseTest {
} }
@Test @Test
public void testReconnection() throws IOException, InterruptedException, TimeoutException { public void testReconnection() {
RedisProcess runner = new RedisRunner() Config config = redisson.getConfig();
.appendonly(true)
.randomDir()
.randomPort()
.run();
Config config = new Config();
config.useSingleServer() config.useSingleServer()
.setConnectionMinimumIdleSize(20) .setConnectionMinimumIdleSize(20)
.setConnectionPoolSize(20) .setConnectionPoolSize(20)
.setSubscriptionConnectionMinimumIdleSize(20) .setSubscriptionConnectionMinimumIdleSize(20)
.setSubscriptionConnectionPoolSize(20) .setSubscriptionConnectionPoolSize(20);
.setAddress(runner.getRedisServerAddressAndPort());
RedissonClient r = Redisson.create(config); RedissonClient r = Redisson.create(config);
r.getBucket("myBucket").set(1); r.getBucket("myBucket").set(1);
assertThat(r.getBucket("myBucket").get()).isEqualTo(1); assertThat(r.getBucket("myBucket").get()).isEqualTo(1);
Assertions.assertEquals(0, runner.stop()); REDIS.getDockerClient().pauseContainerCmd(REDIS.getContainerId()).exec();
AtomicBoolean hasError = new AtomicBoolean(); AtomicBoolean hasError = new AtomicBoolean();
try { try {
r.getBucket("myBucket").get(); r.getBucket("myBucket").get();
@ -532,18 +517,12 @@ public class RedissonTest extends BaseTest {
} }
assertThat(hasError.get()).isTrue(); assertThat(hasError.get()).isTrue();
RedisProcess pp = new RedisRunner() REDIS.getDockerClient().unpauseContainerCmd(REDIS.getContainerId()).exec();
.appendonly(true)
.port(runner.getRedisServerPort())
.dir(runner.getDefaultDir())
.run();
assertThat(r.getBucket("myBucket").get()).isEqualTo(1); assertThat(r.getBucket("myBucket").get()).isEqualTo(1);
r.shutdown(); r.shutdown();
Assertions.assertEquals(0, pp.stop());
} }
@ -565,10 +544,7 @@ public class RedissonTest extends BaseTest {
@Test @Test
public void testShutdown() { public void testShutdown() {
Config config = new Config(); RedissonClient r = createInstance();
config.useSingleServer().setAddress(RedisRunner.getDefaultRedisServerBindAddressAndPort());
RedissonClient r = Redisson.create(config);
Assertions.assertFalse(r.isShuttingDown()); Assertions.assertFalse(r.isShuttingDown());
Assertions.assertFalse(r.isShutdown()); Assertions.assertFalse(r.isShutdown());
r.shutdown(); r.shutdown();
@ -577,30 +553,22 @@ public class RedissonTest extends BaseTest {
} }
@Test @Test
public void testCredentials() throws IOException, InterruptedException { public void testCredentials() {
RedisProcess runner = new RedisRunner() withRedisParams(config -> {
.nosave() config.useSingleServer()
.randomDir() .setCredentialsResolver(new CredentialsResolver() {
.randomPort() @Override
.requirepass("1234") public CompletionStage<Credentials> resolve(InetSocketAddress address) {
.run(); return CompletableFuture.completedFuture(new Credentials(null, "1234"));
}
Config config = new Config(); });
config.useSingleServer()
.setCredentialsResolver(new CredentialsResolver() {
@Override
public CompletionStage<Credentials> resolve(InetSocketAddress address) {
return CompletableFuture.completedFuture(new Credentials(null, "1234"));
}
})
.setAddress(runner.getRedisServerAddressAndPort());
RedissonClient redisson = Redisson.create(config);
RBucket<String> b = redisson.getBucket("test");
b.set("123");
redisson.shutdown(); RedissonClient redisson = Redisson.create(config);
runner.stop(); RBucket<String> b = redisson.getBucket("test");
b.set("123");
redisson.shutdown();
}, "--requirepass", "1234");
} }
@Test @Test
@ -617,29 +585,23 @@ public class RedissonTest extends BaseTest {
RedisException e = Assertions.assertThrows(RedisException.class, () -> { RedisException e = Assertions.assertThrows(RedisException.class, () -> {
b.compareAndSet("test", "v1"); b.compareAndSet("test", "v1");
}); });
assertThat(e.getMessage()).startsWith("ERR unknown command `EVAL_111`"); assertThat(e.getMessage()).startsWith("ERR unknown command 'EVAL_111'");
redisson.shutdown(); redisson.shutdown();
} }
@Test @Test
public void testURIPassword() throws InterruptedException, IOException { public void testURIPassword() {
RedisProcess runner = new RedisRunner() withRedisParams(config -> {
.nosave() RedisURI ur = new RedisURI(config.useSingleServer().getAddress());
.randomDir() config.useSingleServer()
.randomPort() .setAddress("redis://:1234@" + ur.getHost() + ":" + ur.getPort());
.requirepass("1234") RedissonClient redisson = Redisson.create(config);
.run(); RBucket<String> b = redisson.getBucket("test");
b.set("123");
Config config = new Config(); redisson.shutdown();
config.useSingleServer() }, "--requirepass", "1234");
.setAddress("redis://:1234@" + runner.getRedisServerBindAddress() + ":" + runner.getRedisServerPort());
RedissonClient redisson = Redisson.create(config);
RBucket<String> b = redisson.getBucket("test");
b.set("123");
redisson.shutdown();
runner.stop();
} }
@Test @Test
@ -716,67 +678,17 @@ public class RedissonTest extends BaseTest {
@Test @Test
public void testSentinelStartup() throws Exception { public void testSentinelStartup() throws Exception {
RedisRunner.RedisProcess master = new RedisRunner() withSentinel((nodes, config) -> {
.nosave() long t = System.currentTimeMillis();
.randomDir() RedissonClient redisson = Redisson.create(config);
.run(); assertThat(System.currentTimeMillis() - t).isLessThan(2000L);
RedisRunner.RedisProcess slave1 = new RedisRunner() redisson.shutdown();
.port(6380) }, 2);
.nosave()
.randomDir()
.slaveof("127.0.0.1", 6379)
.run();
RedisRunner.RedisProcess slave2 = new RedisRunner()
.port(6381)
.nosave()
.randomDir()
.slaveof("127.0.0.1", 6379)
.run();
RedisRunner.RedisProcess sentinel1 = new RedisRunner()
.nosave()
.randomDir()
.port(26379)
.sentinel()
.sentinelMonitor("myMaster", "127.0.0.1", 6379, 2)
.run();
RedisRunner.RedisProcess sentinel2 = new RedisRunner()
.nosave()
.randomDir()
.port(26380)
.sentinel()
.sentinelMonitor("myMaster", "127.0.0.1", 6379, 2)
.run();
RedisRunner.RedisProcess sentinel3 = new RedisRunner()
.nosave()
.randomDir()
.port(26381)
.sentinel()
.sentinelMonitor("myMaster", "127.0.0.1", 6379, 2)
.run();
Thread.sleep(5000);
Config config = new Config();
config.useSentinelServers()
.setLoadBalancer(new RandomLoadBalancer())
.addSentinelAddress(sentinel3.getRedisServerAddressAndPort()).setMasterName("myMaster");
long t = System.currentTimeMillis();
RedissonClient redisson = Redisson.create(config);
assertThat(System.currentTimeMillis() - t).isLessThan(2000L);
redisson.shutdown();
sentinel1.stop();
sentinel2.stop();
sentinel3.stop();
master.stop();
slave1.stop();
slave2.stop();
} }
@Test @Test
public void testSingleConfigYAML() throws IOException { public void testSingleConfigYAML() throws IOException {
RedissonClient r = BaseTest.createInstance(); RedissonClient r = createInstance();
String t = r.getConfig().toYAML(); String t = r.getConfig().toYAML();
Config c = Config.fromYAML(t); Config c = Config.fromYAML(t);
assertThat(c.toYAML()).isEqualTo(t); assertThat(c.toYAML()).isEqualTo(t);
@ -802,124 +714,102 @@ public class RedissonTest extends BaseTest {
} }
@Test @Test
public void testEvalCache() throws InterruptedException, IOException { public void testEvalCache() {
RedisRunner master1 = new RedisRunner().port(6896).randomDir().nosave(); testInCluster(redissonClient -> {
RedisRunner master2 = new RedisRunner().port(6891).randomDir().nosave(); Config config = redissonClient.getConfig();
RedisRunner master3 = new RedisRunner().port(6892).randomDir().nosave(); config.setUseScriptCache(true);
RedisRunner slave1 = new RedisRunner().port(6900).randomDir().nosave();
RedisRunner slave2 = new RedisRunner().port(6901).randomDir().nosave();
RedisRunner slave3 = new RedisRunner().port(6902).randomDir().nosave();
ClusterRunner clusterRunner = new ClusterRunner()
.addNode(master1, slave1)
.addNode(master2, slave2)
.addNode(master3, slave3);
ClusterRunner.ClusterProcesses process = clusterRunner.run();
Thread.sleep(5000); RedissonClient redisson = Redisson.create(config);
Config config = new Config(); RTimeSeries<String, Object> t = redisson.getTimeSeries("test");
config.setUseScriptCache(true); t.add(4, "40");
config.useClusterServers() t.add(2, "20");
.setLoadBalancer(new RandomLoadBalancer()) t.add(1, "10", 1, TimeUnit.SECONDS);
.addNodeAddress(process.getNodes().stream().findAny().get().getRedisServerAddressAndPort());
RedissonClient redisson = Redisson.create(config);
RTimeSeries<String, Object> t = redisson.getTimeSeries("test"); t.size();
t.add(4, "40");
t.add(2, "20");
t.add(1, "10", 1, TimeUnit.SECONDS);
t.size(); redisson.shutdown();
});
} }
@Test @Test
public void testMovedRedirectInCluster() throws Exception { public void testMovedRedirectInCluster() throws Exception {
RedisRunner master1 = new RedisRunner().randomPort().randomDir().nosave(); withNewCluster(redissonClient -> {
RedisRunner master2 = new RedisRunner().randomPort().randomDir().nosave(); Config config = redissonClient.getConfig();
RedisRunner master3 = new RedisRunner().randomPort().randomDir().nosave(); config.useClusterServers()
RedisRunner slot1 = new RedisRunner().randomPort().randomDir().nosave(); .setScanInterval(100000);
RedisRunner slot2 = new RedisRunner().randomPort().randomDir().nosave();
RedisRunner slot3 = new RedisRunner().randomPort().randomDir().nosave(); RedissonClient redisson = Redisson.create(config);
ClusterRunner clusterRunner = new ClusterRunner() Collection<RedisClusterMaster> ms = redisson.getRedisNodes(RedisNodes.CLUSTER).getMasters();
.addNode(master1, slot1) RedisClusterMaster m = ms.iterator().next();
.addNode(master2, slot2) RedisURI a = config.useClusterServers().getNatMapper().map(
.addNode(master3, slot3); new RedisURI("redis://" + m.getAddr().getHostString() + ":" + m.getAddr().getPort()));
ClusterProcesses process = clusterRunner.run(); RedisClientConfig cfg = new RedisClientConfig();
cfg.setAddress(a);
Config config = new Config(); RedisClient c = RedisClient.create(cfg);
config.useClusterServers() RedisConnection cc = c.connect();
.setScanInterval(100000) List<ClusterNodeInfo> cn = cc.sync(RedisCommands.CLUSTER_NODES);
.setLoadBalancer(new RandomLoadBalancer()) c.shutdownAsync();
.addNodeAddress(process.getNodes().stream().findAny().get().getRedisServerAddressAndPort()); cn = cn.stream().filter(i -> i.containsFlag(Flag.MASTER)).collect(Collectors.toList());
RedissonClient redisson = Redisson.create(config); Iterator<ClusterNodeInfo> nodesIter = cn.iterator();
RedisClientConfig cfg = new RedisClientConfig();
cfg.setAddress(process.getNodes().iterator().next().getRedisServerAddressAndPort()); ClusterNodeInfo source = nodesIter.next();
RedisClient c = RedisClient.create(cfg); ClusterNodeInfo destination = nodesIter.next();
RedisConnection cc = c.connect();
List<ClusterNodeInfo> cn = cc.sync(RedisCommands.CLUSTER_NODES); RedisClientConfig sourceCfg = new RedisClientConfig();
c.shutdownAsync(); sourceCfg.setAddress(config.useClusterServers().getNatMapper().map(source.getAddress()));
cn = cn.stream().filter(i -> i.containsFlag(Flag.MASTER)).collect(Collectors.toList()); RedisClient sourceClient = RedisClient.create(sourceCfg);
Iterator<ClusterNodeInfo> nodesIter = cn.iterator(); RedisConnection sourceConnection = sourceClient.connect();
RedisClientConfig destinationCfg = new RedisClientConfig();
ClusterNodeInfo source = nodesIter.next(); destinationCfg.setAddress(config.useClusterServers().getNatMapper().map(destination.getAddress()));
ClusterNodeInfo destination = nodesIter.next(); RedisClient destinationClient = RedisClient.create(destinationCfg);
RedisConnection destinationConnection = destinationClient.connect();
RedisClientConfig sourceCfg = new RedisClientConfig();
sourceCfg.setAddress(source.getAddress()); String key = null;
RedisClient sourceClient = RedisClient.create(sourceCfg); int slot = 0;
RedisConnection sourceConnection = sourceClient.connect(); for (int i = 0; i < 100000; i++) {
key = "" + i;
RedisClientConfig destinationCfg = new RedisClientConfig(); slot = CRC16.crc16(key.getBytes()) % MasterSlaveConnectionManager.MAX_SLOT;
destinationCfg.setAddress(destination.getAddress()); if (source.getSlotRanges().iterator().next().getStartSlot() == slot) {
RedisClient destinationClient = RedisClient.create(destinationCfg); break;
RedisConnection destinationConnection = destinationClient.connect(); }
String key = null;
int slot = 0;
for (int i = 0; i < 100000; i++) {
key = "" + i;
slot = CRC16.crc16(key.getBytes()) % MasterSlaveConnectionManager.MAX_SLOT;
if (source.getSlotRanges().iterator().next().getStartSlot() == slot) {
break;
} }
}
redisson.getBucket(key).set("123");
destinationConnection.sync(RedisCommands.CLUSTER_SETSLOT, source.getSlotRanges().iterator().next().getStartSlot(), "IMPORTING", source.getNodeId()); redisson.getBucket(key).set("123");
sourceConnection.sync(RedisCommands.CLUSTER_SETSLOT, source.getSlotRanges().iterator().next().getStartSlot(), "MIGRATING", destination.getNodeId());
destinationConnection.sync(RedisCommands.CLUSTER_SETSLOT, source.getSlotRanges().iterator().next().getStartSlot(), "IMPORTING", source.getNodeId());
List<String> keys = sourceConnection.sync(RedisCommands.CLUSTER_GETKEYSINSLOT, source.getSlotRanges().iterator().next().getStartSlot(), 100); sourceConnection.sync(RedisCommands.CLUSTER_SETSLOT, source.getSlotRanges().iterator().next().getStartSlot(), "MIGRATING", destination.getNodeId());
List<Object> params = new ArrayList<Object>();
params.add(destination.getAddress().getHost()); List<String> keys = sourceConnection.sync(RedisCommands.CLUSTER_GETKEYSINSLOT, source.getSlotRanges().iterator().next().getStartSlot(), 100);
params.add(destination.getAddress().getPort()); List<Object> params = new ArrayList<Object>();
params.add(""); params.add(destination.getAddress().getHost());
params.add(0); params.add(destination.getAddress().getPort());
params.add(2000); params.add("");
params.add("KEYS"); params.add(0);
params.addAll(keys); params.add(2000);
sourceConnection.async(RedisCommands.MIGRATE, params.toArray()); params.add("KEYS");
params.addAll(keys);
for (ClusterNodeInfo node : cn) { sourceConnection.async(RedisCommands.MIGRATE, params.toArray());
RedisClientConfig cc1 = new RedisClientConfig();
cc1.setAddress(node.getAddress()); for (ClusterNodeInfo node : cn) {
RedisClient ccc = RedisClient.create(cc1); RedisClientConfig cc1 = new RedisClientConfig();
RedisConnection connection = ccc.connect(); cc1.setAddress(config.useClusterServers().getNatMapper().map(node.getAddress()));
connection.sync(RedisCommands.CLUSTER_SETSLOT, slot, "NODE", destination.getNodeId()); RedisClient ccc = RedisClient.create(cc1);
ccc.shutdownAsync(); RedisConnection connection = ccc.connect();
} connection.sync(RedisCommands.CLUSTER_SETSLOT, slot, "NODE", destination.getNodeId());
ccc.shutdownAsync();
redisson.getBucket(key).set("123"); }
redisson.getBucket(key).get();
sourceClient.shutdown(); redisson.getBucket(key).set("123");
destinationClient.shutdown(); redisson.getBucket(key).get();
redisson.shutdown();
process.shutdown(); sourceClient.shutdown();
destinationClient.shutdown();
redisson.shutdown();
});
} }
@ -995,31 +885,13 @@ public class RedissonTest extends BaseTest {
@Test @Test
public void testManyConnections() { public void testManyConnections() {
Assumptions.assumeFalse(RedissonRuntimeEnvironment.isTravis); Config redisConfig = createConfig();
Config redisConfig = new Config();
redisConfig.useSingleServer() redisConfig.useSingleServer()
.setConnectionMinimumIdleSize(5000) .setConnectionMinimumIdleSize(5000)
.setConnectionPoolSize(5000) .setConnectionPoolSize(5000);
.setAddress(RedisRunner.getDefaultRedisServerBindAddressAndPort());
RedissonClient r = Redisson.create(redisConfig); RedissonClient r = Redisson.create(redisConfig);
r.shutdown(); r.shutdown();
} }
private RedisProcess redisTestSmallMemory() throws IOException, InterruptedException {
return new RedisRunner()
.maxmemory("1mb")
.nosave()
.randomDir()
.randomPort()
.run();
}
private RedisProcess redisTestConnection() throws IOException, InterruptedException {
return new RedisRunner()
.nosave()
.randomDir()
.randomPort()
.run();
}
} }

@ -404,6 +404,8 @@ public class RedissonTopicPatternTest extends RedisDockerTest {
.addNode(master3, slave3); .addNode(master3, slave3);
ClusterRunner.ClusterProcesses process = clusterRunner.run(); ClusterRunner.ClusterProcesses process = clusterRunner.run();
Thread.sleep(7000);
Config config = new Config(); Config config = new Config();
config.useClusterServers() config.useClusterServers()
.setSubscriptionMode(subscriptionMode) .setSubscriptionMode(subscriptionMode)

@ -3,25 +3,23 @@ package org.redisson;
import com.github.dockerjava.api.command.InspectContainerResponse; import com.github.dockerjava.api.command.InspectContainerResponse;
import com.github.dockerjava.api.model.ContainerNetwork; import com.github.dockerjava.api.model.ContainerNetwork;
import com.github.dockerjava.api.model.ExposedPort; import com.github.dockerjava.api.model.ExposedPort;
import com.github.dockerjava.api.model.PortBinding;
import com.github.dockerjava.api.model.Ports; import com.github.dockerjava.api.model.Ports;
import org.awaitility.Awaitility; import org.awaitility.Awaitility;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.redisson.ClusterRunner.ClusterProcesses;
import org.redisson.RedisRunner.KEYSPACE_EVENTS_OPTIONS;
import org.redisson.RedisRunner.RedisProcess;
import org.redisson.api.*; import org.redisson.api.*;
import org.redisson.api.listener.*; import org.redisson.api.listener.*;
import org.redisson.api.redisnode.RedisCluster; import org.redisson.api.redisnode.RedisCluster;
import org.redisson.api.redisnode.RedisClusterMaster; import org.redisson.api.redisnode.RedisClusterMaster;
import org.redisson.api.redisnode.RedisClusterSlave; import org.redisson.api.redisnode.RedisClusterSlave;
import org.redisson.api.redisnode.RedisNodes; import org.redisson.api.redisnode.RedisNodes;
import org.redisson.client.*; import org.redisson.client.RedisClient;
import org.redisson.client.RedisClientConfig;
import org.redisson.client.RedisConnection;
import org.redisson.client.RedisException;
import org.redisson.client.codec.LongCodec; import org.redisson.client.codec.LongCodec;
import org.redisson.client.codec.StringCodec; import org.redisson.client.codec.StringCodec;
import org.redisson.client.protocol.RedisCommands; import org.redisson.client.protocol.RedisCommands;
import org.redisson.client.protocol.RedisStrictCommand;
import org.redisson.cluster.ClusterNodeInfo; import org.redisson.cluster.ClusterNodeInfo;
import org.redisson.config.Config; import org.redisson.config.Config;
import org.redisson.config.SubscriptionMode; import org.redisson.config.SubscriptionMode;
@ -30,11 +28,10 @@ import org.redisson.misc.RedisURI;
import org.testcontainers.containers.ContainerState; import org.testcontainers.containers.ContainerState;
import org.testcontainers.containers.DockerComposeContainer; import org.testcontainers.containers.DockerComposeContainer;
import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.startupcheck.MinimumDurationRunningStartupCheckStrategy; import org.testcontainers.containers.startupcheck.MinimumDurationRunningStartupCheckStrategy;
import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.time.Duration; import java.time.Duration;
import java.util.*; import java.util.*;
@ -43,7 +40,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -87,7 +83,7 @@ public class RedissonTopicTest extends RedisDockerTest {
} }
@Test @Test
public void testCluster() throws IOException, InterruptedException { public void testCluster() throws InterruptedException {
GenericContainer redisCluster = new GenericContainer<>("vishnunair/docker-redis-cluster") GenericContainer redisCluster = new GenericContainer<>("vishnunair/docker-redis-cluster")
.withExposedPorts(6379, 6380, 6381, 6382, 6383, 6384) .withExposedPorts(6379, 6380, 6381, 6382, 6383, 6384)
.withStartupCheckStrategy(new MinimumDurationRunningStartupCheckStrategy(Duration.ofSeconds(10))); .withStartupCheckStrategy(new MinimumDurationRunningStartupCheckStrategy(Duration.ofSeconds(10)));
@ -275,7 +271,7 @@ public class RedissonTopicTest extends RedisDockerTest {
} }
@Test @Test
public void testTopicState() throws InterruptedException { public void testTopicState() {
RTopic stringTopic = redisson.getTopic("test1", StringCodec.INSTANCE); RTopic stringTopic = redisson.getTopic("test1", StringCodec.INSTANCE);
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
AtomicInteger stringMessageReceived = new AtomicInteger(); AtomicInteger stringMessageReceived = new AtomicInteger();
@ -305,7 +301,7 @@ public class RedissonTopicTest extends RedisDockerTest {
} }
@Test @Test
public void testMultiTypeConnection() throws InterruptedException { public void testMultiTypeConnection() {
RTopic stringTopic = redisson.getTopic("test1", StringCodec.INSTANCE); RTopic stringTopic = redisson.getTopic("test1", StringCodec.INSTANCE);
AtomicBoolean stringMessageReceived = new AtomicBoolean(); AtomicBoolean stringMessageReceived = new AtomicBoolean();
stringTopic.addListener(String.class, new MessageListener<String>() { stringTopic.addListener(String.class, new MessageListener<String>() {
@ -585,7 +581,7 @@ public class RedissonTopicTest extends RedisDockerTest {
} }
@Test @Test
public void testRemoveAllListeners2() throws InterruptedException { public void testRemoveAllListeners2() {
RTopic topic1 = redisson.getTopic("topic1"); RTopic topic1 = redisson.getTopic("topic1");
AtomicInteger counter = new AtomicInteger(); AtomicInteger counter = new AtomicInteger();
@ -605,7 +601,7 @@ public class RedissonTopicTest extends RedisDockerTest {
} }
@Test @Test
public void testRemoveByInstance() throws InterruptedException { public void testRemoveByInstance() {
RTopic topic1 = redisson.getTopic("topic1"); RTopic topic1 = redisson.getTopic("topic1");
MessageListener listener = new MessageListener() { MessageListener listener = new MessageListener() {
@Override @Override
@ -724,9 +720,7 @@ public class RedissonTopicTest extends RedisDockerTest {
@Test @Test
public void testReattach() throws Exception { public void testReattach() throws Exception {
GenericContainer<?> redis = GenericContainer<?> redis = createRedis();
new GenericContainer<>("redis:7.2")
.withExposedPorts(6379);
redis.start(); redis.start();
Config config = new Config(); Config config = new Config();
@ -774,9 +768,7 @@ public class RedissonTopicTest extends RedisDockerTest {
@Test @Test
public void testAddListenerFailover() throws Exception { public void testAddListenerFailover() throws Exception {
GenericContainer<?> redis = GenericContainer<?> redis = createRedis();
new GenericContainer<>("redis:7.2")
.withExposedPorts(6379);
redis.start(); redis.start();
Config config = new Config(); Config config = new Config();
@ -908,148 +900,17 @@ public class RedissonTopicTest extends RedisDockerTest {
assertThat(status.peekLast()).isEqualTo("ok"); assertThat(status.peekLast()).isEqualTo("ok");
executor1.shutdown(); executor1.shutdown();
try {
assertThat(executor1.awaitTermination(20, TimeUnit.SECONDS)).isTrue();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
redissonClient.shutdown(); redissonClient.shutdown();
}, 1); }, 1);
} }
private void withSentinel(BiConsumer<List<GenericContainer<?>>, Config> callback, int slaves) throws InterruptedException {
Network network = Network.newNetwork();
List<GenericContainer<? extends GenericContainer<?>>> nodes = new ArrayList<>();
GenericContainer<?> master =
new GenericContainer<>("bitnami/redis:7.2.4")
.withNetwork(network)
.withEnv("REDIS_REPLICATION_MODE", "master")
.withEnv("ALLOW_EMPTY_PASSWORD", "yes")
.withNetworkAliases("redis")
.withExposedPorts(6379);
master.start();
assert master.getNetwork() == network;
int masterPort = master.getFirstMappedPort();
master.withCreateContainerCmdModifier(cmd -> {
cmd.getHostConfig().withPortBindings(
new PortBinding(Ports.Binding.bindPort(Integer.valueOf(masterPort)),
cmd.getExposedPorts()[0]));
});
nodes.add(master);
for (int i = 0; i < slaves; i++) {
GenericContainer<?> slave =
new GenericContainer<>("bitnami/redis:7.2.4")
.withNetwork(network)
.withEnv("REDIS_REPLICATION_MODE", "slave")
.withEnv("REDIS_MASTER_HOST", "redis")
.withEnv("ALLOW_EMPTY_PASSWORD", "yes")
.withNetworkAliases("slave" + i)
.withExposedPorts(6379);
slave.start();
int slavePort = slave.getFirstMappedPort();
slave.withCreateContainerCmdModifier(cmd -> {
cmd.getHostConfig().withPortBindings(
new PortBinding(Ports.Binding.bindPort(Integer.valueOf(slavePort)),
cmd.getExposedPorts()[0]));
});
nodes.add(slave);
}
GenericContainer<?> sentinel1 =
new GenericContainer<>("bitnami/redis-sentinel:7.2.4")
.withNetwork(network)
.withEnv("REDIS_SENTINEL_DOWN_AFTER_MILLISECONDS", "5000")
.withEnv("REDIS_SENTINEL_FAILOVER_TIMEOUT", "10000")
.withNetworkAliases("sentinel1")
.withExposedPorts(26379);
sentinel1.start();
int sentinel1Port = sentinel1.getFirstMappedPort();
sentinel1.withCreateContainerCmdModifier(cmd -> {
cmd.getHostConfig().withPortBindings(
new PortBinding(Ports.Binding.bindPort(Integer.valueOf(sentinel1Port)),
cmd.getExposedPorts()[0]));
});
nodes.add(sentinel1);
GenericContainer<?> sentinel2 =
new GenericContainer<>("bitnami/redis-sentinel:7.2.4")
.withNetwork(network)
.withEnv("REDIS_SENTINEL_DOWN_AFTER_MILLISECONDS", "5000")
.withEnv("REDIS_SENTINEL_FAILOVER_TIMEOUT", "10000")
.withNetworkAliases("sentinel2")
.withExposedPorts(26379);
sentinel2.start();
int sentinel2Port = sentinel2.getFirstMappedPort();
sentinel2.withCreateContainerCmdModifier(cmd -> {
cmd.getHostConfig().withPortBindings(
new PortBinding(Ports.Binding.bindPort(Integer.valueOf(sentinel2Port)),
cmd.getExposedPorts()[0]));
});
nodes.add(sentinel2);
GenericContainer<?> sentinel3 =
new GenericContainer<>("bitnami/redis-sentinel:7.2.4")
.withNetwork(network)
.withEnv("REDIS_SENTINEL_DOWN_AFTER_MILLISECONDS", "5000")
.withEnv("REDIS_SENTINEL_FAILOVER_TIMEOUT", "10000")
.withNetworkAliases("sentinel3")
.withExposedPorts(26379);
sentinel3.start();
int sentinel3Port = sentinel3.getFirstMappedPort();
sentinel3.withCreateContainerCmdModifier(cmd -> {
cmd.getHostConfig().withPortBindings(
new PortBinding(Ports.Binding.bindPort(Integer.valueOf(sentinel3Port)),
cmd.getExposedPorts()[0]));
});
nodes.add(sentinel3);
Thread.sleep(5000);
Config config = new Config();
config.setProtocol(protocol);
config.useSentinelServers()
.setNatMapper(new NatMapper() {
@Override
public RedisURI map(RedisURI uri) {
for (GenericContainer<? extends GenericContainer<?>> node : nodes) {
if (node.getContainerInfo() == null) {
continue;
}
Ports.Binding[] mappedPort = node.getContainerInfo().getNetworkSettings()
.getPorts().getBindings().get(new ExposedPort(uri.getPort()));
Map<String, ContainerNetwork> ss = node.getContainerInfo().getNetworkSettings().getNetworks();
ContainerNetwork s = ss.values().iterator().next();
if (uri.getPort() == 6379 && node.getNetworkAliases().contains("slave0")) {
return new RedisURI(uri.getScheme(), "127.0.0.1", Integer.valueOf(mappedPort[0].getHostPortSpec()));
}
if ("redis".equals(uri.getHost())
&& node.getNetworkAliases().contains(uri.getHost())) {
return new RedisURI(uri.getScheme(), "127.0.0.1", Integer.valueOf(mappedPort[0].getHostPortSpec()));
}
if (mappedPort != null
&& s.getIpAddress().equals(uri.getHost())) {
return new RedisURI(uri.getScheme(), "127.0.0.1", Integer.valueOf(mappedPort[0].getHostPortSpec()));
}
}
return uri;
}
})
.addSentinelAddress("redis://127.0.0.1:" + sentinel1.getFirstMappedPort())
.setMasterName("mymaster");
callback.accept(nodes, config);
nodes.forEach(n -> n.stop());
network.close();
}
@Test @Test
public void testReattachInSentinel() throws Exception { public void testReattachInSentinel() throws Exception {
withSentinel((nodes, config) -> { withSentinel((nodes, config) -> {
@ -1143,25 +1004,25 @@ public class RedissonTopicTest extends RedisDockerTest {
.sentinel() .sentinel()
.sentinelMonitor("myMaster", "127.0.0.1", 6440, 2) .sentinelMonitor("myMaster", "127.0.0.1", 6440, 2)
.run(); .run();
Thread.sleep(5000); Thread.sleep(5000);
Config config = new Config(); Config config = new Config();
config.useSentinelServers() config.useSentinelServers()
.setLoadBalancer(new RandomLoadBalancer()) .setLoadBalancer(new RandomLoadBalancer())
.addSentinelAddress(sentinel3.getRedisServerAddressAndPort()).setMasterName("myMaster"); .addSentinelAddress(sentinel3.getRedisServerAddressAndPort()).setMasterName("myMaster");
RedissonClient redisson = Redisson.create(config); RedissonClient redisson = Redisson.create(config);
final AtomicBoolean executed = new AtomicBoolean(); final AtomicBoolean executed = new AtomicBoolean();
final AtomicInteger subscriptions = new AtomicInteger(); final AtomicInteger subscriptions = new AtomicInteger();
RTopic topic = redisson.getTopic("topic"); RTopic topic = redisson.getTopic("topic");
topic.addListener(new StatusListener() { topic.addListener(new StatusListener() {
@Override @Override
public void onUnsubscribe(String channel) { public void onUnsubscribe(String channel) {
} }
@Override @Override
public void onSubscribe(String channel) { public void onSubscribe(String channel) {
subscriptions.incrementAndGet(); subscriptions.incrementAndGet();
@ -1173,26 +1034,26 @@ public class RedissonTopicTest extends RedisDockerTest {
executed.set(true); executed.set(true);
} }
}); });
sendCommands(redisson, "topic"); sendCommands(redisson, "topic");
sentinel1.stop(); sentinel1.stop();
sentinel2.stop(); sentinel2.stop();
sentinel3.stop(); sentinel3.stop();
master.stop(); master.stop();
slave1.stop(); slave1.stop();
slave2.stop(); slave2.stop();
Thread.sleep(TimeUnit.SECONDS.toMillis(20)); Thread.sleep(TimeUnit.SECONDS.toMillis(20));
topic.removeAllListeners(); topic.removeAllListeners();
long t = System.currentTimeMillis(); long t = System.currentTimeMillis();
topic.addListenerAsync(new StatusListener() { topic.addListenerAsync(new StatusListener() {
@Override @Override
public void onUnsubscribe(String channel) { public void onUnsubscribe(String channel) {
} }
@Override @Override
public void onSubscribe(String channel) { public void onSubscribe(String channel) {
subscriptions.incrementAndGet(); subscriptions.incrementAndGet();
@ -1204,9 +1065,9 @@ public class RedissonTopicTest extends RedisDockerTest {
executed.set(true); executed.set(true);
} }
}); });
Thread.sleep(TimeUnit.SECONDS.toMillis(5)); Thread.sleep(TimeUnit.SECONDS.toMillis(5));
master = new RedisRunner() master = new RedisRunner()
.port(6390) .port(6390)
.nosave() .nosave()
@ -1245,12 +1106,12 @@ public class RedissonTopicTest extends RedisDockerTest {
.sentinel() .sentinel()
.sentinelMonitor("myMaster", "127.0.0.1", 6390, 2) .sentinelMonitor("myMaster", "127.0.0.1", 6390, 2)
.run(); .run();
redisson.getTopic("topic").publish(1); redisson.getTopic("topic").publish(1);
await().atMost(20, TimeUnit.SECONDS).until(() -> subscriptions.get() == 2); await().atMost(20, TimeUnit.SECONDS).until(() -> subscriptions.get() == 2);
assertThat(executed.get()).isTrue(); assertThat(executed.get()).isTrue();
redisson.shutdown(); redisson.shutdown();
sentinel1.stop(); sentinel1.stop();
sentinel2.stop(); sentinel2.stop();
@ -1259,13 +1120,13 @@ public class RedissonTopicTest extends RedisDockerTest {
slave1.stop(); slave1.stop();
slave2.stop(); slave2.stop();
} }
protected Thread sendCommands(RedissonClient redisson, String topicName) { protected Thread sendCommands(RedissonClient redisson, String topicName) {
Thread t = new Thread() { Thread t = new Thread() {
@Override @Override
public void run() { public void run() {
List<RFuture<?>> futures = new ArrayList<RFuture<?>>(); List<RFuture<?>> futures = new ArrayList<RFuture<?>>();
for (int i = 0; i < 100; i++) { for (int i = 0; i < 100; i++) {
RFuture<?> f1 = redisson.getBucket("i" + i).getAsync(); RFuture<?> f1 = redisson.getBucket("i" + i).getAsync();
RFuture<?> f2 = redisson.getBucket("i" + i).setAsync(""); RFuture<?> f2 = redisson.getBucket("i" + i).setAsync("");
@ -1274,7 +1135,7 @@ public class RedissonTopicTest extends RedisDockerTest {
futures.add(f2); futures.add(f2);
futures.add(f3); futures.add(f3);
} }
for (RFuture<?> rFuture : futures) { for (RFuture<?> rFuture : futures) {
try { try {
rFuture.toCompletableFuture().join(); rFuture.toCompletableFuture().join();
@ -1289,7 +1150,7 @@ public class RedissonTopicTest extends RedisDockerTest {
} }
@Test @Test
public void testClusterSharding() throws IOException, InterruptedException { public void testClusterSharding() {
testInCluster(redisson -> { testInCluster(redisson -> {
AtomicInteger counter = new AtomicInteger(); AtomicInteger counter = new AtomicInteger();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
@ -1454,6 +1315,11 @@ public class RedissonTopicTest extends RedisDockerTest {
assertThat(status.peekLast()).isEqualTo("ok"); assertThat(status.peekLast()).isEqualTo("ok");
executor1.shutdown(); executor1.shutdown();
try {
assertThat(executor1.awaitTermination(20, TimeUnit.SECONDS)).isTrue();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
redissonClient.shutdown(); redissonClient.shutdown();
@ -1461,62 +1327,55 @@ public class RedissonTopicTest extends RedisDockerTest {
} }
@Test @Test
public void testReattachInClusterMaster2() throws Exception { public void testReattachInClusterMaster2() {
RedisRunner master1 = new RedisRunner().port(6890).randomDir().nosave(); withClusterFailover(redisson -> {
RedisRunner master2 = new RedisRunner().port(6891).randomDir().nosave();
RedisRunner master3 = new RedisRunner().port(6892).randomDir().nosave();
RedisRunner slave1 = new RedisRunner().port(6900).randomDir().nosave();
RedisRunner slave2 = new RedisRunner().port(6901).randomDir().nosave();
RedisRunner slave3 = new RedisRunner().port(6902).randomDir().nosave();
ClusterRunner clusterRunner = new ClusterRunner()
.addNode(master1, slave1)
.addNode(master2, slave2)
.addNode(master3, slave3);
ClusterProcesses process = clusterRunner.run();
Thread.sleep(7000);
Config config = new Config();
config.useClusterServers()
.addNodeAddress(process.getNodes().stream().findAny().get().getRedisServerAddressAndPort());
RedissonClient redisson = Redisson.create(config);
Queue<String> messages = new ConcurrentLinkedQueue<>(); Queue<String> messages = new ConcurrentLinkedQueue<>();
Queue<String> subscriptions = new ConcurrentLinkedQueue<>(); Queue<String> subscriptions = new ConcurrentLinkedQueue<>();
int topicsAmount = 100;
for (int i = 0; i < topicsAmount; i++) {
RTopic topic = redisson.getTopic("topic" + i);
int finalI = i;
topic.addListener(new StatusListener() {
@Override int topicsAmount = 100;
public void onUnsubscribe(String channel) { for (int i = 0; i < topicsAmount; i++) {
} RTopic topic = redisson.getTopic("topic" + i);
int finalI = i;
topic.addListener(new StatusListener() {
@Override @Override
public void onSubscribe(String channel) { public void onUnsubscribe(String channel) {
subscriptions.add("topic" + finalI); }
}
});
topic.addListener(String.class, (channel, msg) -> messages.add(msg));
}
RedisRunner.RedisProcess master = process.getNodes().stream().filter(x -> x.getRedisServerPort() == master1.getPort()).findFirst().get(); @Override
master.stop(); public void onSubscribe(String channel) {
subscriptions.add("topic" + finalI);
}
});
topic.addListener(String.class, (channel, msg) -> messages.add(msg));
}
Thread.sleep(TimeUnit.SECONDS.toMillis(40)); RedisCluster rnc = redisson.getRedisNodes(RedisNodes.CLUSTER);
Optional<RedisClusterMaster> f = rnc.getMasters().stream().findFirst();
RedisClusterMaster master = f.get();
RedisClientConfig cc = new RedisClientConfig();
cc.setAddress("redis://" + master.getAddr().getHostString() + ":" + master.getAddr().getPort());
RedisClient c = RedisClient.create(cc);
c.connect().async(RedisCommands.SHUTDOWN);
c.shutdown();
assertThat(subscriptions).hasSize(140); Awaitility.waitAtMost(Duration.ofSeconds(40)).untilAsserted(() -> {
assertThat(subscriptions).hasSizeGreaterThan(125);
});
for (int i = 0; i < topicsAmount; i++) { for (int i = 0; i < topicsAmount; i++) {
RTopic topic = redisson.getTopic("topic" + i); RTopic topic = redisson.getTopic("topic" + i);
topic.publish("topic" + i); topic.publish("topic" + i);
} }
Thread.sleep(100); try {
assertThat(messages).hasSize(topicsAmount); Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
assertThat(messages).hasSize(topicsAmount);
});
} }
public void withCluster(Consumer<RedissonClient> callback) { public void withCluster(Consumer<RedissonClient> callback) {
@ -1545,9 +1404,11 @@ public class RedissonTopicTest extends RedisDockerTest {
redisCluster.stop(); redisCluster.stop();
} }
public void withCluster2(Consumer<RedissonClient> callback) { protected void withClusterFailover(Consumer<RedissonClient> callback) {
List<InspectContainerResponse> nodes = new ArrayList<>(); List<InspectContainerResponse> nodes = new ArrayList<>();
LogMessageWaitStrategy wait2 = new LogMessageWaitStrategy().withRegEx(".*REPLICA\ssync\\:\sFinished\swith\ssuccess.*");
DockerComposeContainer environment = DockerComposeContainer environment =
new DockerComposeContainer(new File("src/test/resources/docker-compose.yml")) new DockerComposeContainer(new File("src/test/resources/docker-compose.yml"))
.withExposedService("redis-node-0", 6379) .withExposedService("redis-node-0", 6379)
@ -1555,19 +1416,17 @@ public class RedissonTopicTest extends RedisDockerTest {
.withExposedService("redis-node-2", 6379) .withExposedService("redis-node-2", 6379)
.withExposedService("redis-node-3", 6379) .withExposedService("redis-node-3", 6379)
.withExposedService("redis-node-4", 6379) .withExposedService("redis-node-4", 6379)
.withExposedService("redis-node-5", 6379) .withExposedService("redis-node-5", 6379, wait2);
.withExposedService("redis-node-6", 6379)
.withExposedService("redis-node-7", 6379);
environment.start(); environment.start();
try { try {
Thread.sleep(25000); Thread.sleep(5000);
} catch (InterruptedException e) { } catch (InterruptedException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
for (int i = 0; i < 8; i++) { for (int i = 0; i < 6; i++) {
Optional<ContainerState> cc = environment.getContainerByServiceName("redis-node-" + i); Optional<ContainerState> cc = environment.getContainerByServiceName("redis-node-" + i);
nodes.add(cc.get().getContainerInfo()); nodes.add(cc.get().getContainerInfo());
} }
@ -1599,162 +1458,139 @@ public class RedissonTopicTest extends RedisDockerTest {
RedissonClient redisson = Redisson.create(config); RedissonClient redisson = Redisson.create(config);
RedisCluster nodes2 = redisson.getRedisNodes(RedisNodes.CLUSTER);
for (RedisClusterSlave slave : nodes2.getSlaves()) {
slave.setConfig("cluster-node-timeout", "1000");
}
for (RedisClusterMaster master : nodes2.getMasters()) {
master.setConfig("cluster-node-timeout", "1000");
}
callback.accept(redisson); callback.accept(redisson);
redisson.shutdown(); redisson.shutdown();
environment.stop(); environment.stop();
} }
@Test @Test
public void testReattachInClusterMaster() throws Exception { public void testReattachInClusterMaster() {
RedisRunner master1 = new RedisRunner().randomPort().randomDir().nosave(); withClusterFailover(redissonClient -> {
RedisRunner master2 = new RedisRunner().randomPort().randomDir().nosave(); Config cfg = redissonClient.getConfig();
RedisRunner master3 = new RedisRunner().randomPort().randomDir().nosave(); cfg.useClusterServers().setSubscriptionMode(SubscriptionMode.MASTER);
RedisRunner slave1 = new RedisRunner().randomPort().randomDir().nosave();
RedisRunner slave2 = new RedisRunner().randomPort().randomDir().nosave();
RedisRunner slave3 = new RedisRunner().randomPort().randomDir().nosave();
ClusterRunner clusterRunner = new ClusterRunner() RedissonClient redisson = Redisson.create(cfg);
.addNode(master1, slave1) final AtomicBoolean executed = new AtomicBoolean();
.addNode(master2, slave2) final AtomicInteger subscriptions = new AtomicInteger();
.addNode(master3, slave3);
ClusterProcesses process = clusterRunner.run();
Thread.sleep(5000); RTopic topic = redisson.getTopic("3");
topic.addListener(new StatusListener() {
Config config = new Config(); @Override
config.useClusterServers() public void onUnsubscribe(String channel) {
.setSubscriptionMode(SubscriptionMode.MASTER) }
.setLoadBalancer(new RandomLoadBalancer())
.addNodeAddress(process.getNodes().stream().findAny().get().getRedisServerAddressAndPort());
RedissonClient redisson = Redisson.create(config);
final AtomicBoolean executed = new AtomicBoolean(); @Override
final AtomicInteger subscriptions = new AtomicInteger(); public void onSubscribe(String channel) {
subscriptions.incrementAndGet();
}
});
topic.addListener(Integer.class, new MessageListener<Integer>() {
@Override
public void onMessage(CharSequence channel, Integer msg) {
executed.set(true);
}
});
RTopic topic = redisson.getTopic("3"); sendCommands(redisson, "3");
topic.addListener(new StatusListener() {
@Override RedisCluster rnc = redisson.getRedisNodes(RedisNodes.CLUSTER);
public void onUnsubscribe(String channel) { for (RedisClusterMaster master : rnc.getMasters()) {
RedisClientConfig cc = new RedisClientConfig();
cc.setAddress("redis://" + master.getAddr().getHostString() + ":" + master.getAddr().getPort());
RedisClient c = RedisClient.create(cc);
RedisConnection cn = c.connect();
List<String> channels = cn.sync(RedisCommands.PUBSUB_CHANNELS);
if (channels.contains("3")) {
cn.async(RedisCommands.SHUTDOWN);
}
c.shutdown();
} }
@Override try {
public void onSubscribe(String channel) { Thread.sleep(25000);
subscriptions.incrementAndGet(); } catch (InterruptedException e) {
} throw new RuntimeException(e);
});
topic.addListener(Integer.class, new MessageListener<Integer>() {
@Override
public void onMessage(CharSequence channel, Integer msg) {
executed.set(true);
} }
});
sendCommands(redisson, "3"); redisson.getTopic("3").publish(1);
process.getNodes().stream().filter(x -> master1.getPort() == x.getRedisServerPort()) await().atMost(75, TimeUnit.SECONDS).until(() -> subscriptions.get() == 2);
.forEach(x -> { assertThat(executed.get()).isTrue();
try {
x.stop();
Thread.sleep(18000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
Thread.sleep(25000);
redisson.getTopic("3").publish(1);
await().atMost(75, TimeUnit.SECONDS).until(() -> subscriptions.get() == 2);
assertThat(executed.get()).isTrue();
redisson.shutdown(); redisson.shutdown();
process.shutdown(); });
} }
@Test @Test
public void testReattachPatternTopicListenersOnClusterFailover() throws Exception { public void testReattachPatternTopicListenersOnClusterFailover() {
final KEYSPACE_EVENTS_OPTIONS keyspaceEvents[] = withClusterFailover(redisson -> {
{KEYSPACE_EVENTS_OPTIONS.K, KEYSPACE_EVENTS_OPTIONS.E, KEYSPACE_EVENTS_OPTIONS.A}; RedisCluster nodes = redisson.getRedisNodes(RedisNodes.CLUSTER);
final RedisRunner master = new RedisRunner().randomPort().randomDir().nosave() for (RedisClusterMaster master : nodes.getMasters()) {
.notifyKeyspaceEvents(keyspaceEvents); master.setConfig("notify-keyspace-events", "K$");
final RedisRunner slave = new RedisRunner().randomPort().randomDir().nosave() }
.notifyKeyspaceEvents(keyspaceEvents); for (RedisClusterSlave slave : nodes.getSlaves()) {
slave.setConfig("notify-keyspace-events", "K$");
final ClusterRunner clusterRunner = new ClusterRunner().addNode(master, slave); }
final ClusterProcesses process = clusterRunner.run();
final Config config = new Config();
config.useClusterServers().addNodeAddress(
process.getNodes().stream().findAny().get().getRedisServerAddressAndPort());
final RedissonClient redisson = Redisson.create(config); AtomicInteger subscriptions = new AtomicInteger();
AtomicInteger messagesReceived = new AtomicInteger();
final AtomicInteger subscriptions = new AtomicInteger(); RPatternTopic topic =
final AtomicInteger messagesReceived = new AtomicInteger(); redisson.getPatternTopic("__keyspace*__:i*", StringCodec.INSTANCE);
topic.addListener(new PatternStatusListener() {
@Override
public void onPUnsubscribe(String pattern) {}
final RPatternTopic topic = @Override
redisson.getPatternTopic("__keyspace*__:i*", StringCodec.INSTANCE); public void onPSubscribe(String pattern) {
topic.addListener(new PatternStatusListener() { subscriptions.incrementAndGet();
@Override }
public void onPUnsubscribe(String pattern) {} });
topic.addListener(String.class,
(pattern, channel, msg) -> messagesReceived.incrementAndGet());
assertThat(subscriptions.get()).isEqualTo(1);
@Override try {
public void onPSubscribe(String pattern) { sendCommands(redisson, "dummy").join();
subscriptions.incrementAndGet(); } catch (InterruptedException e) {
throw new RuntimeException(e);
} }
}); await().atMost(30, TimeUnit.SECONDS).until(() -> {
topic.addListener(String.class, return messagesReceived.get() == 100;
(pattern, channel, msg) -> messagesReceived.incrementAndGet()); });
assertThat(subscriptions.get()).isEqualTo(1);
sendCommands(redisson, "dummy").join();
await().atMost(30, TimeUnit.SECONDS).until(() -> messagesReceived.get() == 100);
failover(process, master, slave);
redisson.getBucket("i100").set(""); RedisCluster rnc = redisson.getRedisNodes(RedisNodes.CLUSTER);
await().atMost(30, TimeUnit.SECONDS).until(() -> subscriptions.get() == 2); for (RedisClusterMaster master : rnc.getMasters()) {
await().atMost(5, TimeUnit.SECONDS).until(() -> messagesReceived.get() == 101); RedisClientConfig cc = new RedisClientConfig();
cc.setAddress("redis://" + master.getAddr().getHostString() + ":" + master.getAddr().getPort());
RedisClient c = RedisClient.create(cc);
RedisConnection cn = c.connect();
try {
Boolean res = cn.sync(RedisCommands.EXISTS, "i99");
if (res) {
cn.async(RedisCommands.SHUTDOWN);
}
} catch (Exception e) {
// skip
}
c.shutdown();
}
redisson.shutdown(); await().atMost(30, TimeUnit.SECONDS).until(() -> {
process.shutdown(); return subscriptions.get() == 2;
} });
private void failover(ClusterProcesses processes, RedisRunner master, RedisRunner slave) for (RedisClusterMaster master : nodes.getMasters()) {
throws InterruptedException { master.setConfig("notify-keyspace-events", "K$");
final RedisClient masterClient = connect(processes, master); }
try {
masterClient.connect().sync(new RedisStrictCommand<Void>("DEBUG", "SEGFAULT"));
} catch (RedisTimeoutException e) {
// node goes down, so this command times out waiting for the response
}
Thread.sleep(java.time.Duration.ofSeconds(25).toMillis());
final RedisClient slaveClient = connect(processes, slave); redisson.getBucket("i99").set("");
slaveClient.connect().sync(new RedisStrictCommand<Void>("CLUSTER", "FAILOVER"), "TAKEOVER"); await().atMost(1, TimeUnit.SECONDS).until(() -> {
Thread.sleep(java.time.Duration.ofSeconds(25).toMillis()); System.out.println("messagesReceived.get() " + messagesReceived.get());
return messagesReceived.get() == 101;
});
});
} }
private RedisClient connect(ClusterProcesses processes, RedisRunner runner) {
return RedisClient.create(new RedisClientConfig()
.setAddress(processes.getNodes().stream()
.filter(node -> node.getRedisServerPort() == runner.getPort())
.findFirst()
.map(RedisProcess::getRedisServerAddressAndPort)
.orElseThrow(() -> new IllegalArgumentException(
"Failed to find node running at port: " + runner.getPort()
+ " in cluster processes"))));
}
} }

@ -8,6 +8,7 @@ import java.util.Map;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.redisson.RedisDockerTest;
import org.redisson.RedisRunner; import org.redisson.RedisRunner;
import org.redisson.RedisRunner.RedisProcess; import org.redisson.RedisRunner.RedisProcess;
import org.redisson.Redisson; import org.redisson.Redisson;
@ -15,28 +16,33 @@ import org.redisson.api.RedissonClient;
import org.redisson.client.WriteRedisConnectionException; import org.redisson.client.WriteRedisConnectionException;
import org.redisson.config.Config; import org.redisson.config.Config;
import org.redisson.config.ReadMode; import org.redisson.config.ReadMode;
import org.testcontainers.containers.GenericContainer;
public class WeightedRoundRobinBalancerTest { public class WeightedRoundRobinBalancerTest extends RedisDockerTest {
@Test @Test
public void testUseMasterForReadsIfNoConnectionsToSlaves() { public void testUseMasterForReadsIfNoConnectionsToSlaves() {
Assertions.assertThrows(WriteRedisConnectionException.class, () -> { GenericContainer<?> master = null;
RedisProcess master = null; GenericContainer<?> slave = null;
RedisProcess slave = null;
RedissonClient client = null; RedissonClient client = null;
try { try {
master = redisTestInstance(); master = createRedis();
slave = redisTestInstance(); master.start();
slave = createRedis();
slave.start();
String masterurl = "redis://" + master.getHost() + ":" + master.getFirstMappedPort();
String slaveurl = "redis://" + slave.getHost() + ":" + slave.getFirstMappedPort();
Map<String, Integer> weights = new HashMap<>(); Map<String, Integer> weights = new HashMap<>();
weights.put(master.getRedisServerAddressAndPort(), 1); weights.put(masterurl, 1);
weights.put(slave.getRedisServerAddressAndPort(), 2); weights.put(slaveurl, 2);
Config config = new Config(); Config config = new Config();
config.useMasterSlaveServers() config.useMasterSlaveServers()
.setReadMode(ReadMode.SLAVE) .setReadMode(ReadMode.SLAVE)
.setMasterAddress(master.getRedisServerAddressAndPort()) .setMasterAddress(masterurl)
.addSlaveAddress(slave.getRedisServerAddressAndPort()) .addSlaveAddress(slaveurl)
.setLoadBalancer(new WeightedRoundRobinBalancer(weights, 1)); .setLoadBalancer(new WeightedRoundRobinBalancer(weights, 1));
client = Redisson.create(config); client = Redisson.create(config);
@ -47,7 +53,9 @@ public class WeightedRoundRobinBalancerTest {
slave.stop(); slave.stop();
RedissonClient clientCopy = client; RedissonClient clientCopy = client;
assertThat(clientCopy.getBucket("key").get()).isNull(); Assertions.assertThrows(WriteRedisConnectionException.class, () -> {
clientCopy.getBucket("key").get();
});
} finally { } finally {
if (master != null) { if (master != null) {
master.stop(); master.stop();
@ -59,14 +67,6 @@ public class WeightedRoundRobinBalancerTest {
client.shutdown(); client.shutdown();
} }
} }
});
} }
private RedisProcess redisTestInstance() throws IOException, InterruptedException {
return new RedisRunner()
.nosave()
.randomDir()
.randomPort()
.run();
}
} }

@ -1,33 +1,28 @@
package org.redisson.executor; package org.redisson.executor;
import com.github.dockerjava.api.model.ContainerNetwork;
import com.github.dockerjava.api.model.ExposedPort;
import com.github.dockerjava.api.model.PortBinding;
import com.github.dockerjava.api.model.Ports;
import mockit.Invocation; import mockit.Invocation;
import mockit.Mock; import mockit.Mock;
import mockit.MockUp; import mockit.MockUp;
import org.awaitility.Awaitility; import org.awaitility.Awaitility;
import org.junit.jupiter.api.*; import org.junit.jupiter.api.*;
import org.redisson.*; import org.redisson.RedisDockerTest;
import org.redisson.Redisson;
import org.redisson.RedissonNode;
import org.redisson.api.*; import org.redisson.api.*;
import org.redisson.api.annotation.RInject; import org.redisson.api.annotation.RInject;
import org.redisson.api.executor.TaskFinishedListener; import org.redisson.api.executor.TaskFinishedListener;
import org.redisson.api.executor.TaskStartedListener; import org.redisson.api.executor.TaskStartedListener;
import org.redisson.config.Config; import org.redisson.config.Config;
import org.redisson.config.RedissonNodeConfig; import org.redisson.config.RedissonNodeConfig;
import org.redisson.connection.balancer.RandomLoadBalancer;
import org.redisson.misc.RedisURI;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.time.Duration; import java.time.Duration;
import java.util.*; import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await; import static org.awaitility.Awaitility.await;
@ -37,7 +32,7 @@ public class RedissonExecutorServiceTest extends RedisDockerTest {
private static RedissonNode node; private static RedissonNode node;
@BeforeEach @BeforeEach
public void before() throws IOException, InterruptedException { public void before() {
Config config = createConfig(); Config config = createConfig();
RedissonNodeConfig nodeConfig = new RedissonNodeConfig(config); RedissonNodeConfig nodeConfig = new RedissonNodeConfig(config);
nodeConfig.setExecutorServiceWorkers(Collections.singletonMap("test", 1)); nodeConfig.setExecutorServiceWorkers(Collections.singletonMap("test", 1));
@ -674,140 +669,4 @@ public class RedissonExecutorServiceTest extends RedisDockerTest {
}); });
} }
private void withSentinel(BiConsumer<List<GenericContainer<?>>, Config> callback, int slaves) throws InterruptedException {
Network network = Network.newNetwork();
List<GenericContainer<? extends GenericContainer<?>>> nodes = new ArrayList<>();
GenericContainer<?> master =
new GenericContainer<>("bitnami/redis:7.2.4")
.withNetwork(network)
.withEnv("REDIS_REPLICATION_MODE", "master")
.withEnv("ALLOW_EMPTY_PASSWORD", "yes")
.withNetworkAliases("redis")
.withExposedPorts(6379);
master.start();
assert master.getNetwork() == network;
int masterPort = master.getFirstMappedPort();
master.withCreateContainerCmdModifier(cmd -> {
cmd.getHostConfig().withPortBindings(
new PortBinding(Ports.Binding.bindPort(Integer.valueOf(masterPort)),
cmd.getExposedPorts()[0]));
});
nodes.add(master);
for (int i = 0; i < slaves; i++) {
GenericContainer<?> slave =
new GenericContainer<>("bitnami/redis:7.2.4")
.withNetwork(network)
.withEnv("REDIS_REPLICATION_MODE", "slave")
.withEnv("REDIS_MASTER_HOST", "redis")
.withEnv("ALLOW_EMPTY_PASSWORD", "yes")
.withNetworkAliases("slave" + i)
.withExposedPorts(6379);
slave.start();
int slavePort = slave.getFirstMappedPort();
slave.withCreateContainerCmdModifier(cmd -> {
cmd.getHostConfig().withPortBindings(
new PortBinding(Ports.Binding.bindPort(Integer.valueOf(slavePort)),
cmd.getExposedPorts()[0]));
});
nodes.add(slave);
}
GenericContainer<?> sentinel1 =
new GenericContainer<>("bitnami/redis-sentinel:7.2.4")
.withNetwork(network)
.withEnv("REDIS_SENTINEL_DOWN_AFTER_MILLISECONDS", "5000")
.withEnv("REDIS_SENTINEL_FAILOVER_TIMEOUT", "10000")
.withNetworkAliases("sentinel1")
.withExposedPorts(26379);
sentinel1.start();
int sentinel1Port = sentinel1.getFirstMappedPort();
sentinel1.withCreateContainerCmdModifier(cmd -> {
cmd.getHostConfig().withPortBindings(
new PortBinding(Ports.Binding.bindPort(Integer.valueOf(sentinel1Port)),
cmd.getExposedPorts()[0]));
});
nodes.add(sentinel1);
GenericContainer<?> sentinel2 =
new GenericContainer<>("bitnami/redis-sentinel:7.2.4")
.withNetwork(network)
.withEnv("REDIS_SENTINEL_DOWN_AFTER_MILLISECONDS", "5000")
.withEnv("REDIS_SENTINEL_FAILOVER_TIMEOUT", "10000")
.withNetworkAliases("sentinel2")
.withExposedPorts(26379);
sentinel2.start();
int sentinel2Port = sentinel2.getFirstMappedPort();
sentinel2.withCreateContainerCmdModifier(cmd -> {
cmd.getHostConfig().withPortBindings(
new PortBinding(Ports.Binding.bindPort(Integer.valueOf(sentinel2Port)),
cmd.getExposedPorts()[0]));
});
nodes.add(sentinel2);
GenericContainer<?> sentinel3 =
new GenericContainer<>("bitnami/redis-sentinel:7.2.4")
.withNetwork(network)
.withEnv("REDIS_SENTINEL_DOWN_AFTER_MILLISECONDS", "5000")
.withEnv("REDIS_SENTINEL_FAILOVER_TIMEOUT", "10000")
.withNetworkAliases("sentinel3")
.withExposedPorts(26379);
sentinel3.start();
int sentinel3Port = sentinel3.getFirstMappedPort();
sentinel3.withCreateContainerCmdModifier(cmd -> {
cmd.getHostConfig().withPortBindings(
new PortBinding(Ports.Binding.bindPort(Integer.valueOf(sentinel3Port)),
cmd.getExposedPorts()[0]));
});
nodes.add(sentinel3);
Thread.sleep(5000);
Config config = new Config();
config.setProtocol(protocol);
config.useSentinelServers()
.setNatMapper(new NatMapper() {
@Override
public RedisURI map(RedisURI uri) {
for (GenericContainer<? extends GenericContainer<?>> node : nodes) {
if (node.getContainerInfo() == null) {
continue;
}
Ports.Binding[] mappedPort = node.getContainerInfo().getNetworkSettings()
.getPorts().getBindings().get(new ExposedPort(uri.getPort()));
Map<String, ContainerNetwork> ss = node.getContainerInfo().getNetworkSettings().getNetworks();
ContainerNetwork s = ss.values().iterator().next();
if (uri.getPort() == 6379 && node.getNetworkAliases().contains("slave0")) {
return new RedisURI(uri.getScheme(), "127.0.0.1", Integer.valueOf(mappedPort[0].getHostPortSpec()));
}
if ("redis".equals(uri.getHost())
&& node.getNetworkAliases().contains(uri.getHost())) {
return new RedisURI(uri.getScheme(), "127.0.0.1", Integer.valueOf(mappedPort[0].getHostPortSpec()));
}
if (mappedPort != null
&& s.getIpAddress().equals(uri.getHost())) {
return new RedisURI(uri.getScheme(), "127.0.0.1", Integer.valueOf(mappedPort[0].getHostPortSpec()));
}
}
return uri;
}
})
.addSentinelAddress("redis://127.0.0.1:" + sentinel1.getFirstMappedPort())
.setMasterName("mymaster");
callback.accept(nodes, config);
nodes.forEach(n -> n.stop());
network.close();
}
} }

@ -0,0 +1,100 @@
# Copyright VMware, Inc.
# SPDX-License-Identifier: APACHE-2.0
version: '2'
networks:
app-tier:
driver: bridge
services:
redis-node-0:
image: docker.io/bitnami/redis-cluster:7.2.4
environment:
- 'ALLOW_EMPTY_PASSWORD=yes'
- 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'
ports:
- '6379'
networks:
- app-tier
redis-node-1:
image: docker.io/bitnami/redis-cluster:7.2.4
environment:
- 'ALLOW_EMPTY_PASSWORD=yes'
- 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'
ports:
- '6379'
networks:
- app-tier
redis-node-2:
image: docker.io/bitnami/redis-cluster:7.2.4
environment:
- 'ALLOW_EMPTY_PASSWORD=yes'
- 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'
ports:
- '6379'
networks:
- app-tier
redis-node-3:
image: docker.io/bitnami/redis-cluster:7.2.4
environment:
- 'ALLOW_EMPTY_PASSWORD=yes'
- 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'
ports:
- '6379'
networks:
- app-tier
redis-node-4:
image: docker.io/bitnami/redis-cluster:7.2.4
environment:
- 'ALLOW_EMPTY_PASSWORD=yes'
- 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'
ports:
- '6379'
networks:
- app-tier
# redis-node-5:
# image: docker.io/bitnami/redis-cluster:7.2.4
# environment:
# - 'ALLOW_EMPTY_PASSWORD=yes'
# - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'
# ports:
# - '6379'
# networks:
# - app-tier
# redis-node-6:
# image: docker.io/bitnami/redis-cluster:7.2.4
# environment:
# - 'ALLOW_EMPTY_PASSWORD=yes'
# - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'
# ports:
# - '6379'
# networks:
# - app-tier
redis-node-5:
image: docker.io/bitnami/redis-cluster:7.2.4
depends_on:
- redis-node-0
- redis-node-1
- redis-node-2
- redis-node-3
- redis-node-4
# - redis-node-5
# - redis-node-6
environment:
- 'ALLOW_EMPTY_PASSWORD=yes'
- 'REDIS_CLUSTER_REPLICAS=1'
- 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5'
# - 'REDIS_NODES=redis-node-0 redis-node-1 redis-node-2 redis-node-3 redis-node-4 redis-node-5 redis-node-6 redis-node-7'
- 'REDIS_CLUSTER_CREATOR=yes'
ports:
- '6379'
networks:
- app-tier
Loading…
Cancel
Save