Skip to content

Commit f0dc1ec

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

File tree

1 file changed

+68
-32
lines changed

1 file changed

+68
-32
lines changed

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

Lines changed: 68 additions & 32 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
}
@@ -480,9 +475,9 @@ public final void unlock() {
480475

481476
private void removeLockKey() {
482477
if (RedisLockRegistry.this.unlinkAvailable) {
478+
Boolean unlinkResult = null;
483479
try {
484-
removeLockKeyInnerUnlink();
485-
return;
480+
unlinkResult = removeLockKeyInnerUnlink();
486481
}
487482
catch (Exception ex) {
488483
RedisLockRegistry.this.unlinkAvailable = false;
@@ -495,8 +490,17 @@ private void removeLockKey() {
495490
"falling back to the regular DELETE command: " + ex.getMessage());
496491
}
497492
}
493+
if(Boolean.TRUE.equals(unlinkResult)){
494+
return;
495+
} else if(unlinkResult!= null){
496+
throw new IllegalStateException("Lock was released in the store due to expiration. " +
497+
"The integrity of data protected by this lock may have been compromised.");
498+
}
499+
}
500+
if (!removeLockKeyInnerDelete()) {
501+
throw new IllegalStateException("Lock was released in the store due to expiration. " +
502+
"The integrity of data protected by this lock may have been compromised.");
498503
}
499-
removeLockKeyInnerDelete();
500504
}
501505

502506
@Override
@@ -559,19 +563,23 @@ private RedisLockRegistry getOuterType() {
559563

560564
private final class RedisPubSubLock extends RedisLock {
561565

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";
566+
private static final String UNLINK_UNLOCK_SCRIPT = """
567+
local lockClientId = redis.call('GET', KEYS[1])
568+
if (lockClientId == ARGV[1] and redis.call('UNLINK', KEYS[1]) == 1) then
569+
redis.call('PUBLISH', ARGV[2], KEYS[1])
570+
return true
571+
end
572+
return false
573+
""";
568574

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";
575+
private static final String DELETE_UNLOCK_SCRIPT = """
576+
local lockClientId = redis.call('GET', KEYS[1])
577+
if (lockClientId == ARGV[1] and redis.call('DEL', KEYS[1]) == 1) then
578+
redis.call('PUBLISH', ARGV[2], KEYS[1])
579+
return true
580+
end
581+
return false
582+
""";
575583

576584
private static final RedisScript<Boolean>
577585
UNLINK_UNLOCK_REDIS_SCRIPT = new DefaultRedisScript<>(UNLINK_UNLOCK_SCRIPT, Boolean.class);
@@ -589,17 +597,17 @@ protected boolean tryRedisLockInner(long time) throws ExecutionException, Interr
589597
}
590598

591599
@Override
592-
protected void removeLockKeyInnerUnlink() {
593-
RedisLockRegistry.this.redisTemplate.execute(
600+
protected Boolean removeLockKeyInnerUnlink() {
601+
return RedisLockRegistry.this.redisTemplate.execute(
594602
UNLINK_UNLOCK_REDIS_SCRIPT, Collections.singletonList(this.lockKey),
595-
RedisLockRegistry.this.unLockChannelKey);
603+
RedisLockRegistry.this.clientId,RedisLockRegistry.this.unLockChannelKey);
596604
}
597605

598606
@Override
599-
protected void removeLockKeyInnerDelete() {
600-
RedisLockRegistry.this.redisTemplate.execute(
607+
protected Boolean removeLockKeyInnerDelete() {
608+
return RedisLockRegistry.this.redisTemplate.execute(
601609
DELETE_UNLOCK_REDIS_SCRIPT, Collections.singletonList(this.lockKey),
602-
RedisLockRegistry.this.unLockChannelKey);
610+
RedisLockRegistry.this.clientId,RedisLockRegistry.this.unLockChannelKey);
603611

604612
}
605613

@@ -694,6 +702,30 @@ private void unlockNotify(String lockKey) {
694702

695703
private final class RedisSpinLock extends RedisLock {
696704

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

720752
@Override
721-
protected void removeLockKeyInnerUnlink() {
722-
RedisLockRegistry.this.redisTemplate.unlink(this.lockKey);
753+
protected Boolean removeLockKeyInnerUnlink() {
754+
return RedisLockRegistry.this.redisTemplate.execute(
755+
UNLINK_UNLOCK_REDIS_SCRIPT, Collections.singletonList(this.lockKey),
756+
RedisLockRegistry.this.clientId);
723757
}
724758

725759
@Override
726-
protected void removeLockKeyInnerDelete() {
727-
RedisLockRegistry.this.redisTemplate.delete(this.lockKey);
760+
protected Boolean removeLockKeyInnerDelete() {
761+
return RedisLockRegistry.this.redisTemplate.execute(
762+
DELETE_UNLOCK_REDIS_SCRIPT, Collections.singletonList(this.lockKey),
763+
RedisLockRegistry.this.clientId);
728764
}
729765

730766
}

0 commit comments

Comments
 (0)