Class: Redis::Lock
- Inherits:
-
Object
- Object
- Redis::Lock
- 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
-
#key ⇒ Object
readonly
Returns the value of attribute key.
-
#options ⇒ Object
readonly
Returns the value of attribute options.
-
#redis ⇒ Object
readonly
Returns the value of attribute redis.
Instance Method Summary collapse
-
#clear ⇒ Object
(also: #delete)
Clear the lock.
- #generate_expiration ⇒ Object
-
#initialize(key, *args) ⇒ Lock
constructor
A new instance of Lock.
-
#lock(&block) ⇒ Object
Get the lock and execute the code block.
Constructor Details
#initialize(key, *args) ⇒ Lock
Returns a new instance of Lock.
13 14 15 16 17 18 19 20 |
# File 'lib/redis/lock.rb', line 13 def initialize(key, *args) @key = key @options = args.last.is_a?(Hash) ? args.pop : {} @redis = args.first || $redis @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
#key ⇒ Object (readonly)
Returns the value of attribute key.
12 13 14 |
# File 'lib/redis/lock.rb', line 12 def key @key end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
12 13 14 |
# File 'lib/redis/lock.rb', line 12 def @options end |
#redis ⇒ Object (readonly)
Returns the value of attribute redis.
12 13 14 |
# File 'lib/redis/lock.rb', line 12 def redis @redis end |
Instance Method Details
#clear ⇒ Object 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_expiration ⇒ Object
80 81 82 |
# File 'lib/redis/lock.rb', line 80 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.
32 33 34 35 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 74 75 76 77 78 |
# File 'lib/redis/lock.rb', line 32 def lock(&block) start = Time.now gotit = false expiration = nil while Time.now - start < @options[:timeout] expiration = generate_expiration # Use the expiration as the value of the lock. gotit = redis.setnx(key, expiration) break if gotit # Lock is being held. Now check to see if it's expired (if we're using # lock expiration). # See "Handling Deadlocks" section on http://code.google.com/p/redis/wiki/SetnxCommand 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. if old_expiration < Time.now.to_f gotit = true break end end end sleep 0.1 end raise LockTimeout, "Timeout on lock #{key} exceeded #{@options[:timeout]} sec" unless gotit 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 |