mongo 1.3.1 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|