Class: EM::Hiredis::Lock

Inherits:
Object
  • Object
show all
Defined in:
lib/em-hiredis/lock.rb

Overview

Cross-process re-entrant lock, backed by redis

Instance Method Summary collapse

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

#acquireObject

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

#clearObject

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_sObject



83
84
85
# File 'lib/em-hiredis/lock.rb', line 83

def to_s
  "[lock #{@key}]"
end

#unlockObject

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