diff --git a/spring-integration-redis/src/main/java/org/springframework/integration/redis/util/RedisLockRegistry.java b/spring-integration-redis/src/main/java/org/springframework/integration/redis/util/RedisLockRegistry.java index 8e0f3971c7..964019e7d5 100644 --- a/spring-integration-redis/src/main/java/org/springframework/integration/redis/util/RedisLockRegistry.java +++ b/spring-integration-redis/src/main/java/org/springframework/integration/redis/util/RedisLockRegistry.java @@ -670,7 +670,7 @@ private final class RedisPubSubLock extends RedisLock { private static final String UNLINK_UNLOCK_SCRIPT = """ local lockClientId = redis.call('GET', KEYS[1]) if (lockClientId == ARGV[1] and redis.call('UNLINK', KEYS[1]) == 1) then - redis.call('PUBLISH', KEYS[2], KEYS[1]) + redis.call('PUBLISH', ARGV[2], KEYS[1]) return true end return false @@ -693,8 +693,8 @@ protected boolean tryRedisLockInner(long time, long expireAfter) protected boolean removeLockKeyInnerUnlink() { final String unLockChannelKey = RedisLockRegistry.this.unLockChannelKey + ":" + this.lockKey; return Boolean.TRUE.equals(RedisLockRegistry.this.redisTemplate.execute( - UNLINK_UNLOCK_REDIS_SCRIPT, List.of(this.lockKey, unLockChannelKey), - RedisLockRegistry.this.clientId)); + UNLINK_UNLOCK_REDIS_SCRIPT, List.of(this.lockKey), + RedisLockRegistry.this.clientId, unLockChannelKey)); } private boolean subscribeLock(long time, long expireAfter) throws ExecutionException, InterruptedException { diff --git a/src/reference/antora/modules/ROOT/pages/redis.adoc b/src/reference/antora/modules/ROOT/pages/redis.adoc index bf591ba6ff..3bfbff8b93 100644 --- a/src/reference/antora/modules/ROOT/pages/redis.adoc +++ b/src/reference/antora/modules/ROOT/pages/redis.adoc @@ -865,4 +865,27 @@ When it is set, the lock will be automatically renewed every `1/3` of the expira Starting with version 7.0, the `RedisLock` implements `DistributedLock` interface to support the feature of customized time-to-live (TTL) for the lock status data. A `RedisLock` can now be acquired using the `lock(Duration ttl)` or `tryLock(long time, TimeUnit unit, Duration ttl)` method, with a specified time-to-live (TTL) value. -The `RedisLockRegistry` now provides new `renewLock(Object lockKey, Duration ttl)` method, allowing you to renew the lock with a custom time-to-live value. \ No newline at end of file +The `RedisLockRegistry` now provides new `renewLock(Object lockKey, Duration ttl)` method, allowing you to renew the lock with a custom time-to-live value. + +[[elasticache-valkey-cluster]] +=== AWS ElastiCache for Valkey Support in cluster mode + +Starting with version 6.4.9/6.5.4/7.0.0, `RedisLockRegistry` supports AWS Elasticache for Valkey in cluster mode. +In this version of valkey (a redis drop-in replacement), all PubSub operations (`PUBLISH`, `SUBSCRIBE`, etc.) use their sharded variants (`SPUBLISH`, `SSUBSCRIBE`, etc.) internally. +If you are observing errors in the form of: + +[source] +---- +Caused by: io.lettuce.core.RedisCommandExecutionException: ERR Script attempted to access keys that do not hash to the same slot script: b2dedc0ab01c17f9f20e3e6ddb62dcb6afbed0bd, on @user_script:3. +---- + +in the `unlock` step of the `RedisLockRegistry`, you have to supply a lock key that includes a hash tag `{...}` to ensure all operations in the `unlock` script are hashed to the same cluster slot/shard, e.g.: + +[source] +---- +RedisLockRegistry lockRegistry = new RedisLockRegistry("my-lock-key{choose_your_tag}"); + +lockRegistry.lock(); +# critical section +lockRegistry.unlock(); +---- \ No newline at end of file