class Redis::Client::Connector::Sentinel

Public Class Methods

new(options) click to toggle source
Calls superclass method Redis::Client::Connector::new
# File lib/redis/client.rb, line 535
def initialize(options)
  super(options)

  @options[:db] = DEFAULTS.fetch(:db)

  @sentinels = @options.delete(:sentinels).dup
  @role = @options.fetch(:role, "master").to_s
  @master = @options[:host]
end

Public Instance Methods

check(client) click to toggle source
# File lib/redis/client.rb, line 545
def check(client)
  # Check the instance is really of the role we are looking for.
  # We can't assume the command is supported since it was introduced
  # recently and this client should work with old stuff.
  begin
    role = client.call([:role])[0]
  rescue Redis::CommandError
    # Assume the test is passed if we can't get a reply from ROLE...
    role = @role
  end

  if role != @role
    client.disconnect
    raise ConnectionError, "Instance role mismatch. Expected #{@role}, got #{role}."
  end
end
resolve() click to toggle source
# File lib/redis/client.rb, line 562
def resolve
  result = case @role
           when "master"
             resolve_master
           when "slave"
             resolve_slave
           else
             raise ArgumentError, "Unknown instance role #{@role}"
           end

  result || (raise ConnectionError, "Unable to fetch #{@role} via Sentinel.")
end
resolve_master() click to toggle source
# File lib/redis/client.rb, line 605
def resolve_master
  sentinel_detect do |client|
    if reply = client.call(["sentinel", "get-master-addr-by-name", @master])
      {:host => reply[0], :port => reply[1]}
    end
  end
end
resolve_slave() click to toggle source
# File lib/redis/client.rb, line 613
def resolve_slave
  sentinel_detect do |client|
    if reply = client.call(["sentinel", "slaves", @master])
      slaves = reply.map { |s| s.each_slice(2).to_h }
      slaves.each { |s| s['flags'] = s.fetch('flags').split(',') }
      slaves.reject! { |s| s.fetch('flags').include?('s_down') }

      if slaves.empty?
        raise CannotConnectError, 'No slaves available.'
      else
        slave = slaves.sample
        {
          host: slave.fetch('ip'),
          port: slave.fetch('port'),
        }
      end
    end
  end
end
sentinel_detect() { |client| ... } click to toggle source
# File lib/redis/client.rb, line 575
def sentinel_detect
  @sentinels.each do |sentinel|
    client = Client.new(@options.merge({
      :host => sentinel[:host],
      :port => sentinel[:port],
      :reconnect_attempts => 0,
    }))

    begin
      if result = yield(client)
        # This sentinel responded. Make sure we ask it first next time.
        @sentinels.delete(sentinel)
        @sentinels.unshift(sentinel)

        return result
      end
    rescue BaseConnectionError
    ensure
      client.disconnect
    end
  end

  raise CannotConnectError, "No sentinels available."
rescue Redis::CommandError => err
  # this feature is only available starting with Redis 5.0.1
  raise unless err.message.start_with?('ERR unknown command `auth`')
  @options[:password] = DEFAULTS.fetch(:password)
  retry
end