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 828c18701d..e24d56d271 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 @@ -83,6 +83,7 @@ import org.springframework.util.ReflectionUtils; * @author Unseok Kim * @author Anton Gabov * @author Christian Tzolov + * @author Eddie Cho * * @since 4.0 * @@ -98,7 +99,7 @@ public final class RedisLockRegistry implements ExpirableLockRegistry, Disposabl private final Lock lock = new ReentrantLock(); private final Map locks = - new LinkedHashMap(16, 0.75F, true) { + new LinkedHashMap<>(16, 0.75F, true) { @Override protected boolean removeEldestEntry(Entry eldest) { @@ -343,12 +344,12 @@ public final class RedisLockRegistry implements ExpirableLockRegistry, Disposabl /** * Unlock the lock using the unlink method in redis. */ - protected abstract void removeLockKeyInnerUnlink(); + protected abstract boolean removeLockKeyInnerUnlink(); /** * Unlock the lock using the delete method in redis. */ - protected abstract void removeLockKeyInnerDelete(); + protected abstract boolean removeLockKeyInnerDelete(); @Override public final void lock() { @@ -454,11 +455,6 @@ public final class RedisLockRegistry implements ExpirableLockRegistry, Disposabl return; } try { - if (!isAcquiredInThisProcess()) { - throw new IllegalStateException("Lock was released in the store due to expiration. " + - "The integrity of data protected by this lock may have been compromised."); - } - if (Thread.currentThread().isInterrupted()) { RedisLockRegistry.this.executor.execute(this::removeLockKey); } @@ -481,7 +477,11 @@ public final class RedisLockRegistry implements ExpirableLockRegistry, Disposabl private void removeLockKey() { if (RedisLockRegistry.this.unlinkAvailable) { try { - removeLockKeyInnerUnlink(); + boolean unlinkResult = removeLockKeyInnerUnlink(); + if (!unlinkResult) { + throw new IllegalStateException("Lock was released in the store due to expiration. " + + "The integrity of data protected by this lock may have been compromised."); + } return; } catch (Exception ex) { @@ -496,7 +496,10 @@ public final class RedisLockRegistry implements ExpirableLockRegistry, Disposabl } } } - removeLockKeyInnerDelete(); + if (!removeLockKeyInnerDelete()) { + throw new IllegalStateException("Lock was released in the store due to expiration. " + + "The integrity of data protected by this lock may have been compromised."); + } } @Override @@ -559,19 +562,23 @@ public final class RedisLockRegistry implements ExpirableLockRegistry, Disposabl private final class RedisPubSubLock extends RedisLock { - private static final String UNLINK_UNLOCK_SCRIPT = - "if (redis.call('unlink', KEYS[1]) == 1) then " + - "redis.call('publish', ARGV[1], KEYS[1]) " + - "return true " + - "end " + - "return false"; + 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', ARGV[2], KEYS[1]) + return true + end + return false + """; - private static final String DELETE_UNLOCK_SCRIPT = - "if (redis.call('del', KEYS[1]) == 1) then " + - "redis.call('publish', ARGV[1], KEYS[1]) " + - "return true " + - "end " + - "return false"; + private static final String DELETE_UNLOCK_SCRIPT = """ + local lockClientId = redis.call('GET', KEYS[1]) + if (lockClientId == ARGV[1] and redis.call('DEL', KEYS[1]) == 1) then + redis.call('PUBLISH', ARGV[2], KEYS[1]) + return true + end + return false + """; private static final RedisScript UNLINK_UNLOCK_REDIS_SCRIPT = new DefaultRedisScript<>(UNLINK_UNLOCK_SCRIPT, Boolean.class); @@ -589,18 +596,19 @@ public final class RedisLockRegistry implements ExpirableLockRegistry, Disposabl } @Override - protected void removeLockKeyInnerUnlink() { - RedisLockRegistry.this.redisTemplate.execute( - UNLINK_UNLOCK_REDIS_SCRIPT, Collections.singletonList(this.lockKey), - RedisLockRegistry.this.unLockChannelKey); + protected boolean removeLockKeyInnerUnlink() { + return removeLockKeyWithScript(UNLINK_UNLOCK_REDIS_SCRIPT); } @Override - protected void removeLockKeyInnerDelete() { - RedisLockRegistry.this.redisTemplate.execute( - DELETE_UNLOCK_REDIS_SCRIPT, Collections.singletonList(this.lockKey), - RedisLockRegistry.this.unLockChannelKey); + protected boolean removeLockKeyInnerDelete() { + return removeLockKeyWithScript(DELETE_UNLOCK_REDIS_SCRIPT); + } + private boolean removeLockKeyWithScript(RedisScript redisScript) { + return Boolean.TRUE.equals(RedisLockRegistry.this.redisTemplate.execute( + redisScript, Collections.singletonList(this.lockKey), + RedisLockRegistry.this.clientId, RedisLockRegistry.this.unLockChannelKey)); } private boolean subscribeLock(long time) throws ExecutionException, InterruptedException { @@ -694,6 +702,30 @@ public final class RedisLockRegistry implements ExpirableLockRegistry, Disposabl private final class RedisSpinLock extends RedisLock { + private static final String UNLINK_UNLOCK_SCRIPT = """ + local lockClientId = redis.call('GET', KEYS[1]) + if lockClientId == ARGV[1] then + redis.call('UNLINK', KEYS[1]) + return true + end + return false + """; + + private static final String DELETE_UNLOCK_SCRIPT = """ + local lockClientId = redis.call('GET', KEYS[1]) + if lockClientId == ARGV[1] then + redis.call('DEL', KEYS[1]) + return true + end + return false + """; + + private static final RedisScript + UNLINK_UNLOCK_REDIS_SCRIPT = new DefaultRedisScript<>(UNLINK_UNLOCK_SCRIPT, Boolean.class); + + private static final RedisScript + DELETE_UNLOCK_REDIS_SCRIPT = new DefaultRedisScript<>(DELETE_UNLOCK_SCRIPT, Boolean.class); + private RedisSpinLock(String path) { super(path); } @@ -718,13 +750,19 @@ public final class RedisLockRegistry implements ExpirableLockRegistry, Disposabl } @Override - protected void removeLockKeyInnerUnlink() { - RedisLockRegistry.this.redisTemplate.unlink(this.lockKey); + protected boolean removeLockKeyInnerUnlink() { + return removeLockKeyWithScript(UNLINK_UNLOCK_REDIS_SCRIPT); } @Override - protected void removeLockKeyInnerDelete() { - RedisLockRegistry.this.redisTemplate.delete(this.lockKey); + protected boolean removeLockKeyInnerDelete() { + return removeLockKeyWithScript(DELETE_UNLOCK_REDIS_SCRIPT); + } + + private boolean removeLockKeyWithScript(RedisScript redisScript) { + return Boolean.TRUE.equals(RedisLockRegistry.this.redisTemplate.execute( + redisScript, Collections.singletonList(this.lockKey), + RedisLockRegistry.this.clientId)); } }