Class: MasterLock::RedisLock

Inherits:
Object
  • Object
show all
Defined in:
lib/master_lock/redis_lock.rb

Overview

RedisLock implements a mutex in Redis according to the strategy documented at redis.io/commands/SET#patterns. The lock has a string identifier and when acquired will be registered to an owner, also identified by a string. Locks have an expiration time, after which they will be released automatically so that unexpected failures do not result in locks getting stuck.

Constant Summary collapse

DEFAULT_SLEEP_INTERVAL =
0.1

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(redis:, key:, owner:, ttl:, sleep_interval: DEFAULT_SLEEP_INTERVAL) ⇒ RedisLock

Returns a new instance of RedisLock.



25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/master_lock/redis_lock.rb', line 25

def initialize(
  redis:,
  key:,
  owner:,
  ttl:,
  sleep_interval: DEFAULT_SLEEP_INTERVAL
)
  @redis = redis
  @key = key
  @owner = owner
  @ttl = ttl
  @sleep_interval = sleep_interval
end

Instance Attribute Details

#keyString (readonly)

Returns the unique identifier for the locked resource.

Returns:

  • (String)

    the unique identifier for the locked resource



17
18
19
# File 'lib/master_lock/redis_lock.rb', line 17

def key
  @key
end

#ownerString (readonly)

Returns the identity of the owner acquiring the lock.

Returns:

  • (String)

    the identity of the owner acquiring the lock



20
21
22
# File 'lib/master_lock/redis_lock.rb', line 20

def owner
  @owner
end

#redisRedis (readonly)

Returns the Redis connection used to manage lock.

Returns:

  • (Redis)

    the Redis connection used to manage lock



14
15
16
# File 'lib/master_lock/redis_lock.rb', line 14

def redis
  @redis
end

#ttlFixnum (readonly)

Returns the lifetime of the lock in seconds.

Returns:

  • (Fixnum)

    the lifetime of the lock in seconds



23
24
25
# File 'lib/master_lock/redis_lock.rb', line 23

def ttl
  @ttl
end

Instance Method Details

#acquire(timeout:) ⇒ Boolean

Attempt to acquire the lock. If the lock is already held, this will attempt multiple times to acquire the lock until the timeout period is up.

Parameters:

  • how (Fixnum)

    long to wait to acquire the lock before failing

Returns:

  • (Boolean)

    whether the lock was acquired successfully



44
45
46
47
48
49
50
51
52
# File 'lib/master_lock/redis_lock.rb', line 44

def acquire(timeout:)
  timeout_time = Time.now + timeout
  loop do
    locked = redis.set(redis_key, owner, nx: true, px: ttl_ms)
    return true if locked
    return false if Time.now >= timeout_time
    sleep(@sleep_interval)
  end
end

#extendBoolean

Extend the expiration time of the lock if still held by this owner. If the lock is no longer held by the owner, this method will fail and return false. The lock lifetime is extended by the configured ttl.

Returns:

  • (Boolean)

    whether the lock was extended successfully



59
60
61
62
63
64
65
66
67
# File 'lib/master_lock/redis_lock.rb', line 59

def extend
  result = eval_script(
    RedisScripts::EXTEND_SCRIPT,
    RedisScripts::EXTEND_SCRIPT_HASH,
    keys: [redis_key],
    argv: [owner, ttl_ms]
  )
  result != 0
end

#releaseBoolean

Release the lock if still held by this owner. If the lock is no longer held by the owner, this method will fail and return false.

Returns:

  • (Boolean)

    whether the lock was released successfully



73
74
75
76
77
78
79
80
81
# File 'lib/master_lock/redis_lock.rb', line 73

def release
  result = eval_script(
    RedisScripts::RELEASE_SCRIPT,
    RedisScripts::RELEASE_SCRIPT_HASH,
    keys: [redis_key],
    argv: [owner]
  )
  result != 0
end