module Diplo::Lock

Public Instance Methods

create(resource, ttl = nil) click to toggle source
# File lib/diplo/lock.rb, line 9
def create(resource, ttl = nil)
  ttl ||= Env.fetch_int(:lock_ttl)
  token = create_lock_token
  lock = OpenStruct.new(resource: resource, token: token)

  retry_count.times do
    locked_nodes, start_time = 0, Time.now

    servers.each do |server|
      locked_nodes += 1 if lock_server(server, resource, token, ttl)
    end

    validity = validity_time(start_time, ttl)

    if locked_nodes >= quorum && validity > 0
      lock.validity = validity
      return lock
    else
      remove lock
    end

    delay
  end

  false
end
create_lock_token() click to toggle source
# File lib/diplo/lock.rb, line 110
def create_lock_token
  SecureRandom.hex lock_token_bytes
end
delay() click to toggle source
# File lib/diplo/lock.rb, line 114
def delay
  sleep rand(retry_delay).to_f / 1000
end
drift_factor() click to toggle source
# File lib/diplo/lock.rb, line 86
def drift_factor
  @drift_factor || Env.fetch_float(:lock_drift_factor)
end
drift_factor=(factor) click to toggle source
# File lib/diplo/lock.rb, line 90
def drift_factor=(factor)
  @drift_factor = Float(factor)
end
lock_server(redis, resource, token, ttl) click to toggle source
# File lib/diplo/lock.rb, line 50
def lock_server(redis, resource, token, ttl)
  redis.client.call [:set, resource, token, :nx, :px, ttl]
rescue
  false
end
lock_token_bytes() click to toggle source
# File lib/diplo/lock.rb, line 94
def lock_token_bytes
  @lock_token_bytes || Env.fetch_int(:lock_token_bytes)
end
lock_token_bytes=(count) click to toggle source
# File lib/diplo/lock.rb, line 98
def lock_token_bytes=(count)
  @lock_token_bytes = Integer(count)
end
quorum() click to toggle source
# File lib/diplo/lock.rb, line 102
def quorum
  @quorum || Env.fetch_int(:lock_quorum)
end
quorum=(nodes) click to toggle source
# File lib/diplo/lock.rb, line 106
def quorum=(nodes)
  @quorum = Integer(nodes)
end
remove(lock) click to toggle source
# File lib/diplo/lock.rb, line 36
def remove(lock)
  servers.each do |server|
    unlock_server server, lock.resource, lock.token
  end
end
retry_count() click to toggle source
# File lib/diplo/lock.rb, line 70
def retry_count
  @retry_count || Env.fetch_int(:lock_retry_count)
end
retry_count=(count) click to toggle source
# File lib/diplo/lock.rb, line 74
def retry_count=(count)
  @retry_count = Integer(count)
end
retry_delay() click to toggle source
# File lib/diplo/lock.rb, line 78
def retry_delay
  @retry_delay || Env.fetch_int(:lock_retry_delay)
end
retry_delay=(miliseconds) click to toggle source
# File lib/diplo/lock.rb, line 82
def retry_delay=(miliseconds)
  @retry_delay = Integer(miliseconds)
end
servers() click to toggle source
# File lib/diplo/lock.rb, line 42
def servers
  @servers || begin
    config = Env.fetch(:redis)
    uris = config.gsub(' ', '').split(',')
    uris.map { |u| ::Redis.new url: u }
  end
end
unlock_server(redis, resource, token) click to toggle source
# File lib/diplo/lock.rb, line 56
    def unlock_server(redis, resource, token)
      @unlock_script ||= <<-LUA
        if redis.call("get", KEYS[1]) == ARGV[1] then
          return redis.call("del", KEYS[1])
        else
          return 0
        end
      LUA

      redis.client.call [:eval, @unlock_script, 1, resource, token]
    rescue
      # Nothing to do, unlocking is just a best-effort attempt.
    end
validity_time(start_time, ttl) click to toggle source
# File lib/diplo/lock.rb, line 118
def validity_time(start_time, ttl)
  diff_time = Time.now - start_time
  drift = (ttl* drift_factor).to_i + 2
  ttl - (diff_time * 1000).to_i - drift
end