Sha256: ceb50e48cebea9f4a0a58692e9e1f9d32678f3b6b6eb44e52c0e56d6118872c8

Contents?: true

Size: 1.33 KB

Versions: 1

Compression:

Stored size: 1.33 KB

Contents

require 'memcache-lock/version'
require 'securerandom'

class MemcacheLock
  class Error < RuntimeError; end

  DEFAULT_OPTIONS = {
    :initial_wait => 10e-3, # seconds -- first soft fail will wait for 10ms
    :expiry       => 60,    # seconds
    :retries      => 11,    # these defaults will retry for a total 41sec max
  }

  def initialize(cache)
    @cache = cache
  end

  def synchronize(key, options={})
    if acquired?(key)
      yield
    else
      acquire_lock(key, options)
      begin
        yield
      ensure
        release_lock(key)
      end
    end
  end

  def acquire_lock(key, options={})
    options = DEFAULT_OPTIONS.merge(options)
    1.upto(options[:retries]) do |attempt|
      response = @cache.add("lock/#{key}", uid, options[:expiry])
      return if response == "STORED\r\n"
      break if attempt == options[:retries]
      Kernel.sleep(2 ** (attempt + rand - 1) * options[:initial_wait])
    end
    raise Error, "Couldn't acquire memcache lock for: #{key}"
  end

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

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

  private
  
  # Globally unique ID for the current thread (or close enough)
  def uid
    "#{Socket.gethostname}-#{Process.pid}-#{thread_id}"
  end

  def thread_id
    Thread.current[:thread_uid] ||= SecureRandom.hex(4)
  end
end

Version data entries

1 entries across 1 versions & 1 rubygems

Version Path
ht-memcache-lock-0.3.1 lib/memcache-lock.rb