DATAREDIS-721 - Switch to non-blocking connect methods.
We now connect asynchronously to expose non-blocking connection behavior for reactive API usage. We also use a non-blocking, asynchronous pool implementation for connections that are used through a reactive API. Shared connection usage, which is blocking on the very first access can be pre-initialized during connection factory initialization instead of initialization on first access: LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(); connectionFactory.setEagerInitialization(true); connectionFactory.afterPropertiesSet(); We no longer require offloading of blocking connects to a dedicated scheduler which makes reactive API usage fully non-blocking. Original Pull Request: #315
This commit is contained in:
committed by
Christoph Strobl
parent
2b094850a2
commit
b496aab2fb
@@ -29,6 +29,8 @@ import io.lettuce.core.AbstractRedisClient;
|
||||
import io.lettuce.core.ClientOptions;
|
||||
import io.lettuce.core.RedisClient;
|
||||
import io.lettuce.core.RedisURI;
|
||||
import io.lettuce.core.api.StatefulConnection;
|
||||
import io.lettuce.core.api.StatefulRedisConnection;
|
||||
import io.lettuce.core.cluster.RedisClusterClient;
|
||||
import io.lettuce.core.cluster.api.StatefulRedisClusterConnection;
|
||||
import io.lettuce.core.codec.ByteArrayCodec;
|
||||
@@ -38,6 +40,7 @@ import io.lettuce.core.resource.ClientResources;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@@ -590,7 +593,7 @@ public class LettuceConnectionFactoryUnitTests {
|
||||
|
||||
RedisClusterClient clientMock = mock(RedisClusterClient.class);
|
||||
StatefulRedisClusterConnection<byte[], byte[]> connectionMock = mock(StatefulRedisClusterConnection.class);
|
||||
when(clientMock.connect(ByteArrayCodec.INSTANCE)).thenReturn(connectionMock);
|
||||
when(clientMock.connectAsync(ByteArrayCodec.INSTANCE)).thenReturn(CompletableFuture.completedFuture(connectionMock));
|
||||
|
||||
LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(clusterConfig,
|
||||
LettuceClientConfiguration.defaultConfiguration()) {
|
||||
@@ -608,7 +611,31 @@ public class LettuceConnectionFactoryUnitTests {
|
||||
connectionFactory.getClusterConnection().close();
|
||||
connectionFactory.getClusterConnection().close();
|
||||
|
||||
verify(clientMock).connect(ArgumentMatchers.any(RedisCodec.class));
|
||||
verify(clientMock).connectAsync(ArgumentMatchers.any(RedisCodec.class));
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-721
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldEagerlyInitializeSharedConnection() {
|
||||
|
||||
LettuceConnectionProvider connectionProviderMock = mock(LettuceConnectionProvider.class);
|
||||
StatefulRedisConnection connectionMock = mock(StatefulRedisConnection.class);
|
||||
|
||||
when(connectionProviderMock.getConnectionAsync(any()))
|
||||
.thenReturn(CompletableFuture.completedFuture(connectionMock));
|
||||
|
||||
LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory() {
|
||||
@Override
|
||||
protected LettuceConnectionProvider doCreateConnectionProvider(AbstractRedisClient client,
|
||||
RedisCodec<?, ?> codec) {
|
||||
return connectionProviderMock;
|
||||
}
|
||||
};
|
||||
connectionFactory.setEagerInitialization(true);
|
||||
|
||||
connectionFactory.afterPropertiesSet();
|
||||
|
||||
verify(connectionProviderMock, times(2)).getConnection(StatefulConnection.class);
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-842
|
||||
|
||||
@@ -21,6 +21,7 @@ import static org.mockito.Mockito.anyInt;
|
||||
import static org.mockito.Mockito.anyString;
|
||||
import static org.springframework.data.redis.connection.ClusterTestVariables.*;
|
||||
|
||||
import io.lettuce.core.ConnectionFuture;
|
||||
import io.lettuce.core.api.StatefulRedisConnection;
|
||||
import io.lettuce.core.api.reactive.RedisReactiveCommands;
|
||||
import io.lettuce.core.cluster.RedisClusterClient;
|
||||
@@ -30,6 +31,7 @@ import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -60,8 +62,8 @@ public class LettuceReactiveRedisClusterConnectionUnitTests {
|
||||
@Before
|
||||
public void before() {
|
||||
|
||||
when(connectionProvider.getConnection(any())).thenReturn(sharedConnection);
|
||||
when(sharedConnection.getConnection(anyString(), anyInt())).thenReturn(nodeConnection);
|
||||
when(connectionProvider.getConnectionAsync(any())).thenReturn(CompletableFuture.completedFuture(sharedConnection));
|
||||
when(sharedConnection.getConnectionAsync(anyString(), anyInt())).thenReturn(CompletableFuture.completedFuture(nodeConnection));
|
||||
when(nodeConnection.reactive()).thenReturn(reactiveNodeCommands);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ import reactor.test.StepVerifier;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -53,7 +52,8 @@ public class LettuceReactiveRedisConnectionUnitTests {
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
when(connectionProvider.getConnection(any())).thenReturn(sharedConnection);
|
||||
when(connectionProvider.getConnectionAsync(any())).thenReturn(CompletableFuture.completedFuture(sharedConnection));
|
||||
when(connectionProvider.releaseAsync(any())).thenReturn(CompletableFuture.completedFuture(null));
|
||||
when(sharedConnection.reactive()).thenReturn(reactiveCommands);
|
||||
}
|
||||
|
||||
@@ -65,24 +65,24 @@ public class LettuceReactiveRedisConnectionUnitTests {
|
||||
verifyZeroInteractions(connectionProvider);
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-720
|
||||
@Test // DATAREDIS-720, DATAREDIS-721
|
||||
public void shouldExecuteUsingConnectionProvider() {
|
||||
|
||||
LettuceReactiveRedisConnection connection = new LettuceReactiveRedisConnection(connectionProvider);
|
||||
|
||||
StepVerifier.create(connection.execute(cmd -> Mono.just("foo"))).expectNext("foo").verifyComplete();
|
||||
|
||||
verify(connectionProvider).getConnection(StatefulConnection.class);
|
||||
verify(connectionProvider).getConnectionAsync(StatefulConnection.class);
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-720
|
||||
@Test // DATAREDIS-720, DATAREDIS-721
|
||||
public void shouldExecuteDedicatedUsingConnectionProvider() {
|
||||
|
||||
LettuceReactiveRedisConnection connection = new LettuceReactiveRedisConnection(connectionProvider);
|
||||
|
||||
StepVerifier.create(connection.executeDedicated(cmd -> Mono.just("foo"))).expectNext("foo").verifyComplete();
|
||||
|
||||
verify(connectionProvider).getConnection(StatefulConnection.class);
|
||||
verify(connectionProvider).getConnectionAsync(StatefulConnection.class);
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-720
|
||||
@@ -96,7 +96,7 @@ public class LettuceReactiveRedisConnectionUnitTests {
|
||||
verifyZeroInteractions(connectionProvider);
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-720
|
||||
@Test // DATAREDIS-720, DATAREDIS-721
|
||||
public void shouldExecuteDedicatedWithSharedConnection() {
|
||||
|
||||
LettuceReactiveRedisConnection connection = new LettuceReactiveRedisConnection(sharedConnection,
|
||||
@@ -104,20 +104,20 @@ public class LettuceReactiveRedisConnectionUnitTests {
|
||||
|
||||
StepVerifier.create(connection.executeDedicated(cmd -> Mono.just("foo"))).expectNext("foo").verifyComplete();
|
||||
|
||||
verify(connectionProvider).getConnection(StatefulConnection.class);
|
||||
verify(connectionProvider).getConnectionAsync(StatefulConnection.class);
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-720
|
||||
@Test // DATAREDIS-720, DATAREDIS-721
|
||||
public void shouldOperateOnDedicatedConnection() {
|
||||
|
||||
LettuceReactiveRedisConnection connection = new LettuceReactiveRedisConnection(connectionProvider);
|
||||
|
||||
StepVerifier.create(connection.getConnection()).expectNextCount(1).verifyComplete();
|
||||
|
||||
verify(connectionProvider).getConnection(StatefulConnection.class);
|
||||
verify(connectionProvider).getConnectionAsync(StatefulConnection.class);
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-720
|
||||
@Test // DATAREDIS-720, DATAREDIS-721
|
||||
public void shouldCloseOnlyDedicatedConnection() {
|
||||
|
||||
LettuceReactiveRedisConnection connection = new LettuceReactiveRedisConnection(sharedConnection,
|
||||
@@ -128,11 +128,11 @@ public class LettuceReactiveRedisConnectionUnitTests {
|
||||
|
||||
connection.close();
|
||||
|
||||
verify(sharedConnection, never()).close();
|
||||
verify(connectionProvider, times(1)).release(sharedConnection);
|
||||
verify(sharedConnection, never()).closeAsync();
|
||||
verify(connectionProvider, times(1)).releaseAsync(sharedConnection);
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-720
|
||||
@Test // DATAREDIS-720, DATAREDIS-721
|
||||
public void shouldCloseConnectionOnlyOnce() {
|
||||
|
||||
LettuceReactiveRedisConnection connection = new LettuceReactiveRedisConnection(connectionProvider);
|
||||
@@ -142,21 +142,16 @@ public class LettuceReactiveRedisConnectionUnitTests {
|
||||
connection.close();
|
||||
connection.close();
|
||||
|
||||
verify(connectionProvider, times(1)).release(sharedConnection);
|
||||
verify(connectionProvider, times(1)).releaseAsync(sharedConnection);
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-720
|
||||
@Test // DATAREDIS-720, DATAREDIS-721
|
||||
@SuppressWarnings("unchecked")
|
||||
public void multipleCallsInProgressShouldConnectOnlyOnce() throws Exception {
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
CompletableFuture<StatefulConnection<?, ?>> connectionFuture = new CompletableFuture<>();
|
||||
reset(connectionProvider);
|
||||
when(connectionProvider.getConnection(any())).thenAnswer(invocation -> {
|
||||
|
||||
latch.await();
|
||||
return sharedConnection;
|
||||
});
|
||||
when(connectionProvider.getConnectionAsync(any())).thenReturn(connectionFuture);
|
||||
|
||||
LettuceReactiveRedisConnection connection = new LettuceReactiveRedisConnection(connectionProvider);
|
||||
|
||||
@@ -168,9 +163,9 @@ public class LettuceReactiveRedisConnectionUnitTests {
|
||||
assertThat(first).isNotDone();
|
||||
assertThat(second).isNotDone();
|
||||
|
||||
verify(connectionProvider, times(1)).getConnection(StatefulConnection.class);
|
||||
verify(connectionProvider, times(1)).getConnectionAsync(StatefulConnection.class);
|
||||
|
||||
latch.countDown();
|
||||
connectionFuture.complete(sharedConnection);
|
||||
|
||||
first.get(10, TimeUnit.SECONDS);
|
||||
second.get(10, TimeUnit.SECONDS);
|
||||
@@ -179,24 +174,25 @@ public class LettuceReactiveRedisConnectionUnitTests {
|
||||
assertThat(second).isCompletedWithValue(sharedConnection);
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-720
|
||||
@Test // DATAREDIS-720, DATAREDIS-721
|
||||
public void shouldPropagateConnectionFailures() {
|
||||
|
||||
reset(connectionProvider);
|
||||
when(connectionProvider.getConnection(any())).thenThrow(new RedisConnectionException("something went wrong"));
|
||||
when(connectionProvider.getConnectionAsync(any()))
|
||||
.thenReturn(LettuceFutureUtils.failed(new RedisConnectionException("something went wrong")));
|
||||
|
||||
LettuceReactiveRedisConnection connection = new LettuceReactiveRedisConnection(connectionProvider);
|
||||
|
||||
StepVerifier.create(connection.getConnection()).expectError(RedisConnectionFailureException.class).verify();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class) // DATAREDIS-720
|
||||
@Test // DATAREDIS-720, DATAREDIS-721
|
||||
public void shouldRejectCommandsAfterClose() {
|
||||
|
||||
LettuceReactiveRedisConnection connection = new LettuceReactiveRedisConnection(connectionProvider);
|
||||
connection.close();
|
||||
|
||||
connection.getConnection();
|
||||
StepVerifier.create(connection.getConnection()).expectError(IllegalStateException.class).verify();
|
||||
}
|
||||
|
||||
@Test // DATAREDIS-659, DATAREDIS-708
|
||||
|
||||
Reference in New Issue
Block a user