Class: Redis::Client::Connector::Sentinel

Inherits:
Redis::Client::Connector show all
Defined in:
lib/redis/client.rb

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ Sentinel

Returns a new instance of Sentinel.



569
570
571
572
573
574
575
576
577
# File 'lib/redis/client.rb', line 569

def initialize(options)
  super(options)

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

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

Instance Method Details

#check(client) ⇒ Object



579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
# File 'lib/redis/client.rb', line 579

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

#resolveObject



596
597
598
599
600
601
602
603
604
605
606
607
# File 'lib/redis/client.rb', line 596

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_masterObject



636
637
638
639
640
641
642
# File 'lib/redis/client.rb', line 636

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_slaveObject



644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
# File 'lib/redis/client.rb', line 644

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_detectObject

Raises:



609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
# File 'lib/redis/client.rb', line 609

def sentinel_detect
  @sentinels.each do |sentinel|
    client = Client.new(@options.merge({
                                         host: sentinel[:host] || sentinel["host"],
                                         port: sentinel[:port] || sentinel["port"],
                                         username: sentinel[:username] || sentinel["username"],
                                         password: sentinel[:password] || sentinel["password"],
                                         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."
end