DATAREDIS-580 - Support Master/Slave connections with ReadFrom settings using Lettuce.

We now allow configuration of ReadFrom settings using the Lettuce driver to enable slave reads. If ReadFrom is configured, we opt-in to Master/Slave connections instead of plain connections. Master/Slave connections route commands to the configured type of node depending on whether the command is a read or write command.

LettuceClientConfiguration configuration = LettuceClientConfiguration.builder().readFrom(ReadFrom.SLAVE).build();

ReadFrom is available for:

* Static Master/Slave Redis without Redis Sentinel
* Sentinel-Managed Master/Slave Redis
* Redis Cluster

ReadFrom is not configured for Pub/Sub connections or connections to the actual Sentinel servers.

Original Pull Request: #287
This commit is contained in:
Mark Paluch
2017-10-13 15:13:07 +02:00
committed by Christoph Strobl
parent 9775734f6f
commit b414fdf6f1
13 changed files with 212 additions and 27 deletions

View File

@@ -23,6 +23,7 @@ import static org.junit.Assume.*;
import io.lettuce.core.EpollProvider;
import io.lettuce.core.KqueueProvider;
import io.lettuce.core.ReadFrom;
import io.lettuce.core.RedisException;
import io.lettuce.core.api.async.RedisAsyncCommands;
import io.lettuce.core.api.reactive.BaseRedisReactiveCommands;
@@ -385,6 +386,28 @@ public class LettuceConnectionFactoryTests {
factory.destroy();
}
@Test // DATAREDIS-580
public void factoryUsesMasterSlaveConnections() {
LettuceClientConfiguration configuration = LettuceTestClientConfiguration.builder().readFrom(ReadFrom.SLAVE)
.build();
LettuceConnectionFactory factory = new LettuceConnectionFactory(SettingsUtils.standaloneConfiguration(),
configuration);
factory.afterPropertiesSet();
RedisConnection connection = factory.getConnection();
try {
assertThat(connection.ping(), is(equalTo("PONG")));
assertThat(connection.info().getProperty("role"), is(equalTo("slave")));
} finally {
this.connection.close();
}
factory.destroy();
}
@Test // DATAREDIS-576
public void connectionAppliesClientName() {

View File

@@ -22,6 +22,8 @@ import io.lettuce.core.api.sync.RedisCommands;
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
import io.lettuce.core.cluster.api.sync.RedisClusterCommands;
import java.util.Optional;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
@@ -43,7 +45,8 @@ public abstract class LettuceReactiveClusterCommandsTestsBase {
assumeThat(clientProvider.test(), is(true));
nativeCommands = clientProvider.getClient().connect().sync();
connection = new LettuceReactiveRedisClusterConnection(
new ClusterConnectionProvider(clientProvider.getClient(), LettuceReactiveRedisConnection.CODEC),
new ClusterConnectionProvider(clientProvider.getClient(), LettuceReactiveRedisConnection.CODEC,
Optional.empty()),
clientProvider.getClient());
}

View File

@@ -26,6 +26,7 @@ import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.junit.After;
import org.junit.Before;
@@ -89,9 +90,9 @@ public abstract class LettuceReactiveCommandsTestsBase {
List<Object[]> parameters = new ArrayList<>();
StandaloneConnectionProvider standaloneProvider = new StandaloneConnectionProvider(standalone.getClient(),
LettuceReactiveRedisConnection.CODEC);
LettuceReactiveRedisConnection.CODEC, Optional.empty());
StandaloneConnectionProvider nativeConnectionProvider = new StandaloneConnectionProvider(standalone.getClient(),
StringCodec.UTF8);
StringCodec.UTF8, Optional.empty());
parameters.add(new Object[] { standaloneProvider, nativeConnectionProvider, "Standalone" });
parameters.add(new Object[] {
@@ -101,9 +102,9 @@ public abstract class LettuceReactiveCommandsTestsBase {
if (cluster.test()) {
ClusterConnectionProvider clusterProvider = new ClusterConnectionProvider(cluster.getClient(),
LettuceReactiveRedisConnection.CODEC);
LettuceReactiveRedisConnection.CODEC, Optional.empty());
ClusterConnectionProvider nativeClusterConnectionProvider = new ClusterConnectionProvider(cluster.getClient(),
StringCodec.UTF8);
StringCodec.UTF8, Optional.empty());
parameters.add(new Object[] { clusterProvider, nativeClusterConnectionProvider, "Cluster" });
}

View File

@@ -20,6 +20,8 @@ import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.*;
import io.lettuce.core.ReadFrom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -33,8 +35,10 @@ import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.springframework.data.redis.ConnectionFactoryTracker;
import org.springframework.data.redis.SettingsUtils;
import org.springframework.data.redis.connection.AbstractConnectionIntegrationTests;
import org.springframework.data.redis.connection.DefaultStringRedisConnection;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.RedisSentinelConnection;
import org.springframework.data.redis.connection.RedisServer;
@@ -170,4 +174,64 @@ public class LettuceSentinelIntegrationTests extends AbstractConnectionIntegrati
connection.close();
}
}
@Test // DATAREDIS-580
public void factoryWithReadFromMasterSettings() {
LettuceConnectionFactory factory = new LettuceConnectionFactory(SENTINEL_CONFIG,
LettuceTestClientConfiguration.builder().readFrom(ReadFrom.MASTER).build());
factory.afterPropertiesSet();
ConnectionFactoryTracker.add(factory);
StringRedisConnection connection = new DefaultStringRedisConnection(factory.getConnection());
try {
assertThat(connection.ping(), is(equalTo("PONG")));
assertThat(connection.info().getProperty("role"), is(equalTo("master")));
} finally {
connection.close();
}
}
@Test // DATAREDIS-580
public void factoryWithReadFromSlaveSettings() {
LettuceConnectionFactory factory = new LettuceConnectionFactory(SENTINEL_CONFIG,
LettuceTestClientConfiguration.builder().readFrom(ReadFrom.SLAVE).build());
factory.afterPropertiesSet();
ConnectionFactoryTracker.add(factory);
StringRedisConnection connection = new DefaultStringRedisConnection(factory.getConnection());
try {
assertThat(connection.ping(), is(equalTo("PONG")));
assertThat(connection.info().getProperty("role"), is(equalTo("slave")));
} finally {
connection.close();
}
}
@Test // DATAREDIS-580
public void factoryUsesMasterSlaveConnections() {
LettuceClientConfiguration configuration = LettuceTestClientConfiguration.builder().readFrom(ReadFrom.SLAVE)
.build();
LettuceConnectionFactory factory = new LettuceConnectionFactory(SettingsUtils.standaloneConfiguration(),
configuration);
factory.afterPropertiesSet();
RedisConnection connection = factory.getConnection();
try {
assertThat(connection.ping(), is(equalTo("PONG")));
assertThat(connection.info().getProperty("role"), is(equalTo("slave")));
} finally {
this.connection.close();
}
factory.destroy();
}
}

View File

@@ -17,6 +17,8 @@ package org.springframework.data.redis.test.util;
import static org.hamcrest.CoreMatchers.*;
import redis.clients.jedis.Jedis;
import org.junit.Assume;
import org.junit.rules.ExternalResource;
import org.junit.rules.TestRule;
@@ -24,8 +26,6 @@ import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.jedis.JedisConverters;
import redis.clients.jedis.Jedis;
/**
* Simple {@link TestRule} implementation that check Redis is running in cluster mode.
*
@@ -59,7 +59,7 @@ public class RedisClusterRule extends ExternalResource {
* @see org.junit.rules.ExternalResource#before()
*/
@Override
protected void before() throws Throwable {
public void before() {
Assume.assumeThat(mode, is("cluster"));
}