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