From 0b60d632cce191252fa0b083e6153b3b6a8f5b2d Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 27 Feb 2018 11:32:19 +0100 Subject: [PATCH] DATAREDIS-775 - Expose Lettuce RedisClient through RedisClientProvider. We now expose the underlying Redis Client instance via RedisClientProvider.getRedisClient() to components that require direct client interaction. The client is exposed through ClusterConnectionProvider and now also through LettucePoolingConnectionProvider. Exposing the client via LettucePoolingConnectionProvider allows Redis Cluster usage with connection pooling. Original Pull Request: #316 --- .../lettuce/ClusterConnectionProvider.java | 10 +++- .../lettuce/LettuceClusterConnection.java | 10 +++- .../LettucePoolingConnectionProvider.java | 19 ++++++- .../lettuce/RedisClientProvider.java | 34 +++++++++++++ .../LettuceClusterConnectionTests.java | 50 ++++++++++++++++--- .../LettuceReactiveCommandsTestsBase.java | 3 +- 6 files changed, 114 insertions(+), 12 deletions(-) create mode 100644 src/main/java/org/springframework/data/redis/connection/lettuce/RedisClientProvider.java diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/ClusterConnectionProvider.java b/src/main/java/org/springframework/data/redis/connection/lettuce/ClusterConnectionProvider.java index de4b96c5b..9e2b92f5a 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/ClusterConnectionProvider.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/ClusterConnectionProvider.java @@ -15,6 +15,7 @@ */ package org.springframework.data.redis.connection.lettuce; +import io.lettuce.core.AbstractRedisClient; import io.lettuce.core.ReadFrom; import io.lettuce.core.api.StatefulConnection; import io.lettuce.core.cluster.RedisClusterClient; @@ -34,7 +35,7 @@ import org.springframework.util.Assert; * @author Christoph Strobl * @since 2.0 */ -class ClusterConnectionProvider implements LettuceConnectionProvider { +class ClusterConnectionProvider implements LettuceConnectionProvider, RedisClientProvider { private final RedisClusterClient client; private final RedisCodec codec; @@ -91,7 +92,12 @@ class ClusterConnectionProvider implements LettuceConnectionProvider { throw new UnsupportedOperationException("Connection type " + connectionType + " not supported!"); } - public RedisClusterClient getClient() { + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.lettuce.RedisClientProvider#getRedisClient() + */ + @Override + public AbstractRedisClient getRedisClient() { return client; } } diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnection.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnection.java index be1f90f57..900b809b6 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnection.java @@ -154,7 +154,15 @@ public class LettuceClusterConnection extends LettuceConnection implements Defau * @return access to {@link RedisClusterClient} for non-connection access. */ private RedisClusterClient getClient() { - return ((ClusterConnectionProvider) getConnectionProvider()).getClient(); + + LettuceConnectionProvider connectionProvider = getConnectionProvider(); + + if (connectionProvider instanceof RedisClientProvider) { + return (RedisClusterClient) ((RedisClientProvider) getConnectionProvider()).getRedisClient(); + } + + throw new IllegalStateException(String.format("Connection provider %s does not implement RedisClientProvider!", + connectionProvider.getClass().getName())); } /* diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java index d23201682..993afb116 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettucePoolingConnectionProvider.java @@ -15,6 +15,7 @@ */ package org.springframework.data.redis.connection.lettuce; +import io.lettuce.core.AbstractRedisClient; import io.lettuce.core.api.StatefulConnection; import io.lettuce.core.support.ConnectionPoolSupport; @@ -42,7 +43,7 @@ import org.springframework.util.Assert; * @since 2.0 * @see #getConnection(Class) */ -class LettucePoolingConnectionProvider implements LettuceConnectionProvider, DisposableBean { +class LettucePoolingConnectionProvider implements LettuceConnectionProvider, RedisClientProvider, DisposableBean { private final static Log log = LogFactory.getLog(LettucePoolingConnectionProvider.class); @@ -86,6 +87,22 @@ class LettucePoolingConnectionProvider implements LettuceConnectionProvider, Dis } } + /* + * (non-Javadoc) + * @see org.springframework.data.redis.connection.lettuce.RedisClientProvider#getRedisClient() + */ + @Override + public AbstractRedisClient getRedisClient() { + + if (connectionProvider instanceof RedisClientProvider) { + return ((RedisClientProvider) connectionProvider).getRedisClient(); + } + + throw new IllegalStateException( + String.format("Undyerlying connection provider %s does not implement RedisClientProvider!", + connectionProvider.getClass().getName())); + } + /* * (non-Javadoc) * @see org.springframework.data.redis.connection.lettuce.LettuceConnectionProvider#release(io.lettuce.core.api.StatefulConnection) diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/RedisClientProvider.java b/src/main/java/org/springframework/data/redis/connection/lettuce/RedisClientProvider.java new file mode 100644 index 000000000..2ce12d9e1 --- /dev/null +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/RedisClientProvider.java @@ -0,0 +1,34 @@ +/* + * Copyright 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.redis.connection.lettuce; + +import io.lettuce.core.AbstractRedisClient; + +/** + * Extension to {@link LettuceConnectionProvider} for providers exposing {@link io.lettuce.core.RedisClient}. + * + * @author Mark Paluch + * @since 2.0.5 + */ +interface RedisClientProvider { + + /** + * Returns the underlying Redis Client. + * + * @return the {@link AbstractRedisClient}. + */ + AbstractRedisClient getRedisClient(); +} diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java index e561cdb3d..f2d3b5698 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceClusterConnectionTests.java @@ -28,6 +28,7 @@ import io.lettuce.core.cluster.RedisClusterClient; import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands; import java.nio.charset.Charset; +import java.time.Duration; import java.util.*; import java.util.concurrent.TimeUnit; @@ -41,16 +42,10 @@ import org.springframework.data.geo.Circle; import org.springframework.data.geo.Distance; import org.springframework.data.geo.GeoResults; import org.springframework.data.geo.Point; -import org.springframework.data.redis.connection.ClusterConnectionTests; -import org.springframework.data.redis.connection.ClusterSlotHashUtil; -import org.springframework.data.redis.connection.DataType; -import org.springframework.data.redis.connection.DefaultSortParameters; -import org.springframework.data.redis.connection.DefaultTuple; -import org.springframework.data.redis.connection.RedisClusterNode; +import org.springframework.data.redis.connection.*; import org.springframework.data.redis.connection.RedisClusterNode.SlotRange; import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation; import org.springframework.data.redis.connection.RedisListCommands.Position; -import org.springframework.data.redis.connection.RedisNode; import org.springframework.data.redis.connection.RedisStringCommands.BitOperation; import org.springframework.data.redis.connection.RedisStringCommands.SetOption; import org.springframework.data.redis.connection.RedisZSetCommands.Range; @@ -121,6 +116,47 @@ public class LettuceClusterConnectionTests implements ClusterConnectionTests { client.shutdown(0, 0, TimeUnit.MILLISECONDS); } + @Test // DATAREDIS-775 + public void shouldCreateConnectionWithPooling() { + + LettuceConnectionFactory factory = createConnectionFactory(); + factory.afterPropertiesSet(); + + RedisConnection connection = factory.getConnection(); + + assertThat(connection.ping(), is("PONG")); + connection.close(); + + factory.destroy(); + } + + @Test // DATAREDIS-775 + public void shouldCreateClusterConnectionWithPooling() { + + LettuceConnectionFactory factory = createConnectionFactory(); + factory.afterPropertiesSet(); + + RedisClusterConnection clusterConnection = factory.getClusterConnection(); + + assertThat(clusterConnection.ping(ClusterTestVariables.CLUSTER_NODE_1), is("PONG")); + clusterConnection.close(); + + factory.destroy(); + } + + private static LettuceConnectionFactory createConnectionFactory() { + + LettucePoolingClientConfiguration clientConfiguration = LettucePoolingClientConfiguration.builder() // + .clientResources(LettuceTestClientResources.getSharedClientResources()) // + .shutdownTimeout(Duration.ZERO) // + .build(); + + RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration(); + clusterConfiguration.addClusterNode(ClusterTestVariables.CLUSTER_NODE_1); + + return new LettuceConnectionFactory(clusterConfiguration, clientConfiguration); + } + @Test // DATAREDIS-315 public void appendShouldAddValueCorrectly() { diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveCommandsTestsBase.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveCommandsTestsBase.java index fd45ca049..6452dff0f 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveCommandsTestsBase.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveCommandsTestsBase.java @@ -17,6 +17,7 @@ package org.springframework.data.redis.connection.lettuce; import io.lettuce.core.api.StatefulRedisConnection; import io.lettuce.core.api.sync.RedisCommands; +import io.lettuce.core.cluster.RedisClusterClient; import io.lettuce.core.cluster.api.StatefulRedisClusterConnection; import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands; import io.lettuce.core.cluster.api.sync.RedisClusterCommands; @@ -122,7 +123,7 @@ public abstract class LettuceReactiveCommandsTestsBase { ClusterConnectionProvider clusterConnectionProvider = (ClusterConnectionProvider) nativeConnectionProvider; nativeCommands = nativeConnectionProvider.getConnection(StatefulRedisClusterConnection.class).sync(); this.connection = new LettuceReactiveRedisClusterConnection(connectionProvider, - clusterConnectionProvider.getClient()); + (RedisClusterClient) clusterConnectionProvider.getRedisClient()); } }