diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java index 8b8497b3aa..c40c018c4b 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java @@ -326,7 +326,7 @@ public RedisClusterServerCommands serverCommands() { */ @Override public RedisScriptingCommands scriptingCommands() { - return JedisClusterScriptingCommands.INSTANCE; + return new JedisClusterScriptingCommands(this); } private JedisClusterKeyCommands doGetKeyCommands() { diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterScriptingCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterScriptingCommands.java index 2f237297f6..62d361632e 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterScriptingCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterScriptingCommands.java @@ -17,17 +17,22 @@ import java.util.List; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.springframework.dao.DataAccessException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.redis.connection.RedisScriptingCommands; import org.springframework.data.redis.connection.ReturnType; +import redis.clients.jedis.JedisCluster; /** * @author Mark Paluch * @since 2.0 */ -enum JedisClusterScriptingCommands implements RedisScriptingCommands { +@RequiredArgsConstructor +class JedisClusterScriptingCommands implements RedisScriptingCommands { - INSTANCE; + private final @NonNull JedisClusterConnection clusterConnection; /* * (non-Javadoc) @@ -70,8 +75,15 @@ public List scriptExists(String... scriptShas) { * @see org.springframework.data.redis.connection.RedisScriptingCommands#eval(byte[], org.springframework.data.redis.connection.ReturnType, int, byte[][]) */ @Override + @SuppressWarnings("unchecked") public T eval(byte[] script, ReturnType returnType, int numKeys, byte[]... keysAndArgs) { - throw new InvalidDataAccessApiUsageException("Eval is not supported in cluster environment."); + checkConnection(); + try { + return (T) new JedisScriptReturnConverter(returnType) + .convert(getCluster().eval(script, JedisConverters.toBytes(numKeys), keysAndArgs)); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } } /* @@ -80,7 +92,7 @@ public T eval(byte[] script, ReturnType returnType, int numKeys, byte[]... k */ @Override public T evalSha(String scriptSha, ReturnType returnType, int numKeys, byte[]... keysAndArgs) { - throw new InvalidDataAccessApiUsageException("EvalSha is not supported in cluster environment."); + return evalSha(JedisConverters.toBytes(scriptSha), returnType, numKeys, keysAndArgs); } /* @@ -88,7 +100,43 @@ public T evalSha(String scriptSha, ReturnType returnType, int numKeys, byte[ * @see org.springframework.data.redis.connection.RedisScriptingCommands#evalSha(byte[], org.springframework.data.redis.connection.ReturnType, int, byte[][]) */ @Override + @SuppressWarnings("unchecked") public T evalSha(byte[] scriptSha, ReturnType returnType, int numKeys, byte[]... keysAndArgs) { - throw new InvalidDataAccessApiUsageException("EvalSha is not supported in cluster environment."); + checkConnection(); + try { + return (T) new JedisScriptReturnConverter(returnType) + .convert(getCluster().evalsha(scriptSha, numKeys, keysAndArgs)); + } catch (Exception ex) { + throw convertJedisAccessException(ex); + } + } + + public JedisClusterConnection getClusterConnection() { + return clusterConnection; + } + + protected RuntimeException convertJedisAccessException(Exception ex) { + return clusterConnection.convertJedisAccessException(ex); + } + + private JedisCluster getCluster() { + return clusterConnection.getCluster(); + } + + protected void checkConnection() { + if (isQueueing()) { + throw new UnsupportedOperationException(); + } + if (isPipelined()) { + throw new UnsupportedOperationException(); + } + } + + private boolean isPipelined() { + return clusterConnection.isPipelined(); + } + + private boolean isQueueing() { + return clusterConnection.isQueueing(); } } diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java index 3236b61078..3d0a0eb82c 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java @@ -51,6 +51,7 @@ 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.ReturnType; import org.springframework.data.redis.connection.DefaultSortParameters; import org.springframework.data.redis.connection.DefaultTuple; import org.springframework.data.redis.connection.RedisClusterNode; @@ -70,6 +71,7 @@ import org.springframework.data.redis.test.util.MinimumRedisVersionRule; import org.springframework.data.redis.test.util.RedisClusterRule; import org.springframework.test.annotation.IfProfileValue; +import org.springframework.data.redis.core.script.DigestUtils; /** * @author Christoph Strobl @@ -2421,4 +2423,28 @@ public void bitfieldShouldWorkUsingNonZeroBasedOffset() { .valueAt(offset(0L).multipliedByTypeLength()) .get(INT_8).valueAt(offset(1L).multipliedByTypeLength())), contains(100L, -56L)); } + + @Test // DATAREDIS-1005 + @IfProfileValue(name = "redisVersion", value = "2.6+") + public void evalOK() { + byte[] keyAndArgs = JedisConverters.toBytes("FOO"); + + String luaScript = "return redis.call(\"INCR\", KEYS[1])"; + byte[] luaScriptBin = JedisConverters.toBytes(luaScript); + Long result = clusterConnection.scriptingCommands().eval(luaScriptBin, ReturnType.VALUE, 1, keyAndArgs); + assertThat(result, is(1L)); + } + + @Test // DATAREDIS-1005 + @IfProfileValue(name = "redisVersion", value = "2.6+") + public void evalShaOK() { + byte[] keyAndArgs = JedisConverters.toBytes("FOO"); + + String luaScript = "return redis.call(\"INCR\", KEYS[1])"; + String digest = DigestUtils.sha1DigestAsHex(luaScript); + byte[] luaScriptBin = JedisConverters.toBytes(digest); + Long result = clusterConnection.scriptingCommands().evalSha(luaScriptBin, ReturnType.VALUE, 1, keyAndArgs); + assertThat(result, is(1L)); + } + }