From 6044a8cc54fd020425d1b8d95db0cfb7383d1837 Mon Sep 17 00:00:00 2001 From: Severin Kistler Date: Thu, 6 Nov 2025 09:07:08 +0100 Subject: [PATCH 1/3] pass unLockChannelKey as `ARGV[2]` instead of `KEYS[2]` to make this unlock script compatible with both redis cluster and AWS Elastic valkey cluster Signed-off-by: Severin Kistler --- .../integration/redis/util/RedisLockRegistry.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 { From 508d73f50ebf1be1cc77e670dc3a9bbbff52272c Mon Sep 17 00:00:00 2001 From: Severin Kistler Date: Fri, 7 Nov 2025 08:53:59 +0100 Subject: [PATCH 2/3] add documentation for AWS ElastiCache Valkey Support in RedisLockRegistry Signed-off-by: Severin Kistler --- .../antora/modules/ROOT/pages/redis.adoc | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/reference/antora/modules/ROOT/pages/redis.adoc b/src/reference/antora/modules/ROOT/pages/redis.adoc index bf591ba6ff..e4c86554d9 100644 --- a/src/reference/antora/modules/ROOT/pages/redis.adoc +++ b/src/reference/antora/modules/ROOT/pages/redis.adoc @@ -865,4 +865,26 @@ 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 From 4e3aeaeccd7c6306f3ffdb282d1ef55cbf44f980 Mon Sep 17 00:00:00 2001 From: Severin Kistler Date: Sat, 8 Nov 2025 08:01:53 +0100 Subject: [PATCH 3/3] redis valkey cluster docs PR fixes Signed-off-by: Severin Kistler --- src/reference/antora/modules/ROOT/pages/redis.adoc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/reference/antora/modules/ROOT/pages/redis.adoc b/src/reference/antora/modules/ROOT/pages/redis.adoc index e4c86554d9..3bfbff8b93 100644 --- a/src/reference/antora/modules/ROOT/pages/redis.adoc +++ b/src/reference/antora/modules/ROOT/pages/redis.adoc @@ -870,8 +870,9 @@ The `RedisLockRegistry` now provides new `renewLock(Object lockKey, Duration ttl [[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 +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] ----