mongo 2.10.5 → 2.11.0.rc0

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.
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