Skip to content

Commit 9040ad4

Browse files
committed
GH-8699: Enhancing unlock() Method of RedisLock with Atomic Redis Operation
1 parent 50bf00e commit 9040ad4

File tree

1 file changed

+64
-31
lines changed

1 file changed

+64
-31
lines changed

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

Lines changed: 64 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -343,12 +343,12 @@ public long getLockedAt() {
343343
/**
344344
* Unlock the lock using the unlink method in redis.
345345
*/
346-
protected abstract void removeLockKeyInnerUnlink();
346+
protected abstract Boolean removeLockKeyInnerUnlink();
347347

348348
/**
349349
* Unlock the lock using the delete method in redis.
350350
*/
351-
protected abstract void removeLockKeyInnerDelete();
351+
protected abstract Boolean removeLockKeyInnerDelete();
352352

353353
@Override
354354
public final void lock() {
@@ -454,11 +454,6 @@ public final void unlock() {
454454
return;
455455
}
456456
try {
457-
if (!isAcquiredInThisProcess()) {
458-
throw new IllegalStateException("Lock was released in the store due to expiration. " +
459-
"The integrity of data protected by this lock may have been compromised.");
460-
}
461-
462457
if (Thread.currentThread().isInterrupted()) {
463458
RedisLockRegistry.this.executor.execute(this::removeLockKey);
464459
}
@@ -481,7 +476,10 @@ public final void unlock() {
481476
private void removeLockKey() {
482477
if (RedisLockRegistry.this.unlinkAvailable) {
483478
try {
484-
removeLockKeyInnerUnlink();
479+
if (!removeLockKeyInnerUnlink()) {
480+
throw new IllegalStateException("Lock was released in the store due to expiration. " +
481+
"The integrity of data protected by this lock may have been compromised.");
482+
}
485483
return;
486484
}
487485
catch (Exception ex) {
@@ -496,7 +494,10 @@ private void removeLockKey() {
496494
}
497495
}
498496
}
499-
removeLockKeyInnerDelete();
497+
if (!removeLockKeyInnerDelete()) {
498+
throw new IllegalStateException("Lock was released in the store due to expiration. " +
499+
"The integrity of data protected by this lock may have been compromised.");
500+
}
500501
}
501502

502503
@Override
@@ -559,19 +560,23 @@ private RedisLockRegistry getOuterType() {
559560

560561
private final class RedisPubSubLock extends RedisLock {
561562

562-
private static final String UNLINK_UNLOCK_SCRIPT =
563-
"if (redis.call('unlink', KEYS[1]) == 1) then " +
564-
"redis.call('publish', ARGV[1], KEYS[1]) " +
565-
"return true " +
566-
"end " +
567-
"return false";
563+
private static final String UNLINK_UNLOCK_SCRIPT = """
564+
local lockClientId = redis.call('GET', KEYS[1])
565+
if (lockClientId == ARGV[1] and redis.call('UNLINK', KEYS[1]) == 1) then
566+
redis.call('PUBLISH', ARGV[2], KEYS[1])
567+
return true
568+
end
569+
return false
570+
""";
568571

569-
private static final String DELETE_UNLOCK_SCRIPT =
570-
"if (redis.call('del', KEYS[1]) == 1) then " +
571-
"redis.call('publish', ARGV[1], KEYS[1]) " +
572-
"return true " +
573-
"end " +
574-
"return false";
572+
private static final String DELETE_UNLOCK_SCRIPT = """
573+
local lockClientId = redis.call('GET', KEYS[1])
574+
if (lockClientId == ARGV[1] and redis.call('DEL', KEYS[1]) == 1) then
575+
redis.call('PUBLISH', ARGV[2], KEYS[1])
576+
return true
577+
end
578+
return false
579+
""";
575580

576581
private static final RedisScript<Boolean>
577582
UNLINK_UNLOCK_REDIS_SCRIPT = new DefaultRedisScript<>(UNLINK_UNLOCK_SCRIPT, Boolean.class);
@@ -589,17 +594,17 @@ protected boolean tryRedisLockInner(long time) throws ExecutionException, Interr
589594
}
590595

591596
@Override
592-
protected void removeLockKeyInnerUnlink() {
593-
RedisLockRegistry.this.redisTemplate.execute(
597+
protected Boolean removeLockKeyInnerUnlink() {
598+
return RedisLockRegistry.this.redisTemplate.execute(
594599
UNLINK_UNLOCK_REDIS_SCRIPT, Collections.singletonList(this.lockKey),
595-
RedisLockRegistry.this.unLockChannelKey);
600+
RedisLockRegistry.this.clientId,RedisLockRegistry.this.unLockChannelKey);
596601
}
597602

598603
@Override
599-
protected void removeLockKeyInnerDelete() {
600-
RedisLockRegistry.this.redisTemplate.execute(
604+
protected Boolean removeLockKeyInnerDelete() {
605+
return RedisLockRegistry.this.redisTemplate.execute(
601606
DELETE_UNLOCK_REDIS_SCRIPT, Collections.singletonList(this.lockKey),
602-
RedisLockRegistry.this.unLockChannelKey);
607+
RedisLockRegistry.this.clientId,RedisLockRegistry.this.unLockChannelKey);
603608

604609
}
605610

@@ -694,6 +699,30 @@ private void unlockNotify(String lockKey) {
694699

695700
private final class RedisSpinLock extends RedisLock {
696701

702+
private static final String UNLINK_UNLOCK_SCRIPT = """
703+
local lockClientId = redis.call('GET', KEYS[1])
704+
if lockClientId == ARGV[1] then
705+
redis.call('UNLINK', KEYS[1])
706+
return true
707+
end
708+
return false
709+
""";
710+
711+
private static final String DELETE_UNLOCK_SCRIPT = """
712+
local lockClientId = redis.call('GET', KEYS[1])
713+
if lockClientId == ARGV[1] then
714+
redis.call('DEL', KEYS[1])
715+
return true
716+
end
717+
return false
718+
""";
719+
720+
private static final RedisScript<Boolean>
721+
UNLINK_UNLOCK_REDIS_SCRIPT = new DefaultRedisScript<>(UNLINK_UNLOCK_SCRIPT, Boolean.class);
722+
723+
private static final RedisScript<Boolean>
724+
DELETE_UNLOCK_REDIS_SCRIPT = new DefaultRedisScript<>(DELETE_UNLOCK_SCRIPT, Boolean.class);
725+
697726
private RedisSpinLock(String path) {
698727
super(path);
699728
}
@@ -718,13 +747,17 @@ protected boolean tryRedisLockInner(long time) throws InterruptedException {
718747
}
719748

720749
@Override
721-
protected void removeLockKeyInnerUnlink() {
722-
RedisLockRegistry.this.redisTemplate.unlink(this.lockKey);
750+
protected Boolean removeLockKeyInnerUnlink() {
751+
return RedisLockRegistry.this.redisTemplate.execute(
752+
UNLINK_UNLOCK_REDIS_SCRIPT, Collections.singletonList(this.lockKey),
753+
RedisLockRegistry.this.clientId);
723754
}
724755

725756
@Override
726-
protected void removeLockKeyInnerDelete() {
727-
RedisLockRegistry.this.redisTemplate.delete(this.lockKey);
757+
protected Boolean removeLockKeyInnerDelete() {
758+
return RedisLockRegistry.this.redisTemplate.execute(
759+
DELETE_UNLOCK_REDIS_SCRIPT, Collections.singletonList(this.lockKey),
760+
RedisLockRegistry.this.clientId);
728761
}
729762

730763
}

0 commit comments

Comments
 (0)