class NoBrainer::Lock

Public Class Methods

find(key) click to toggle source
Calls superclass method
# File lib/no_brainer/lock.rb, line 17
def self.find(key)
  super(Digest::SHA1.base64digest(key.to_s))
end
new(key, options={}) click to toggle source
Calls superclass method
# File lib/no_brainer/lock.rb, line 21
def initialize(key, options={})
  if options[:from_db]
    super
    # We reset our instance_token to allow recoveries.
    self.instance_token = get_new_instance_token
  else
    @default_options = options.slice(:expire, :timeout)
    options.delete(:expire); options.delete(:timeout);

    key = key.to_s if key.is_a?(Symbol)
    super(options.merge(:key => key))
    raise ArgumentError unless valid?
  end
end

Public Instance Methods

delete(*) click to toggle source
# File lib/no_brainer/lock.rb, line 116
def delete(*); raise NotImplementedError; end
get_new_instance_token() click to toggle source
# File lib/no_brainer/lock.rb, line 36
def get_new_instance_token
  NoBrainer::Document::PrimaryKey::Generator.generate
end
lock(options={}) click to toggle source
# File lib/no_brainer/lock.rb, line 49
def lock(options={})
  options.assert_valid_keys(:expire, :timeout)
  timeout = get_option_value(options, :timeout)
  sleep_amount = 0.1

  start_at = Time.now
  loop do
    return if try_lock(options.slice(:expire))
    raise_lock_unavailable! if Time.now - start_at + sleep_amount > timeout
    sleep(sleep_amount)
    sleep_amount = [1, sleep_amount * 2].min
  end
end
refresh(options={}) click to toggle source
# File lib/no_brainer/lock.rb, line 93
def refresh(options={})
  options.assert_valid_keys(:expire)
  raise_unless_locked!

  set_expiration(options.merge(:use_previous_expire => true))

  result = NoBrainer.run do |r|
    selector.update do |doc|
      r.branch(doc[:instance_token].eq(self.instance_token),
               { :expires_at => self.expires_at }, nil)
    end
  end

  # Note: If we are too quick, expires_at may not change, and the returned
  # 'replaced' won't be 1. We'll generate a spurious error. This is very
  # unlikely to happen and should not harmful.
  unless result['replaced'] == 1
    @locked = false
    raise_lost_lock!
  end
end
save?(*) click to toggle source
# File lib/no_brainer/lock.rb, line 115
def save?(*);  raise NotImplementedError; end
synchronize(options={}, &block) click to toggle source
# File lib/no_brainer/lock.rb, line 40
def synchronize(options={}, &block)
  lock(options)
  begin
    block.call
  ensure
    unlock if @locked
  end
end
try_lock(options={}) click to toggle source
# File lib/no_brainer/lock.rb, line 63
def try_lock(options={})
  options.assert_valid_keys(:expire)
  raise_if_locked!

  set_expiration(options)

  result = NoBrainer.run do |r|
    selector.replace do |doc|
      r.branch(doc.eq(nil).or(doc[:expires_at] < r.now),
               self.attributes, doc)
    end
  end

  return @locked = (result['inserted'] + result['replaced']) == 1
end
unlock() click to toggle source
# File lib/no_brainer/lock.rb, line 79
def unlock
  raise_unless_locked!

  result = NoBrainer.run do |r|
    selector.replace do |doc|
      r.branch(doc[:instance_token].default(nil).eq(self.instance_token),
               nil, doc)
    end
  end

  @locked = false
  raise_lost_lock! unless result['deleted'] == 1
end

Private Instance Methods

get_option_value(options, key) click to toggle source
# File lib/no_brainer/lock.rb, line 127
def get_option_value(options, key)
  NoBrainer::Config.lock_options.merge(@default_options || {}).merge(options)[key]
end
raise_if_locked!() click to toggle source
# File lib/no_brainer/lock.rb, line 131
def raise_if_locked!
  raise NoBrainer::Error::LockInvalidOp.new("Lock instance `#{key}' already locked") if @locked
end
raise_lock_unavailable!() click to toggle source
# File lib/no_brainer/lock.rb, line 143
def raise_lock_unavailable!
  raise NoBrainer::Error::LockUnavailable.new("Lock on `#{key}' unavailable")
end
raise_lost_lock!() click to toggle source
# File lib/no_brainer/lock.rb, line 139
def raise_lost_lock!
  raise NoBrainer::Error::LostLock.new("Lost lock on `#{key}'")
end
raise_unless_locked!() click to toggle source
# File lib/no_brainer/lock.rb, line 135
def raise_unless_locked!
  raise NoBrainer::Error::LockInvalidOp.new("Lock instance `#{key}' not locked") unless @locked
end
set_expiration(options) click to toggle source
# File lib/no_brainer/lock.rb, line 120
def set_expiration(options)
  expire = @previous_expire if options[:use_previous_expire] && !options[:expire]
  expire ||= get_option_value(options, :expire)
  @previous_expire = expire
  self.expires_at = RethinkDB::RQL.new.now + expire
end