Class: Distlock::Redis::ExclusiveLock

Inherits:
Object
  • Object
show all
Includes:
Common
Defined in:
lib/distlock/redis/exclusive_lock.rb

Overview

Constant Summary collapse

DEFAULT_LEASE_FOR =

5 mins

300
DEFAULT_RETRY_FOR =

1 min

60
DEFAULT_RETRY_IN =

1 sec

1

Instance Method Summary collapse

Methods included from Common

#logger, #logger=, #redis

Constructor Details

#initialize(options = {}) ⇒ ExclusiveLock

Returns a new instance of ExclusiveLock.



15
16
# File 'lib/distlock/redis/exclusive_lock.rb', line 15

def initialize(options={})
end

Instance Method Details

#generate_lock_value(lease_for = DEFAULT_LEASE_FOR, id = Process.pid) ⇒ Object



66
67
68
# File 'lib/distlock/redis/exclusive_lock.rb', line 66

def generate_lock_value(lease_for=DEFAULT_LEASE_FOR, id=Process.pid)
  "#{Time.now.to_i + lease_for + 1}-#{id}"
end

#lock(path, lease_for = DEFAULT_LEASE_FOR, retry_for = DEFAULT_RETRY_FOR) ⇒ Object

Raises:



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/distlock/redis/exclusive_lock.rb', line 22

def lock(path, lease_for = DEFAULT_LEASE_FOR, retry_for = DEFAULT_RETRY_FOR)
  now = Time.now
  retry_until = now + retry_for

  @my_lock = path

  lock_value = generate_lock_value(lease_for)

  while Time.now < retry_until
    if redis.setnx(path, lock_value)
      logger.debug "acquired lock (setnx) - #{my_lock}, #{lock_value}"
      return true
    end

    current_lock = redis.get(my_lock)
    if (current_lock.to_s.split('-').first.to_i) < Time.now.to_i
      updated_lock = redis.getset(my_lock, lock_value)
      if updated_lock == current_lock
        logger.debug "acquired lock (getset) - #{my_lock}, #{lock_value}"
        return true
      end
    end

    sleep DEFAULT_RETRY_IN
  end

  raise LockError.new("failed to get the lock")
end

#my_lockObject



18
19
20
# File 'lib/distlock/redis/exclusive_lock.rb', line 18

def my_lock
  @my_lock
end

#unlockObject



51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/distlock/redis/exclusive_lock.rb', line 51

def unlock
  lock_value = redis.get(my_lock)
  unless lock_value
    logger.debug "no lock to release"
    return true
  end

  lease_expires, owner = lock_value.split('-')
  if (lease_expires.to_i > Time.now.to_i) && (owner.to_i == Process.pid)
    redis.del(my_lock)
    logger.debug "released lock - #{my_lock}, #{lock_value}"
    return true
  end
end

#with_lock(path = '/distlock/redis/exclusive_lock/default', lease_for = DEFAULT_LEASE_FOR, retry_for = DEFAULT_RETRY_FOR) ⇒ Object



70
71
72
73
74
75
76
77
78
79
# File 'lib/distlock/redis/exclusive_lock.rb', line 70

def with_lock(path='/distlock/redis/exclusive_lock/default', lease_for=DEFAULT_LEASE_FOR, retry_for=DEFAULT_RETRY_FOR)
  begin
    lock(path, lease_for, retry_for)
    yield if block_given?
  ensure
    # TODO - store lock path so we don't need to pass it here
    # unlock(path)
    unlock
  end
end