Skip to content

Commit 0cffe5c

Browse files
authored
GH-10546: Fix RedisLockRegistry for cluster
Fixes: #10546 * pass `unLockChannelKey` as `ARGV[2]` instead of `KEYS[2]` to make this unlock script compatible with both Redis cluster and AWS Elastic valkey cluster * add documentation for AWS ElastiCache Valkey Support in `RedisLockRegistry` Signed-off-by: Severin Kistler <kistlerseverin@gmail.com> **Auto-cherry-pick to `6.5.x` & `6.4.x`**
1 parent de6f561 commit 0cffe5c

File tree

2 files changed

+27
-4
lines changed

2 files changed

+27
-4
lines changed

spring-integration-redis/src/main/java/org/springframework/integration/redis/util/RedisLockRegistry.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,7 @@ private final class RedisPubSubLock extends RedisLock {
670670
private static final String UNLINK_UNLOCK_SCRIPT = """
671671
local lockClientId = redis.call('GET', KEYS[1])
672672
if (lockClientId == ARGV[1] and redis.call('UNLINK', KEYS[1]) == 1) then
673-
redis.call('PUBLISH', KEYS[2], KEYS[1])
673+
redis.call('PUBLISH', ARGV[2], KEYS[1])
674674
return true
675675
end
676676
return false
@@ -693,8 +693,8 @@ protected boolean tryRedisLockInner(long time, long expireAfter)
693693
protected boolean removeLockKeyInnerUnlink() {
694694
final String unLockChannelKey = RedisLockRegistry.this.unLockChannelKey + ":" + this.lockKey;
695695
return Boolean.TRUE.equals(RedisLockRegistry.this.redisTemplate.execute(
696-
UNLINK_UNLOCK_REDIS_SCRIPT, List.of(this.lockKey, unLockChannelKey),
697-
RedisLockRegistry.this.clientId));
696+
UNLINK_UNLOCK_REDIS_SCRIPT, List.of(this.lockKey),
697+
RedisLockRegistry.this.clientId, unLockChannelKey));
698698
}
699699

700700
private boolean subscribeLock(long time, long expireAfter) throws ExecutionException, InterruptedException {

src/reference/antora/modules/ROOT/pages/redis.adoc

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -865,4 +865,27 @@ When it is set, the lock will be automatically renewed every `1/3` of the expira
865865

866866
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.
867867
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.
868-
The `RedisLockRegistry` now provides new `renewLock(Object lockKey, Duration ttl)` method, allowing you to renew the lock with a custom time-to-live value.
868+
The `RedisLockRegistry` now provides new `renewLock(Object lockKey, Duration ttl)` method, allowing you to renew the lock with a custom time-to-live value.
869+
870+
[[elasticache-valkey-cluster]]
871+
=== AWS ElastiCache for Valkey Support in cluster mode
872+
873+
Starting with version 6.4.9/6.5.4/7.0.0, `RedisLockRegistry` supports AWS Elasticache for Valkey in cluster mode.
874+
In this version of valkey (a redis drop-in replacement), all PubSub operations (`PUBLISH`, `SUBSCRIBE`, etc.) use their sharded variants (`SPUBLISH`, `SSUBSCRIBE`, etc.) internally.
875+
If you are observing errors in the form of:
876+
877+
[source]
878+
----
879+
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.
880+
----
881+
882+
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.:
883+
884+
[source]
885+
----
886+
RedisLockRegistry lockRegistry = new RedisLockRegistry("my-lock-key{choose_your_tag}");
887+
888+
lockRegistry.lock();
889+
# critical section
890+
lockRegistry.unlock();
891+
----

0 commit comments

Comments
 (0)