class LockAndCacheMsgpack::Action

@private

Constants

NIL

Attributes

blk[R]
key[R]
options[R]

Public Class Methods

new(key, options, blk) click to toggle source
# File lib/lock_and_cache_msgpack/action.rb, line 8
def initialize(key, options, blk)
  raise "need a block" unless blk
  @key = key
  @options = options.stringify_keys
  @blk = blk
end

Public Instance Methods

digest() click to toggle source
# File lib/lock_and_cache_msgpack/action.rb, line 25
def digest
  @digest ||= key.digest
end
expires() click to toggle source
# File lib/lock_and_cache_msgpack/action.rb, line 15
def expires
  return @expires if defined?(@expires)
  @expires = options.has_key?('expires') ? options['expires'].to_f.round : nil
end
lock_digest() click to toggle source
# File lib/lock_and_cache_msgpack/action.rb, line 29
def lock_digest
  @lock_digest ||= key.lock_digest
end
nil_expires() click to toggle source
# File lib/lock_and_cache_msgpack/action.rb, line 20
def nil_expires
  return @nil_expires if defined?(@nil_expires)
  @nil_expires = options.has_key?('nil_expires') ? options['nil_expires'].to_f.round : nil
end
perform() click to toggle source
# File lib/lock_and_cache_msgpack/action.rb, line 37
def perform
  max_lock_wait = options.fetch 'max_lock_wait', LockAndCacheMsgpack.max_lock_wait
  heartbeat_expires = options.fetch('heartbeat_expires', LockAndCacheMsgpack.heartbeat_expires).to_f.ceil
  raise "heartbeat_expires must be >= 2 seconds" unless heartbeat_expires >= 2
  heartbeat_frequency = (heartbeat_expires / 2).ceil
  LockAndCacheMsgpack.logger.debug { "[lock_and_cache] A1 #{key.debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" }
  if storage.exists(digest) and (existing = storage.get(digest)).is_a?(String)
    return MessagePack.unpack(existing)
  end
  LockAndCacheMsgpack.logger.debug { "[lock_and_cache] B1 #{key.debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" }
  retval = nil
  lock_manager = LockAndCacheMsgpack.lock_manager
  lock_info = nil
  begin
    Timeout.timeout(max_lock_wait, TimeoutWaitingForLock) do
      until lock_info = lock_manager.lock(lock_digest, heartbeat_expires*1000)
        LockAndCacheMsgpack.logger.debug { "[lock_and_cache] C1 #{key.debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" }
        sleep rand
      end
    end
    LockAndCacheMsgpack.logger.debug { "[lock_and_cache] D1 #{key.debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" }
    if storage.exists(digest) and (existing = storage.get(digest)).is_a?(String)
      LockAndCacheMsgpack.logger.debug { "[lock_and_cache] E1 #{key.debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" }
      retval = MessagePack.unpack existing
    end
    unless retval
      LockAndCacheMsgpack.logger.debug { "[lock_and_cache] F1 #{key.debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" }
      done = false
      begin
        lock_extender = Thread.new do
          loop do
            LockAndCacheMsgpack.logger.debug { "[lock_and_cache] heartbeat1 #{key.debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" }
            break if done
            sleep heartbeat_frequency
            break if done
            LockAndCacheMsgpack.logger.debug { "[lock_and_cache] heartbeat2 #{key.debug} #{Base64.encode64(digest).strip} #{Digest::SHA1.hexdigest digest}" }
            lock_manager.lock lock_digest, heartbeat_expires*1000, extend: lock_info
          end
        end
        retval = blk.call
        retval.nil? ? set_nil : set_non_nil(retval)
      ensure
        done = true
        lock_extender.join if lock_extender.status.nil?
      end
    end
  ensure
    lock_manager.unlock lock_info if lock_info
  end
  retval
end
set_nil() click to toggle source
# File lib/lock_and_cache_msgpack/action.rb, line 90
def set_nil
  if nil_expires
    storage.setex digest, nil_expires, NIL
  elsif expires
    storage.setex digest, expires, NIL
  else
    storage.set digest, NIL
  end
end
set_non_nil(retval) click to toggle source
# File lib/lock_and_cache_msgpack/action.rb, line 100
def set_non_nil(retval)
  raise "expected not null #{retval.inspect}" if retval.nil?
  if expires
    storage.setex digest, expires, MessagePack.pack(retval)
  else
    storage.set digest, MessagePack.pack(retval)
  end
end
storage() click to toggle source
# File lib/lock_and_cache_msgpack/action.rb, line 33
def storage
  @storage ||= LockAndCacheMsgpack.storage or raise("must set LockAndCacheMsgpack.storage=[Redis]")
end