Class: Stoplight::Infrastructure::Redis::Storage::RecoveryLock

Inherits:
Object
  • Object
show all
Includes:
Domain::_RecoveryLockStore
Defined in:
lib/stoplight/infrastructure/redis/storage/recovery_lock.rb,
sig/_private/stoplight/infrastructure/redis/storage/recovery_lock.rbs

Overview

Distributed recovery lock using Redis SET NX (set-if-not-exists).

Lock Acquisition:

  • Uses unique UUID token to prevent accidental release of others' locks
  • Atomic SET with NX flag ensures only one process acquires recovery_lock
  • TTL (px: lock_timeout) auto-releases recovery_lock if process crashes

Lock Release:

  • Lua script ensures only token holder can release (token comparison)
  • Best-effort release; TTL cleanup handles failures

Failure Modes:

  • Lock contention: Returns false, caller should skip probe
  • Redis unavailable: raises an error and let caller decide
  • Crashed holder: raises an error and let caller decide. Lock auto-expires after lock_timeout
  • Release failure: Lock auto-expires after lock_timeout

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config:, redis:, scripting:, key_space:) ⇒ RecoveryLock

Returns a new instance of RecoveryLock.



25
26
27
28
29
30
# File 'lib/stoplight/infrastructure/redis/storage/recovery_lock.rb', line 25

def initialize(config:, redis:, scripting:, key_space:)
  @config = config
  @redis = redis
  @scripting = scripting
  @key_space = key_space
end

Instance Attribute Details

#configDomain::Config (readonly)

Returns the value of attribute config.

Returns:



53
54
55
# File 'lib/stoplight/infrastructure/redis/storage/recovery_lock.rb', line 53

def config
  @config
end

#key_spaceKeySpace (readonly)

Returns the value of attribute key_space.

Returns:



56
57
58
# File 'lib/stoplight/infrastructure/redis/storage/recovery_lock.rb', line 56

def key_space
  @key_space
end

#redisredis (readonly)

Returns the value of attribute redis.

Returns:



54
55
56
# File 'lib/stoplight/infrastructure/redis/storage/recovery_lock.rb', line 54

def redis
  @redis
end

#scriptingScripting (readonly)

Returns the value of attribute scripting.

Returns:



55
56
57
# File 'lib/stoplight/infrastructure/redis/storage/recovery_lock.rb', line 55

def scripting
  @scripting
end

Instance Method Details

#acquire_lockObject



32
33
34
35
36
37
38
39
40
# File 'lib/stoplight/infrastructure/redis/storage/recovery_lock.rb', line 32

def acquire_lock
  recovery_lock = Domain::Storage::RecoveryLockToken.new

  acquired = redis.then do |client|
    client.set(lock_key, recovery_lock.token, nx: true, px: lock_timeout)
  end

  recovery_lock if acquired
end

#lock_keyString

Returns:

  • (String)


58
# File 'lib/stoplight/infrastructure/redis/storage/recovery_lock.rb', line 58

def lock_key = key_space.key(:locks, :recovery)

#lock_timeoutInteger

Returns:

  • (Integer)


59
# File 'lib/stoplight/infrastructure/redis/storage/recovery_lock.rb', line 59

def lock_timeout = config.cool_off_time_in_milliseconds

#release_lock(recovery_lock) ⇒ void

This method returns an undefined value.



44
45
46
47
48
49
# File 'lib/stoplight/infrastructure/redis/storage/recovery_lock.rb', line 44

def release_lock(recovery_lock)
  scripting.call(
    :"recovery_lock/release_lock",
    keys: [lock_key], args: [recovery_lock.token]
  )
end