Class: RedisExpLock

Inherits:
Object
  • Object
show all
Defined in:
lib/redis_exp_lock.rb,
lib/redis_exp_lock/version.rb

Defined Under Namespace

Classes: AlreadyAcquiredLockError, TooManyLockAttemptsError

Constant Summary collapse

LUA =
{
  # Deletes keys if they equal the given values
  :delequal => """
    local deleted = 0
    if redis.call('GET', KEYS[1]) == ARGV[1] then
      return redis.call('DEL', KEYS[1])
    end
    return 0
  """,
}
VERSION =
'0.1.1'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(lock_key, opts) ⇒ RedisExpLock

Returns a new instance of RedisExpLock.

Raises:

  • (ArgumentError)


22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/redis_exp_lock.rb', line 22

def initialize(lock_key, opts)
  defaults = {
    :expiry => nil, # in seconds
    :retries => 0,
    :retry_interval => 0.01, # in seconds
  }
  opts = {}.merge(defaults).merge(opts)

  @lock_key = lock_key.to_s
  raise ArgumentError.new('Invalid lock key') unless @lock_key.size > 0
  @lock_uuid = nil

  @redis = opts[:redis]
  @shavaluator = Shavaluator.new(:redis => @redis)
  @shavaluator.add(LUA)

  @expiry = opts[:expiry]
  @retries = opts[:retries]
  @retry_interval = opts[:retry_interval]
end

Instance Attribute Details

#expiryObject (readonly)

Returns the value of attribute expiry.



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

def expiry
  @expiry
end

#lock_keyObject (readonly)

Returns the value of attribute lock_key.



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

def lock_key
  @lock_key
end

#lock_uuidObject (readonly)

Returns the value of attribute lock_uuid.



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

def lock_uuid
  @lock_uuid
end

#redisObject (readonly)

Returns the value of attribute redis.



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

def redis
  @redis
end

#retriesObject (readonly)

Returns the value of attribute retries.



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

def retries
  @retries
end

#retry_intervalObject (readonly)

Returns the value of attribute retry_interval.



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

def retry_interval
  @retry_interval
end

Instance Method Details

#key_locked?Boolean

Returns:

  • (Boolean)


47
48
49
# File 'lib/redis_exp_lock.rb', line 47

def key_locked?
  @redis.exists(@lock_key)
end

#key_owned?Boolean

Returns:

  • (Boolean)


51
52
53
# File 'lib/redis_exp_lock.rb', line 51

def key_owned?
  !@lock_uuid.nil? && @lock_uuid == @redis.get(@lock_key)
end

#lockObject



74
75
76
77
78
79
80
81
82
# File 'lib/redis_exp_lock.rb', line 74

def lock
  attempts = 0
  while attempts <= @retries
    attempts += 1
    return attempts if try_lock
    sleep @retry_interval
  end
  raise TooManyLockAttemptsError
end

#locked?Boolean

Returns:

  • (Boolean)


43
44
45
# File 'lib/redis_exp_lock.rb', line 43

def locked?
  !@lock_uuid.nil?
end

#synchronize(&crit_sec) ⇒ Object



91
92
93
94
95
# File 'lib/redis_exp_lock.rb', line 91

def synchronize(&crit_sec)
  attempts = lock
  crit_sec.call attempts
  unlock
end

#try_lockObject

Attempt to acquire the lock, returning true if the lock was succesfully acquired, and false if not.



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/redis_exp_lock.rb', line 57

def try_lock
  raise AlreadyAcquiredLockError if locked?

  uuid = SecureRandom.uuid
  set_opts = {
    :nx => true
  }
  set_opts[:px] = Integer(@expiry * 1000) if @expiry

  if @redis.set(@lock_key, uuid, set_opts)
    @lock_uuid = uuid
    true
  else
    false
  end
end

#unlockObject



84
85
86
87
88
89
# File 'lib/redis_exp_lock.rb', line 84

def unlock
  return false unless locked?
  was_locked = @shavaluator.exec(:delequal, :keys => [@lock_key], :argv => [@lock_uuid]) == 1
  @lock_uuid = nil
  was_locked
end