Class RedisLockService

  • All Implemented Interfaces:

    
    public class RedisLockService
    
                        

    Redis based lock service. Reentrant, read/write locks, deadlock detection

    The Redis clock is used as a time source such that all JVMs use the same source. Note: A forward clock jump on the Redis host can let two threads hold the same lock.

    A Redis restart (and potential data loss) is detected and handled. However, a failover to a replica - as done by Redis Cluster or Sentinel - is not detected, and recent lock writes might not have been replicated yet. Two threads could then hold the same lock. Must be used only in combination with standalone Redis, without failover.

    If a Redis restart is detected (which might have led to (partial) data loss - or not), then the lock is considered lost.

    • Nested Class Summary

      Nested Classes 
      Modifier and Type Class Description
    • Field Summary

      Fields 
      Modifier and Type Field Description
    • Enum Constant Summary

      Enum Constants 
      Enum Constant Description
    • Method Summary

      Modifier and Type Method Description
      final List<LockInfo> getLockInfo(Collection<String> lockNames) Fetches the LockInfos for given lockNames, if still available in Redis
      final <T extends Any> Pair<Boolean, T> tryLock(Collection<Lock> locks, Long timeoutMs, Boolean keepLockDataAfterUse, Function1<Function0<Boolean>, T> protectedCode) Tries to acquire all locks atomically and runs the protected code.
      • Methods inherited from class java.lang.Object

        clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    • Constructor Detail

      • RedisLockService

        RedisLockService(RedisConnectionPool connectionPool, RedisPubSubService redisPubSubService, Long lockLeaseTimeSeconds, Long lockLeaseRenewalTimeSeconds, Long pollDelayWhenWaitingForLockMs, Duration connectionPoolTimeout, Long expireLocksWhichShallNotBeKeptSeconds, String lockKeyPrefix, LockServiceJsonHandler lockServiceJsonHandler, Function1<Runnable, Runnable> onNewThreadHandler)
        Parameters:
        connectionPool - the pool to be used for communicating with Redis
        redisPubSubService - RedisPubSubService used for broadcasting waiters and faster reaction time upon releases
        lockLeaseTimeSeconds - duration in seconds, for how long a lock should be marked as taken in Redis (lease time).
        lockLeaseRenewalTimeSeconds - duration in seconds, for how often the lease time should automatically be extended while the locks are being held.
        pollDelayWhenWaitingForLockMs - time to wait between attempts to acquire the locks.
        expireLocksWhichShallNotBeKeptSeconds - expire time in seconds for locks, for which keepLockDataAfterUse (see tryLock) was set to false.
        lockKeyPrefix - prefix that shall be used for all keys in Redis (namespacing)
        onNewThreadHandler - each time a new virtual thread is started for renewing the leases of an acquisition, this handler is used to wrap the Runnable.
    • Method Detail

      • tryLock

         final <T extends Any> Pair<Boolean, T> tryLock(Collection<Lock> locks, Long timeoutMs, Boolean keepLockDataAfterUse, Function1<Function0<Boolean>, T> protectedCode)

        Tries to acquire all locks atomically and runs the protected code.

        While the lock is being held and the protected code is being executed, the lease is automatically renewed to ensure that the lock does not expire. The protected code can use the provided hasLock lambda to check if the lock is still being held. It should abort if this lambda returns false.

        The calling thread should preferably be a VirtualThread. It is put to wait (for max timeoutMs) when the lock is currently unavailable, which should allow the JVM to reschedule the carrier thread during this period.

        Writers are preferred: a read lock is not granted while another thread waits for the write lock of the same name (also with timeoutMs = 0). Read locks the current thread already holds are not affected.

        Parameters:
        locks - that must all be acquired before running the protected code
        timeoutMs - max time to wait in case one or more of the requested locks are currently taken/unavailable
        keepLockDataAfterUse - if true, the lock data, stored in Redis, is kept after use.
        protectedCode - the code to be executed once all locks are acquired
        Returns:

        If the locks were acquired and the protected code was executed, then true+result is returned. If the locks could not be acquired within the timeout, then false+null is returned. That is also the case for a nested call, if one of the locks the current thread already holds was lost meanwhile (see hasLock) - it is not acquired again