From 7024ef33e04436f2f06e438229ed5851c5598ec6 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 24 Feb 2016 08:30:13 +0100 Subject: [PATCH] DATAREDIS-468 - Guard multi/exec bocks in RedisCache to allow usage with cluster. We now guard multi/exec blocks by checking the connection type. This allows to skip those commands when running in cluster. Original pull request: #173. --- .../data/redis/cache/RedisCache.java | 24 +++++++-- .../data/redis/cache/RedisCacheUnitTests.java | 49 +++++++++++++++++++ 2 files changed, 68 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/cache/RedisCache.java b/src/main/java/org/springframework/data/redis/cache/RedisCache.java index c01785ea2..6707121d9 100644 --- a/src/main/java/org/springframework/data/redis/cache/RedisCache.java +++ b/src/main/java/org/springframework/data/redis/cache/RedisCache.java @@ -28,6 +28,7 @@ import org.springframework.cache.Cache; import org.springframework.cache.support.SimpleValueWrapper; import org.springframework.dao.DataAccessException; import org.springframework.data.redis.RedisSystemException; +import org.springframework.data.redis.connection.RedisClusterConnection; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.ReturnType; import org.springframework.data.redis.core.RedisCallback; @@ -670,14 +671,18 @@ public class RedisCache implements Cache { @Override public Void doInRedis(BinaryRedisCacheElement element, RedisConnection connection) throws DataAccessException { - connection.multi(); + if (!isClusterConnection(connection)) { + connection.multi(); + } connection.set(element.getKeyBytes(), element.get()); processKeyExpiration(element, connection); maintainKnownKeys(element, connection); - connection.exec(); + if (!isClusterConnection(connection)) { + connection.exec(); + } return null; } } @@ -742,8 +747,11 @@ public class RedisCache implements Cache { return value; } - connection.watch(element.getKeyBytes()); - connection.multi(); + if (!isClusterConnection(connection)) { + + connection.watch(element.getKeyBytes()); + connection.multi(); + } value = element.get(); connection.set(element.getKeyBytes(), value); @@ -751,7 +759,9 @@ public class RedisCache implements Cache { processKeyExpiration(element, connection); maintainKnownKeys(element, connection); - connection.exec(); + if (!isClusterConnection(connection)) { + connection.exec(); + } return value; } catch (RuntimeException e) { @@ -799,4 +809,8 @@ public class RedisCache implements Cache { } } + private static boolean isClusterConnection(RedisConnection connection) { + return connection instanceof RedisClusterConnection; + } + } diff --git a/src/test/java/org/springframework/data/redis/cache/RedisCacheUnitTests.java b/src/test/java/org/springframework/data/redis/cache/RedisCacheUnitTests.java index b344bb088..5a9b7338b 100644 --- a/src/test/java/org/springframework/data/redis/cache/RedisCacheUnitTests.java +++ b/src/test/java/org/springframework/data/redis/cache/RedisCacheUnitTests.java @@ -31,6 +31,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.data.redis.RedisSystemException; +import org.springframework.data.redis.connection.RedisClusterConnection; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.ReturnType; @@ -217,4 +218,52 @@ public class RedisCacheUnitTests { verifyZeroInteractions(callableMock); } + /** + * @see DATAREDIS-468 + */ + @Test + public void noMultiExecForCluster() { + + RedisClusterConnection clusterConnectionMock = mock(RedisClusterConnection.class); + when(connectionFactoryMock.getConnection()).thenReturn(clusterConnectionMock); + + cache = new RedisCache(CACHE_NAME, NO_PREFIX_BYTES, templateSpy, 0L); + + when(connectionMock.exists(KEY_BYTES)).thenReturn(true); + when(connectionMock.get(KEY_BYTES)).thenReturn(null).thenReturn(VALUE_BYTES); + + cache.put(KEY, VALUE); + + verify(clusterConnectionMock, times(1)).set(eq(KEY_BYTES), eq(VALUE_BYTES)); + verify(clusterConnectionMock, never()).multi(); + verify(clusterConnectionMock, never()).exec(); + verifyZeroInteractions(connectionMock); + } + + /** + * @see DATAREDIS-468 + */ + @Test + public void getWithCallableForCluster() { + + RedisClusterConnection clusterConnectionMock = mock(RedisClusterConnection.class); + when(connectionFactoryMock.getConnection()).thenReturn(clusterConnectionMock); + + cache = new RedisCache(CACHE_NAME, NO_PREFIX_BYTES, templateSpy, 0L); + + cache.get(KEY, new Callable() { + @Override + public Object call() throws Exception { + return VALUE; + } + }); + + verify(clusterConnectionMock, times(2)).get(eq(KEY_BYTES)); + verify(clusterConnectionMock, times(1)).set(eq(KEY_BYTES), eq(VALUE_BYTES)); + + verify(clusterConnectionMock, never()).multi(); + verify(clusterConnectionMock, never()).exec(); + verifyZeroInteractions(connectionMock); + } + }