Class: RaceGuard::Distributed::RedisLockStore

Inherits:
Object
  • Object
show all
Includes:
LockStore
Defined in:
lib/race_guard/distributed/redis_lock_store.rb

Overview

Redis-backed LockStore using SET key token NX EX ttl and Lua scripts for compare-and-extend and compare-and-delete.

redis must support set(…, nx: true, ex: seconds) and eval(script, keys:, argv:) (the redis gem satisfies this).

Constant Summary collapse

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

Instance Method Summary collapse

Constructor Details

#initialize(redis) ⇒ RedisLockStore

Returns a new instance of RedisLockStore.



31
32
33
# File 'lib/race_guard/distributed/redis_lock_store.rb', line 31

def initialize(redis)
  @redis = redis
end

Instance Method Details

#claim(key:, token:, ttl:) ⇒ Object

Raises:

  • (ArgumentError)


35
36
37
38
39
# File 'lib/race_guard/distributed/redis_lock_store.rb', line 35

def claim(key:, token:, ttl:)
  raise ArgumentError, 'ttl must be positive' unless ttl.is_a?(Integer) && ttl.positive?

  !!@redis.set(key.to_s, token.to_s, nx: true, ex: ttl)
end

#release(key:, token:) ⇒ Object



48
49
50
51
# File 'lib/race_guard/distributed/redis_lock_store.rb', line 48

def release(key:, token:)
  n = @redis.eval(RELEASE_SCRIPT, keys: [key.to_s], argv: [token.to_s])
  n.to_i == 1
end

#renew(key:, token:, ttl:) ⇒ Object

Raises:

  • (ArgumentError)


41
42
43
44
45
46
# File 'lib/race_guard/distributed/redis_lock_store.rb', line 41

def renew(key:, token:, ttl:)
  raise ArgumentError, 'ttl must be positive' unless ttl.is_a?(Integer) && ttl.positive?

  n = @redis.eval(RENEW_SCRIPT, keys: [key.to_s], argv: [token.to_s, ttl.to_s])
  n.to_i == 1
end