mongo 1.10.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/LICENSE +190 -0
  5. data/README.md +149 -0
  6. data/Rakefile +31 -0
  7. data/VERSION +1 -0
  8. data/bin/mongo_console +43 -0
  9. data/ext/jsasl/target/jsasl.jar +0 -0
  10. data/lib/mongo.rb +90 -0
  11. data/lib/mongo/bulk_write_collection_view.rb +380 -0
  12. data/lib/mongo/collection.rb +1164 -0
  13. data/lib/mongo/collection_writer.rb +364 -0
  14. data/lib/mongo/connection.rb +19 -0
  15. data/lib/mongo/connection/node.rb +239 -0
  16. data/lib/mongo/connection/pool.rb +347 -0
  17. data/lib/mongo/connection/pool_manager.rb +325 -0
  18. data/lib/mongo/connection/sharding_pool_manager.rb +67 -0
  19. data/lib/mongo/connection/socket.rb +18 -0
  20. data/lib/mongo/connection/socket/socket_util.rb +37 -0
  21. data/lib/mongo/connection/socket/ssl_socket.rb +95 -0
  22. data/lib/mongo/connection/socket/tcp_socket.rb +86 -0
  23. data/lib/mongo/connection/socket/unix_socket.rb +39 -0
  24. data/lib/mongo/cursor.rb +719 -0
  25. data/lib/mongo/db.rb +735 -0
  26. data/lib/mongo/exception.rb +88 -0
  27. data/lib/mongo/functional.rb +21 -0
  28. data/lib/mongo/functional/authentication.rb +318 -0
  29. data/lib/mongo/functional/logging.rb +85 -0
  30. data/lib/mongo/functional/read_preference.rb +174 -0
  31. data/lib/mongo/functional/sasl_java.rb +48 -0
  32. data/lib/mongo/functional/uri_parser.rb +374 -0
  33. data/lib/mongo/functional/write_concern.rb +66 -0
  34. data/lib/mongo/gridfs.rb +18 -0
  35. data/lib/mongo/gridfs/grid.rb +112 -0
  36. data/lib/mongo/gridfs/grid_ext.rb +53 -0
  37. data/lib/mongo/gridfs/grid_file_system.rb +163 -0
  38. data/lib/mongo/gridfs/grid_io.rb +484 -0
  39. data/lib/mongo/legacy.rb +140 -0
  40. data/lib/mongo/mongo_client.rb +702 -0
  41. data/lib/mongo/mongo_replica_set_client.rb +523 -0
  42. data/lib/mongo/mongo_sharded_client.rb +159 -0
  43. data/lib/mongo/networking.rb +370 -0
  44. data/lib/mongo/utils.rb +19 -0
  45. data/lib/mongo/utils/conversions.rb +110 -0
  46. data/lib/mongo/utils/core_ext.rb +70 -0
  47. data/lib/mongo/utils/server_version.rb +69 -0
  48. data/lib/mongo/utils/support.rb +80 -0
  49. data/lib/mongo/utils/thread_local_variable_manager.rb +25 -0
  50. data/mongo.gemspec +36 -0
  51. data/test/functional/authentication_test.rb +35 -0
  52. data/test/functional/bulk_api_stress_test.rb +133 -0
  53. data/test/functional/bulk_write_collection_view_test.rb +1129 -0
  54. data/test/functional/client_test.rb +565 -0
  55. data/test/functional/collection_test.rb +2073 -0
  56. data/test/functional/collection_writer_test.rb +83 -0
  57. data/test/functional/conversions_test.rb +163 -0
  58. data/test/functional/cursor_fail_test.rb +63 -0
  59. data/test/functional/cursor_message_test.rb +57 -0
  60. data/test/functional/cursor_test.rb +625 -0
  61. data/test/functional/db_api_test.rb +819 -0
  62. data/test/functional/db_connection_test.rb +27 -0
  63. data/test/functional/db_test.rb +344 -0
  64. data/test/functional/grid_file_system_test.rb +285 -0
  65. data/test/functional/grid_io_test.rb +252 -0
  66. data/test/functional/grid_test.rb +273 -0
  67. data/test/functional/pool_test.rb +62 -0
  68. data/test/functional/safe_test.rb +98 -0
  69. data/test/functional/ssl_test.rb +29 -0
  70. data/test/functional/support_test.rb +62 -0
  71. data/test/functional/timeout_test.rb +58 -0
  72. data/test/functional/uri_test.rb +330 -0
  73. data/test/functional/write_concern_test.rb +118 -0
  74. data/test/helpers/general.rb +50 -0
  75. data/test/helpers/test_unit.rb +317 -0
  76. data/test/replica_set/authentication_test.rb +35 -0
  77. data/test/replica_set/basic_test.rb +174 -0
  78. data/test/replica_set/client_test.rb +341 -0
  79. data/test/replica_set/complex_connect_test.rb +77 -0
  80. data/test/replica_set/connection_test.rb +138 -0
  81. data/test/replica_set/count_test.rb +64 -0
  82. data/test/replica_set/cursor_test.rb +212 -0
  83. data/test/replica_set/insert_test.rb +140 -0
  84. data/test/replica_set/max_values_test.rb +145 -0
  85. data/test/replica_set/pinning_test.rb +55 -0
  86. data/test/replica_set/query_test.rb +73 -0
  87. data/test/replica_set/read_preference_test.rb +214 -0
  88. data/test/replica_set/refresh_test.rb +175 -0
  89. data/test/replica_set/replication_ack_test.rb +94 -0
  90. data/test/replica_set/ssl_test.rb +32 -0
  91. data/test/sharded_cluster/basic_test.rb +197 -0
  92. data/test/shared/authentication/basic_auth_shared.rb +286 -0
  93. data/test/shared/authentication/bulk_api_auth_shared.rb +259 -0
  94. data/test/shared/authentication/gssapi_shared.rb +164 -0
  95. data/test/shared/authentication/sasl_plain_shared.rb +96 -0
  96. data/test/shared/ssl_shared.rb +235 -0
  97. data/test/test_helper.rb +56 -0
  98. data/test/threading/basic_test.rb +120 -0
  99. data/test/tools/mongo_config.rb +608 -0
  100. data/test/tools/mongo_config_test.rb +160 -0
  101. data/test/unit/client_test.rb +347 -0
  102. data/test/unit/collection_test.rb +166 -0
  103. data/test/unit/connection_test.rb +325 -0
  104. data/test/unit/cursor_test.rb +299 -0
  105. data/test/unit/db_test.rb +136 -0
  106. data/test/unit/grid_test.rb +76 -0
  107. data/test/unit/mongo_sharded_client_test.rb +48 -0
  108. data/test/unit/node_test.rb +93 -0
  109. data/test/unit/pool_manager_test.rb +142 -0
  110. data/test/unit/read_pref_test.rb +115 -0
  111. data/test/unit/read_test.rb +159 -0
  112. data/test/unit/safe_test.rb +158 -0
  113. data/test/unit/sharding_pool_manager_test.rb +84 -0
  114. data/test/unit/write_concern_test.rb +175 -0
  115. metadata +260 -0
  116. metadata.gz.sig +0 -0
@@ -0,0 +1,347 @@
1
+ # Copyright (C) 2009-2013 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Mongo
16
+ class Pool
17
+ PING_ATTEMPTS = 6
18
+ MAX_PING_TIME = 1_000_000
19
+ PRUNE_INTERVAL = 10_000
20
+
21
+ attr_accessor :host,
22
+ :port,
23
+ :address,
24
+ :size,
25
+ :timeout,
26
+ :checked_out,
27
+ :client,
28
+ :node
29
+
30
+ # Create a new pool of connections.
31
+ def initialize(client, host, port, opts={})
32
+ @client = client
33
+
34
+ @host, @port = host, port
35
+
36
+ # A Mongo::Node object.
37
+ @node = opts[:node]
38
+
39
+ # The string address
40
+ @address = "#{@host}:#{@port}"
41
+
42
+ # Pool size and timeout.
43
+ @size = opts.fetch(:size, 20)
44
+ @timeout = opts.fetch(:timeout, 30)
45
+
46
+ # Mutex for synchronizing pool access
47
+ @connection_mutex = Mutex.new
48
+
49
+ # Mutex for synchronizing pings
50
+ @ping_mutex = Mutex.new
51
+
52
+ # Condition variable for signal and wait
53
+ @queue = ConditionVariable.new
54
+
55
+ @sockets = []
56
+ @checked_out = []
57
+ @ping_time = nil
58
+ @last_ping = nil
59
+ @closed = false
60
+ @thread_ids_to_sockets = {}
61
+ @checkout_counter = 0
62
+ end
63
+
64
+ # Close this pool.
65
+ #
66
+ # @option opts [Boolean]:soft (false) If true,
67
+ # close only those sockets that are not checked out.
68
+ def close(opts={})
69
+ @connection_mutex.synchronize do
70
+ if opts[:soft] && !@checked_out.empty?
71
+ @closing = true
72
+ close_sockets(@sockets - @checked_out)
73
+ else
74
+ close_sockets(@sockets)
75
+ @closed = true
76
+ end
77
+ @node.close if @node
78
+ end
79
+ true
80
+ end
81
+
82
+ def tags
83
+ @node.tags
84
+ end
85
+
86
+ def healthy?
87
+ close if @sockets.all?(&:closed?)
88
+ !closed? && node.healthy?
89
+ end
90
+
91
+ def closed?
92
+ @closed
93
+ end
94
+
95
+ def up?
96
+ !@closed
97
+ end
98
+
99
+ def inspect
100
+ "#<Mongo::Pool:0x#{self.object_id.to_s(16)} @host=#{@host} @port=#{port} " +
101
+ "@ping_time=#{@ping_time} #{@checked_out.size}/#{@size} sockets available " +
102
+ "up=#{!closed?}>"
103
+ end
104
+
105
+ def host_string
106
+ "#{@host}:#{@port}"
107
+ end
108
+
109
+ def host_port
110
+ [@host, @port]
111
+ end
112
+
113
+ # Refresh ping time only if we haven't
114
+ # checked within the last five minutes.
115
+ def ping_time
116
+ @ping_mutex.synchronize do
117
+ if !@last_ping || (Time.now - @last_ping) > 300
118
+ @ping_time = refresh_ping_time
119
+ @last_ping = Time.now
120
+ end
121
+ end
122
+ @ping_time
123
+ end
124
+
125
+ # Return the time it takes on average
126
+ # to do a round-trip against this node.
127
+ def refresh_ping_time
128
+ trials = []
129
+ PING_ATTEMPTS.times do
130
+ t1 = Time.now
131
+ if !self.ping
132
+ return MAX_PING_TIME
133
+ end
134
+ trials << (Time.now - t1) * 1000
135
+ end
136
+
137
+ trials.sort!
138
+
139
+ # Delete shortest and longest times
140
+ trials.delete_at(trials.length-1)
141
+ trials.delete_at(0)
142
+
143
+ total = 0.0
144
+ trials.each { |t| total += t }
145
+
146
+ (total / trials.length).ceil
147
+ end
148
+
149
+ def ping
150
+ begin
151
+ return self.client['admin'].command({:ping => 1}, :socket => @node.socket, :timeout => MAX_PING_TIME)
152
+ rescue ConnectionFailure, OperationFailure, SocketError, SystemCallError, IOError
153
+ return false
154
+ end
155
+ end
156
+
157
+ # Return a socket to the pool.
158
+ def checkin(socket)
159
+ @connection_mutex.synchronize do
160
+ if @checked_out.delete(socket)
161
+ @queue.broadcast
162
+ else
163
+ return false
164
+ end
165
+ end
166
+ true
167
+ end
168
+
169
+ # Adds a new socket to the pool and checks it out.
170
+ #
171
+ # This method is called exclusively from #checkout;
172
+ # therefore, it runs within a mutex.
173
+ def checkout_new_socket
174
+ begin
175
+ socket = @client.socket_class.new(@host, @port, @client.op_timeout,
176
+ @client.connect_timeout,
177
+ @client.socket_opts)
178
+ socket.pool = self
179
+ rescue => ex
180
+ socket.close if socket
181
+ @node.close if @node
182
+ raise ConnectionFailure, "Failed to connect to host #{@host} and port #{@port}: #{ex}"
183
+ end
184
+
185
+ # If any saved authentications exist, we want to apply those
186
+ # when creating new sockets and process logouts.
187
+ check_auths(socket)
188
+
189
+ @sockets << socket
190
+ @checked_out << socket
191
+ @thread_ids_to_sockets[Thread.current.object_id] = socket
192
+ socket
193
+ end
194
+
195
+ # If a user calls DB#authenticate, and several sockets exist,
196
+ # then we need a way to apply the authentication on each socket.
197
+ # So we store the apply_authentication method, and this will be
198
+ # applied right before the next use of each socket.
199
+ #
200
+ # @deprecated This method has been replaced by Pool#check_auths (private)
201
+ # and it isn't necessary to ever invoke this method directly.
202
+ def authenticate_existing
203
+ @connection_mutex.synchronize do
204
+ @sockets.each do |socket|
205
+ check_auths(socket)
206
+ end
207
+ end
208
+ end
209
+
210
+ # Store the logout op for each existing socket to be applied before
211
+ # the next use of each socket.
212
+ #
213
+ # @deprecated This method has been replaced by Pool#check_auths (private)
214
+ # and it isn't necessary to ever invoke this method directly.
215
+ def logout_existing(database)
216
+ @connection_mutex.synchronize do
217
+ @sockets.each do |socket|
218
+ check_auths(socket)
219
+ end
220
+ end
221
+ end
222
+
223
+ # Checks out the first available socket from the pool.
224
+ #
225
+ # If the pid has changed, remove the socket and check out
226
+ # new one.
227
+ #
228
+ # This method is called exclusively from #checkout;
229
+ # therefore, it runs within a mutex.
230
+ def checkout_existing_socket(socket=nil)
231
+ if !socket
232
+ available = @sockets - @checked_out
233
+ socket = available[rand(available.length)]
234
+ end
235
+
236
+ if socket.pid != Process.pid
237
+ @sockets.delete(socket)
238
+ if socket
239
+ socket.close unless socket.closed?
240
+ end
241
+ checkout_new_socket
242
+ else
243
+ @checked_out << socket
244
+ @thread_ids_to_sockets[Thread.current.object_id] = socket
245
+ socket
246
+ end
247
+ end
248
+
249
+ def prune_threads
250
+ live_threads = Thread.list.map(&:object_id)
251
+ @thread_ids_to_sockets.reject! do |key, value|
252
+ !live_threads.include?(key)
253
+ end
254
+ end
255
+
256
+ def check_prune
257
+ if @checkout_counter > PRUNE_INTERVAL
258
+ @checkout_counter = 0
259
+ prune_threads
260
+ else
261
+ @checkout_counter += 1
262
+ end
263
+ end
264
+
265
+ # Check out an existing socket or create a new socket if the maximum
266
+ # pool size has not been exceeded. Otherwise, wait for the next
267
+ # available socket.
268
+ def checkout
269
+ @client.connect if !@client.connected?
270
+ start_time = Time.now
271
+ loop do
272
+ if (Time.now - start_time) > @timeout
273
+ raise ConnectionTimeoutError, "could not obtain connection within " +
274
+ "#{@timeout} seconds. The max pool size is currently #{@size}; " +
275
+ "consider increasing the pool size or timeout."
276
+ end
277
+
278
+ @connection_mutex.synchronize do
279
+ check_prune
280
+ socket = nil
281
+ if socket_for_thread = @thread_ids_to_sockets[Thread.current.object_id]
282
+ if !@checked_out.include?(socket_for_thread)
283
+ socket = checkout_existing_socket(socket_for_thread)
284
+ end
285
+ else
286
+ if @sockets.size < @size
287
+ socket = checkout_new_socket
288
+ elsif @checked_out.size < @sockets.size
289
+ socket = checkout_existing_socket
290
+ end
291
+ end
292
+
293
+ if socket
294
+ check_auths(socket)
295
+
296
+ if socket.closed?
297
+ @checked_out.delete(socket)
298
+ @sockets.delete(socket)
299
+ @thread_ids_to_sockets.delete(Thread.current.object_id)
300
+ socket = checkout_new_socket
301
+ end
302
+
303
+ return socket
304
+ else
305
+ # Otherwise, wait
306
+ @queue.wait(@connection_mutex)
307
+ end
308
+ end
309
+ end
310
+ end
311
+
312
+ private
313
+
314
+ # Helper method to handle keeping track of auths/logouts for sockets.
315
+ #
316
+ # @param socket [Socket] The socket instance to be checked.
317
+ #
318
+ # @return [Socket] The authenticated socket instance.
319
+ def check_auths(socket)
320
+ # find and handle logouts
321
+ (socket.auths - @client.auths).each do |auth|
322
+ @client.issue_logout(auth[:source], :socket => socket)
323
+ socket.auths.delete(auth)
324
+ end
325
+
326
+ # find and handle new auths
327
+ (@client.auths - socket.auths).each do |auth|
328
+ @client.issue_authentication(auth, :socket => socket)
329
+ socket.auths.add(auth)
330
+ end
331
+
332
+ socket
333
+ end
334
+
335
+ def close_sockets(sockets)
336
+ sockets.each do |socket|
337
+ @sockets.delete(socket)
338
+ begin
339
+ socket.close unless socket.closed?
340
+ rescue IOError => ex
341
+ warn "IOError when attempting to close socket connected to #{@host}:#{@port}: #{ex.inspect}"
342
+ end
343
+ end
344
+ end
345
+
346
+ end
347
+ end
@@ -0,0 +1,325 @@
1
+ # Copyright (C) 2009-2013 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Mongo
16
+ class PoolManager
17
+ include ThreadLocalVariableManager
18
+
19
+ attr_reader :client,
20
+ :primary,
21
+ :primary_pool,
22
+ :seeds,
23
+ :max_bson_size,
24
+ :max_message_size,
25
+ :max_wire_version,
26
+ :min_wire_version
27
+
28
+ # Create a new set of connection pools.
29
+ #
30
+ # The pool manager will by default use the original seed list passed
31
+ # to the connection objects, accessible via connection.seeds. In addition,
32
+ # the user may pass an additional list of seeds nodes discovered in real
33
+ # time. The union of these lists will be used when attempting to connect,
34
+ # with the newly-discovered nodes being used first.
35
+ def initialize(client, seeds=[])
36
+ @client = client
37
+ @seeds = seeds
38
+
39
+ @pools = Set.new
40
+ @primary = nil
41
+ @primary_pool = nil
42
+ @secondaries = Set.new
43
+ @secondary_pools = []
44
+ @hosts = Set.new
45
+ @members = Set.new
46
+ @refresh_required = false
47
+ @max_bson_size = DEFAULT_MAX_BSON_SIZE
48
+ @max_message_size = @max_bson_size * MESSAGE_SIZE_FACTOR
49
+ @max_wire_version = 0
50
+ @min_wire_version = 0
51
+ @connect_mutex = Mutex.new
52
+ thread_local[:locks][:connecting_manager] = false
53
+ end
54
+
55
+ def inspect
56
+ "<Mongo::PoolManager:0x#{self.object_id.to_s(16)} @seeds=#{@seeds}>"
57
+ end
58
+
59
+ def connect
60
+ @connect_mutex.synchronize do
61
+ begin
62
+ thread_local[:locks][:connecting_manager] = true
63
+ @refresh_required = false
64
+ disconnect_old_members
65
+ connect_to_members
66
+ initialize_pools(@members)
67
+ update_max_sizes
68
+ @seeds = discovered_seeds
69
+ ensure
70
+ thread_local[:locks][:connecting_manager] = false
71
+ end
72
+ end
73
+ end
74
+
75
+ def refresh!(additional_seeds)
76
+ @seeds |= additional_seeds
77
+ connect
78
+ end
79
+
80
+ # We're healthy if all members are pingable and if the view
81
+ # of the replica set returned by isMaster is equivalent
82
+ # to our view. If any of these isn't the case,
83
+ # set @refresh_required to true, and return.
84
+ def check_connection_health
85
+ return if thread_local[:locks][:connecting_manager]
86
+ members = copy_members
87
+ begin
88
+ seed = get_valid_seed_node
89
+ rescue ConnectionFailure
90
+ @refresh_required = true
91
+ return
92
+ end
93
+
94
+ unless current_config = seed.config
95
+ @refresh_required = true
96
+ seed.close
97
+ return
98
+ end
99
+
100
+ if current_config['hosts'].length != members.length
101
+ @refresh_required = true
102
+ seed.close
103
+ return
104
+ end
105
+
106
+ current_config['hosts'].each do |host|
107
+ member = members.detect do |m|
108
+ m.address == host
109
+ end
110
+
111
+ if member && validate_existing_member(current_config, member)
112
+ next
113
+ else
114
+ @refresh_required = true
115
+ seed.close
116
+ return
117
+ end
118
+ end
119
+
120
+ seed.close
121
+ end
122
+
123
+ # The replica set connection should initiate a full refresh.
124
+ def refresh_required?
125
+ @refresh_required
126
+ end
127
+
128
+ def closed?
129
+ pools.all? { |pool| pool.closed? }
130
+ end
131
+
132
+ def close(opts={})
133
+ begin
134
+ pools.each { |pool| pool.close(opts) }
135
+ rescue ConnectionFailure
136
+ end
137
+ end
138
+
139
+ def read
140
+ read_pool.host_port
141
+ end
142
+
143
+ def hosts
144
+ @connect_mutex.synchronize do
145
+ @hosts.nil? ? nil : @hosts.clone
146
+ end
147
+ end
148
+
149
+ def pools
150
+ @connect_mutex.synchronize do
151
+ @pools.nil? ? nil : @pools.clone
152
+ end
153
+ end
154
+
155
+ def secondaries
156
+ @connect_mutex.synchronize do
157
+ @secondaries.nil? ? nil : @secondaries.clone
158
+ end
159
+ end
160
+
161
+ def secondary_pools
162
+ @connect_mutex.synchronize do
163
+ @secondary_pools.nil? ? nil : @secondary_pools.clone
164
+ end
165
+ end
166
+
167
+ def arbiters
168
+ @connect_mutex.synchronize do
169
+ @arbiters.nil? ? nil : @arbiters.clone
170
+ end
171
+ end
172
+
173
+ def state_snapshot
174
+ @connect_mutex.synchronize do
175
+ { :pools => @pools.nil? ? nil : @pools.clone,
176
+ :secondaries => @secondaries.nil? ? nil : @secondaries.clone,
177
+ :secondary_pools => @secondary_pools.nil? ? nil : @secondary_pools.clone,
178
+ :hosts => @hosts.nil? ? nil : @hosts.clone,
179
+ :arbiters => @arbiters.nil? ? nil : @arbiters.clone
180
+ }
181
+ end
182
+ end
183
+
184
+ private
185
+
186
+ def update_max_sizes
187
+ unless @members.size == 0
188
+ @max_bson_size = @members.map(&:max_bson_size).min
189
+ @max_message_size = @members.map(&:max_message_size).min
190
+ @max_wire_version = @members.map(&:max_wire_version).min
191
+ @min_wire_version = @members.map(&:min_wire_version).max
192
+ end
193
+ end
194
+
195
+ def validate_existing_member(current_config, member)
196
+ if current_config['ismaster'] && member.last_state != :primary
197
+ return false
198
+ elsif member.last_state != :other
199
+ return false
200
+ end
201
+ return true
202
+ end
203
+
204
+ # For any existing members, close and remove any that are unhealthy or already closed.
205
+ def disconnect_old_members
206
+ @pools.reject! {|pool| !pool.healthy? }
207
+ @members.reject! {|node| !node.healthy? }
208
+ end
209
+
210
+ # Connect to each member of the replica set
211
+ # as reported by the given seed node.
212
+ def connect_to_members
213
+ seed = get_valid_seed_node
214
+ seed.node_list.each do |host|
215
+ if existing = @members.detect {|node| node =~ host }
216
+ if existing.healthy?
217
+ # Refresh this node's configuration
218
+ existing.set_config
219
+ # If we are unhealthy after refreshing our config, drop from the set.
220
+ if !existing.healthy?
221
+ @members.delete(existing)
222
+ else
223
+ next
224
+ end
225
+ else
226
+ existing.close
227
+ @members.delete(existing)
228
+ end
229
+ end
230
+
231
+ node = Mongo::Node.new(self.client, host)
232
+ node.connect
233
+ @members << node if node.healthy?
234
+ end
235
+ seed.close
236
+
237
+ if @members.empty?
238
+ raise ConnectionFailure, "Failed to connect to any given member."
239
+ end
240
+ end
241
+
242
+ # Initialize the connection pools for the primary and secondary nodes.
243
+ def initialize_pools(members)
244
+ @primary_pool = nil
245
+ @primary = nil
246
+ @secondaries.clear
247
+ @secondary_pools.clear
248
+ @hosts.clear
249
+
250
+ members.each do |member|
251
+ member.last_state = nil
252
+ @hosts << member.host_string
253
+ if member.primary?
254
+ assign_primary(member)
255
+ elsif member.secondary?
256
+ # member could be not primary but secondary still is false
257
+ assign_secondary(member)
258
+ end
259
+ end
260
+
261
+ @arbiters = members.first.arbiters
262
+ end
263
+
264
+ def assign_primary(member)
265
+ member.last_state = :primary
266
+ @primary = member.host_port
267
+ if existing = @pools.detect {|pool| pool.node == member }
268
+ @primary_pool = existing
269
+ else
270
+ @primary_pool = Pool.new(self.client, member.host, member.port,
271
+ :size => self.client.pool_size,
272
+ :timeout => self.client.pool_timeout,
273
+ :node => member
274
+ )
275
+ @pools << @primary_pool
276
+ end
277
+ end
278
+
279
+ def assign_secondary(member)
280
+ member.last_state = :secondary
281
+ @secondaries << member.host_port
282
+ if existing = @pools.detect {|pool| pool.node == member }
283
+ @secondary_pools << existing
284
+ else
285
+ pool = Pool.new(self.client, member.host, member.port,
286
+ :size => self.client.pool_size,
287
+ :timeout => self.client.pool_timeout,
288
+ :node => member
289
+ )
290
+ @secondary_pools << pool
291
+ @pools << pool
292
+ end
293
+ end
294
+
295
+ # Iterate through the list of provided seed
296
+ # nodes until we've gotten a response from the
297
+ # replica set we're trying to connect to.
298
+ #
299
+ # If we don't get a response, raise an exception.
300
+ def get_valid_seed_node
301
+ @seeds.each do |seed|
302
+ node = Mongo::Node.new(self.client, seed)
303
+ node.connect
304
+ return node if node.healthy?
305
+ end
306
+
307
+ raise ConnectionFailure, "Cannot connect to a replica set using seeds " +
308
+ "#{@seeds.map {|s| "#{s[0]}:#{s[1]}" }.join(', ')}"
309
+ end
310
+
311
+ def discovered_seeds
312
+ @members.map(&:host_port)
313
+ end
314
+
315
+ def copy_members
316
+ members = Set.new
317
+ @connect_mutex.synchronize do
318
+ @members.map do |m|
319
+ members << m.dup
320
+ end
321
+ end
322
+ members
323
+ end
324
+ end
325
+ end