Class: EM::Hiredis::Lock
- Inherits:
-
Object
- Object
- EM::Hiredis::Lock
- Defined in:
- lib/em-hiredis/lock.rb
Overview
Cross-process re-entrant lock, backed by redis
Instance Method Summary collapse
-
#acquire ⇒ Object
Acquire the lock.
-
#clear ⇒ Object
This should not be used in normal operation.
-
#initialize(redis, key, timeout) ⇒ Lock
constructor
A new instance of Lock.
-
#onexpire(&blk) ⇒ Object
Register a callback which will be called 1s before the lock expires This is an informational callback, there is no hard guarantee on the timing of its invocation because the callback firing and lock key expiry are handled by different clocks (the client process and redis server respectively).
- #to_s ⇒ Object
-
#unlock ⇒ Object
Release the lock.
Constructor Details
#initialize(redis, key, timeout) ⇒ Lock
Returns a new instance of Lock.
15 16 17 18 19 20 21 |
# File 'lib/em-hiredis/lock.rb', line 15 def initialize(redis, key, timeout) unless timeout.kind_of?(Fixnum) && timeout >= 1 raise "Timeout must be an integer and >= 1s" end @redis, @key, @timeout = redis, key, timeout @token = SecureRandom.hex end |
Instance Method Details
#acquire ⇒ Object
Acquire the lock
This is a re-entrant lock, re-acquiring will succeed and extend the timeout
Returns a deferrable which either succeeds if the lock can be acquired, or fails if it cannot.
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/em-hiredis/lock.rb', line 28 def acquire df = EM::DefaultDeferrable.new @redis.lock_acquire([@key], [@token, @timeout]).callback { |success| if (success) EM::Hiredis.logger.debug "#{to_s} acquired" EM.cancel_timer(@expire_timer) if @expire_timer @expire_timer = EM.add_timer(@timeout - 1) { EM::Hiredis.logger.debug "#{to_s} Expires in 1s" @onexpire.call if @onexpire } df.succeed else EM::Hiredis.logger.debug "#{to_s} failed to acquire" df.fail("Lock is not available") end }.errback { |e| EM::Hiredis.logger.error "#{to_s} Error acquiring lock #{e}" df.fail(e) } df end |
#clear ⇒ Object
This should not be used in normal operation. Force clear without regard to who owns the lock.
76 77 78 79 80 81 |
# File 'lib/em-hiredis/lock.rb', line 76 def clear EM::Hiredis.logger.warn "#{to_s} Force clearing lock (unsafe)" EM.cancel_timer(@expire_timer) if @expire_timer @redis.del(@key) end |
#onexpire(&blk) ⇒ Object
Register a callback which will be called 1s before the lock expires This is an informational callback, there is no hard guarantee on the timing of its invocation because the callback firing and lock key expiry are handled by different clocks (the client process and redis server respectively)
13 |
# File 'lib/em-hiredis/lock.rb', line 13 def onexpire(&blk); @onexpire = blk; end |
#to_s ⇒ Object
83 84 85 |
# File 'lib/em-hiredis/lock.rb', line 83 def to_s "[lock #{@key}]" end |
#unlock ⇒ Object
Release the lock
Returns a deferrable
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/em-hiredis/lock.rb', line 55 def unlock EM.cancel_timer(@expire_timer) if @expire_timer df = EM::DefaultDeferrable.new @redis.lock_release([@key], [@token]).callback { |keys_removed| if keys_removed > 0 EM::Hiredis.logger.debug "#{to_s} released" df.succeed else EM::Hiredis.logger.debug "#{to_s} could not release, not held" df.fail("Cannot release a lock we do not hold") end }.errback { |e| EM::Hiredis.logger.error "#{to_s} Error releasing lock #{e}" df.fail(e) } df end |