class EM::Hiredis::Lock

Cross-process re-entrant lock, backed by redis

Public Class Methods

new(redis, key, timeout) click to toggle source
# 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

Public Instance Methods

acquire() click to toggle source

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.

# 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() click to toggle source

This should not be used in normal operation. Force clear without regard to who owns the lock.

# 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) click to toggle source

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)

# File lib/em-hiredis/lock.rb, line 13
def onexpire(&blk); @onexpire = blk; end
to_s() click to toggle source
# File lib/em-hiredis/lock.rb, line 83
def to_s
  "[lock #{@key}]"
end
unlock() click to toggle source

Release the lock

Returns a deferrable

# 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