mongo 1.3.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +9 -6
- data/Rakefile +3 -4
- data/docs/HISTORY.md +20 -2
- data/docs/READ_PREFERENCE.md +39 -0
- data/docs/RELEASES.md +1 -1
- data/docs/REPLICA_SETS.md +23 -2
- data/docs/TAILABLE_CURSORS.md +51 -0
- data/docs/TUTORIAL.md +4 -4
- data/docs/WRITE_CONCERN.md +5 -2
- data/lib/mongo.rb +7 -22
- data/lib/mongo/collection.rb +96 -29
- data/lib/mongo/connection.rb +107 -62
- data/lib/mongo/cursor.rb +136 -57
- data/lib/mongo/db.rb +26 -5
- data/lib/mongo/exceptions.rb +17 -1
- data/lib/mongo/gridfs/grid.rb +1 -1
- data/lib/mongo/repl_set_connection.rb +273 -156
- data/lib/mongo/util/logging.rb +42 -0
- data/lib/mongo/util/node.rb +183 -0
- data/lib/mongo/util/pool.rb +76 -13
- data/lib/mongo/util/pool_manager.rb +208 -0
- data/lib/mongo/util/ssl_socket.rb +38 -0
- data/lib/mongo/util/support.rb +9 -1
- data/lib/mongo/util/timeout.rb +42 -0
- data/lib/mongo/version.rb +3 -0
- data/mongo.gemspec +2 -2
- data/test/bson/binary_test.rb +1 -1
- data/test/bson/bson_string_test.rb +30 -0
- data/test/bson/bson_test.rb +6 -3
- data/test/bson/byte_buffer_test.rb +1 -1
- data/test/bson/hash_with_indifferent_access_test.rb +1 -1
- data/test/bson/json_test.rb +1 -1
- data/test/bson/object_id_test.rb +2 -18
- data/test/bson/ordered_hash_test.rb +38 -3
- data/test/bson/test_helper.rb +46 -0
- data/test/bson/timestamp_test.rb +32 -10
- data/test/collection_test.rb +89 -3
- data/test/connection_test.rb +35 -20
- data/test/cursor_test.rb +63 -2
- data/test/db_test.rb +12 -2
- data/test/pool_test.rb +21 -0
- data/test/replica_sets/connect_test.rb +26 -13
- data/test/replica_sets/connection_string_test.rb +1 -4
- data/test/replica_sets/count_test.rb +1 -0
- data/test/replica_sets/insert_test.rb +1 -0
- data/test/replica_sets/pooled_insert_test.rb +4 -1
- data/test/replica_sets/query_secondaries.rb +2 -1
- data/test/replica_sets/query_test.rb +2 -1
- data/test/replica_sets/read_preference_test.rb +43 -0
- data/test/replica_sets/refresh_test.rb +123 -0
- data/test/replica_sets/replication_ack_test.rb +9 -4
- data/test/replica_sets/rs_test_helper.rb +2 -2
- data/test/timeout_test.rb +14 -0
- data/test/tools/repl_set_manager.rb +134 -23
- data/test/unit/collection_test.rb +6 -8
- data/test/unit/connection_test.rb +4 -4
- data/test/unit/cursor_test.rb +23 -5
- data/test/unit/db_test.rb +2 -0
- data/test/unit/grid_test.rb +2 -0
- data/test/unit/node_test.rb +73 -0
- data/test/unit/pool_manager_test.rb +47 -0
- data/test/unit/read_test.rb +101 -0
- metadata +214 -138
- data/lib/mongo/test.rb +0 -20
- data/test/async/collection_test.rb +0 -224
- data/test/async/connection_test.rb +0 -24
- data/test/async/cursor_test.rb +0 -162
- data/test/async/worker_pool_test.rb +0 -99
- data/test/load/resque/load.rb +0 -21
- data/test/load/resque/processor.rb +0 -26
- data/test/load/unicorn/unicorn.rb +0 -29
- data/test/tools/load.rb +0 -58
- data/test/tools/sharding_manager.rb +0 -202
- data/test/tools/test.rb +0 -4
- data/test/unit/repl_set_connection_test.rb +0 -59
@@ -0,0 +1,42 @@
|
|
1
|
+
module Mongo
|
2
|
+
module Logging
|
3
|
+
|
4
|
+
# Log a message with the given level.
|
5
|
+
def log(level, msg)
|
6
|
+
return unless @logger
|
7
|
+
case level
|
8
|
+
when :debug then
|
9
|
+
@logger.debug "MONGODB [DEBUG] #{msg}"
|
10
|
+
when :warn then
|
11
|
+
@logger.warn "MONGODB [WARNING] #{msg}"
|
12
|
+
when :error then
|
13
|
+
@logger.error "MONGODB [ERROR] #{msg}"
|
14
|
+
when :fatal then
|
15
|
+
@logger.fatal "MONGODB [FATAL] #{msg}"
|
16
|
+
else
|
17
|
+
@logger.info "MONGODB [INFO] #{msg}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Execute the block and log the operation described by name and payload.
|
22
|
+
def instrument(name, payload = {}, &blk)
|
23
|
+
res = yield
|
24
|
+
log_operation(name, payload)
|
25
|
+
res
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def log_operation(name, payload)
|
31
|
+
@logger ||= nil
|
32
|
+
return unless @logger
|
33
|
+
msg = "#{payload[:database]}['#{payload[:collection]}'].#{name}("
|
34
|
+
msg += payload.values_at(:selector, :document, :documents, :fields ).compact.map(&:inspect).join(', ') + ")"
|
35
|
+
msg += ".skip(#{payload[:skip]})" if payload[:skip]
|
36
|
+
msg += ".limit(#{payload[:limit]})" if payload[:limit]
|
37
|
+
msg += ".sort(#{payload[:order]})" if payload[:order]
|
38
|
+
@logger.debug "MONGODB #{msg}"
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
module Mongo
|
2
|
+
class Node
|
3
|
+
|
4
|
+
attr_accessor :host, :port, :address, :config, :connection, :socket
|
5
|
+
|
6
|
+
def initialize(connection, data)
|
7
|
+
@connection = connection
|
8
|
+
if data.is_a?(String)
|
9
|
+
@host, @port = split_nodes(data)
|
10
|
+
else
|
11
|
+
@host = data[0]
|
12
|
+
@port = data[1].nil? ? Connection::DEFAULT_PORT : data[1].to_i
|
13
|
+
end
|
14
|
+
@address = "#{host}:#{port}"
|
15
|
+
@config = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def eql?(other)
|
19
|
+
other.is_a?(Node) && host == other.host && port == other.port
|
20
|
+
end
|
21
|
+
alias :== :eql?
|
22
|
+
|
23
|
+
def host_string
|
24
|
+
address
|
25
|
+
end
|
26
|
+
|
27
|
+
def inspect
|
28
|
+
"<Mongo::Node:0x#{self.object_id.to_s(16)} @host=#{@host} @port=#{@port}>"
|
29
|
+
end
|
30
|
+
|
31
|
+
# Create a connection to the provided node,
|
32
|
+
# and, if successful, return the socket. Otherwise,
|
33
|
+
# return nil.
|
34
|
+
def connect
|
35
|
+
begin
|
36
|
+
socket = nil
|
37
|
+
if @connection.connect_timeout
|
38
|
+
Mongo::TimeoutHandler.timeout(@connection.connect_timeout, OperationTimeout) do
|
39
|
+
socket = @connection.socket_class.new(@host, @port)
|
40
|
+
end
|
41
|
+
else
|
42
|
+
socket = @connection.socket_class.new(@host, @port)
|
43
|
+
end
|
44
|
+
|
45
|
+
if socket.nil?
|
46
|
+
return nil
|
47
|
+
else
|
48
|
+
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
49
|
+
end
|
50
|
+
rescue OperationTimeout, OperationFailure, SocketError, SystemCallError, IOError => ex
|
51
|
+
@connection.log(:debug, "Failed connection to #{host_string} with #{ex.class}, #{ex.message}.")
|
52
|
+
socket.close if socket
|
53
|
+
return nil
|
54
|
+
end
|
55
|
+
|
56
|
+
@socket = socket
|
57
|
+
end
|
58
|
+
|
59
|
+
def close
|
60
|
+
if @socket
|
61
|
+
@socket.close
|
62
|
+
@socket = nil
|
63
|
+
@config = nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def connected?
|
68
|
+
@socket != nil
|
69
|
+
end
|
70
|
+
|
71
|
+
def active?
|
72
|
+
begin
|
73
|
+
result = @connection['admin'].command({:ping => 1}, :socket => @socket)
|
74
|
+
return result['ok'] == 1
|
75
|
+
rescue OperationFailure, SocketError, SystemCallError, IOError => ex
|
76
|
+
return nil
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Get the configuration for the provided node as returned by the
|
81
|
+
# ismaster command. Additionally, check that the replica set name
|
82
|
+
# matches with the name provided.
|
83
|
+
def set_config
|
84
|
+
begin
|
85
|
+
@config = @connection['admin'].command({:ismaster => 1}, :socket => @socket)
|
86
|
+
|
87
|
+
if @config['msg'] && @logger
|
88
|
+
@connection.log(:warn, "#{config['msg']}")
|
89
|
+
end
|
90
|
+
|
91
|
+
check_set_membership(config)
|
92
|
+
check_set_name(config)
|
93
|
+
rescue ConnectionFailure, OperationFailure, SocketError, SystemCallError, IOError => ex
|
94
|
+
@connection.log(:warn, "Attempted connection to node #{host_string} raised " +
|
95
|
+
"#{ex.class}: #{ex.message}")
|
96
|
+
return nil
|
97
|
+
end
|
98
|
+
|
99
|
+
@config
|
100
|
+
end
|
101
|
+
|
102
|
+
# Return a list of replica set nodes from the config.
|
103
|
+
# Note: this excludes arbiters.
|
104
|
+
def node_list
|
105
|
+
connect unless connected?
|
106
|
+
set_config unless @config
|
107
|
+
|
108
|
+
return [] unless config
|
109
|
+
|
110
|
+
nodes = []
|
111
|
+
nodes += config['hosts'] if config['hosts']
|
112
|
+
nodes += config['passives'] if config['passives']
|
113
|
+
nodes
|
114
|
+
end
|
115
|
+
|
116
|
+
def arbiters
|
117
|
+
connect unless connected?
|
118
|
+
set_config unless @config
|
119
|
+
return [] unless config['arbiters']
|
120
|
+
|
121
|
+
config['arbiters'].map do |arbiter|
|
122
|
+
split_nodes(arbiter)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def tags
|
127
|
+
connect unless connected?
|
128
|
+
set_config unless @config
|
129
|
+
return {} unless config['tags'] && !config['tags'].empty?
|
130
|
+
|
131
|
+
config['tags']
|
132
|
+
end
|
133
|
+
|
134
|
+
def primary?
|
135
|
+
@config['ismaster'] == true || @config['ismaster'] == 1
|
136
|
+
end
|
137
|
+
|
138
|
+
def secondary?
|
139
|
+
@config['secondary'] == true || @config['secondary'] == 1
|
140
|
+
end
|
141
|
+
|
142
|
+
def host_port
|
143
|
+
[@host, @port]
|
144
|
+
end
|
145
|
+
|
146
|
+
def hash
|
147
|
+
address.hash
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def split_nodes(host_string)
|
153
|
+
data = host_string.split(":")
|
154
|
+
host = data[0]
|
155
|
+
port = data[1].nil? ? Connection::DEFAULT_PORT : data[1].to_i
|
156
|
+
|
157
|
+
[host, port]
|
158
|
+
end
|
159
|
+
|
160
|
+
# Ensure that this node is a member of a replica set.
|
161
|
+
def check_set_membership(config)
|
162
|
+
if !config['hosts']
|
163
|
+
message = "Will not connect to #{host_string} because it's not a member " +
|
164
|
+
"of a replica set."
|
165
|
+
raise ConnectionFailure, message
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Ensure that this node is part of a replica set of the expected name.
|
170
|
+
def check_set_name(config)
|
171
|
+
if @connection.replica_set_name
|
172
|
+
if !config['setName']
|
173
|
+
@connection.log(:warn, "Could not verify replica set name for member #{host_string} " +
|
174
|
+
"because ismaster does not return name in this version of MongoDB")
|
175
|
+
elsif @connection.replica_set_name != config['setName']
|
176
|
+
message = "Attempting to connect to replica set '#{config['setName']}' on member #{host_string} " +
|
177
|
+
"but expected '#{@connection.replica_set_name}'"
|
178
|
+
raise ReplicaSetConnectionError, message
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
data/lib/mongo/util/pool.rb
CHANGED
@@ -17,8 +17,9 @@
|
|
17
17
|
|
18
18
|
module Mongo
|
19
19
|
class Pool
|
20
|
+
PING_ATTEMPTS = 6
|
20
21
|
|
21
|
-
attr_accessor :host, :port, :size, :timeout, :safe, :checked_out
|
22
|
+
attr_accessor :host, :port, :size, :timeout, :safe, :checked_out, :connection
|
22
23
|
|
23
24
|
# Create a new pool of connections.
|
24
25
|
#
|
@@ -27,6 +28,9 @@ module Mongo
|
|
27
28
|
|
28
29
|
@host, @port = host, port
|
29
30
|
|
31
|
+
# A Mongo::Node object.
|
32
|
+
@node = opts[:node]
|
33
|
+
|
30
34
|
# Pool size and timeout.
|
31
35
|
@size = opts[:size] || 1
|
32
36
|
@timeout = opts[:timeout] || 5.0
|
@@ -43,20 +47,77 @@ module Mongo
|
|
43
47
|
@sockets = []
|
44
48
|
@pids = {}
|
45
49
|
@checked_out = []
|
50
|
+
@ping_time = nil
|
51
|
+
@last_ping = nil
|
46
52
|
end
|
47
53
|
|
48
54
|
def close
|
49
|
-
@
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
55
|
+
@connection_mutex.synchronize do
|
56
|
+
@sockets.each do |sock|
|
57
|
+
begin
|
58
|
+
sock.close
|
59
|
+
rescue IOError => ex
|
60
|
+
warn "IOError when attempting to close socket connected to #{@host}:#{@port}: #{ex.inspect}"
|
61
|
+
end
|
54
62
|
end
|
63
|
+
@host = @port = nil
|
64
|
+
@sockets.clear
|
65
|
+
@pids.clear
|
66
|
+
@checked_out.clear
|
55
67
|
end
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
@
|
68
|
+
end
|
69
|
+
|
70
|
+
def inspect
|
71
|
+
"#<Mongo::Pool:0x#{self.object_id.to_s(16)} @host=#{@host} @port=#{port} " +
|
72
|
+
"@ping_time=#{@ping_time} #{@checked_out.size}/#{@size} sockets available.>"
|
73
|
+
end
|
74
|
+
|
75
|
+
def host_string
|
76
|
+
"#{@host}:#{@port}"
|
77
|
+
end
|
78
|
+
|
79
|
+
def host_port
|
80
|
+
[@host, @port]
|
81
|
+
end
|
82
|
+
|
83
|
+
# Refresh ping time only if we haven't
|
84
|
+
# checked within the last five minutes.
|
85
|
+
def ping_time
|
86
|
+
if !@last_ping
|
87
|
+
@last_ping = Time.now
|
88
|
+
@ping_time = refresh_ping_time
|
89
|
+
elsif Time.now - @last_ping > 300
|
90
|
+
@last_ping = Time.now
|
91
|
+
@ping_time = refresh_ping_time
|
92
|
+
else
|
93
|
+
@ping_time
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Return the time it takes on average
|
98
|
+
# to do a round-trip against this node.
|
99
|
+
def refresh_ping_time
|
100
|
+
trials = []
|
101
|
+
begin
|
102
|
+
PING_ATTEMPTS.times do
|
103
|
+
t1 = Time.now
|
104
|
+
self.connection['admin'].command({:ping => 1}, :socket => @node.socket)
|
105
|
+
trials << (Time.now - t1) * 1000
|
106
|
+
end
|
107
|
+
rescue OperationFailure, SocketError, SystemCallError, IOError => ex
|
108
|
+
return nil
|
109
|
+
end
|
110
|
+
|
111
|
+
trials.sort!
|
112
|
+
|
113
|
+
# Delete shortest and longest times
|
114
|
+
trials.delete_at(trials.length-1)
|
115
|
+
trials.delete_at(0)
|
116
|
+
|
117
|
+
total = 0.0
|
118
|
+
trials.each { |t| total += t }
|
119
|
+
|
120
|
+
(total / trials.length).ceil
|
60
121
|
end
|
61
122
|
|
62
123
|
# Return a socket to the pool.
|
@@ -74,10 +135,12 @@ module Mongo
|
|
74
135
|
# therefore, it runs within a mutex.
|
75
136
|
def checkout_new_socket
|
76
137
|
begin
|
77
|
-
|
78
|
-
|
138
|
+
socket = self.connection.socket_class.new(@host, @port)
|
139
|
+
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
79
140
|
rescue => ex
|
141
|
+
socket.close if socket
|
80
142
|
raise ConnectionFailure, "Failed to connect to host #{@host} and port #{@port}: #{ex}"
|
143
|
+
@node.close if @node
|
81
144
|
end
|
82
145
|
|
83
146
|
# If any saved authentications exist, we want to apply those
|
@@ -128,7 +191,7 @@ module Mongo
|
|
128
191
|
if @pids[socket] != Process.pid
|
129
192
|
@pids[socket] = nil
|
130
193
|
@sockets.delete(socket)
|
131
|
-
socket.close
|
194
|
+
socket.close if socket
|
132
195
|
checkout_new_socket
|
133
196
|
else
|
134
197
|
@checked_out << socket
|
@@ -0,0 +1,208 @@
|
|
1
|
+
module Mongo
|
2
|
+
class PoolManager
|
3
|
+
|
4
|
+
attr_reader :connection, :seeds, :arbiters, :primary, :secondaries,
|
5
|
+
:primary_pool, :read_pool, :secondary_pools, :hosts, :nodes, :max_bson_size,
|
6
|
+
:tags_to_pools, :members
|
7
|
+
|
8
|
+
def initialize(connection, seeds)
|
9
|
+
@connection = connection
|
10
|
+
@seeds = seeds
|
11
|
+
@refresh_node = nil
|
12
|
+
@previously_connected = false
|
13
|
+
end
|
14
|
+
|
15
|
+
def inspect
|
16
|
+
"<Mongo::PoolManager:0x#{self.object_id.to_s(16)} @seeds=#{@seeds}>"
|
17
|
+
end
|
18
|
+
|
19
|
+
def connect
|
20
|
+
if @previously_connected
|
21
|
+
close
|
22
|
+
end
|
23
|
+
|
24
|
+
initialize_data
|
25
|
+
members = connect_to_members
|
26
|
+
initialize_pools(members)
|
27
|
+
update_seed_list(members)
|
28
|
+
|
29
|
+
@members = members
|
30
|
+
@previously_connected = true
|
31
|
+
end
|
32
|
+
|
33
|
+
def healthy?
|
34
|
+
if !@refresh_node || !refresh_node.set_config
|
35
|
+
return false
|
36
|
+
end
|
37
|
+
|
38
|
+
#if refresh_node.node_list
|
39
|
+
end
|
40
|
+
|
41
|
+
def close
|
42
|
+
begin
|
43
|
+
if @primary_pool
|
44
|
+
@primary_pool.close
|
45
|
+
end
|
46
|
+
|
47
|
+
if @secondary_pools
|
48
|
+
@secondary_pools.each do |pool|
|
49
|
+
pool.close
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
if @members
|
54
|
+
@members.each do |member|
|
55
|
+
member.close
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
rescue ConnectionFailure
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def initialize_data
|
66
|
+
@primary = nil
|
67
|
+
@primary_pool = nil
|
68
|
+
@read_pool = nil
|
69
|
+
@arbiters = []
|
70
|
+
@secondaries = []
|
71
|
+
@secondary_pools = []
|
72
|
+
@hosts = Set.new
|
73
|
+
@members = Set.new
|
74
|
+
@tags_to_pools = {}
|
75
|
+
end
|
76
|
+
|
77
|
+
# Connect to each member of the replica set
|
78
|
+
# as reported by the given seed node, and return
|
79
|
+
# as a list of Mongo::Node objects.
|
80
|
+
def connect_to_members
|
81
|
+
members = []
|
82
|
+
|
83
|
+
seed = get_valid_seed_node
|
84
|
+
|
85
|
+
seed.node_list.each do |host|
|
86
|
+
node = Mongo::Node.new(self.connection, host)
|
87
|
+
if node.connect && node.set_config
|
88
|
+
members << node
|
89
|
+
end
|
90
|
+
end
|
91
|
+
seed.close
|
92
|
+
|
93
|
+
if members.empty?
|
94
|
+
raise ConnectionFailure, "Failed to connect to any given member."
|
95
|
+
end
|
96
|
+
|
97
|
+
members
|
98
|
+
end
|
99
|
+
|
100
|
+
def associate_tags_with_pool(tags, pool)
|
101
|
+
tags.each_key do |key|
|
102
|
+
@tags_to_pools[{key => tags[key]}] ||= []
|
103
|
+
@tags_to_pools[{key => tags[key]}] << pool
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Initialize the connection pools for the primary and secondary nodes.
|
108
|
+
def initialize_pools(members)
|
109
|
+
members.each do |member|
|
110
|
+
@hosts << member.host_string
|
111
|
+
|
112
|
+
if member.primary?
|
113
|
+
@primary = member.host_port
|
114
|
+
@primary_pool = Pool.new(self.connection, member.host, member.port,
|
115
|
+
:size => self.connection.pool_size,
|
116
|
+
:timeout => self.connection.connect_timeout,
|
117
|
+
:node => member)
|
118
|
+
associate_tags_with_pool(member.tags, @primary_pool)
|
119
|
+
elsif member.secondary? && !@secondaries.include?(member.host_port)
|
120
|
+
@secondaries << member.host_port
|
121
|
+
pool = Pool.new(self.connection, member.host, member.port,
|
122
|
+
:size => self.connection.pool_size,
|
123
|
+
:timeout => self.connection.connect_timeout,
|
124
|
+
:node => member)
|
125
|
+
@secondary_pools << pool
|
126
|
+
associate_tags_with_pool(member.tags, pool)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
@max_bson_size = members.first.config['maxBsonObjectSize'] ||
|
132
|
+
Mongo::DEFAULT_MAX_BSON_SIZE
|
133
|
+
@arbiters = members.first.arbiters
|
134
|
+
|
135
|
+
set_read_pool
|
136
|
+
set_primary_tag_pools
|
137
|
+
end
|
138
|
+
|
139
|
+
# If there's more than one pool associated with
|
140
|
+
# a given tag, choose a close one using the bucket method.
|
141
|
+
def set_primary_tag_pools
|
142
|
+
@tags_to_pools.each do |k, pool_list|
|
143
|
+
if pool_list.length == 1
|
144
|
+
@tags_to_pools[k] = pool_list.first
|
145
|
+
else
|
146
|
+
@tags_to_pools[k] = nearby_pool_from_set(pool_list)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Pick a node from the set of possible secondaries.
|
152
|
+
# If more than one node is available, use the ping
|
153
|
+
# time to figure out which nodes to choose from.
|
154
|
+
def set_read_pool
|
155
|
+
if @secondary_pools.empty?
|
156
|
+
@read_pool = @primary_pool
|
157
|
+
elsif @secondary_pools.size == 1
|
158
|
+
@read_pool = @secondary_pools[0]
|
159
|
+
else
|
160
|
+
@read_pool = nearby_pool_from_set(@secondary_pools)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def nearby_pool_from_set(pool_set)
|
165
|
+
ping_ranges = Array.new(3) { |i| Array.new }
|
166
|
+
pool_set.each do |pool|
|
167
|
+
case pool.ping_time
|
168
|
+
when 0..150
|
169
|
+
ping_ranges[0] << pool
|
170
|
+
when 150..1000
|
171
|
+
ping_ranges[1] << pool
|
172
|
+
else
|
173
|
+
ping_ranges[2] << pool
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
for list in ping_ranges do
|
178
|
+
break if !list.empty?
|
179
|
+
end
|
180
|
+
|
181
|
+
list[rand(list.length)]
|
182
|
+
end
|
183
|
+
|
184
|
+
# Iterate through the list of provided seed
|
185
|
+
# nodes until we've gotten a response from the
|
186
|
+
# replica set we're trying to connect to.
|
187
|
+
#
|
188
|
+
# If we don't get a response, raise an exception.
|
189
|
+
def get_valid_seed_node
|
190
|
+
@seeds.each do |seed|
|
191
|
+
node = Mongo::Node.new(self.connection, seed)
|
192
|
+
if node.connect && node.set_config
|
193
|
+
return node
|
194
|
+
else
|
195
|
+
node.close
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
raise ConnectionFailure, "Cannot connect to a replica set using seeds " +
|
200
|
+
"#{@seeds.map {|s| "#{s[0]}:#{s[1]}" }.join(', ')}"
|
201
|
+
end
|
202
|
+
|
203
|
+
def update_seed_list(members)
|
204
|
+
@seeds = members.map { |n| n.host_port }
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
end
|