-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
In what version(s) of Spring Integration are you seeing this issue?
spring-integration-redis 6.5.0
Describe the bug
Basically stopRenew() can be called from multiple threads and results in:
java.lang.NullPointerException: Cannot invoke "java.util.concurrent.ScheduledFuture.cancel(boolean)" because "this.renewFuture" is null at org.springframework.integration.redis.util.RedisLockRegistry$RedisLock.stopRenew(RedisLockRegistry.java:601) ~[spring-integration-redis-6.5.0.jar:6.5.0] at org.springframework.integration.redis.util.RedisLockRegistry$RedisLock.removeLockKey(RedisLockRegistry.java:572) ~[spring-integration-redis-6.5.0.jar:6.5.0] at org.springframework.integration.redis.util.RedisLockRegistry$RedisLock.unlock(RedisLockRegistry.java:536) ~[spring-integration-redis-6.5.0.jar:6.5.0]
To Reproduce
Isn't easy to reproduce, I am not an expert of Redis. BUT by static code analysis I can tell that there is one opportunity for this error to occur.
- we call unlock(), which calls stopRenew()
- There is also a taskExecutor that periodically runs renew(). So at the same time renew() could be running in separate thread, and it could happen (?) that
Boolean.TRUE.equals(RedisLockRegistry.this.redisTemplate.execute( RENEW_REDIS_SCRIPT, Collections.singletonList(this.lockKey), RedisLockRegistry.this.clientId, String.valueOf(RedisLockRegistry.this.expireAfter)));
returns false, thus we go into stopRenew() and we have a race condition.
I am not sure why would we get "FALSE" there, but maybe someone here can answer this question?
Expected behavior
I am expecting unlock() to be a safe method, doing it job regardless the internal state of a lock.
Sample
Just happened on production, and as it is a race condition exception I think static analysis should be sufficient.
I will create a PR with proposition to add "synchronised" keyword on stopRenew() method.
Renewable logic is very helpful and was introduced in commit: Commit 4d08e11
Tagging @artembilan , seems like you will be able to tell whether adding "synchronised" is sufficient :)