Module: Legion::Lock

Defined in:
lib/legion/lock.rb

Defined Under Namespace

Classes: NotAcquired

Constant Summary collapse

RELEASE_SCRIPT =
<<~LUA
  if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("del", KEYS[1])
  else
    return 0
  end
LUA
EXTEND_SCRIPT =
<<~LUA
  if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("pexpire", KEYS[1], ARGV[2])
  else
    return 0
  end
LUA

Class Method Summary collapse

Class Method Details

.acquire(name, ttl: 30_000) ⇒ Object



26
27
28
29
30
31
32
33
34
# File 'lib/legion/lock.rb', line 26

def acquire(name, ttl: 30_000)
  token = SecureRandom.uuid
  key = lock_key(name)
  result = with_redis { |conn| conn.set(key, token, nx: true, px: ttl) }
  result ? token : nil
rescue StandardError => e
  Legion::Logging.debug "Lock#acquire failed for name=#{name}: #{e.message}" if defined?(Legion::Logging)
  nil
end

.extend_lock(name, token, ttl: 30_000) ⇒ Object



54
55
56
57
58
59
60
61
# File 'lib/legion/lock.rb', line 54

def extend_lock(name, token, ttl: 30_000)
  key = lock_key(name)
  result = with_redis { |conn| conn.eval(EXTEND_SCRIPT, keys: [key], argv: [token, ttl.to_s]) }
  result == 1
rescue StandardError => e
  Legion::Logging.debug "Lock#extend_lock failed for name=#{name}: #{e.message}" if defined?(Legion::Logging)
  false
end

.locked?(name) ⇒ Boolean

Returns:

  • (Boolean)


63
64
65
66
67
68
# File 'lib/legion/lock.rb', line 63

def locked?(name)
  with_redis { |conn| conn.exists?(lock_key(name)) }
rescue StandardError => e
  Legion::Logging.debug "Lock#locked? failed for name=#{name}: #{e.message}" if defined?(Legion::Logging)
  false
end

.release(name, token) ⇒ Object



36
37
38
39
40
41
42
43
# File 'lib/legion/lock.rb', line 36

def release(name, token)
  key = lock_key(name)
  result = with_redis { |conn| conn.eval(RELEASE_SCRIPT, keys: [key], argv: [token]) }
  result == 1
rescue StandardError => e
  Legion::Logging.debug "Lock#release failed for name=#{name}: #{e.message}" if defined?(Legion::Logging)
  false
end

.with_lock(name, ttl: 30_000) ⇒ Object



45
46
47
48
49
50
51
52
# File 'lib/legion/lock.rb', line 45

def with_lock(name, ttl: 30_000)
  token = acquire(name, ttl: ttl)
  raise NotAcquired, "could not acquire lock: #{name}" unless token

  yield
ensure
  release(name, token) if token
end