ReplSetConnection updates

This commit is contained in:
Kyle Banker 2010-12-13 14:07:32 -05:00
parent 08b7cddc81
commit 27b410f869
11 changed files with 212 additions and 285 deletions

View File

@ -92,47 +92,18 @@ module Mongo
#
# @core connections
def initialize(host=nil, port=nil, options={})
@auths = options.fetch(:auths, [])
@host_to_try = format_pair(host, port)
# Host and port of current master.
@host = @port = nil
# Lock for request ids.
@id_lock = Mutex.new
# Pool size and timeout.
@pool_size = options[:pool_size] || 1
@timeout = options[:timeout] || 5.0
# Mutex for synchronizing pool access
@connection_mutex = Mutex.new
# Global safe option. This is false by default.
@safe = options[:safe] || false
# Create a mutex when a new key, in this case a socket,
# is added to the hash.
@safe_mutexes = Hash.new { |h, k| h[k] = Mutex.new }
# Condition variable for signal and wait
@queue = ConditionVariable.new
# slave_ok can be true only if one node is specified
@slave_ok = options[:slave_ok]
@primary = nil
# Connection pool for primay node
@primary_pool = nil
@logger = options[:logger] || nil
should_connect = options.fetch(:connect, true)
connect if should_connect
setup(options)
end
# DEPRECATED
#
# Initialize a connection to a MongoDB replica set using an array of seed nodes.
@ -160,19 +131,10 @@ module Mongo
#
# @deprecated
def self.multi(nodes, opts={})
unless nodes.length > 0 && nodes.all? {|n| n.is_a? Array}
raise MongoArgumentError, "Connection.multi requires at least one node to be specified."
end
warn "Connection.multi is now deprecated. Please use ReplSetConnection.new instead."
# Block returns an array, the first element being an array of nodes and the second an array
# of authorizations for the database.
new(nil, nil, opts) do |con|
nodes.map do |node|
con.instance_variable_set(:@replica_set, true)
con.instance_variable_set(:@read_secondary, true) if opts[:read_secondary]
con.pair_val_to_connection(node)
end
end
nodes << opts
ReplSetConnection.new(*nodes)
end
# Initialize a connection to MongoDB using the MongoDB URI spec:
@ -191,6 +153,8 @@ module Mongo
elsif nodes.length > 1
nodes << opts
ReplSetConnection.new(*nodes)
else
raise MongoArgumentError, "No nodes specified. Please ensure that you've provided at least one node."
end
end
@ -500,40 +464,8 @@ module Mongo
@primary_pool = nil
end
## Configuration helper methods
# Returns a host-port pair.
#
# @return [Array]
#
# @private
def format_pair(host, port)
case host
when String
[host, port ? port.to_i : DEFAULT_PORT]
when nil
['localhost', DEFAULT_PORT]
end
end
# Convert an argument containing a host name string and a
# port number integer into a [host, port] pair array.
#
# @private
def pair_val_to_connection(a)
case a
when nil
['localhost', DEFAULT_PORT]
when String
[a, DEFAULT_PORT]
when Integer
['localhost', a]
when Array
a
end
end
# Checkout a socket for reading (i.e., a secondary node).
# Checkout a socket for reading (i.e., a secondary node).
def checkout_reader
connect unless connected?
@ -567,6 +499,77 @@ module Mongo
end
end
protected
# Generic initialization code.
# @protected
def setup(options)
# Authentication objects
@auths = options.fetch(:auths, [])
# Lock for request ids.
@id_lock = Mutex.new
# Pool size and timeout.
@pool_size = options[:pool_size] || 1
@timeout = options[:timeout] || 5.0
# Mutex for synchronizing pool access
@connection_mutex = Mutex.new
# Global safe option. This is false by default.
@safe = options[:safe] || false
# Create a mutex when a new key, in this case a socket,
# is added to the hash.
@safe_mutexes = Hash.new { |h, k| h[k] = Mutex.new }
# Condition variable for signal and wait
@queue = ConditionVariable.new
# Connection pool for primay node
@primary = nil
@primary_pool = nil
@logger = options[:logger] || nil
should_connect = options.fetch(:connect, true)
connect if should_connect
end
## Configuration helper methods
# Returns a host-port pair.
#
# @return [Array]
#
# @private
def format_pair(host, port)
case host
when String
[host, port ? port.to_i : DEFAULT_PORT]
when nil
['localhost', DEFAULT_PORT]
end
end
# Convert an argument containing a host name string and a
# port number integer into a [host, port] pair array.
#
# @private
def pair_val_to_connection(a)
case a
when nil
['localhost', DEFAULT_PORT]
when String
[a, DEFAULT_PORT]
when Integer
['localhost', a]
when Array
a
end
end
private
# If a ConnectionFailure is raised, this method will be called
@ -584,7 +587,7 @@ module Mongo
# apply any saved authentication.
# TODO: simplify
def is_primary?(config)
config && (config['ismaster'] == 1 || config['ismaster'] == true) || !@replica_set && @slave_ok
config && (config['ismaster'] == 1 || config['ismaster'] == true) || @slave_ok
end
def check_is_master(node)

View File

@ -19,19 +19,25 @@
module Mongo
# Instantiates and manages connections to MongoDB.
class ReplSetConnection
class ReplSetConnection < Connection
attr_reader :nodes, :secondaries, :arbiters, :read_pool, :secondary_pools
def initialize(*args)
if args.last.is_a?(Hash)
options = args.pop
opts = args.pop
else
opts = {}
end
unless args.length > 0
raise MongoArgumentError, "A ReplSetConnection requires at least one node."
end
# Get seed nodes
@nodes = args
# Replica set name
@replica_set_name = options[:rs_name]
@replica_set = opts[:rs_name]
# Cache the various node types when connecting to a replica set.
@secondaries = []
@ -41,7 +47,10 @@ module Mongo
@secondary_pools = []
@read_pool = nil
super
# Are we allowing reads from secondaries?
@read_secondary = opts.fetch(:read_secondary, false)
setup(opts)
end
# Create a new socket and attempt to connect to master.
@ -86,13 +95,14 @@ module Mongo
# If a ConnectionFailure is raised, this method will be called
# to close the connection and reset connection values.
# TODO: what's the point of this method?
def reset_connection
super
@secondaries = []
@secondary_pools = []
@arbiters = []
@nodes_tried = []
@nodes_to_try = []
@nodes_tried = []
@nodes_to_try = []
end
private
@ -124,7 +134,12 @@ module Mongo
config
end
# Primary, when connecting to a replica can, can only be a true primary node.
# (And not a slave, which is possible when connecting with the standard
# Connection class.
def is_primary?(config)
config && (config['ismaster'] == 1 || config['ismaster'] == true)
end
# Pick a node randomly from the set of possible secondaries.
def pick_secondary_for_read
@ -135,15 +150,15 @@ module Mongo
# Make sure that we're connected to the expected replica set.
def check_set_name(config, socket)
if @replica_set_name
if @replica_set
config = self['admin'].command({:replSetGetStatus => 1},
:sock => socket, :check_response => false)
if !Mongo::Support.ok?(config)
raise ReplicaSetConnectionError, config['errmsg']
elsif config['set'] != @replica_set_name
elsif config['set'] != @replica_set
raise ReplicaSetConnectionError,
"Attempting to connect to replica set '#{config['set']}' but expected '#{@replica_set_name}'"
"Attempting to connect to replica set '#{config['set']}' but expected '#{@replica_set}'"
end
end
end

View File

@ -9,15 +9,15 @@ class ConnectTest < Test::Unit::TestCase
include Mongo
def test_connect_bad_name
assert_raise_error(ReplicaSetConnectionError, "expected 'wrong-repl-set-name'") do
Mongo::Connection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1], [TEST_HOST, TEST_PORT + 2]],
:rs_name => "wrong-repl-set-name")
assert_raise_error(ReplicaSetReplSetConnectionError, "expected 'wrong-repl-set-name'") do
ReplSetConnection.multi([TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1],
[TEST_HOST, TEST_PORT + 2], :rs_name => "wrong-repl-set-name")
end
end
def test_connect
@conn = Mongo::Connection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1], [TEST_HOST, TEST_PORT + 2]],
:name => "foo")
@conn = ReplSetConnection.multi([TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1],
[TEST_HOST, TEST_PORT + 2], :name => "foo")
assert @conn.connected?
end
@ -25,7 +25,8 @@ class ConnectTest < Test::Unit::TestCase
puts "Please kill the node at #{TEST_PORT}."
gets
@conn = Mongo::Connection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1], [TEST_HOST, TEST_PORT + 2]])
@conn = ReplSetConnection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1],
[TEST_HOST, TEST_PORT + 2]])
assert @conn.connected?
end
@ -33,7 +34,8 @@ class ConnectTest < Test::Unit::TestCase
puts "Please kill the node at #{TEST_PORT + 1}."
gets
@conn = Mongo::Connection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1], [TEST_HOST, TEST_PORT + 2]])
@conn = ReplSetConnection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1],
[TEST_HOST, TEST_PORT + 2]])
assert @conn.connected?
end
@ -41,7 +43,8 @@ class ConnectTest < Test::Unit::TestCase
puts "Please kill the node at #{TEST_PORT + 2}."
gets
@conn = Mongo::Connection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1], [TEST_HOST, TEST_PORT + 2]])
@conn = ReplSetConnection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1],
[TEST_HOST, TEST_PORT + 2]])
assert @conn.connected?
end
end

View File

@ -9,7 +9,8 @@ class ReplicaSetCountTest < Test::Unit::TestCase
include Mongo
def setup
@conn = Mongo::Connection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1], [TEST_HOST, TEST_PORT + 2]])
@conn = ReplSetConnection.multi([TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1],
[TEST_HOST, TEST_PORT + 2])
@db = @conn.db(MONGO_TEST_DB)
@db.drop_collection("test-sets")
@coll = @db.collection("test-sets")

View File

@ -9,7 +9,8 @@ class ReplicaSetInsertTest < Test::Unit::TestCase
include Mongo
def setup
@conn = Mongo::Connection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1], [TEST_HOST, TEST_PORT + 2]])
@conn = ReplSetConnection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1],
[TEST_HOST, TEST_PORT + 2]])
@db = @conn.db(MONGO_TEST_DB)
@db.drop_collection("test-sets")
@coll = @db.collection("test-sets")

View File

@ -9,7 +9,8 @@ class ReplicaSetNodeTypeTest < Test::Unit::TestCase
include Mongo
def setup
@conn = Mongo::Connection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1], [TEST_HOST, TEST_PORT + 2]])
@conn = ReplSetConnection.multi([TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1],
[TEST_HOST, TEST_PORT + 2])
@db = @conn.db(MONGO_TEST_DB)
@db.drop_collection("test-sets")
@coll = @db.collection("test-sets")

View File

@ -9,8 +9,8 @@ class ReplicaSetPooledInsertTest < Test::Unit::TestCase
include Mongo
def setup
@conn = Mongo::Connection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1], [TEST_HOST, TEST_PORT + 2]],
:pool_size => 10, :timeout => 5)
@conn = ReplSetConnection.multi([TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1],
[TEST_HOST, TEST_PORT + 2], :pool_size => 10, :timeout => 5)
@db = @conn.db(MONGO_TEST_DB)
@db.drop_collection("test-sets")
@coll = @db.collection("test-sets")

View File

@ -9,7 +9,7 @@ class ReplicaSetQuerySecondariesTest < Test::Unit::TestCase
include Mongo
def setup
@conn = Mongo::Connection.multi([[TEST_HOST, TEST_PORT]], :read_secondary => true)
@conn = ReplSetConnection.multi([TEST_HOST, TEST_PORT], :read_secondary => true)
@db = @conn.db(MONGO_TEST_DB)
@db.drop_collection("test-sets")
@coll = @db.collection("test-sets", :safe => {:w => 2, :wtimeout => 100})

View File

@ -9,7 +9,8 @@ class ReplicaSetQueryTest < Test::Unit::TestCase
include Mongo
def setup
@conn = Mongo::Connection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1], [TEST_HOST, TEST_PORT + 2]])
@conn = ReplSetConnection.multi([TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1],
[TEST_HOST, TEST_PORT + 2])
@db = @conn.db(MONGO_TEST_DB)
@db.drop_collection("test-sets")
@coll = @db.collection("test-sets")

View File

@ -8,11 +8,13 @@ class ReplicaSetAckTest < Test::Unit::TestCase
include Mongo
def setup
@conn = Mongo::Connection.multi([[TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1], [TEST_HOST, TEST_PORT + 2]])
@conn = ReplSetConnection.multi([TEST_HOST, TEST_PORT], [TEST_HOST, TEST_PORT + 1],
[TEST_HOST, TEST_PORT + 2])
master = [@conn.primary_pool.host, @conn.primary_pool.port]
@slave1 = Mongo::Connection.new(@conn.secondary_pools[0].host, @conn.secondary_pools[0].port, :slave_ok => true)
@slave1 = Connection.new(@conn.secondary_pools[0].host,
@conn.secondary_pools[0].port, :slave_ok => true)
@db = @conn.db(MONGO_TEST_DB)
@db.drop_collection("test-sets")
@ -37,7 +39,6 @@ class ReplicaSetAckTest < Test::Unit::TestCase
assert @col.insert({:foo => "0" * 10000}, :safe => {:w => 2, :wtimeout => 1000})
assert_equal 2, @slave1[MONGO_TEST_DB]["test-sets"].count
assert @col.update({:baz => "bar"}, {:baz => "foo"}, :safe => {:w => 2, :wtimeout => 1000})
assert @slave1[MONGO_TEST_DB]["test-sets"].find_one({:baz => "foo"})

View File

@ -1,181 +1,82 @@
require './test/test_helper'
include Mongo
#class ConnectionTest < Test::Unit::TestCase
# context "Initialization: " do
# setup do
# def new_mock_socket(host='localhost', port=27017)
# socket = Object.new
# socket.stubs(:setsockopt).with(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
# socket.stubs(:close)
# socket
# end
#
# def new_mock_db
# db = Object.new
# end
# end
#
# context "given a single node" do
# setup do
# @conn = Connection.new('localhost', 27017, :connect => false)
# TCPSocket.stubs(:new).returns(new_mock_socket)
#
# admin_db = new_mock_db
# admin_db.expects(:command).returns({'ok' => 1, 'ismaster' => 1})
# @conn.expects(:[]).with('admin').returns(admin_db)
# @conn.connect
# end
#
# should "set localhost and port to master" do
# assert_equal 'localhost', @conn.primary_pool.host
# assert_equal 27017, @conn.primary_pool.port
# end
#
# should "set connection pool to 1" do
# assert_equal 1, @conn.primary_pool.size
# end
#
# should "default slave_ok to false" do
# assert !@conn.slave_ok?
# end
# end
#
# context "connecting to a replica set" do
# setup do
# TCPSocket.stubs(:new).returns(new_mock_socket('localhost', 27017))
# @conn = Connection.multi([['localhost', 27017]], :connect => false, :read_secondary => true)
#
# admin_db = new_mock_db
# @hosts = ['localhost:27018', 'localhost:27019', 'localhost:27020']
#
# admin_db.stubs(:command).returns({'ok' => 1, 'ismaster' => 1, 'hosts' => @hosts}).
# then.returns({'ok' => 1, 'ismaster' => 0, 'hosts' => @hosts, 'secondary' => 1}).
# then.returns({'ok' => 1, 'ismaster' => 0, 'hosts' => @hosts, 'secondary' => 1}).
# then.returns({'ok' => 1, 'ismaster' => 0, 'arbiterOnly' => 1})
#
# @conn.stubs(:[]).with('admin').returns(admin_db)
# @conn.connect
# end
#
# should "store the hosts returned from the ismaster command" do
# assert_equal 'localhost', @conn.primary_pool.host
# assert_equal 27017, @conn.primary_pool.port
#
# assert_equal 'localhost', @conn.secondary_pools[0].host
# assert_equal 27018, @conn.secondary_pools[0].port
#
# assert_equal 'localhost', @conn.secondary_pools[1].host
# assert_equal 27019, @conn.secondary_pools[1].port
#
# assert_equal 2, @conn.secondary_pools.length
# end
# end
#
# context "connecting to a replica set and providing seed nodes" do
# setup do
# TCPSocket.stubs(:new).returns(new_mock_socket)
# @conn = Connection.multi([['localhost', 27017], ['localhost', 27019]], :connect => false)
#
# admin_db = new_mock_db
# @hosts = ['localhost:27017', 'localhost:27018', 'localhost:27019']
# admin_db.stubs(:command).returns({'ok' => 1, 'ismaster' => 1, 'hosts' => @hosts})
# @conn.stubs(:[]).with('admin').returns(admin_db)
# @conn.connect
# end
#
# should "not store any hosts redundantly" do
# end
# end
#
# context "initializing a paired connection" do
# should "require left and right nodes" do
# assert_raise MongoArgumentError do
# Connection.multi(['localhost', 27018], :connect => false)
# end
#
# assert_raise MongoArgumentError do
# Connection.multi(['localhost', 27018], :connect => false)
# end
# end
#
# should "store both nodes" do
# @conn = Connection.multi([['localhost', 27017], ['localhost', 27018]], :connect => false)
#
# assert_equal ['localhost', 27017], @conn.nodes[0]
# assert_equal ['localhost', 27018], @conn.nodes[1]
# end
# end
#
# context "initializing with a mongodb uri" do
# should "parse a simple uri" do
# @conn = Connection.from_uri("mongodb://localhost", :connect => false)
# assert_equal ['localhost', 27017], @conn.primary
# end
#
# should "allow a complex host names" do
# host_name = "foo.bar-12345.org"
# @conn = Connection.from_uri("mongodb://#{host_name}", :connect => false)
# assert_equal [host_name, 27017], @conn.primary
# end
#
# should "parse a uri specifying multiple nodes" do
# #@conn = Connection.from_uri("mongodb://localhost:27017,mydb.com:27018", :connect => false)
# #assert_equal ['localhost', 27017], @conn.nodes[0]
# #assert_equal ['mydb.com', 27018], @conn.nodes[1]
# end
#
# should "parse a uri specifying multiple nodes with auth" do
# #@conn = Connection.from_uri("mongodb://kyle:s3cr3t@localhost:27017/app,mickey:m0u5e@mydb.com:27018/dsny", :connect => false)
# #assert_equal ['localhost', 27017], @conn.nodes[0]
# #assert_equal ['mydb.com', 27018], @conn.nodes[1]
# #auth_hash = {'username' => 'kyle', 'password' => 's3cr3t', 'db_name' => 'app'}
# #assert_equal auth_hash, @conn.auths[0]
# #auth_hash = {'username' => 'mickey', 'password' => 'm0u5e', 'db_name' => 'dsny'}
# #assert_equal auth_hash, @conn.auths[1]
# end
#
# should "parse a uri with a hyphen & underscore in the username or password" do
# @conn = Connection.from_uri("mongodb://hyphen-user_name:p-s_s@localhost:27017/db", :connect => false)
# assert_equal ['localhost', 27017], @conn.nodes[0]
# auth_hash = { 'db_name' => 'db', 'username' => 'hyphen-user_name', "password" => 'p-s_s' }
# assert_equal auth_hash, @conn.auths[0]
# end
#
# should "attempt to connect" do
# TCPSocket.stubs(:new).returns(new_mock_socket)
# @conn = Connection.from_uri("mongodb://localhost", :connect => false)
#
# admin_db = new_mock_db
# admin_db.expects(:command).returns({'ok' => 1, 'ismaster' => 1})
# @conn.expects(:[]).with('admin').returns(admin_db)
# @conn.expects(:apply_saved_authentication)
# @conn.connect
# end
#
# should "raise an error on invalid uris" do
# assert_raise MongoArgumentError do
# Connection.from_uri("mongo://localhost", :connect => false)
# end
#
# assert_raise MongoArgumentError do
# Connection.from_uri("mongodb://localhost:abc", :connect => false)
# end
#
# assert_raise MongoArgumentError do
# Connection.from_uri("mongodb://localhost:27017, my.db.com:27018, ", :connect => false)
# end
# end
#
# should "require all of username, password, and database if any one is specified" do
# assert_raise MongoArgumentError do
# Connection.from_uri("mongodb://localhost/db", :connect => false)
# end
#
# assert_raise MongoArgumentError do
# Connection.from_uri("mongodb://kyle:password@localhost", :connect => false)
# end
# end
# end
# end
#end
class ReplSetConnectionTest < Test::Unit::TestCase
context "Initialization: " do
setup do
def new_mock_socket(host='localhost', port=27017)
socket = Object.new
socket.stubs(:setsockopt).with(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
socket.stubs(:close)
socket
end
def new_mock_db
db = Object.new
end
end
context "connecting to a replica set" do
setup do
TCPSocket.stubs(:new).returns(new_mock_socket('localhost', 27017))
@conn = ReplSetConnection.new(['localhost', 27017], :connect => false, :read_secondary => true)
admin_db = new_mock_db
@hosts = ['localhost:27018', 'localhost:27019', 'localhost:27020']
admin_db.stubs(:command).returns({'ok' => 1, 'ismaster' => 1, 'hosts' => @hosts}).
then.returns({'ok' => 1, 'ismaster' => 0, 'hosts' => @hosts, 'secondary' => 1}).
then.returns({'ok' => 1, 'ismaster' => 0, 'hosts' => @hosts, 'secondary' => 1}).
then.returns({'ok' => 1, 'ismaster' => 0, 'arbiterOnly' => 1})
@conn.stubs(:[]).with('admin').returns(admin_db)
@conn.connect
end
should "store the hosts returned from the ismaster command" do
assert_equal 'localhost', @conn.primary_pool.host
assert_equal 27017, @conn.primary_pool.port
assert_equal 'localhost', @conn.secondary_pools[0].host
assert_equal 27018, @conn.secondary_pools[0].port
assert_equal 'localhost', @conn.secondary_pools[1].host
assert_equal 27019, @conn.secondary_pools[1].port
assert_equal 2, @conn.secondary_pools.length
end
end
context "connecting to a replica set and providing seed nodes" do
setup do
TCPSocket.stubs(:new).returns(new_mock_socket)
@conn = ReplSetConnection.new(['localhost', 27017], ['localhost', 27019], :connect => false)
admin_db = new_mock_db
@hosts = ['localhost:27017', 'localhost:27018', 'localhost:27019']
admin_db.stubs(:command).returns({'ok' => 1, 'ismaster' => 1, 'hosts' => @hosts})
@conn.stubs(:[]).with('admin').returns(admin_db)
@conn.connect
end
end
context "initializing with a mongodb uri" do
should "parse a uri specifying multiple nodes" do
@conn = Connection.from_uri("mongodb://localhost:27017,mydb.com:27018", :connect => false)
assert_equal ['localhost', 27017], @conn.nodes[0]
assert_equal ['mydb.com', 27018], @conn.nodes[1]
end
should "parse a uri specifying multiple nodes with auth" do
@conn = Connection.from_uri("mongodb://kyle:s3cr3t@localhost:27017/app,mickey:m0u5e@mydb.com:27018/dsny", :connect => false)
assert_equal ['localhost', 27017], @conn.nodes[0]
assert_equal ['mydb.com', 27018], @conn.nodes[1]
auth_hash = {'username' => 'kyle', 'password' => 's3cr3t', 'db_name' => 'app'}
assert_equal auth_hash, @conn.auths[0]
auth_hash = {'username' => 'mickey', 'password' => 'm0u5e', 'db_name' => 'dsny'}
assert_equal auth_hash, @conn.auths[1]
end
end
end
end