Sha256: e574428cb7fad5c046d8c309ba37e3b90382af33a60e0c7f139d80c0a614a672

Contents?: true

Size: 1.6 KB

Versions: 1

Compression:

Stored size: 1.6 KB

Contents

require 'socket'

module Cash
  class Lock
    class Error < RuntimeError; end

    INITIAL_WAIT = 2
    DEFAULT_RETRY = 8
    DEFAULT_EXPIRY = 30

    def initialize(cache)
      @cache = cache
    end

    def synchronize(key, lock_expiry = DEFAULT_EXPIRY, retries = DEFAULT_RETRY, initial_wait = INITIAL_WAIT)
      if recursive_lock?(key)
        yield
      else
        acquire_lock(key, lock_expiry, retries, initial_wait)
        begin
          yield
        ensure
          release_lock(key)
        end
      end
    end

    def acquire_lock(key, lock_expiry = DEFAULT_EXPIRY, retries = DEFAULT_RETRY, initial_wait = INITIAL_WAIT)
      retries.times do |count|
        response = @cache.add("lock/#{key}", host_pid, lock_expiry)
        return if response == "STORED\r\n"
        return if recursive_lock?(key)
        exponential_sleep(count, initial_wait) unless count == retries - 1
      end
      debug_lock(key)
      raise Error, "Couldn't acquire memcache lock on 'lock/#{key}'"
    end

    def release_lock(key)
      @cache.delete("lock/#{key}")
    end

    def exponential_sleep(count, initial_wait)
      sleep((2**count) * initial_wait)
    end

    private

    def recursive_lock?(key)
      @cache.get("lock/#{key}") == host_pid
    end

    def debug_lock(key)
      @cache.logger.warn("Cash::Lock[#{key}]: #{@cache.get("lock/#{key}")}") if @cache.respond_to?(:logger) && @cache.logger.respond_to?(:warn)
    rescue
      @cache.logger.warn("#{$!}") if @cache.respond_to?(:logger) && @cache.logger.respond_to?(:warn)
    end
    
    def host_pid
      "#{Socket.gethostname} #{Process.pid}"
    end
  end
end

Version data entries

1 entries across 1 versions & 1 rubygems

Version Path
ngmoco-cache-money-0.2.24.2 lib/cash/lock.rb