Class: Redis::Lock

Inherits:
BaseObject show all
Defined in:
lib/redis/lock.rb

Overview

Class representing a lock. This functions like a proxy class, in that you can say @object.lock_name { block } to use the lock and also directly, but it is better to use the lock :foo class method in your class to define a lock.

Defined Under Namespace

Classes: LockTimeout

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from BaseObject

#allow_expiration, #as_json, #redis, #set_expiration, #to_hash, #to_json

Constructor Details

#initialize(key, *args) ⇒ Lock

Returns a new instance of Lock.


15
16
17
18
19
20
# File 'lib/redis/lock.rb', line 15

def initialize(key, *args)
  super(key, *args)
  @options[:timeout] ||= 5
  @options[:init] = false if @options[:init].nil? # default :init to false
  redis.setnx(key, @options[:start]) unless @options[:start] == 0 || @options[:init] === false
end

Instance Attribute Details

#keyObject (readonly)

Returns the value of attribute key


14
15
16
# File 'lib/redis/lock.rb', line 14

def key
  @key
end

#optionsObject (readonly)

Returns the value of attribute options


14
15
16
# File 'lib/redis/lock.rb', line 14

def options
  @options
end

Instance Method Details

#clearObject Also known as: delete

Clear the lock. Should only be needed if there's a server crash or some other event that gets locks in a stuck state.


24
25
26
# File 'lib/redis/lock.rb', line 24

def clear
  redis.del(key)
end

#generate_expirationObject


75
76
77
# File 'lib/redis/lock.rb', line 75

def generate_expiration
  @options[:expiration].nil? ? 1 : (Time.now + @options[:expiration].to_f + 1).to_f
end

#lock(&block) ⇒ Object

Get the lock and execute the code block. Any other code that needs the lock (on any server) will spin waiting for the lock up to the :timeout that was specified when the lock was defined.


36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/redis/lock.rb', line 36

def lock(&block)
  expiration = nil
  try_until_timeout do
    expiration = generate_expiration
    # Use the expiration as the value of the lock.
    break if redis.setnx(key, expiration)

    # Lock is being held.  Now check to see if it's expired (if we're using
    # lock expiration).
    # See "Handling Deadlocks" section on http://redis.io/commands/setnx
    if !@options[:expiration].nil?
      old_expiration = redis.get(key).to_f

      if old_expiration < Time.now.to_f
        # If it's expired, use GETSET to update it.
        expiration = generate_expiration
        old_expiration = redis.getset(key, expiration).to_f

        # Since GETSET returns the old value of the lock, if the old expiration
        # is still in the past, we know no one else has expired the locked
        # and we now have it.
        break if old_expiration < Time.now.to_f
      end
    end
  end
  begin
    yield
  ensure
    # We need to be careful when cleaning up the lock key.  If we took a really long
    # time for some reason, and the lock expired, someone else may have it, and
    # it's not safe for us to remove it.  Check how much time has passed since we
    # wrote the lock key and only delete it if it hasn't expired (or we're not using
    # lock expiration)
    if @options[:expiration].nil? || expiration > Time.now.to_f
      redis.del(key)
    end
  end
end

#valueObject


29
30
31
# File 'lib/redis/lock.rb', line 29

def value
  nil
end