From ce8999a93e325ba1281c3f246d94b2e23f35249f Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 12 Nov 2021 08:51:41 +0100 Subject: [PATCH] Avoid cast to StatefulRedisConnection upon eager LettuceConnectionFactory initialization. We now no longer try to cast the Lettuce connection to StatefulRedisConnection when eagerly initializing the shared connection. Instead, we now introduced another method to obtain the cluster connection. Closes #2186 --- .../lettuce/LettuceConnectionFactory.java | 34 ++++++++++---- .../LettuceConnectionFactoryTests.java | 47 +++++++++++++++++-- 2 files changed, 69 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java index 8c4c67f1c..e5d671c99 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java @@ -44,6 +44,7 @@ import java.util.stream.Collectors; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.dao.DataAccessException; @@ -56,7 +57,6 @@ import org.springframework.data.redis.connection.RedisConfiguration.ClusterConfi import org.springframework.data.redis.connection.RedisConfiguration.DomainSocketConfiguration; import org.springframework.data.redis.connection.RedisConfiguration.WithDatabaseIndex; import org.springframework.data.redis.connection.RedisConfiguration.WithPassword; -import org.springframework.data.redis.connection.lettuce.LettuceConnection.*; import org.springframework.data.util.Optionals; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -374,9 +374,7 @@ public class LettuceConnectionFactory RedisClusterClient clusterClient = (RedisClusterClient) client; - StatefulRedisClusterConnection sharedConnection = getShareNativeConnection() - ? (StatefulRedisClusterConnection) getOrCreateSharedConnection().getConnection() - : null; + StatefulRedisClusterConnection sharedConnection = getSharedClusterConnection(); LettuceClusterTopologyProvider topologyProvider = new LettuceClusterTopologyProvider(clusterClient); return doCreateLettuceClusterConnection(sharedConnection, connectionProvider, topologyProvider, @@ -472,7 +470,12 @@ public class LettuceConnectionFactory resetConnection(); - getSharedConnection(); + if (isClusterAware()) { + getSharedClusterConnection(); + } else { + getSharedConnection(); + } + getSharedReactiveConnection(); } @@ -971,12 +974,27 @@ public class LettuceConnectionFactory } /** - * @return the shared connection using {@literal byte} array encoding for imperative API use. {@literal null} if - * {@link #getShareNativeConnection() connection sharing} is disabled. + * @return the shared connection using {@literal byte[]} encoding for imperative API use. {@literal null} if + * {@link #getShareNativeConnection() connection sharing} is disabled or when connected to Redis Cluster. */ @Nullable protected StatefulRedisConnection getSharedConnection() { - return shareNativeConnection ? (StatefulRedisConnection) getOrCreateSharedConnection().getConnection() : null; + return shareNativeConnection && !isClusterAware() + ? (StatefulRedisConnection) getOrCreateSharedConnection().getConnection() + : null; + } + + /** + * @return the shared cluster connection using {@literal byte[]} encoding for imperative API use. {@literal null} if + * {@link #getShareNativeConnection() connection sharing} is disabled or when connected to Redis + * Standalone/Sentinel/Master-Replica. + * @since 2.4.15 + */ + @Nullable + protected StatefulRedisClusterConnection getSharedClusterConnection() { + return shareNativeConnection && isClusterAware() + ? (StatefulRedisClusterConnection) getOrCreateSharedConnection().getConnection() + : null; } /** diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryTests.java index c8da2fa6b..b296117a7 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactoryTests.java @@ -48,6 +48,7 @@ import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.RedisStaticMasterReplicaConfiguration; import org.springframework.data.redis.connection.StringRedisConnection; import org.springframework.data.redis.connection.lettuce.extension.LettuceConnectionFactoryExtension; +import org.springframework.data.redis.test.condition.EnabledOnRedisClusterAvailable; import org.springframework.data.redis.test.extension.LettuceTestClientResources; /** @@ -85,7 +86,6 @@ class LettuceConnectionFactoryTests { factory.destroy(); } - @SuppressWarnings("rawtypes") @Test void testGetNewConnectionOnError() throws Exception { @@ -462,9 +462,8 @@ class LettuceConnectionFactoryTests { RedisConnection connection = factory.getConnection(); - assertThatThrownBy(() -> connection.pSubscribe((message, pattern) -> { - }, "foo".getBytes())).isInstanceOf(RedisConnectionFailureException.class) - .hasCauseInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(() -> connection.pSubscribe((message, pattern) -> {}, "foo".getBytes())) + .isInstanceOf(RedisConnectionFailureException.class).hasCauseInstanceOf(UnsupportedOperationException.class); connection.close(); factory.destroy(); @@ -559,4 +558,44 @@ class LettuceConnectionFactoryTests { connection.close(); } + + @Test // GH-2186 + void shouldInitializeMasterReplicaConnectionsEagerly() { + + LettuceClientConfiguration configuration = LettuceClientConfiguration.builder() + .clientResources(LettuceTestClientResources.getSharedClientResources()).build(); + + RedisStaticMasterReplicaConfiguration elastiCache = new RedisStaticMasterReplicaConfiguration( + SettingsUtils.getHost()).node(SettingsUtils.getHost(), SettingsUtils.getPort() + 1); + + LettuceConnectionFactory factory = new LettuceConnectionFactory(elastiCache, configuration); + factory.setEagerInitialization(true); + factory.afterPropertiesSet(); + + assertThat(factory.getSharedConnection()).isNotNull(); + assertThat(factory.getSharedClusterConnection()).isNull(); + + factory.getConnection().close(); + factory.destroy(); + } + + @Test // GH-2186 + @EnabledOnRedisClusterAvailable + void shouldInitializeClusterConnectionsEagerly() { + + LettuceClientConfiguration configuration = LettuceClientConfiguration.builder() + .clientResources(LettuceTestClientResources.getSharedClientResources()).build(); + + LettuceConnectionFactory factory = new LettuceConnectionFactory(SettingsUtils.clusterConfiguration(), + configuration); + factory.setEagerInitialization(true); + factory.afterPropertiesSet(); + + assertThat(factory.getSharedConnection()).isNull(); + assertThat(factory.getSharedClusterConnection()).isNotNull(); + + factory.getConnection().close(); + factory.destroy(); + } + }