2011-08-09 13:45:36 +00:00
|
|
|
module Mongo
|
|
|
|
class Node
|
2011-08-22 15:52:11 +00:00
|
|
|
|
2011-09-01 15:42:56 +00:00
|
|
|
attr_accessor :host, :port, :address, :config, :connection, :socket
|
2011-08-16 20:47:07 +00:00
|
|
|
|
|
|
|
def initialize(connection, data)
|
|
|
|
self.connection = connection
|
|
|
|
if data.is_a?(String)
|
|
|
|
self.host, self.port = split_nodes(data)
|
|
|
|
else
|
2011-08-25 18:57:24 +00:00
|
|
|
self.host = data[0]
|
|
|
|
self.port = data[1].nil? ? Connection::DEFAULT_PORT : data[1].to_i
|
2011-08-16 20:47:07 +00:00
|
|
|
end
|
2011-08-09 13:45:36 +00:00
|
|
|
self.address = "#{host}:#{port}"
|
2011-08-30 19:59:04 +00:00
|
|
|
self.config = nil
|
2011-08-09 13:45:36 +00:00
|
|
|
end
|
2011-08-16 20:47:07 +00:00
|
|
|
|
2011-08-09 13:45:36 +00:00
|
|
|
def eql?(other)
|
|
|
|
other.is_a?(Node) && host == other.host && port == other.port
|
|
|
|
end
|
|
|
|
alias :== :eql?
|
2011-08-16 20:47:07 +00:00
|
|
|
|
2011-08-24 22:34:00 +00:00
|
|
|
def host_string
|
2011-09-01 15:42:56 +00:00
|
|
|
address
|
|
|
|
end
|
|
|
|
|
|
|
|
def inspect
|
|
|
|
"<Mongo::Node:0x#{self.object_id.to_s(16)} @host=#{@host} @port=#{@port}>"
|
2011-08-24 22:34:00 +00:00
|
|
|
end
|
|
|
|
|
2011-08-16 20:47:07 +00:00
|
|
|
# Create a connection to the provided node,
|
|
|
|
# and, if successful, return the socket. Otherwise,
|
|
|
|
# return nil.
|
|
|
|
def connect
|
|
|
|
begin
|
2011-08-25 15:27:58 +00:00
|
|
|
socket = nil
|
2011-08-16 20:47:07 +00:00
|
|
|
if self.connection.connect_timeout
|
|
|
|
Mongo::TimeoutHandler.timeout(self.connection.connect_timeout, OperationTimeout) do
|
2011-08-26 21:35:40 +00:00
|
|
|
socket = self.connection.socket_class.new(self.host, self.port)
|
2011-08-16 20:47:07 +00:00
|
|
|
end
|
|
|
|
else
|
2011-08-26 21:35:40 +00:00
|
|
|
socket = self.connection.socket_class.new(self.host, self.port)
|
2011-08-16 20:47:07 +00:00
|
|
|
end
|
|
|
|
|
2011-08-22 15:52:11 +00:00
|
|
|
if socket.nil?
|
|
|
|
return nil
|
|
|
|
else
|
|
|
|
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
|
|
|
end
|
2011-08-25 15:27:58 +00:00
|
|
|
rescue OperationTimeout, OperationFailure, SocketError, SystemCallError, IOError => ex
|
|
|
|
self.connection.log(:debug, "Failed connection to #{host_string} with #{ex.class}, #{ex.message}.")
|
|
|
|
socket.close if socket
|
|
|
|
return nil
|
2011-08-16 20:47:07 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
self.socket = socket
|
|
|
|
end
|
|
|
|
|
|
|
|
def disconnect
|
|
|
|
if self.socket
|
|
|
|
self.socket.close
|
|
|
|
self.socket = nil
|
|
|
|
self.config = nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def connected?
|
|
|
|
self.socket != nil
|
|
|
|
end
|
|
|
|
|
2011-08-24 22:34:00 +00:00
|
|
|
def active?
|
|
|
|
begin
|
|
|
|
result = self.connection['admin'].command({:ping => 1}, :socket => self.socket)
|
|
|
|
return result['ok'] == 1
|
|
|
|
rescue OperationFailure, SocketError, SystemCallError, IOError => ex
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-08-16 20:47:07 +00:00
|
|
|
# Get the configuration for the provided node as returned by the
|
|
|
|
# ismaster command. Additionally, check that the replica set name
|
|
|
|
# matches with the name provided.
|
|
|
|
def set_config
|
|
|
|
begin
|
|
|
|
self.config = self.connection['admin'].command({:ismaster => 1}, :socket => self.socket)
|
|
|
|
|
|
|
|
if self.config['msg'] && @logger
|
2011-08-26 16:40:13 +00:00
|
|
|
self.connection.log(:warn, "#{config['msg']}")
|
2011-08-16 20:47:07 +00:00
|
|
|
end
|
|
|
|
|
2011-08-26 16:40:13 +00:00
|
|
|
check_set_membership(config)
|
|
|
|
check_set_name(config)
|
2011-08-31 20:05:21 +00:00
|
|
|
rescue ConnectionFailure, OperationFailure, SocketError, SystemCallError, IOError => ex
|
2011-08-26 16:40:13 +00:00
|
|
|
self.connection.log(:warn, "Attempted connection to node #{host_string} raised " +
|
|
|
|
"#{ex.class}: #{ex.message}")
|
2011-08-16 20:47:07 +00:00
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
|
|
|
self.config
|
|
|
|
end
|
|
|
|
|
|
|
|
# Return a list of replica set nodes from the config.
|
|
|
|
# Note: this excludes arbiters.
|
|
|
|
def node_list
|
|
|
|
connect unless connected?
|
2011-08-30 19:59:04 +00:00
|
|
|
set_config unless self.config
|
2011-08-24 22:34:00 +00:00
|
|
|
|
|
|
|
return [] unless config
|
2011-08-16 20:47:07 +00:00
|
|
|
|
|
|
|
nodes = []
|
|
|
|
nodes += config['hosts'] if config['hosts']
|
|
|
|
nodes += config['passives'] if config['passives']
|
|
|
|
nodes
|
|
|
|
end
|
|
|
|
|
|
|
|
def arbiters
|
|
|
|
connect unless connected?
|
2011-08-30 19:59:04 +00:00
|
|
|
set_config unless self.config
|
2011-08-24 22:34:00 +00:00
|
|
|
return [] unless config['arbiters']
|
2011-08-16 20:47:07 +00:00
|
|
|
|
|
|
|
config['arbiters'].map do |arbiter|
|
|
|
|
split_nodes(arbiter)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-08-30 19:59:04 +00:00
|
|
|
def tags
|
|
|
|
connect unless connected?
|
|
|
|
set_config unless self.config
|
|
|
|
return {} unless config['tags'] && !config['tags'].empty?
|
|
|
|
|
|
|
|
config['tags']
|
|
|
|
end
|
|
|
|
|
2011-08-16 20:47:07 +00:00
|
|
|
def primary?
|
|
|
|
self.config['ismaster'] == true || self.config['ismaster'] == 1
|
|
|
|
end
|
|
|
|
|
|
|
|
def secondary?
|
|
|
|
self.config['secondary'] == true || self.config['secondary'] == 1
|
|
|
|
end
|
|
|
|
|
|
|
|
def host_port
|
|
|
|
[self.host, self.port]
|
|
|
|
end
|
|
|
|
|
2011-08-09 13:45:36 +00:00
|
|
|
def hash
|
|
|
|
address.hash
|
|
|
|
end
|
2011-08-16 20:47:07 +00:00
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def split_nodes(host_string)
|
|
|
|
data = host_string.split(":")
|
|
|
|
host = data[0]
|
2011-08-25 18:57:24 +00:00
|
|
|
port = data[1].nil? ? Connection::DEFAULT_PORT : data[1].to_i
|
2011-08-16 20:47:07 +00:00
|
|
|
|
|
|
|
[host, port]
|
|
|
|
end
|
|
|
|
|
2011-08-26 16:40:13 +00:00
|
|
|
# Ensure that this node is a member of a replica set.
|
|
|
|
def check_set_membership(config)
|
|
|
|
if !config['hosts']
|
|
|
|
message = "Will not connect to #{host_string} because it's not a member " +
|
|
|
|
"of a replica set."
|
2011-08-31 20:05:21 +00:00
|
|
|
raise ConnectionFailure, message
|
2011-08-26 16:40:13 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Ensure that this node is part of a replica set of the expected name.
|
|
|
|
def check_set_name(config)
|
2011-08-16 20:47:07 +00:00
|
|
|
if self.connection.replica_set_name
|
2011-08-26 16:40:13 +00:00
|
|
|
if !config['setName']
|
|
|
|
self.connection.log(:warn, "Could not verify replica set name for member #{host_string} " +
|
2011-08-16 20:47:07 +00:00
|
|
|
"because ismaster does not return name in this version of MongoDB")
|
2011-08-26 16:40:13 +00:00
|
|
|
elsif self.connection.replica_set_name != config['setName']
|
|
|
|
message = "Attempting to connect to replica set '#{config['setName']}' on member #{host_string} " +
|
2011-08-16 20:47:07 +00:00
|
|
|
"but expected '#{self.connection.replica_set_name}'"
|
2011-08-31 15:46:33 +00:00
|
|
|
raise ReplicaSetConnectionError, message
|
2011-08-16 20:47:07 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2011-08-09 13:45:36 +00:00
|
|
|
end
|
2011-08-16 20:47:07 +00:00
|
|
|
end
|