lib/redis-sentinel/client.rb in redis-sentinel-1.3.0 vs lib/redis-sentinel/client.rb in redis-sentinel-1.4.0

- old
+ new

@@ -2,15 +2,18 @@ class Redis::Client DEFAULT_FAILOVER_RECONNECT_WAIT_SECONDS = 0.1 class_eval do + attr_reader :current_sentinel + attr_reader :current_sentinel_options + def initialize_with_sentinel(options={}) options = options.dup # Don't touch my options @master_name = fetch_option(options, :master_name) @master_password = fetch_option(options, :master_password) - @sentinels = fetch_option(options, :sentinels) + @sentinels_options = _parse_sentinel_options(fetch_option(options, :sentinels)) @failover_reconnect_timeout = fetch_option(options, :failover_reconnect_timeout) @failover_reconnect_wait = fetch_option(options, :failover_reconnect_wait) || DEFAULT_FAILOVER_RECONNECT_WAIT_SECONDS initialize_without_sentinel(options) @@ -32,11 +35,11 @@ alias connect_without_sentinel connect alias connect connect_with_sentinel def sentinel? - @master_name && @sentinels + @master_name && @sentinels_options end def auto_retry_with_timeout(&block) deadline = @failover_reconnect_timeout.to_i + Time.now.to_f begin @@ -47,49 +50,56 @@ retry end end def try_next_sentinel - @sentinels << @sentinels.shift - if @logger && @logger.debug? - @logger.debug "Trying next sentinel: #{@sentinels[0][:host]}:#{@sentinels[0][:port]}" + sentinel_options = @sentinels_options.shift + if sentinel_options + @logger.debug "Trying next sentinel: #{sentinel_options[:host]}:#{sentinel_options[:port]}" if @logger && @logger.debug? + @current_sentinel_options = sentinel_options + @current_sentinel = Redis.new sentinel_options + else + raise Redis::CannotConnectError end - return @sentinels[0] end + def refresh_sentinels_list + responses = current_sentinel.sentinel("sentinels", @master_name) + @sentinels_options = responses.map do |response| + {:host => response[3], :port => response[5]} + end.unshift(:host => current_sentinel_options[:host], :port => current_sentinel_options[:port]) + end + def discover_master while true - sentinel = redis_sentinels[@sentinels[0]] + try_next_sentinel begin - host, port = sentinel.sentinel("get-master-addr-by-name", @master_name) - if !host && !port - raise Redis::ConnectionError.new("No master named: #{@master_name}") + master_host, master_port = current_sentinel.sentinel("get-master-addr-by-name", @master_name) + if master_host && master_port + # An ip:port pair + @options.merge!(:host => master_host, :port => master_port.to_i, :password => @master_password) + refresh_sentinels_list + break + else + # A null reply end - is_down, runid = sentinel.sentinel("is-master-down-by-addr", host, port) - break + rescue Redis::CommandError + # An -IDONTKNOWN reply rescue Redis::CannotConnectError - try_next_sentinel + # faile to connect to current sentinel server end end - - if is_down.to_s == "1" || runid == '?' - raise Redis::CannotConnectError.new("The master: #{@master_name} is currently not available.") - else - @options.merge!(:host => host, :port => port.to_i, :password => @master_password) - end end - def reconnect_with_sentinels - redis_sentinels.each do |config, sentinel| - sentinel.client.reconnect - end - reconnect_without_sentinels + def disconnect_with_sentinels + current_sentinel.client.disconnect if current_sentinel + disconnect_without_sentinels end - alias reconnect_without_sentinels reconnect - alias reconnect reconnect_with_sentinels + alias disconnect_without_sentinels disconnect + alias disconnect disconnect_with_sentinels def call_with_readonly_protection(*args, &block) tries = 0 call_without_readonly_protection(*args, &block) rescue Redis::CommandError => e @@ -108,12 +118,24 @@ def fetch_option(options, key) options.delete(key) || options.delete(key.to_s) end - def redis_sentinels - @redis_sentinels ||= Hash.new do |hash, config| - hash[config] = Redis.new(config) + def _parse_sentinel_options(options) + return if options.nil? + + sentinel_options = [] + options.each do |sentinel_option| + if sentinel_option.is_a?(Hash) + sentinel_options << sentinel_option + else + uri = URI.parse(sentinel_option) + sentinel_options << { + host: uri.host, + port: uri.port + } + end end + sentinel_options end end end