package com.lambdaworks.redis;
import static com.lambdaworks.Connections.getChannel;
import static com.lambdaworks.Connections.getConnectionWatchdog;
import static com.lambdaworks.Connections.getStatefulConnection;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
import com.lambdaworks.Wait;
import com.lambdaworks.redis.api.StatefulRedisConnection;
import com.lambdaworks.redis.api.async.RedisAsyncCommands;
import com.lambdaworks.redis.api.sync.RedisCommands;
import com.lambdaworks.redis.codec.Utf8StringCodec;
import com.lambdaworks.redis.output.StatusOutput;
import com.lambdaworks.redis.protocol.*;
import io.netty.channel.Channel;
/**
* @author Mark Paluch
*/
public class ClientOptionsTest extends AbstractRedisClientTest {
@Test
public void testNew() throws Exception {
checkAssertions(ClientOptions.create());
}
@Test
public void testBuilder() throws Exception {
checkAssertions(ClientOptions.builder().build());
}
@Test
public void testCopy() throws Exception {
checkAssertions(ClientOptions.copyOf(ClientOptions.builder().build()));
}
protected void checkAssertions(ClientOptions sut) {
assertThat(sut.isAutoReconnect()).isEqualTo(true);
assertThat(sut.isCancelCommandsOnReconnectFailure()).isEqualTo(false);
assertThat(sut.isPingBeforeActivateConnection()).isEqualTo(false);
assertThat(sut.isSuspendReconnectOnProtocolFailure()).isEqualTo(false);
assertThat(sut.getDisconnectedBehavior()).isEqualTo(ClientOptions.DisconnectedBehavior.DEFAULT);
}
@Test
public void variousClientOptions() throws Exception {
RedisAsyncCommands<String, String> plain = client.connect().async();
assertThat(getStatefulConnection(plain).getOptions().isAutoReconnect()).isTrue();
client.setOptions(ClientOptions.builder().autoReconnect(false).build());
RedisAsyncCommands<String, String> connection = client.connect().async();
assertThat(getStatefulConnection(connection).getOptions().isAutoReconnect()).isFalse();
assertThat(getStatefulConnection(plain).getOptions().isAutoReconnect()).isTrue();
plain.close();
connection.close();
}
@Test
public void requestQueueSize() throws Exception {
client.setOptions(ClientOptions.builder().requestQueueSize(10).build());
RedisAsyncCommands<String, String> connection = client.connect().async();
getConnectionWatchdog(connection.getStatefulConnection()).setListenOnChannelInactive(false);
connection.quit();
Wait.untilTrue(() -> !connection.isOpen()).waitOrTimeout();
for (int i = 0; i < 10; i++) {
connection.ping();
}
try {
connection.ping();
fail("missing RedisException");
} catch (RedisException e) {
assertThat(e).hasMessageContaining("Request queue size exceeded");
}
connection.close();
}
@Test
public void disconnectedWithoutReconnect() throws Exception {
client.setOptions(ClientOptions.builder().autoReconnect(false).build());
RedisAsyncCommands<String, String> connection = client.connect().async();
connection.quit();
Wait.untilTrue(() -> !connection.isOpen()).waitOrTimeout();
try {
connection.get(key);
} catch (Exception e) {
assertThat(e).isInstanceOf(RedisException.class).hasMessageContaining("not connected");
} finally {
connection.close();
}
}
@Test
public void disconnectedRejectCommands() throws Exception {
client.setOptions(ClientOptions.builder().disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS)
.build());
RedisAsyncCommands<String, String> connection = client.connect().async();
getConnectionWatchdog(connection.getStatefulConnection()).setListenOnChannelInactive(false);
connection.quit();
Wait.untilTrue(() -> !connection.isOpen()).waitOrTimeout();
try {
connection.get(key);
} catch (Exception e) {
assertThat(e).isInstanceOf(RedisException.class).hasMessageContaining("not connected");
} finally {
connection.close();
}
}
@Test
public void disconnectedAcceptCommands() throws Exception {
client.setOptions(ClientOptions.builder().autoReconnect(false)
.disconnectedBehavior(ClientOptions.DisconnectedBehavior.ACCEPT_COMMANDS).build());
RedisAsyncCommands<String, String> connection = client.connect().async();
connection.quit();
Wait.untilTrue(() -> !connection.isOpen()).waitOrTimeout();
connection.get(key);
connection.close();
}
@Test(timeout = 10000)
public void pingBeforeConnect() throws Exception {
redis.set(key, value);
client.setOptions(ClientOptions.builder().pingBeforeActivateConnection(true).build());
RedisCommands<String, String> connection = client.connect().sync();
try {
String result = connection.get(key);
assertThat(result).isEqualTo(value);
} finally {
connection.close();
}
}
@Test
public void pingBeforeConnectWithAuthentication() throws Exception {
new WithPasswordRequired() {
@Override
protected void run(RedisClient client) throws Exception {
client.setOptions(ClientOptions.builder().pingBeforeActivateConnection(true).build());
RedisURI redisURI = RedisURI.Builder.redis(host, port).withPassword(passwd).build();
RedisCommands<String, String> connection = client.connect(redisURI).sync();
try {
String result = connection.info();
assertThat(result).contains("memory");
} finally {
connection.close();
}
}
};
}
@Test
public void pingBeforeConnectWithSslAndAuthentication() throws Exception {
new WithPasswordRequired() {
@Override
protected void run(RedisClient client) throws Exception {
client.setOptions(ClientOptions.builder().pingBeforeActivateConnection(true).build());
RedisURI redisURI = RedisURI.Builder.redis(host, 6443).withPassword(passwd).withVerifyPeer(false)
.withSsl(true).build();
RedisCommands<String, String> connection = client.connect(redisURI).sync();
try {
String result = connection.info();
assertThat(result).contains("memory");
} finally {
connection.close();
}
}
};
}
@Test
public void pingBeforeConnectWithAuthenticationFails() throws Exception {
new WithPasswordRequired() {
@Override
protected void run(RedisClient client) throws Exception {
client.setOptions(ClientOptions.builder().pingBeforeActivateConnection(true).build());
RedisURI redisURI = RedisURI.builder().redis(host, port).build();
try {
client.connect(redisURI);
fail("Missing RedisConnectionException");
} catch (RedisConnectionException e) {
assertThat(e).hasRootCauseInstanceOf(RedisCommandExecutionException.class);
}
}
};
}
@Test
public void pingBeforeConnectWithSslAndAuthenticationFails() throws Exception {
new WithPasswordRequired() {
@Override
protected void run(RedisClient client) throws Exception {
client.setOptions(ClientOptions.builder().pingBeforeActivateConnection(true).build());
RedisURI redisURI = RedisURI.builder().redis(host, 6443).withVerifyPeer(false).withSsl(true).build();
try {
client.connect(redisURI);
fail("Missing RedisConnectionException");
} catch (RedisConnectionException e) {
assertThat(e).hasRootCauseInstanceOf(RedisCommandExecutionException.class);
}
}
};
}
@Test(timeout = 10000)
public void pingBeforeConnectWithQueuedCommandsAndReconnect() throws Exception {
StatefulRedisConnection<String, String> controlConnection = client.connect();
client.setOptions(new ClientOptions.Builder().pingBeforeActivateConnection(true).build());
Utf8StringCodec codec = new Utf8StringCodec();
StatefulRedisConnection<String, String> redisConnection = client.connect(RedisURI.create("redis://localhost:6479/5"));
redisConnection.async().set("key1", "value1");
redisConnection.async().set("key2", "value2");
RedisFuture<String> sleep = controlConnection.dispatch(new AsyncCommand<>(
new Command<>(CommandType.DEBUG, new StatusOutput<>(codec), new CommandArgs<>(codec).add("SLEEP").add(2))));
sleep.await(100, TimeUnit.MILLISECONDS);
Channel channel = getChannel(redisConnection);
ConnectionWatchdog connectionWatchdog = getConnectionWatchdog(redisConnection);
connectionWatchdog.setReconnectSuspended(true);
channel.close().get();
sleep.get();
redisConnection.async().get(key).cancel(true);
RedisFuture<String> getFuture1 = redisConnection.async().get("key1");
RedisFuture<String> getFuture2 = redisConnection.async().get("key2");
getFuture1.await(100, TimeUnit.MILLISECONDS);
connectionWatchdog.setReconnectSuspended(false);
connectionWatchdog.scheduleReconnect();
assertThat(getFuture1.get()).isEqualTo("value1");
assertThat(getFuture2.get()).isEqualTo("value2");
controlConnection.close();
redisConnection.close();
}
@Test(timeout = 10000)
public void authenticatedPingBeforeConnectWithQueuedCommandsAndReconnect() throws Exception {
new WithPasswordRequired() {
@Override
protected void run(RedisClient client) throws Exception {
RedisURI redisURI = RedisURI.Builder.redis(host, port).withPassword(passwd).withDatabase(5).build();
StatefulRedisConnection<String, String> controlConnection = client.connect(redisURI);
client.setOptions(new ClientOptions.Builder().pingBeforeActivateConnection(true).build());
Utf8StringCodec codec = new Utf8StringCodec();
StatefulRedisConnection<String, String> redisConnection = client.connect(redisURI);
redisConnection.async().set("key1", "value1");
redisConnection.async().set("key2", "value2");
RedisFuture<String> sleep = controlConnection.dispatch(new AsyncCommand<>(new Command<>(CommandType.DEBUG,
new StatusOutput<>(codec), new CommandArgs<>(codec).add("SLEEP").add(2))));
sleep.await(100, TimeUnit.MILLISECONDS);
Channel channel = getChannel(redisConnection);
ConnectionWatchdog connectionWatchdog = getConnectionWatchdog(redisConnection);
connectionWatchdog.setReconnectSuspended(true);
channel.close().get();
sleep.get();
redisConnection.async().get(key).cancel(true);
RedisFuture<String> getFuture1 = redisConnection.async().get("key1");
RedisFuture<String> getFuture2 = redisConnection.async().get("key2");
getFuture1.await(100, TimeUnit.MILLISECONDS);
connectionWatchdog.setReconnectSuspended(false);
connectionWatchdog.scheduleReconnect();
assertThat(getFuture1.get()).isEqualTo("value1");
assertThat(getFuture2.get()).isEqualTo("value2");
controlConnection.close();
redisConnection.close();
}
};
}
}