mongo 2.10.5 → 2.11.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (191) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CONTRIBUTING.md +1 -1
  5. data/lib/mongo.rb +2 -0
  6. data/lib/mongo/address.rb +4 -0
  7. data/lib/mongo/address/validator.rb +99 -0
  8. data/lib/mongo/auth.rb +7 -2
  9. data/lib/mongo/auth/user.rb +1 -7
  10. data/lib/mongo/background_thread.rb +135 -0
  11. data/lib/mongo/bulk_write/transformable.rb +3 -3
  12. data/lib/mongo/client.rb +74 -16
  13. data/lib/mongo/cluster.rb +193 -41
  14. data/lib/mongo/cluster/periodic_executor.rb +31 -43
  15. data/lib/mongo/cluster/sdam_flow.rb +26 -3
  16. data/lib/mongo/cluster/srv_monitor.rb +127 -0
  17. data/lib/mongo/collection/view/readable.rb +3 -5
  18. data/lib/mongo/collection/view/writable.rb +3 -3
  19. data/lib/mongo/cursor/builder/get_more_command.rb +1 -4
  20. data/lib/mongo/cursor/builder/kill_cursors_command.rb +5 -23
  21. data/lib/mongo/cursor/builder/op_get_more.rb +2 -2
  22. data/lib/mongo/cursor/builder/op_kill_cursors.rb +5 -24
  23. data/lib/mongo/error.rb +1 -0
  24. data/lib/mongo/error/auth_error.rb +1 -1
  25. data/lib/mongo/error/connection_check_out_timeout.rb +7 -8
  26. data/lib/mongo/error/invalid_address.rb +24 -0
  27. data/lib/mongo/error/notable.rb +2 -2
  28. data/lib/mongo/error/operation_failure.rb +3 -3
  29. data/lib/mongo/error/pool_closed_error.rb +11 -4
  30. data/lib/mongo/event.rb +1 -1
  31. data/lib/mongo/grid/file.rb +0 -5
  32. data/lib/mongo/grid/file/chunk.rb +0 -2
  33. data/lib/mongo/grid/fs_bucket.rb +13 -15
  34. data/lib/mongo/grid/stream/write.rb +3 -9
  35. data/lib/mongo/loggable.rb +5 -1
  36. data/lib/mongo/monitoring.rb +1 -0
  37. data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +7 -0
  38. data/lib/mongo/monitoring/event/cmap/connection_checked_in.rb +11 -3
  39. data/lib/mongo/monitoring/event/cmap/connection_checked_out.rb +11 -3
  40. data/lib/mongo/monitoring/event/cmap/pool_closed.rb +11 -3
  41. data/lib/mongo/monitoring/event/cmap/pool_created.rb +12 -3
  42. data/lib/mongo/monitoring/unified_sdam_log_subscriber.rb +62 -0
  43. data/lib/mongo/operation/shared/executable.rb +5 -10
  44. data/lib/mongo/operation/shared/sessions_supported.rb +1 -5
  45. data/lib/mongo/protocol/get_more.rb +1 -2
  46. data/lib/mongo/protocol/kill_cursors.rb +13 -6
  47. data/lib/mongo/protocol/serializers.rb +4 -20
  48. data/lib/mongo/retryable.rb +9 -34
  49. data/lib/mongo/semaphore.rb +1 -1
  50. data/lib/mongo/server.rb +113 -42
  51. data/lib/mongo/server/connection.rb +12 -5
  52. data/lib/mongo/server/connection_pool.rb +250 -40
  53. data/lib/mongo/server/connection_pool/populator.rb +58 -0
  54. data/lib/mongo/server/description.rb +9 -2
  55. data/lib/mongo/server/monitor.rb +68 -93
  56. data/lib/mongo/server/monitor/connection.rb +2 -0
  57. data/lib/mongo/server_selector/selectable.rb +13 -5
  58. data/lib/mongo/session.rb +0 -13
  59. data/lib/mongo/srv.rb +17 -0
  60. data/lib/mongo/srv/monitor.rb +96 -0
  61. data/lib/mongo/srv/resolver.rb +130 -0
  62. data/lib/mongo/srv/result.rb +126 -0
  63. data/lib/mongo/srv/warning_result.rb +35 -0
  64. data/lib/mongo/uri.rb +45 -55
  65. data/lib/mongo/uri/srv_protocol.rb +89 -42
  66. data/lib/mongo/version.rb +1 -1
  67. data/mongo.gemspec +3 -4
  68. data/spec/README.md +6 -1
  69. data/spec/enterprise_auth/kerberos_spec.rb +7 -6
  70. data/spec/integration/change_stream_examples_spec.rb +0 -4
  71. data/spec/integration/client_construction_spec.rb +14 -2
  72. data/spec/integration/connect_single_rs_name_spec.rb +2 -2
  73. data/spec/integration/connection_pool_populator_spec.rb +296 -0
  74. data/spec/integration/connection_spec.rb +31 -22
  75. data/spec/integration/cursor_reaping_spec.rb +1 -2
  76. data/spec/integration/docs_examples_spec.rb +0 -4
  77. data/spec/integration/heartbeat_events_spec.rb +17 -15
  78. data/spec/integration/reconnect_spec.rb +144 -1
  79. data/spec/integration/retryable_writes_errors_spec.rb +0 -4
  80. data/spec/integration/retryable_writes_spec.rb +36 -36
  81. data/spec/integration/sdam_error_handling_spec.rb +31 -25
  82. data/spec/integration/sdam_events_spec.rb +2 -6
  83. data/spec/integration/server_monitor_spec.rb +28 -0
  84. data/spec/integration/server_selector_spec.rb +7 -5
  85. data/spec/integration/srv_monitoring_spec.rb +360 -0
  86. data/spec/integration/step_down_spec.rb +4 -6
  87. data/spec/lite_spec_helper.rb +22 -0
  88. data/spec/mongo/address/validator_spec.rb +51 -0
  89. data/spec/mongo/auth/cr_spec.rb +1 -29
  90. data/spec/mongo/auth/ldap_spec.rb +1 -29
  91. data/spec/mongo/auth/scram/conversation_spec.rb +0 -2
  92. data/spec/mongo/auth/scram/negotiation_spec.rb +1 -1
  93. data/spec/mongo/auth/scram_spec.rb +1 -29
  94. data/spec/mongo/auth/user/view_spec.rb +1 -36
  95. data/spec/mongo/auth/user_spec.rb +0 -12
  96. data/spec/mongo/auth/x509_spec.rb +1 -29
  97. data/spec/mongo/bulk_write_spec.rb +2 -2
  98. data/spec/mongo/client_construction_spec.rb +56 -15
  99. data/spec/mongo/client_spec.rb +31 -27
  100. data/spec/mongo/cluster/periodic_executor_spec.rb +16 -0
  101. data/spec/mongo/cluster/srv_monitor_spec.rb +214 -0
  102. data/spec/mongo/cluster/topology/replica_set_spec.rb +16 -11
  103. data/spec/mongo/cluster/topology/sharded_spec.rb +12 -9
  104. data/spec/mongo/cluster/topology/single_spec.rb +20 -11
  105. data/spec/mongo/cluster_spec.rb +45 -29
  106. data/spec/mongo/collection/view/map_reduce_spec.rb +14 -9
  107. data/spec/mongo/collection/view/readable_spec.rb +0 -16
  108. data/spec/mongo/collection_spec.rb +0 -44
  109. data/spec/mongo/cursor/builder/get_more_command_spec.rb +2 -4
  110. data/spec/mongo/cursor/builder/op_get_more_spec.rb +2 -4
  111. data/spec/mongo/cursor_spec.rb +27 -7
  112. data/spec/mongo/monitoring/event/cmap/connection_checked_in_spec.rb +10 -3
  113. data/spec/mongo/monitoring/event/cmap/connection_checked_out_spec.rb +10 -3
  114. data/spec/mongo/monitoring/event/cmap/pool_closed_spec.rb +10 -3
  115. data/spec/mongo/monitoring/event/cmap/pool_created_spec.rb +10 -3
  116. data/spec/mongo/operation/delete/op_msg_spec.rb +17 -8
  117. data/spec/mongo/operation/insert/op_msg_spec.rb +50 -35
  118. data/spec/mongo/operation/update/op_msg_spec.rb +14 -7
  119. data/spec/mongo/retryable_spec.rb +52 -31
  120. data/spec/mongo/server/app_metadata_spec.rb +0 -8
  121. data/spec/mongo/server/connection_auth_spec.rb +5 -2
  122. data/spec/mongo/server/connection_pool/populator_spec.rb +101 -0
  123. data/spec/mongo/server/connection_pool_spec.rb +256 -107
  124. data/spec/mongo/server/connection_spec.rb +22 -33
  125. data/spec/mongo/server/description_spec.rb +42 -4
  126. data/spec/mongo/server/monitor/connection_spec.rb +22 -11
  127. data/spec/mongo/server/monitor_spec.rb +66 -107
  128. data/spec/mongo/server_spec.rb +82 -60
  129. data/spec/mongo/session/session_pool_spec.rb +1 -5
  130. data/spec/mongo/session_spec.rb +0 -4
  131. data/spec/mongo/socket/ssl_spec.rb +2 -2
  132. data/spec/mongo/srv/monitor_spec.rb +211 -0
  133. data/spec/mongo/srv/result_spec.rb +54 -0
  134. data/spec/mongo/uri/srv_protocol_spec.rb +30 -15
  135. data/spec/mongo/uri_spec.rb +125 -4
  136. data/spec/spec_helper.rb +6 -0
  137. data/spec/spec_tests/auth_spec.rb +39 -0
  138. data/spec/spec_tests/cmap_spec.rb +55 -8
  139. data/spec/spec_tests/connection_string_spec.rb +6 -31
  140. data/spec/spec_tests/data/auth/connection-string.yml +297 -0
  141. data/spec/spec_tests/data/cmap/pool-checkout-error-closed.yml +4 -1
  142. data/spec/spec_tests/data/cmap/pool-create-with-options.yml +1 -0
  143. data/spec/spec_tests/data/command_monitoring/insertMany.yml +1 -1
  144. data/spec/spec_tests/data/connection_string/invalid-uris.yml +20 -0
  145. data/spec/spec_tests/data/connection_string/valid-auth.yml +16 -0
  146. data/spec/spec_tests/data/connection_string/valid-warnings.yml +26 -30
  147. data/spec/spec_tests/data/transactions/abort.yml +3 -3
  148. data/spec/spec_tests/data/transactions/error-labels.yml +3 -3
  149. data/spec/spec_tests/data/transactions_api/callback-retry.yml +3 -3
  150. data/spec/spec_tests/data/uri_options/auth-options.yml +1 -1
  151. data/spec/spec_tests/max_staleness_spec.rb +7 -2
  152. data/spec/spec_tests/retryable_reads_spec.rb +0 -31
  153. data/spec/spec_tests/sdam_monitoring_spec.rb +12 -12
  154. data/spec/spec_tests/sdam_spec.rb +4 -7
  155. data/spec/spec_tests/server_selection_spec.rb +6 -2
  156. data/spec/spec_tests/transactions_spec.rb +0 -2
  157. data/spec/spec_tests/uri_options_spec.rb +4 -2
  158. data/spec/stress/connection_pool_stress_spec.rb +203 -0
  159. data/spec/stress/connection_pool_timing_spec.rb +181 -0
  160. data/spec/support/auth.rb +113 -0
  161. data/spec/support/background_thread_registry.rb +63 -0
  162. data/spec/support/client_registry.rb +11 -2
  163. data/spec/support/cluster_config.rb +65 -46
  164. data/spec/support/cluster_tools.rb +2 -2
  165. data/spec/support/cmap.rb +13 -14
  166. data/spec/support/cmap/verifier.rb +4 -5
  167. data/spec/support/command_monitoring.rb +0 -5
  168. data/spec/support/common_shortcuts.rb +101 -1
  169. data/spec/support/constraints.rb +25 -0
  170. data/spec/support/dns.rb +13 -0
  171. data/spec/support/event_subscriber.rb +0 -7
  172. data/spec/support/json_ext_formatter.rb +5 -1
  173. data/spec/support/lite_constraints.rb +22 -6
  174. data/spec/support/local_resource_registry.rb +34 -0
  175. data/spec/support/sdam_monitoring.rb +115 -0
  176. data/spec/support/spec_config.rb +20 -6
  177. data/spec/support/spec_setup.rb +2 -2
  178. data/spec/support/transactions.rb +1 -1
  179. data/spec/support/transactions/test.rb +1 -1
  180. data/spec/support/utils.rb +1 -16
  181. metadata +685 -659
  182. metadata.gz.sig +0 -0
  183. data/lib/mongo/event/description_changed.rb +0 -52
  184. data/spec/integration/bson_symbol_spec.rb +0 -34
  185. data/spec/integration/crud_spec.rb +0 -45
  186. data/spec/integration/get_more_spec.rb +0 -32
  187. data/spec/integration/grid_fs_bucket_spec.rb +0 -48
  188. data/spec/integration/retryable_errors_spec.rb +0 -265
  189. data/spec/integration/size_limit_spec.rb~12e1e9c4f... RUBY-2242 Fix zlib compression (#2021) +0 -98
  190. data/spec/mongo/cursor/builder/op_kill_cursors_spec.rb +0 -56
  191. data/spec/runners/sdam/verifier.rb +0 -88
@@ -123,6 +123,14 @@ module Mongo
123
123
  options[:generation]
124
124
  end
125
125
 
126
+ # The connection pool from which this connection was created.
127
+ # May be nil.
128
+ #
129
+ # @api private
130
+ def connection_pool
131
+ options[:connection_pool]
132
+ end
133
+
126
134
  # Whether the connection was closed.
127
135
  #
128
136
  # Closed connections should no longer be used. Instead obtain a new
@@ -151,7 +159,7 @@ module Mongo
151
159
  def connect!
152
160
  if closed?
153
161
  if Lint.enabled?
154
- raise Error::LintError, "Reconnecting closed connections is no longer supported"
162
+ raise Error::LintError, "Reconnecting closed connections is no longer supported (for #{address})"
155
163
  else
156
164
  log_warn("Reconnecting closed connections is deprecated (for #{address})")
157
165
  end
@@ -290,14 +298,14 @@ module Mongo
290
298
 
291
299
  def handshake!(socket)
292
300
  unless socket
293
- raise Error::HandshakeError, "Cannot handshake because there is no usable socket"
301
+ raise Error::HandshakeError, "Cannot handshake because there is no usable socket (for #{address})"
294
302
  end
295
303
 
296
304
  response = average_rtt = nil
297
305
  @server.handle_handshake_failure! do
298
306
  begin
299
307
  response, exc, rtt, average_rtt =
300
- @server.monitor.round_trip_time_averager.measure do
308
+ @server.round_trip_time_averager.measure do
301
309
  socket.write(app_metadata.ismaster_bytes)
302
310
  Protocol::Message.deserialize(socket, max_message_size).documents[0]
303
311
  end
@@ -349,7 +357,7 @@ module Mongo
349
357
  end
350
358
 
351
359
  new_description = Description.new(address, response, average_rtt)
352
- @server.monitor.publish(Event::DESCRIPTION_CHANGED, @server.description, new_description)
360
+ @server.cluster.run_sdam_flow(@server.description, new_description)
353
361
  end
354
362
 
355
363
  def authenticate!(pending_connection)
@@ -376,7 +384,6 @@ module Mongo
376
384
  # Important: timeout errors are not handled here
377
385
  rescue Error::SocketError
378
386
  @server.unknown!
379
- @server.pool.disconnect!
380
387
  raise
381
388
  end
382
389
  end
@@ -11,6 +11,7 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+ require 'mongo/server/connection_pool/populator'
14
15
 
15
16
  module Mongo
16
17
  class Server
@@ -35,8 +36,21 @@ module Mongo
35
36
 
36
37
  # The default timeout, in seconds, to wait for a connection.
37
38
  #
39
+ # This timeout applies while in flow threads are waiting for background
40
+ # threads to establish connections (and hence they must connect, handshake
41
+ # and auth in the allotted time).
42
+ #
43
+ # It is currently set to 10 seconds. The default connect timeout is
44
+ # 10 seconds by itself, but setting large timeouts can get applications
45
+ # in trouble if their requests get timed out by the reverse proxy,
46
+ # thus anything over 15 seconds is potentially dangerous.
47
+ #
38
48
  # @since 2.9.0
39
- DEFAULT_WAIT_TIMEOUT = 1.freeze
49
+ DEFAULT_WAIT_TIMEOUT = 10.freeze
50
+
51
+ # Condition variable broadcast when the size of the pool changes
52
+ # to wake up the populator
53
+ attr_reader :populate_semaphore
40
54
 
41
55
  # Create the new connection pool.
42
56
  #
@@ -58,6 +72,9 @@ module Mongo
58
72
  # are given, their values must be identical.
59
73
  # @option options [ Float ] :max_idle_time The time, in seconds,
60
74
  # after which idle connections should be closed by the pool.
75
+ # Note: Additionally, options for connections created by this pool should
76
+ # be included in the options passed here, and they will be forwarded to
77
+ # any connections created by the pool.
61
78
  #
62
79
  # @since 2.0.0, API changed in 2.9.0
63
80
  def initialize(server, options = {})
@@ -99,6 +116,7 @@ module Mongo
99
116
  # or in the checked out connections set.
100
117
  @available_connections = available_connections = []
101
118
  @checked_out_connections = Set.new
119
+ @pending_connections = Set.new
102
120
 
103
121
  # Mutex used for synchronizing access to @available_connections and
104
122
  # @checked_out_connections. The pool object is thread-safe, thus
@@ -111,25 +129,26 @@ module Mongo
111
129
  # available connection when pool is at max size
112
130
  @available_semaphore = Semaphore.new
113
131
 
114
- finalizer = proc do
115
- available_connections.each do |connection|
116
- connection.disconnect!(reason: :pool_closed)
117
- end
118
- available_connections.clear
119
- # Finalizer does not close checked out connections.
120
- # Those would have to be garbage collected on their own
121
- # and that should close them.
122
- end
123
- ObjectSpace.define_finalizer(self, finalizer)
132
+ # Background thread reponsible for maintaining the size of
133
+ # the pool to at least min_size
134
+ @populator = Populator.new(self, options)
135
+ @populate_semaphore = Semaphore.new
136
+
137
+ ObjectSpace.define_finalizer(self, self.class.finalize(@available_connections, @pending_connections, @populator))
124
138
 
125
139
  publish_cmap_event(
126
- Monitoring::Event::Cmap::PoolCreated.new(@server.address, options)
140
+ Monitoring::Event::Cmap::PoolCreated.new(@server.address, options, self)
127
141
  )
142
+
143
+ @populator.run! if min_size > 0
128
144
  end
129
145
 
130
146
  # @return [ Hash ] options The pool options.
131
147
  attr_reader :options
132
148
 
149
+ # @api private
150
+ def_delegators :@server, :address
151
+
133
152
  # Get the maximum size of the connection pool.
134
153
  #
135
154
  # @return [ Integer ] The maximum size of the connection pool.
@@ -194,7 +213,7 @@ module Mongo
194
213
  # already holding the lock as Ruby does not allow a thread holding a
195
214
  # lock to acquire this lock again.
196
215
  def unsynchronized_size
197
- @available_connections.length + @checked_out_connections.size
216
+ @available_connections.length + @checked_out_connections.length + @pending_connections.length
198
217
  end
199
218
  private :unsynchronized_size
200
219
 
@@ -220,9 +239,23 @@ module Mongo
220
239
  !!@closed
221
240
  end
222
241
 
242
+ # @note This method is experimental and subject to change.
243
+ #
244
+ # @api experimental
245
+ # @since 2.11.0
246
+ def summary
247
+ @lock.synchronize do
248
+ "#<ConnectionPool size=#{unsynchronized_size} (#{min_size}-#{max_size}) " +
249
+ "used=#{@checked_out_connections.length} avail=#{@available_connections.length} pending=#{@pending_connections.length}>"
250
+ end
251
+ end
252
+
223
253
  # @since 2.9.0
224
254
  def_delegators :@server, :monitoring
225
255
 
256
+ # @api private
257
+ attr_reader :populator
258
+
226
259
  # Checks a connection out of the pool.
227
260
  #
228
261
  # If there are active connections in the pool, the most recently used
@@ -236,17 +269,26 @@ module Mongo
236
269
  # checked back in via the check_in method.
237
270
  #
238
271
  # @return [ Mongo::Server::Connection ] The checked out connection.
272
+ # @raise [ Error::PoolClosedError ] If the pool has been closed.
239
273
  # @raise [ Timeout::Error ] If the connection pool is at maximum size
240
274
  # and remains so for longer than the wait timeout.
241
275
  #
242
276
  # @since 2.9.0
243
277
  def check_out
244
- raise_if_closed!
245
-
246
278
  publish_cmap_event(
247
279
  Monitoring::Event::Cmap::ConnectionCheckOutStarted.new(@server.address)
248
280
  )
249
281
 
282
+ if closed?
283
+ publish_cmap_event(
284
+ Monitoring::Event::Cmap::ConnectionCheckOutFailed.new(
285
+ @server.address,
286
+ Monitoring::Event::Cmap::ConnectionCheckOutFailed::POOL_CLOSED
287
+ ),
288
+ )
289
+ raise Error::PoolClosedError.new(@server.address, self)
290
+ end
291
+
250
292
  deadline = Time.now + wait_timeout
251
293
  connection = nil
252
294
  # It seems that synchronize sets up its own loop, thus a simple break
@@ -264,6 +306,7 @@ module Mongo
264
306
  # Stale connections should be disconnected in the clear
265
307
  # method, but if any don't, check again here
266
308
  connection.disconnect!(reason: :stale)
309
+ @populate_semaphore.signal
267
310
  next
268
311
  end
269
312
 
@@ -271,19 +314,19 @@ module Mongo
271
314
  Time.now - connection.last_checkin > max_idle_time
272
315
  then
273
316
  connection.disconnect!(reason: :idle)
317
+ @populate_semaphore.signal
274
318
  next
275
319
  end
276
320
 
321
+ @pending_connections << connection
277
322
  throw(:done)
278
323
  end
279
324
 
280
325
  # Ruby does not allow a thread to lock a mutex which it already
281
326
  # holds.
282
327
  if unsynchronized_size < max_size
283
- # This does not currently connect the socket and handshake,
284
- # but if it did, it would be performing i/o under our lock,
285
- # which is bad. Fix in the future.
286
328
  connection = create_connection
329
+ @pending_connections << connection
287
330
  throw(:done)
288
331
  end
289
332
  end
@@ -296,15 +339,45 @@ module Mongo
296
339
  Monitoring::Event::Cmap::ConnectionCheckOutFailed::TIMEOUT,
297
340
  ),
298
341
  )
299
- raise Error::ConnectionCheckOutTimeout.new(@server.address, wait_timeout)
342
+
343
+ msg = @lock.synchronize do
344
+ "Timed out attempting to check out a connection " +
345
+ "from pool for #{@server.address} after #{wait_timeout} sec. " +
346
+ "Connections in pool: #{@available_connections.length} available, " +
347
+ "#{@checked_out_connections.length} checked out, " +
348
+ "#{@pending_connections.length} pending"
349
+ end
350
+ raise Error::ConnectionCheckOutTimeout.new(msg, address: @server.address)
300
351
  end
301
352
  @available_semaphore.wait(wait)
302
353
  end
303
354
  end
304
355
 
305
- @checked_out_connections << connection
356
+ begin
357
+ connect_connection(connection)
358
+ rescue Exception
359
+ # Handshake or authentication failed
360
+ @lock.synchronize do
361
+ @pending_connections.delete(connection)
362
+ end
363
+ @populate_semaphore.signal
364
+
365
+ publish_cmap_event(
366
+ Monitoring::Event::Cmap::ConnectionCheckOutFailed.new(
367
+ @server.address,
368
+ Monitoring::Event::Cmap::ConnectionCheckOutFailed::CONNECTION_ERROR
369
+ ),
370
+ )
371
+ raise
372
+ end
373
+
374
+ @lock.synchronize do
375
+ @checked_out_connections << connection
376
+ @pending_connections.delete(connection)
377
+ end
378
+
306
379
  publish_cmap_event(
307
- Monitoring::Event::Cmap::ConnectionCheckedOut.new(@server.address, connection.id),
380
+ Monitoring::Event::Cmap::ConnectionCheckedOut.new(@server.address, connection.id, self),
308
381
  )
309
382
  connection
310
383
  end
@@ -318,19 +391,23 @@ module Mongo
318
391
  # @since 2.9.0
319
392
  def check_in(connection)
320
393
  @lock.synchronize do
321
- unless @checked_out_connections.include?(connection)
322
- raise ArgumentError, "Trying to check in a connection which is not currently checked out by this pool: #{connection}"
394
+ unless connection.connection_pool == self
395
+ raise ArgumentError, "Trying to check in a connection which was not checked out by this pool: #{connection} checked out from pool #{connection.connection_pool} (for #{self})"
323
396
  end
324
397
 
325
- @checked_out_connections.delete(connection)
398
+ unless @checked_out_connections.include?(connection)
399
+ raise ArgumentError, "Trying to check in a connection which is not currently checked out by this pool: #{connection} (for #{self})"
400
+ end
326
401
 
327
402
  # Note: if an event handler raises, resource will not be signaled.
328
403
  # This means threads waiting for a connection to free up when
329
404
  # the pool is at max size may time out.
330
405
  # Threads that begin waiting after this method completes (with
331
406
  # the exception) should be fine.
407
+
408
+ @checked_out_connections.delete(connection)
332
409
  publish_cmap_event(
333
- Monitoring::Event::Cmap::ConnectionCheckedIn.new(@server.address, connection.id)
410
+ Monitoring::Event::Cmap::ConnectionCheckedIn.new(@server.address, connection.id, self)
334
411
  )
335
412
 
336
413
  if closed?
@@ -341,8 +418,10 @@ module Mongo
341
418
  if connection.closed?
342
419
  # Connection was closed - for example, because it experienced
343
420
  # a network error. Nothing else needs to be done here.
421
+ @populate_semaphore.signal
344
422
  elsif connection.generation != @generation
345
423
  connection.disconnect!(reason: :stale)
424
+ @populate_semaphore.signal
346
425
  else
347
426
  connection.record_checkin!
348
427
  @available_connections << connection
@@ -362,6 +441,8 @@ module Mongo
362
441
  # @option options [ true | false ] :lazy If true, do not close any of
363
442
  # the idle connections and instead let them be closed during a
364
443
  # subsequent check out operation.
444
+ # @option options [ true | false ] :stop_populator Whether to stop
445
+ # the populator background thread. For internal driver use only.
365
446
  #
366
447
  # @return [ true ] true.
367
448
  #
@@ -369,6 +450,10 @@ module Mongo
369
450
  def clear(options = nil)
370
451
  raise_if_closed!
371
452
 
453
+ if options && options[:stop_populator]
454
+ stop_populator
455
+ end
456
+
372
457
  @lock.synchronize do
373
458
  @generation += 1
374
459
 
@@ -380,6 +465,7 @@ module Mongo
380
465
  until @available_connections.empty?
381
466
  connection = @available_connections.pop
382
467
  connection.disconnect!(reason: :stale)
468
+ @populate_semaphore.signal
383
469
  end
384
470
  end
385
471
  end
@@ -400,31 +486,37 @@ module Mongo
400
486
  # @option options [ true | false ] :force Also close all checked out
401
487
  # connections.
402
488
  #
403
- # @return [ true ] true.
489
+ # @return [ true ] Always true.
404
490
  #
405
491
  # @since 2.9.0
406
492
  def close(options = nil)
407
493
  return if closed?
408
494
 
495
+ options ||= {}
496
+
497
+ stop_populator
498
+
409
499
  @lock.synchronize do
410
500
  until @available_connections.empty?
411
501
  connection = @available_connections.pop
412
502
  connection.disconnect!(reason: :pool_closed)
413
503
  end
414
504
 
415
- if options && options[:force]
505
+ if options[:force]
416
506
  until @checked_out_connections.empty?
417
507
  connection = @checked_out_connections.take(1).first
418
508
  connection.disconnect!(reason: :pool_closed)
419
509
  @checked_out_connections.delete(connection)
420
510
  end
421
511
  end
422
- end
423
512
 
424
- @closed = true
513
+ # mark pool as closed before releasing lock so
514
+ # no connections can be created, checked in, or checked out
515
+ @closed = true
516
+ end
425
517
 
426
518
  publish_cmap_event(
427
- Monitoring::Event::Cmap::PoolClosed.new(@server.address)
519
+ Monitoring::Event::Cmap::PoolClosed.new(@server.address, self)
428
520
  )
429
521
 
430
522
  true
@@ -485,6 +577,7 @@ module Mongo
485
577
  if (Time.now - last_checkin) > max_idle_time
486
578
  connection.disconnect!(reason: :idle)
487
579
  @available_connections.delete_at(i)
580
+ @populate_semaphore.signal
488
581
  next
489
582
  end
490
583
  end
@@ -493,25 +586,131 @@ module Mongo
493
586
  end
494
587
  end
495
588
 
496
- # Creates up to the min size connections.
589
+ # Stop the background populator thread and clean up any connections created
590
+ # which have not been connected yet.
497
591
  #
498
- # Used by the spec test runner.
592
+ # Used when closing the pool or when terminating the bg thread for testing
593
+ # purposes. In the latter case, this method must be called before the pool
594
+ # is used, to ensure no connections in pending_connections were created in-flow
595
+ # by the check_out method.
596
+ #
597
+ # @api private
598
+ def stop_populator
599
+ @populator.stop!
600
+
601
+ @lock.synchronize do
602
+ # If stop_populator is called while populate is running, there may be
603
+ # connections waiting to be connected, connections which have not yet
604
+ # been moved to available_connections, or connections moved to available_connections
605
+ # but not deleted from pending_connections. These should be cleaned up.
606
+ until @pending_connections.empty?
607
+ connection = @pending_connections.take(1).first
608
+ connection.disconnect!
609
+ @pending_connections.delete(connection)
610
+ end
611
+ end
612
+ end
613
+
614
+ # Creates and adds a connection to the pool, if the pool's size is below
615
+ # min_size. Retries once if a socket-related error is encountered during
616
+ # this process and raises if a second error or a non socket-related error occurs.
617
+ #
618
+ # Used by the pool populator background thread.
619
+ #
620
+ # @return [ true | false ] Whether this method should be called again
621
+ # to create more connections.
622
+ # @raise [ Error::AuthError, Error ] The second socket-related error raised if a retry
623
+ # occured, or the non socket-related error
499
624
  #
500
625
  # @api private
501
626
  def populate
502
- while size < min_size
503
- @available_connections << create_connection
627
+ return false if closed?
628
+
629
+ begin
630
+ return create_and_add_connection
631
+ rescue Error::SocketError, Error::SocketTimeoutError => e
632
+ # an error was encountered while connecting the connection,
633
+ # ignore this first error and try again.
634
+ log_warn("Populator failed to connect a connection for #{address}: #{e.class}: #{e}. It will retry.")
635
+ end
636
+
637
+ return create_and_add_connection
638
+ rescue Error::AuthError, Error
639
+ # wake up one thread waiting for connections, since one could not
640
+ # be created here, and can instead be created in flow
641
+ @available_semaphore.signal
642
+ raise
643
+ end
644
+
645
+ # Finalize the connection pool for garbage collection.
646
+ #
647
+ # @param [ List<Mongo::Connection> ] available_connections The available connections.
648
+ # @param [ List<Mongo::Connection> ] pending_connections The pending connections.
649
+ # @param [ Populator ] populator The populator.
650
+ #
651
+ # @return [ Proc ] The Finalizer.
652
+ def self.finalize(available_connections, pending_connections, populator)
653
+ proc do
654
+ populator.stop!
655
+
656
+ available_connections.each do |connection|
657
+ connection.disconnect!(reason: :pool_closed)
658
+ end
659
+ available_connections.clear
660
+
661
+ pending_connections.each do |connection|
662
+ connection.disconnect!(reason: :pool_closed)
663
+ end
664
+ pending_connections.clear
665
+
666
+ # Finalizer does not close checked out connections.
667
+ # Those would have to be garbage collected on their own
668
+ # and that should close them.
504
669
  end
505
670
  end
506
671
 
507
672
  private
508
673
 
509
674
  def create_connection
510
- connection = Connection.new(@server, options.merge(generation: generation))
511
- # CMAP spec requires connections to be returned from the pool
512
- # fully established.
513
- #connection.connect!
514
- connection
675
+ connection = Connection.new(@server, options.merge(generation: generation,
676
+ connection_pool: self))
677
+ end
678
+
679
+ # Create a connection, connect it, and add it to the pool.
680
+ #
681
+ # @return [ true | false ] True if a connection was created and
682
+ # added to the pool, false otherwise
683
+ # @raise [ Mongo::Error ] An error encountered during connection connect
684
+ def create_and_add_connection
685
+ connection = nil
686
+
687
+ @lock.synchronize do
688
+ if !closed? && unsynchronized_size < min_size
689
+ connection = create_connection
690
+ @pending_connections << connection
691
+ else
692
+ return false
693
+ end
694
+ end
695
+
696
+ begin
697
+ connect_connection(connection)
698
+ rescue Exception
699
+ @lock.synchronize do
700
+ @pending_connections.delete(connection)
701
+ end
702
+ raise
703
+ end
704
+
705
+ @lock.synchronize do
706
+ @available_connections << connection
707
+ @pending_connections.delete(connection)
708
+
709
+ # wake up one thread waiting for connections, since one was created
710
+ @available_semaphore.signal
711
+ end
712
+
713
+ true
515
714
  end
516
715
 
517
716
  # Asserts that the pool has not been closed.
@@ -521,7 +720,18 @@ module Mongo
521
720
  # @since 2.9.0
522
721
  def raise_if_closed!
523
722
  if closed?
524
- raise Error::PoolClosedError.new(@server.address)
723
+ raise Error::PoolClosedError.new(@server.address, self)
724
+ end
725
+ end
726
+
727
+ # Attempts to connect (handshake and auth) the connection. If an error is
728
+ # encountered, closes the connection and raises the error.
729
+ def connect_connection(connection)
730
+ begin
731
+ connection.connect!
732
+ rescue Exception
733
+ connection.disconnect!(reason: :error)
734
+ raise
525
735
  end
526
736
  end
527
737
  end