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

Inherits:
Object
  • Object
show all
Defined in:
lib/stoplight/infrastructure/redis/storage/recovery_lock.rb

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 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 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

#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