lib/redis_failover/node.rb in redis_failover-0.3.0 vs lib/redis_failover/node.rb in redis_failover-0.4.0

- old
+ new

@@ -1,55 +1,70 @@ module RedisFailover # Represents a redis node (master or slave). class Node include Util + MAX_OP_WAIT_TIME = 5 + attr_reader :host, :port def initialize(options = {}) @host = options.fetch(:host) { raise InvalidNodeError, 'missing host'} @port = Integer(options[:port] || 6379) @password = options[:password] end - def ping - perform_operation do - redis.ping - end - end - def master? role == 'master' end def slave? !master? end - def wait_until_unreachable - perform_operation do - redis.blpop(wait_key, 0) + def slave_of?(master) + current_master == master + end + + def current_master + info = fetch_info + return unless info[:role] == 'slave' + Node.new(:host => info[:master_host], :port => info[:master_port].to_i) + end + + # Waits until something interesting happens. If the connection + # with this node dies, the blpop call will raise an error. If + # the blpop call returns without error, then this will be due to + # a graceful shutdown signaled by #wakeup or a timeout. + def wait + perform_operation do |redis| + redis.blpop(wait_key, MAX_OP_WAIT_TIME - 3) redis.del(wait_key) end end - def stop_waiting - perform_operation do + def wakeup + perform_operation do |redis| redis.lpush(wait_key, '1') end end def make_slave!(master) - perform_operation do - redis.slaveof(master.host, master.port) + perform_operation do |redis| + unless slave_of?(master) + redis.slaveof(master.host, master.port) + wakeup + end end end def make_master! - perform_operation do - # yes, this is a real redis operation! - redis.slaveof('no', 'one') + perform_operation do |redis| + unless master? + redis.slaveof('no', 'one') + wakeup + end end end def inspect "<RedisFailover::Node #{to_s}>" @@ -68,34 +83,57 @@ def hash to_s.hash end - private + def fetch_info + perform_operation do |redis| + symbolize_keys(redis.info) + end + end + alias_method :ping, :fetch_info - def role - fetch_info[:role] + def prohibits_stale_reads? + perform_operation do |redis| + redis.config('get', 'slave-serve-stale-data').last == 'no' + end end - def fetch_info - perform_operation do - symbolize_keys(redis.info) + def syncing_with_master? + perform_operation do |redis| + fetch_info[:master_sync_in_progress] == '1' end end + private + + def role + fetch_info[:role] + end + def wait_key @wait_key ||= "_redis_failover_#{SecureRandom.hex(32)}" end - def redis + def new_client Redis.new(:host => @host, :password => @password, :port => @port) - rescue - raise NodeUnreachableError.new(self) end def perform_operation - yield + redis = nil + Timeout.timeout(MAX_OP_WAIT_TIME) do + redis = new_client + yield redis + end rescue - raise NodeUnreachableError.new(self) + raise NodeUnavailableError.new(self) + ensure + if redis + begin + redis.client.disconnect + rescue + raise NodeUnavailableError.new(self) + end + end end end end