mongo 2.23.0 → 2.24.1

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 (463) hide show
  1. checksums.yaml +4 -4
  2. data/bin/mongo_console +0 -1
  3. data/lib/mongo/active_support.rb +1 -2
  4. data/lib/mongo/address/ipv4.rb +3 -6
  5. data/lib/mongo/address/ipv6.rb +6 -10
  6. data/lib/mongo/address/unix.rb +1 -4
  7. data/lib/mongo/address/validator.rb +16 -28
  8. data/lib/mongo/address.rb +30 -40
  9. data/lib/mongo/auth/aws/conversation.rb +6 -10
  10. data/lib/mongo/auth/aws/credentials.rb +0 -1
  11. data/lib/mongo/auth/aws/credentials_cache.rb +0 -1
  12. data/lib/mongo/auth/aws/credentials_retriever.rb +45 -59
  13. data/lib/mongo/auth/aws/request.rb +20 -35
  14. data/lib/mongo/auth/aws.rb +1 -2
  15. data/lib/mongo/auth/base.rb +20 -29
  16. data/lib/mongo/auth/conversation_base.rb +14 -18
  17. data/lib/mongo/auth/cr/conversation.rb +0 -3
  18. data/lib/mongo/auth/cr.rb +1 -4
  19. data/lib/mongo/auth/credential_cache.rb +0 -2
  20. data/lib/mongo/auth/gssapi/conversation.rb +3 -8
  21. data/lib/mongo/auth/gssapi.rb +1 -4
  22. data/lib/mongo/auth/ldap/conversation.rb +0 -3
  23. data/lib/mongo/auth/ldap.rb +1 -4
  24. data/lib/mongo/auth/roles.rb +16 -19
  25. data/lib/mongo/auth/sasl_conversation_base.rb +7 -11
  26. data/lib/mongo/auth/scram/conversation.rb +2 -5
  27. data/lib/mongo/auth/scram.rb +5 -10
  28. data/lib/mongo/auth/scram256/conversation.rb +2 -5
  29. data/lib/mongo/auth/scram256.rb +1 -3
  30. data/lib/mongo/auth/scram_conversation_base.rb +18 -24
  31. data/lib/mongo/auth/stringprep/profiles/sasl.rb +17 -18
  32. data/lib/mongo/auth/stringprep/tables.rb +2209 -2210
  33. data/lib/mongo/auth/stringprep/unicode_normalize/normalize.rb +36 -38
  34. data/lib/mongo/auth/stringprep/unicode_normalize/tables.rb +1142 -1150
  35. data/lib/mongo/auth/stringprep.rb +9 -12
  36. data/lib/mongo/auth/user/view.rb +3 -5
  37. data/lib/mongo/auth/user.rb +14 -24
  38. data/lib/mongo/auth/x509/conversation.rb +0 -3
  39. data/lib/mongo/auth/x509.rb +7 -9
  40. data/lib/mongo/auth.rb +18 -30
  41. data/lib/mongo/background_thread.rb +9 -17
  42. data/lib/mongo/bson.rb +0 -2
  43. data/lib/mongo/bulk_write/combineable.rb +0 -3
  44. data/lib/mongo/bulk_write/ordered_combiner.rb +1 -3
  45. data/lib/mongo/bulk_write/result.rb +20 -17
  46. data/lib/mongo/bulk_write/result_combiner.rb +17 -13
  47. data/lib/mongo/bulk_write/transformable.rb +16 -19
  48. data/lib/mongo/bulk_write/unordered_combiner.rb +1 -3
  49. data/lib/mongo/bulk_write/validatable.rb +11 -18
  50. data/lib/mongo/bulk_write.rb +76 -91
  51. data/lib/mongo/caching_cursor.rb +2 -7
  52. data/lib/mongo/client.rb +230 -275
  53. data/lib/mongo/client_encryption.rb +4 -5
  54. data/lib/mongo/cluster/periodic_executor.rb +2 -5
  55. data/lib/mongo/cluster/reapers/cursor_reaper.rb +21 -29
  56. data/lib/mongo/cluster/reapers/socket_reaper.rb +1 -6
  57. data/lib/mongo/cluster/sdam_flow.rb +136 -159
  58. data/lib/mongo/cluster/topology/base.rb +15 -18
  59. data/lib/mongo/cluster/topology/load_balanced.rb +24 -14
  60. data/lib/mongo/cluster/topology/no_replica_set_options.rb +3 -6
  61. data/lib/mongo/cluster/topology/replica_set_no_primary.rb +20 -23
  62. data/lib/mongo/cluster/topology/replica_set_with_primary.rb +0 -2
  63. data/lib/mongo/cluster/topology/sharded.rb +19 -9
  64. data/lib/mongo/cluster/topology/single.rb +24 -14
  65. data/lib/mongo/cluster/topology/unknown.rb +20 -10
  66. data/lib/mongo/cluster/topology.rb +29 -25
  67. data/lib/mongo/cluster.rb +148 -183
  68. data/lib/mongo/cluster_time.rb +14 -31
  69. data/lib/mongo/collection/helpers.rb +5 -8
  70. data/lib/mongo/collection/view/aggregation.rb +5 -10
  71. data/lib/mongo/collection/view/builder/aggregation.rb +6 -9
  72. data/lib/mongo/collection/view/builder/map_reduce.rb +18 -17
  73. data/lib/mongo/collection/view/builder.rb +0 -1
  74. data/lib/mongo/collection/view/change_stream/retryable.rb +3 -8
  75. data/lib/mongo/collection/view/change_stream.rb +59 -58
  76. data/lib/mongo/collection/view/explainable.rb +11 -20
  77. data/lib/mongo/collection/view/immutable.rb +1 -3
  78. data/lib/mongo/collection/view/iterable.rb +35 -28
  79. data/lib/mongo/collection/view/map_reduce.rb +20 -25
  80. data/lib/mongo/collection/view/readable.rb +50 -57
  81. data/lib/mongo/collection/view/writable.rb +56 -72
  82. data/lib/mongo/collection/view.rb +9 -8
  83. data/lib/mongo/collection.rb +63 -76
  84. data/lib/mongo/condition_variable.rb +4 -4
  85. data/lib/mongo/config/options.rb +0 -3
  86. data/lib/mongo/config/validators/option.rb +3 -5
  87. data/lib/mongo/config.rb +7 -4
  88. data/lib/mongo/crypt/auto_decryption_context.rb +0 -3
  89. data/lib/mongo/crypt/auto_encrypter.rb +34 -43
  90. data/lib/mongo/crypt/auto_encryption_context.rb +0 -3
  91. data/lib/mongo/crypt/binary.rb +5 -9
  92. data/lib/mongo/crypt/binding.rb +149 -155
  93. data/lib/mongo/crypt/context.rb +10 -17
  94. data/lib/mongo/crypt/data_key_context.rb +2 -7
  95. data/lib/mongo/crypt/encryption_io.rb +29 -39
  96. data/lib/mongo/crypt/explicit_decryption_context.rb +0 -3
  97. data/lib/mongo/crypt/explicit_encrypter.rb +1 -1
  98. data/lib/mongo/crypt/explicit_encryption_context.rb +19 -30
  99. data/lib/mongo/crypt/explicit_encryption_expression_context.rb +0 -2
  100. data/lib/mongo/crypt/handle.rb +42 -48
  101. data/lib/mongo/crypt/hooks.rb +12 -15
  102. data/lib/mongo/crypt/kms/aws/credentials.rb +12 -16
  103. data/lib/mongo/crypt/kms/aws/master_document.rb +6 -9
  104. data/lib/mongo/crypt/kms/aws.rb +0 -2
  105. data/lib/mongo/crypt/kms/azure/credentials_retriever.rb +2 -7
  106. data/lib/mongo/crypt/kms/azure/master_document.rb +15 -19
  107. data/lib/mongo/crypt/kms/azure.rb +0 -1
  108. data/lib/mongo/crypt/kms/credentials.rb +13 -27
  109. data/lib/mongo/crypt/kms/gcp/credentials.rb +12 -14
  110. data/lib/mongo/crypt/kms/gcp/credentials_retriever.rb +7 -9
  111. data/lib/mongo/crypt/kms/gcp/master_document.rb +12 -16
  112. data/lib/mongo/crypt/kms/gcp.rb +0 -2
  113. data/lib/mongo/crypt/kms/kmip/credentials.rb +7 -8
  114. data/lib/mongo/crypt/kms/kmip/master_document.rb +3 -5
  115. data/lib/mongo/crypt/kms/kmip.rb +0 -1
  116. data/lib/mongo/crypt/kms/local/credentials.rb +7 -8
  117. data/lib/mongo/crypt/kms/local/master_document.rb +2 -6
  118. data/lib/mongo/crypt/kms/local.rb +0 -1
  119. data/lib/mongo/crypt/kms/master_key_document.rb +11 -15
  120. data/lib/mongo/crypt/kms.rb +14 -16
  121. data/lib/mongo/crypt/kms_context.rb +0 -2
  122. data/lib/mongo/crypt/rewrap_many_data_key_context.rb +2 -7
  123. data/lib/mongo/crypt/rewrap_many_data_key_result.rb +2 -4
  124. data/lib/mongo/crypt/status.rb +12 -14
  125. data/lib/mongo/crypt.rb +0 -1
  126. data/lib/mongo/csot_timeout_holder.rb +3 -2
  127. data/lib/mongo/cursor/kill_spec.rb +7 -10
  128. data/lib/mongo/cursor.rb +74 -64
  129. data/lib/mongo/cursor_host.rb +8 -10
  130. data/lib/mongo/database/view.rb +16 -37
  131. data/lib/mongo/database.rb +52 -56
  132. data/lib/mongo/dbref.rb +0 -1
  133. data/lib/mongo/distinguishing_semaphore.rb +0 -1
  134. data/lib/mongo/error/auth_error.rb +0 -2
  135. data/lib/mongo/error/bad_load_balancer_target.rb +0 -2
  136. data/lib/mongo/error/bulk_write_error.rb +35 -10
  137. data/lib/mongo/error/change_stream_resumable.rb +0 -2
  138. data/lib/mongo/error/client_closed.rb +0 -2
  139. data/lib/mongo/error/closed_stream.rb +1 -4
  140. data/lib/mongo/error/connection_check_out_timeout.rb +3 -6
  141. data/lib/mongo/error/connection_perished.rb +0 -2
  142. data/lib/mongo/error/connection_unavailable.rb +0 -2
  143. data/lib/mongo/error/credential_check_error.rb +0 -2
  144. data/lib/mongo/error/crypt_error.rb +0 -2
  145. data/lib/mongo/error/extra_file_chunk.rb +1 -4
  146. data/lib/mongo/error/failed_string_prep_validation.rb +5 -6
  147. data/lib/mongo/error/file_not_found.rb +0 -3
  148. data/lib/mongo/error/handshake_error.rb +0 -2
  149. data/lib/mongo/error/insufficient_iteration_count.rb +1 -4
  150. data/lib/mongo/error/internal_driver_error.rb +0 -2
  151. data/lib/mongo/error/invalid_address.rb +0 -2
  152. data/lib/mongo/error/invalid_application_name.rb +0 -3
  153. data/lib/mongo/error/invalid_bulk_operation.rb +1 -4
  154. data/lib/mongo/error/invalid_bulk_operation_type.rb +1 -4
  155. data/lib/mongo/error/invalid_collection_name.rb +1 -4
  156. data/lib/mongo/error/invalid_config_option.rb +0 -3
  157. data/lib/mongo/error/invalid_cursor_operation.rb +0 -2
  158. data/lib/mongo/error/invalid_database_name.rb +1 -4
  159. data/lib/mongo/error/invalid_document.rb +1 -4
  160. data/lib/mongo/error/invalid_file.rb +0 -3
  161. data/lib/mongo/error/invalid_file_revision.rb +0 -3
  162. data/lib/mongo/error/invalid_min_pool_size.rb +0 -3
  163. data/lib/mongo/error/invalid_nonce.rb +0 -3
  164. data/lib/mongo/error/invalid_read_concern.rb +2 -4
  165. data/lib/mongo/error/invalid_read_option.rb +0 -3
  166. data/lib/mongo/error/invalid_replacement_document.rb +2 -5
  167. data/lib/mongo/error/invalid_server_auth_host.rb +0 -2
  168. data/lib/mongo/error/invalid_server_auth_response.rb +0 -2
  169. data/lib/mongo/error/invalid_server_preference.rb +10 -16
  170. data/lib/mongo/error/invalid_session.rb +1 -4
  171. data/lib/mongo/error/invalid_signature.rb +0 -3
  172. data/lib/mongo/error/invalid_transaction_operation.rb +5 -8
  173. data/lib/mongo/error/invalid_txt_record.rb +0 -2
  174. data/lib/mongo/error/invalid_update_document.rb +2 -5
  175. data/lib/mongo/error/invalid_uri.rb +1 -4
  176. data/lib/mongo/error/invalid_write_concern.rb +2 -5
  177. data/lib/mongo/error/kms_error.rb +0 -2
  178. data/lib/mongo/error/labelable.rb +0 -3
  179. data/lib/mongo/error/lint_error.rb +0 -2
  180. data/lib/mongo/error/max_bson_size.rb +8 -11
  181. data/lib/mongo/error/max_message_size.rb +2 -5
  182. data/lib/mongo/error/mismatched_domain.rb +0 -2
  183. data/lib/mongo/error/missing_connection.rb +0 -2
  184. data/lib/mongo/error/missing_file_chunk.rb +0 -3
  185. data/lib/mongo/error/missing_password.rb +0 -2
  186. data/lib/mongo/error/missing_resume_token.rb +1 -4
  187. data/lib/mongo/error/missing_scram_server_signature.rb +2 -4
  188. data/lib/mongo/error/missing_service_id.rb +0 -2
  189. data/lib/mongo/error/mongocryptd_spawn_error.rb +0 -2
  190. data/lib/mongo/error/multi_index_drop.rb +0 -3
  191. data/lib/mongo/error/need_primary_server.rb +0 -2
  192. data/lib/mongo/error/no_server_available.rb +3 -8
  193. data/lib/mongo/error/no_service_connection_available.rb +1 -3
  194. data/lib/mongo/error/no_srv_records.rb +0 -2
  195. data/lib/mongo/error/notable.rb +8 -16
  196. data/lib/mongo/error/operation_failure.rb +62 -36
  197. data/lib/mongo/error/parser.rb +33 -75
  198. data/lib/mongo/error/pool_cleared_error.rb +1 -3
  199. data/lib/mongo/error/pool_closed_error.rb +0 -3
  200. data/lib/mongo/error/pool_error.rb +0 -3
  201. data/lib/mongo/error/pool_paused_error.rb +0 -2
  202. data/lib/mongo/error/raise_original_error.rb +1 -3
  203. data/lib/mongo/error/read_write_retryable.rb +14 -17
  204. data/lib/mongo/error/sdam_error_detection.rb +3 -5
  205. data/lib/mongo/error/server_api_conflict.rb +0 -2
  206. data/lib/mongo/error/server_certificate_revoked.rb +0 -2
  207. data/lib/mongo/error/server_not_usable.rb +0 -2
  208. data/lib/mongo/error/session_ended.rb +1 -3
  209. data/lib/mongo/error/session_not_materialized.rb +1 -3
  210. data/lib/mongo/error/sessions_not_supported.rb +1 -4
  211. data/lib/mongo/error/snapshot_session_invalid_server_version.rb +1 -4
  212. data/lib/mongo/error/snapshot_session_transaction_prohibited.rb +1 -4
  213. data/lib/mongo/error/socket_error.rb +0 -2
  214. data/lib/mongo/error/socket_timeout_error.rb +0 -2
  215. data/lib/mongo/error/transactions_not_supported.rb +3 -6
  216. data/lib/mongo/error/unchangeable_collection_option.rb +1 -4
  217. data/lib/mongo/error/unexpected_chunk_length.rb +0 -3
  218. data/lib/mongo/error/unexpected_response.rb +1 -4
  219. data/lib/mongo/error/unknown_payload_type.rb +0 -3
  220. data/lib/mongo/error/unmet_dependency.rb +0 -2
  221. data/lib/mongo/error/unsupported_array_filters.rb +3 -24
  222. data/lib/mongo/error/unsupported_collation.rb +3 -24
  223. data/lib/mongo/error/unsupported_features.rb +0 -2
  224. data/lib/mongo/error/unsupported_message_type.rb +0 -2
  225. data/lib/mongo/error/unsupported_option.rb +19 -21
  226. data/lib/mongo/error/write_retryable.rb +0 -2
  227. data/lib/mongo/error.rb +10 -24
  228. data/lib/mongo/event/base.rb +0 -2
  229. data/lib/mongo/event/listeners.rb +0 -3
  230. data/lib/mongo/event/publisher.rb +0 -3
  231. data/lib/mongo/event/subscriber.rb +0 -4
  232. data/lib/mongo/event.rb +4 -6
  233. data/lib/mongo/grid/file/chunk.rb +7 -10
  234. data/lib/mongo/grid/file/info.rb +20 -24
  235. data/lib/mongo/grid/file.rb +7 -8
  236. data/lib/mongo/grid/fs_bucket.rb +40 -48
  237. data/lib/mongo/grid/stream/read.rb +25 -35
  238. data/lib/mongo/grid/stream/write.rb +17 -22
  239. data/lib/mongo/grid/stream.rb +2 -4
  240. data/lib/mongo/grid.rb +0 -1
  241. data/lib/mongo/id.rb +0 -1
  242. data/lib/mongo/index/view.rb +49 -48
  243. data/lib/mongo/index.rb +7 -10
  244. data/lib/mongo/lint.rb +31 -37
  245. data/lib/mongo/loggable.rb +5 -8
  246. data/lib/mongo/logger.rb +1 -7
  247. data/lib/mongo/monitoring/cmap_log_subscriber.rb +0 -2
  248. data/lib/mongo/monitoring/command_log_subscriber.rb +25 -33
  249. data/lib/mongo/monitoring/event/cmap/base.rb +0 -2
  250. data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +1 -4
  251. data/lib/mongo/monitoring/event/cmap/connection_check_out_started.rb +0 -3
  252. data/lib/mongo/monitoring/event/cmap/connection_checked_in.rb +1 -4
  253. data/lib/mongo/monitoring/event/cmap/connection_checked_out.rb +2 -5
  254. data/lib/mongo/monitoring/event/cmap/connection_closed.rb +1 -4
  255. data/lib/mongo/monitoring/event/cmap/connection_created.rb +1 -4
  256. data/lib/mongo/monitoring/event/cmap/connection_ready.rb +1 -4
  257. data/lib/mongo/monitoring/event/cmap/pool_cleared.rb +0 -3
  258. data/lib/mongo/monitoring/event/cmap/pool_closed.rb +1 -4
  259. data/lib/mongo/monitoring/event/cmap/pool_created.rb +1 -4
  260. data/lib/mongo/monitoring/event/cmap/pool_ready.rb +1 -4
  261. data/lib/mongo/monitoring/event/cmap.rb +0 -1
  262. data/lib/mongo/monitoring/event/command_failed.rb +5 -9
  263. data/lib/mongo/monitoring/event/command_started.rb +8 -12
  264. data/lib/mongo/monitoring/event/command_succeeded.rb +7 -15
  265. data/lib/mongo/monitoring/event/secure.rb +15 -20
  266. data/lib/mongo/monitoring/event/server_closed.rb +1 -4
  267. data/lib/mongo/monitoring/event/server_description_changed.rb +4 -8
  268. data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +5 -10
  269. data/lib/mongo/monitoring/event/server_heartbeat_started.rb +1 -4
  270. data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +3 -8
  271. data/lib/mongo/monitoring/event/server_opening.rb +1 -4
  272. data/lib/mongo/monitoring/event/topology_changed.rb +2 -5
  273. data/lib/mongo/monitoring/event/topology_closed.rb +1 -4
  274. data/lib/mongo/monitoring/event/topology_opening.rb +1 -4
  275. data/lib/mongo/monitoring/event.rb +0 -1
  276. data/lib/mongo/monitoring/publishable.rb +20 -30
  277. data/lib/mongo/monitoring/sdam_log_subscriber.rb +0 -2
  278. data/lib/mongo/monitoring/server_closed_log_subscriber.rb +0 -3
  279. data/lib/mongo/monitoring/server_description_changed_log_subscriber.rb +2 -3
  280. data/lib/mongo/monitoring/server_opening_log_subscriber.rb +0 -3
  281. data/lib/mongo/monitoring/topology_changed_log_subscriber.rb +8 -8
  282. data/lib/mongo/monitoring/topology_closed_log_subscriber.rb +0 -3
  283. data/lib/mongo/monitoring/topology_opening_log_subscriber.rb +0 -3
  284. data/lib/mongo/monitoring/unified_sdam_log_subscriber.rb +1 -3
  285. data/lib/mongo/monitoring.rb +38 -39
  286. data/lib/mongo/operation/aggregate/op_msg.rb +0 -2
  287. data/lib/mongo/operation/aggregate/result.rb +3 -6
  288. data/lib/mongo/operation/aggregate.rb +0 -2
  289. data/lib/mongo/operation/collections_info/result.rb +0 -3
  290. data/lib/mongo/operation/collections_info.rb +0 -2
  291. data/lib/mongo/operation/command/op_msg.rb +1 -4
  292. data/lib/mongo/operation/command.rb +0 -2
  293. data/lib/mongo/operation/context.rb +13 -16
  294. data/lib/mongo/operation/count/op_msg.rb +2 -4
  295. data/lib/mongo/operation/count.rb +0 -2
  296. data/lib/mongo/operation/create/op_msg.rb +2 -5
  297. data/lib/mongo/operation/create.rb +0 -2
  298. data/lib/mongo/operation/create_index/op_msg.rb +3 -7
  299. data/lib/mongo/operation/create_index.rb +0 -2
  300. data/lib/mongo/operation/create_user/op_msg.rb +2 -4
  301. data/lib/mongo/operation/create_user.rb +0 -2
  302. data/lib/mongo/operation/delete/bulk_result.rb +2 -3
  303. data/lib/mongo/operation/delete/op_msg.rb +3 -10
  304. data/lib/mongo/operation/delete/result.rb +0 -3
  305. data/lib/mongo/operation/delete.rb +1 -5
  306. data/lib/mongo/operation/distinct/op_msg.rb +2 -5
  307. data/lib/mongo/operation/distinct.rb +0 -2
  308. data/lib/mongo/operation/drop/op_msg.rb +0 -2
  309. data/lib/mongo/operation/drop.rb +0 -2
  310. data/lib/mongo/operation/drop_database/op_msg.rb +0 -2
  311. data/lib/mongo/operation/drop_database.rb +0 -2
  312. data/lib/mongo/operation/drop_index/op_msg.rb +4 -6
  313. data/lib/mongo/operation/drop_index.rb +0 -2
  314. data/lib/mongo/operation/explain/op_msg.rb +0 -2
  315. data/lib/mongo/operation/explain/result.rb +0 -3
  316. data/lib/mongo/operation/explain.rb +0 -2
  317. data/lib/mongo/operation/find/builder/command.rb +4 -12
  318. data/lib/mongo/operation/find/builder/flags.rb +9 -15
  319. data/lib/mongo/operation/find/builder/modifiers.rb +1 -4
  320. data/lib/mongo/operation/find/builder.rb +0 -1
  321. data/lib/mongo/operation/find/op_msg.rb +4 -12
  322. data/lib/mongo/operation/find/result.rb +0 -3
  323. data/lib/mongo/operation/find.rb +0 -2
  324. data/lib/mongo/operation/get_more/command_builder.rb +1 -6
  325. data/lib/mongo/operation/get_more/op_msg.rb +10 -4
  326. data/lib/mongo/operation/get_more/result.rb +0 -3
  327. data/lib/mongo/operation/get_more.rb +0 -2
  328. data/lib/mongo/operation/indexes/op_msg.rb +0 -2
  329. data/lib/mongo/operation/indexes/result.rb +1 -5
  330. data/lib/mongo/operation/indexes.rb +0 -2
  331. data/lib/mongo/operation/insert/bulk_result.rb +2 -6
  332. data/lib/mongo/operation/insert/op_msg.rb +2 -4
  333. data/lib/mongo/operation/insert/result.rb +0 -3
  334. data/lib/mongo/operation/insert.rb +2 -5
  335. data/lib/mongo/operation/kill_cursors/command_builder.rb +0 -3
  336. data/lib/mongo/operation/kill_cursors/op_msg.rb +1 -3
  337. data/lib/mongo/operation/kill_cursors.rb +0 -2
  338. data/lib/mongo/operation/list_collections/op_msg.rb +4 -6
  339. data/lib/mongo/operation/list_collections/result.rb +1 -4
  340. data/lib/mongo/operation/list_collections.rb +0 -2
  341. data/lib/mongo/operation/map_reduce/op_msg.rb +0 -2
  342. data/lib/mongo/operation/map_reduce/result.rb +3 -6
  343. data/lib/mongo/operation/map_reduce.rb +0 -2
  344. data/lib/mongo/operation/op_msg_base.rb +0 -1
  345. data/lib/mongo/operation/parallel_scan/op_msg.rb +4 -5
  346. data/lib/mongo/operation/parallel_scan/result.rb +2 -5
  347. data/lib/mongo/operation/parallel_scan.rb +0 -2
  348. data/lib/mongo/operation/remove_user/op_msg.rb +2 -4
  349. data/lib/mongo/operation/remove_user.rb +0 -2
  350. data/lib/mongo/operation/result.rb +38 -47
  351. data/lib/mongo/operation/shared/bypass_document_validation.rb +3 -7
  352. data/lib/mongo/operation/shared/causal_consistency_supported.rb +0 -3
  353. data/lib/mongo/operation/shared/executable.rb +20 -29
  354. data/lib/mongo/operation/shared/executable_no_validate.rb +0 -3
  355. data/lib/mongo/operation/shared/executable_transaction_label.rb +0 -2
  356. data/lib/mongo/operation/shared/idable.rb +3 -6
  357. data/lib/mongo/operation/shared/limited.rb +0 -3
  358. data/lib/mongo/operation/shared/object_id_generator.rb +0 -3
  359. data/lib/mongo/operation/shared/op_msg_executable.rb +0 -2
  360. data/lib/mongo/operation/shared/polymorphic_lookup.rb +0 -2
  361. data/lib/mongo/operation/shared/polymorphic_result.rb +2 -4
  362. data/lib/mongo/operation/shared/read_preference_supported.rb +10 -15
  363. data/lib/mongo/operation/shared/response_handling.rb +13 -26
  364. data/lib/mongo/operation/shared/result/aggregatable.rb +12 -13
  365. data/lib/mongo/operation/shared/sessions_supported.rb +87 -99
  366. data/lib/mongo/operation/shared/specifiable.rb +32 -58
  367. data/lib/mongo/operation/shared/write.rb +12 -17
  368. data/lib/mongo/operation/shared/write_concern_supported.rb +4 -7
  369. data/lib/mongo/operation/update/bulk_result.rb +13 -17
  370. data/lib/mongo/operation/update/op_msg.rb +2 -5
  371. data/lib/mongo/operation/update/result.rb +5 -5
  372. data/lib/mongo/operation/update.rb +1 -5
  373. data/lib/mongo/operation/update_user/op_msg.rb +2 -4
  374. data/lib/mongo/operation/update_user.rb +0 -2
  375. data/lib/mongo/operation/users_info/op_msg.rb +2 -4
  376. data/lib/mongo/operation/users_info/result.rb +1 -4
  377. data/lib/mongo/operation/users_info.rb +0 -2
  378. data/lib/mongo/operation/write_command/op_msg.rb +2 -10
  379. data/lib/mongo/operation/write_command.rb +0 -2
  380. data/lib/mongo/operation.rb +9 -14
  381. data/lib/mongo/options/mapper.rb +8 -15
  382. data/lib/mongo/options/redacted.rb +7 -9
  383. data/lib/mongo/options.rb +0 -1
  384. data/lib/mongo/protocol/bit_vector.rb +3 -5
  385. data/lib/mongo/protocol/caching_hash.rb +2 -7
  386. data/lib/mongo/protocol/compressed.rb +5 -10
  387. data/lib/mongo/protocol/get_more.rb +2 -8
  388. data/lib/mongo/protocol/kill_cursors.rb +2 -8
  389. data/lib/mongo/protocol/message.rb +103 -105
  390. data/lib/mongo/protocol/msg.rb +48 -63
  391. data/lib/mongo/protocol/query.rb +32 -41
  392. data/lib/mongo/protocol/registry.rb +2 -5
  393. data/lib/mongo/protocol/reply.rb +10 -16
  394. data/lib/mongo/protocol/serializers.rb +41 -59
  395. data/lib/mongo/protocol.rb +0 -1
  396. data/lib/mongo/query_cache.rb +7 -15
  397. data/lib/mongo/retryable/backpressure.rb +31 -0
  398. data/lib/mongo/retryable/base_worker.rb +39 -13
  399. data/lib/mongo/retryable/read_worker.rb +77 -21
  400. data/lib/mongo/retryable/retry_policy.rb +59 -0
  401. data/lib/mongo/retryable/write_worker.rb +155 -56
  402. data/lib/mongo/retryable.rb +70 -9
  403. data/lib/mongo/search_index/view.rb +1 -1
  404. data/lib/mongo/semaphore.rb +0 -1
  405. data/lib/mongo/server/app_metadata/environment.rb +3 -3
  406. data/lib/mongo/server/app_metadata.rb +4 -5
  407. data/lib/mongo/server/connection.rb +61 -61
  408. data/lib/mongo/server/connection_base.rb +43 -53
  409. data/lib/mongo/server/connection_common.rb +41 -64
  410. data/lib/mongo/server/connection_pool/generation_manager.rb +6 -11
  411. data/lib/mongo/server/connection_pool/populator.rb +1 -4
  412. data/lib/mongo/server/connection_pool.rb +250 -175
  413. data/lib/mongo/server/description/features.rb +23 -60
  414. data/lib/mongo/server/description/load_balancer.rb +0 -2
  415. data/lib/mongo/server/description.rb +138 -137
  416. data/lib/mongo/server/monitor/app_metadata.rb +3 -4
  417. data/lib/mongo/server/monitor/connection.rb +28 -35
  418. data/lib/mongo/server/monitor.rb +65 -60
  419. data/lib/mongo/server/pending_connection.rb +70 -71
  420. data/lib/mongo/server/push_monitor/connection.rb +0 -3
  421. data/lib/mongo/server/push_monitor.rb +21 -29
  422. data/lib/mongo/server/round_trip_time_calculator.rb +11 -17
  423. data/lib/mongo/server.rb +60 -93
  424. data/lib/mongo/server_selector/base.rb +146 -157
  425. data/lib/mongo/server_selector/nearest.rb +5 -5
  426. data/lib/mongo/server_selector/primary.rb +4 -5
  427. data/lib/mongo/server_selector/primary_preferred.rb +5 -6
  428. data/lib/mongo/server_selector/secondary.rb +5 -6
  429. data/lib/mongo/server_selector/secondary_preferred.rb +4 -5
  430. data/lib/mongo/server_selector.rb +3 -4
  431. data/lib/mongo/session/server_session.rb +6 -7
  432. data/lib/mongo/session/session_pool.rb +20 -34
  433. data/lib/mongo/session.rb +321 -189
  434. data/lib/mongo/socket/ocsp_cache.rb +8 -13
  435. data/lib/mongo/socket/ocsp_verifier.rb +69 -70
  436. data/lib/mongo/socket/ssl.rb +44 -43
  437. data/lib/mongo/socket/tcp.rb +5 -8
  438. data/lib/mongo/socket/unix.rb +0 -4
  439. data/lib/mongo/socket.rb +80 -102
  440. data/lib/mongo/srv/monitor.rb +6 -11
  441. data/lib/mongo/srv/resolver.rb +15 -24
  442. data/lib/mongo/srv/result.rb +18 -24
  443. data/lib/mongo/srv.rb +0 -1
  444. data/lib/mongo/timeout.rb +4 -11
  445. data/lib/mongo/topology_version.rb +8 -13
  446. data/lib/mongo/tracing/open_telemetry/command_tracer.rb +28 -1
  447. data/lib/mongo/tracing/open_telemetry/operation_tracer.rb +1 -1
  448. data/lib/mongo/tracing/open_telemetry/tracer.rb +1 -1
  449. data/lib/mongo/uri/options_mapper.rb +135 -126
  450. data/lib/mongo/uri/srv_protocol.rb +25 -38
  451. data/lib/mongo/uri.rb +95 -139
  452. data/lib/mongo/utils.rb +5 -12
  453. data/lib/mongo/version.rb +1 -1
  454. data/lib/mongo/write_concern/acknowledged.rb +0 -2
  455. data/lib/mongo/write_concern/base.rb +6 -6
  456. data/lib/mongo/write_concern/unacknowledged.rb +0 -2
  457. data/lib/mongo/write_concern.rb +14 -15
  458. data/lib/mongo.rb +2 -3
  459. data/mongo.gemspec +17 -17
  460. metadata +5 -5
  461. data/lib/mongo/error/server_api_not_supported.rb +0 -27
  462. data/lib/mongo/operation/shared/result/use_legacy_error_parser.rb +0 -32
  463. data/lib/mongo/operation/shared/validatable.rb +0 -87
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- # rubocop:todo all
3
2
 
4
3
  # Copyright (C) 2014-2020 MongoDB Inc.
5
4
  #
@@ -17,7 +16,6 @@
17
16
 
18
17
  module Mongo
19
18
  class Server
20
-
21
19
  # Represents a connection pool for server connections.
22
20
  #
23
21
  # @since 2.0.0, largely rewritten in 2.9.0
@@ -52,7 +50,7 @@ module Mongo
52
50
  # thus anything over 15 seconds is potentially dangerous.
53
51
  #
54
52
  # @since 2.9.0
55
- DEFAULT_WAIT_TIMEOUT = 10.freeze
53
+ DEFAULT_WAIT_TIMEOUT = 10
56
54
 
57
55
  # Condition variable broadcast when the size of the pool changes
58
56
  # to wake up the populator
@@ -100,31 +98,32 @@ module Mongo
100
98
  # @since 2.0.0, API changed in 2.9.0
101
99
 
102
100
  def initialize(server, options = {})
103
- unless server.is_a?(Server)
104
- raise ArgumentError, 'First argument must be a Server instance'
105
- end
101
+ raise ArgumentError, 'First argument must be a Server instance' unless server.is_a?(Server)
102
+
106
103
  options = options.dup
107
104
  if options[:min_size] && options[:min_pool_size] && options[:min_size] != options[:min_pool_size]
108
- raise ArgumentError, "Min size #{options[:min_size]} is not identical to min pool size #{options[:min_pool_size]}"
105
+ raise ArgumentError,
106
+ "Min size #{options[:min_size]} is not identical to min pool size #{options[:min_pool_size]}"
109
107
  end
110
108
  if options[:max_size] && options[:max_pool_size] && options[:max_size] != options[:max_pool_size]
111
- raise ArgumentError, "Max size #{options[:max_size]} is not identical to max pool size #{options[:max_pool_size]}"
109
+ raise ArgumentError,
110
+ "Max size #{options[:max_size]} is not identical to max pool size #{options[:max_pool_size]}"
112
111
  end
113
112
  if options[:wait_timeout] && options[:wait_queue_timeout] && options[:wait_timeout] != options[:wait_queue_timeout]
114
- raise ArgumentError, "Wait timeout #{options[:wait_timeout]} is not identical to wait queue timeout #{options[:wait_queue_timeout]}"
113
+ raise ArgumentError,
114
+ "Wait timeout #{options[:wait_timeout]} is not identical to wait queue timeout #{options[:wait_queue_timeout]}"
115
115
  end
116
+
116
117
  options[:min_size] ||= options[:min_pool_size]
117
118
  options.delete(:min_pool_size)
118
119
  options[:max_size] ||= options[:max_pool_size]
119
120
  options.delete(:max_pool_size)
120
121
  if options[:min_size] && options[:max_size] &&
121
- (options[:max_size] != 0 && options[:min_size] > options[:max_size])
122
- then
122
+ options[:max_size] != 0 && options[:min_size] > options[:max_size]
123
123
  raise ArgumentError, "Cannot have min size #{options[:min_size]} exceed max size #{options[:max_size]}"
124
124
  end
125
- if options[:wait_queue_timeout]
126
- options[:wait_timeout] ||= options[:wait_queue_timeout]
127
- end
125
+
126
+ options[:wait_timeout] ||= options[:wait_queue_timeout] if options[:wait_queue_timeout]
128
127
  options.delete(:wait_queue_timeout)
129
128
 
130
129
  @server = server
@@ -137,18 +136,25 @@ module Mongo
137
136
  # A connection owned by this pool should be either in the
138
137
  # available connections array (which is used as a stack)
139
138
  # or in the checked out connections set.
140
- @available_connections = available_connections = []
139
+ @available_connections = []
141
140
  @checked_out_connections = Set.new
142
141
  @pending_connections = Set.new
143
142
  @interrupt_connections = []
144
143
 
144
+ # RUBY-3364: count threads currently blocked on size_cv /
145
+ # max_connecting_cv. When non-zero, a newly-arriving thread must
146
+ # enter the wait queue even if the gate predicate is currently
147
+ # satisfied, to prevent barging past existing waiters.
148
+ @size_waiters = 0
149
+ @max_connecting_waiters = 0
150
+
145
151
  # Mutex used for synchronizing access to @available_connections and
146
152
  # @checked_out_connections. The pool object is thread-safe, thus
147
153
  # all methods that retrieve or modify instance variables generally
148
154
  # must do so under this lock.
149
155
  @lock = Mutex.new
150
156
 
151
- # Background thread reponsible for maintaining the size of
157
+ # Background thread responsible for maintaining the size of
152
158
  # the pool to at least min_size
153
159
  @populator = Populator.new(self, options)
154
160
  @populate_semaphore = Semaphore.new
@@ -164,12 +170,13 @@ module Mongo
164
170
  @connection_requests = 0
165
171
 
166
172
  # Condition variable to enforce the second check in check_out: max_connecting.
167
- # Thei condition variable should be signaled when the number of pending
173
+ # This condition variable should be signaled when the number of pending
168
174
  # connections decreases.
169
175
  @max_connecting_cv = Mongo::ConditionVariable.new(@lock)
170
176
  @max_connecting = options.fetch(:max_connecting, DEFAULT_MAX_CONNECTING)
171
177
 
172
- ObjectSpace.define_finalizer(self, self.class.finalize(@available_connections, @pending_connections, @populator))
178
+ ObjectSpace.define_finalizer(self,
179
+ self.class.finalize(@available_connections, @pending_connections, @populator))
173
180
 
174
181
  publish_cmap_event(
175
182
  Monitoring::Event::Cmap::PoolCreated.new(@server.address, options, self)
@@ -191,7 +198,7 @@ module Mongo
191
198
  #
192
199
  # @since 2.9.0
193
200
  def max_size
194
- @max_size ||= options[:max_size] || [DEFAULT_MAX_SIZE, min_size].max
201
+ @max_size ||= options[:max_size] || [ DEFAULT_MAX_SIZE, min_size ].max
195
202
  end
196
203
 
197
204
  # Get the minimum size of the connection pool.
@@ -321,12 +328,12 @@ module Mongo
321
328
  def summary
322
329
  @lock.synchronize do
323
330
  state = if closed?
324
- 'closed'
325
- elsif !@ready
326
- 'paused'
327
- else
328
- 'ready'
329
- end
331
+ 'closed'
332
+ elsif !@ready
333
+ 'paused'
334
+ else
335
+ 'ready'
336
+ end
330
337
  "#<ConnectionPool size=#{unsynchronized_size} (#{min_size}-#{max_size}) " +
331
338
  "used=#{@checked_out_connections.length} avail=#{@available_connections.length} pending=#{@pending_connections.length} #{state}>"
332
339
  end
@@ -378,13 +385,12 @@ module Mongo
378
385
  )
379
386
 
380
387
  publish_cmap_event(
381
- Monitoring::Event::Cmap::ConnectionCheckedOut.new(@server.address, connection.id, self),
388
+ Monitoring::Event::Cmap::ConnectionCheckedOut.new(@server.address, connection.id, self)
382
389
  )
383
390
 
384
- if Lint.enabled?
385
- unless connection.connected?
386
- raise Error::LintError, "Connection pool for #{address} checked out a disconnected connection #{connection.generation}:#{connection.id}"
387
- end
391
+ if Lint.enabled? && !connection.connected?
392
+ raise Error::LintError,
393
+ "Connection pool for #{address} checked out a disconnected connection #{connection.generation}:#{connection.id}"
388
394
  end
389
395
 
390
396
  connection
@@ -392,6 +398,23 @@ module Mongo
392
398
  check_invariants
393
399
  end
394
400
 
401
+ # Returns a pinned connection that is already checked out, if one
402
+ # exists with the given global id. Returns nil otherwise.
403
+ #
404
+ # @param [ Integer ] connection_global_id The global id of the pinned
405
+ # connection.
406
+ #
407
+ # @return [ Connection | nil ] The pinned connection, or nil.
408
+ #
409
+ # @api private
410
+ def check_out_pinned_connection(connection_global_id)
411
+ @lock.synchronize do
412
+ @checked_out_connections.detect do |conn|
413
+ conn.global_id == connection_global_id && conn.pinned?
414
+ end
415
+ end
416
+ end
417
+
395
418
  # Check a connection back into the pool.
396
419
  #
397
420
  # The connection must have been previously created by this pool.
@@ -420,14 +443,16 @@ module Mongo
420
443
  return if connection.closed? && connection.interrupted?
421
444
 
422
445
  unless connection.connection_pool == self
423
- 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})"
446
+ raise ArgumentError,
447
+ "Trying to check in a connection which was not checked out by this pool: #{connection} checked out from pool #{connection.connection_pool} (for #{self})"
424
448
  end
425
449
 
426
450
  unless @checked_out_connections.include?(connection)
427
- raise ArgumentError, "Trying to check in a connection which is not currently checked out by this pool: #{connection} (for #{self})"
451
+ raise ArgumentError,
452
+ "Trying to check in a connection which is not currently checked out by this pool: #{connection} (for #{self})"
428
453
  end
429
454
 
430
- # Note: if an event handler raises, resource will not be signaled.
455
+ # NOTE: if an event handler raises, resource will not be signaled.
431
456
  # This means threads waiting for a connection to free up when
432
457
  # the pool is at max size may time out.
433
458
  # Threads that begin waiting after this method completes (with
@@ -495,7 +520,7 @@ module Mongo
495
520
  raise Error::LintError, "Attempting to pause pool for server #{@server.summary} which is known"
496
521
  end
497
522
 
498
- return if !@ready
523
+ return unless @ready
499
524
 
500
525
  @ready = false
501
526
  end
@@ -551,9 +576,7 @@ module Mongo
551
576
  # Generation must be bumped before emitting pool cleared event.
552
577
  @generation_manager.bump(service_id: service_id)
553
578
 
554
- unless options && options[:lazy]
555
- close_available_connections(service_id)
556
- end
579
+ close_available_connections(service_id) unless options && options[:lazy]
557
580
 
558
581
  if options && options[:interrupt_in_use_connections]
559
582
  schedule_for_interruption(@checked_out_connections, service_id)
@@ -618,12 +641,12 @@ module Mongo
618
641
  Monitoring::Event::Cmap::PoolReady.new(@server.address, options, self)
619
642
  )
620
643
 
621
- if options.fetch(:populator_io, true)
622
- if @populator.running?
623
- @populate_semaphore.signal
624
- else
625
- @populator.run!
626
- end
644
+ return unless options.fetch(:populator_io, true)
645
+
646
+ if @populator.running?
647
+ @populate_semaphore.signal
648
+ else
649
+ @populator.run!
627
650
  end
628
651
  end
629
652
 
@@ -715,16 +738,35 @@ module Mongo
715
738
  def with_connection(connection_global_id: nil, context: nil)
716
739
  raise_if_closed!
717
740
 
718
- connection = check_out(
741
+ # If a specific connection is requested and it is already checked out
742
+ # and pinned (e.g. for a transaction or cursor in load-balanced mode),
743
+ # reuse it directly without going through the check_out/check_in cycle.
744
+ if connection_global_id
745
+ connection = @lock.synchronize do
746
+ @checked_out_connections.detect do |conn|
747
+ conn.global_id == connection_global_id && conn.pinned?
748
+ end
749
+ end
750
+ end
751
+
752
+ connection ||= check_out(
719
753
  connection_global_id: connection_global_id,
720
754
  context: context
721
755
  )
756
+
722
757
  yield(connection)
723
758
  rescue Error::SocketError, Error::SocketTimeoutError, Error::ConnectionPerished => e
724
759
  maybe_raise_pool_cleared!(connection, e)
725
760
  ensure
726
- if connection
727
- check_in(connection)
761
+ if connection && !connection.pinned?
762
+ # Do not check in if the connection is pinned (the session or cursor
763
+ # owns it and will check it in later when unpinning). Also skip
764
+ # check-in if the connection was already checked in during the block
765
+ # (e.g. by Session#unpin after an error on the first operation).
766
+ checked_out = @lock.synchronize do
767
+ @checked_out_connections.include?(connection)
768
+ end
769
+ check_in(connection) if checked_out
728
770
  end
729
771
  end
730
772
 
@@ -740,13 +782,11 @@ module Mongo
740
782
  i = 0
741
783
  while i < @available_connections.length
742
784
  connection = @available_connections[i]
743
- if last_checkin = connection.last_checkin
744
- if (Time.now - last_checkin) > max_idle_time
745
- connection.disconnect!(reason: :idle)
746
- @available_connections.delete_at(i)
747
- @populate_semaphore.signal
748
- next
749
- end
785
+ if (last_checkin = connection.last_checkin) && ((Time.now - last_checkin) > max_idle_time)
786
+ connection.disconnect!(reason: :idle)
787
+ @available_connections.delete_at(i)
788
+ @populate_semaphore.signal
789
+ next
750
790
  end
751
791
  i += 1
752
792
  end
@@ -787,7 +827,7 @@ module Mongo
787
827
  # @return [ true | false ] Whether this method should be called again
788
828
  # to create more connections.
789
829
  # @raise [ Error::AuthError, Error ] The second socket-related error raised if a retry
790
- # occured, or the non socket-related error
830
+ # occurred, or the non socket-related error
791
831
  #
792
832
  # @api private
793
833
  def populate
@@ -801,7 +841,7 @@ module Mongo
801
841
  log_warn("Populator failed to connect a connection for #{address}: #{e.class}: #{e}. It will retry.")
802
842
  end
803
843
 
804
- return create_and_add_connection
844
+ create_and_add_connection
805
845
  end
806
846
 
807
847
  # Finalize the connection pool for garbage collection.
@@ -811,7 +851,7 @@ module Mongo
811
851
  # @param [ Populator ] populator The populator.
812
852
  #
813
853
  # @return [ Proc ] The Finalizer.
814
- def self.finalize(available_connections, pending_connections, populator)
854
+ def self.finalize(available_connections, pending_connections, _populator)
815
855
  proc do
816
856
  available_connections.each do |connection|
817
857
  connection.disconnect!(reason: :pool_closed)
@@ -841,9 +881,7 @@ module Mongo
841
881
  conn = @available_connections.detect do |conn|
842
882
  conn.global_id == connection_global_id
843
883
  end
844
- if conn
845
- @available_connections.delete(conn)
846
- end
884
+ @available_connections.delete(conn) if conn
847
885
  conn
848
886
  else
849
887
  @available_connections.pop
@@ -851,16 +889,14 @@ module Mongo
851
889
  end
852
890
 
853
891
  def create_connection
854
- r, _ = @generation_manager.pipe_fds(service_id: server.description.service_id)
892
+ r, = @generation_manager.pipe_fds(service_id: server.description.service_id)
855
893
  opts = options.merge(
856
894
  connection_pool: self,
857
895
  pipe: r
858
896
  # Do not pass app metadata - this will be retrieved by the connection
859
897
  # based on the auth needs.
860
898
  )
861
- unless @server.load_balancer?
862
- opts[:generation] = generation
863
- end
899
+ opts[:generation] = generation unless @server.load_balancer?
864
900
  Connection.new(@server, opts)
865
901
  end
866
902
 
@@ -875,14 +911,14 @@ module Mongo
875
911
 
876
912
  @lock.synchronize do
877
913
  if !closed? && @ready &&
878
- (unsynchronized_size + @connection_requests) < min_size &&
879
- @pending_connections.length < @max_connecting
880
- then
914
+ (unsynchronized_size + @connection_requests) < min_size &&
915
+ @pending_connections.length < @max_connecting
881
916
  connection = create_connection
882
917
  @pending_connections << connection
883
918
  else
884
919
  return true if remove_interrupted_connections
885
920
  return true if remove_stale_connection
921
+
886
922
  return false
887
923
  end
888
924
  end
@@ -910,11 +946,11 @@ module Mongo
910
946
 
911
947
  # Removes and disconnects all stale available connections.
912
948
  def remove_stale_connection
913
- if conn = @available_connections.detect(&method(:connection_stale_unlocked?))
914
- conn.disconnect!(reason: :stale)
915
- @available_connections.delete(conn)
916
- return true
917
- end
949
+ return unless conn = @available_connections.detect { |c| connection_stale_unlocked?(c) }
950
+
951
+ conn.disconnect!(reason: :stale)
952
+ @available_connections.delete(conn)
953
+ true
918
954
  end
919
955
 
920
956
  # Interrupt connections scheduled for interruption.
@@ -957,7 +993,7 @@ module Mongo
957
993
  # @return [ true | false ] Whether the connection is stale.
958
994
  def connection_stale_unlocked?(connection)
959
995
  connection.generation != generation_unlocked(service_id: connection.service_id) &&
960
- !connection.pinned?
996
+ !connection.pinned?
961
997
  end
962
998
 
963
999
  # Asserts that the pool has not been closed.
@@ -966,9 +1002,9 @@ module Mongo
966
1002
  #
967
1003
  # @since 2.9.0
968
1004
  def raise_if_closed!
969
- if closed?
970
- raise Error::PoolClosedError.new(@server.address, self)
971
- end
1005
+ return unless closed?
1006
+
1007
+ raise Error::PoolClosedError.new(@server.address, self)
972
1008
  end
973
1009
 
974
1010
  # If the connection was interrupted, raise a pool cleared error. If it
@@ -980,31 +1016,36 @@ module Mongo
980
1016
  # @raise [ Mongo::Error | Mongo::Error::PoolClearedError ] A PoolClearedError
981
1017
  # if the connection was interrupted, the original error if not.
982
1018
  def maybe_raise_pool_cleared!(connection, e)
983
- if connection&.interrupted?
984
- err = Error::PoolClearedError.new(connection.server.address, connection.server.pool_internal).tap do |err|
985
- e.labels.each { |l| err.add_label(l) }
986
- end
987
- raise err
988
- else
989
- raise e
1019
+ raise e unless connection&.interrupted?
1020
+
1021
+ err = Error::PoolClearedError.new(connection.server.address, connection.server.pool_internal).tap do |err|
1022
+ e.labels.each { |l| err.add_label(l) }
990
1023
  end
1024
+ raise err
991
1025
  end
992
1026
 
993
1027
  # Attempts to connect (handshake and auth) the connection. If an error is
994
1028
  # encountered, closes the connection and raises the error.
995
1029
  def connect_connection(connection, context = nil)
996
- begin
997
- connection.connect!(context)
998
- rescue Exception
999
- connection.disconnect!(reason: :error)
1000
- raise
1030
+ connection.connect!(context)
1031
+ rescue Exception => e
1032
+ # When a connection encounters an error during creation
1033
+ # (including handshake and authentication), mark the server
1034
+ # as Unknown so the pool is cleared (per CMAP spec).
1035
+ # The unknown! call must happen before disconnect! so that
1036
+ # the ConnectionPoolCleared event is published before the
1037
+ # ConnectionClosed event.
1038
+ # Errors with the SystemOverloadedError label (network errors
1039
+ # during handshake) are excluded per the SDAM spec — those
1040
+ # must not change the server description.
1041
+ if e.is_a?(Mongo::Error) && !e.label?('SystemOverloadedError')
1042
+ @server.unknown!(
1043
+ generation: e.generation,
1044
+ service_id: e.service_id,
1045
+ stop_push_monitor: true
1046
+ )
1001
1047
  end
1002
- rescue Error::SocketError, Error::SocketTimeoutError => exc
1003
- @server.unknown!(
1004
- generation: exc.generation,
1005
- service_id: exc.service_id,
1006
- stop_push_monitor: true,
1007
- )
1048
+ connection.disconnect!(reason: :error)
1008
1049
  raise
1009
1050
  end
1010
1051
 
@@ -1039,15 +1080,13 @@ module Mongo
1039
1080
  loop do
1040
1081
  conn = @available_connections.detect do |conn|
1041
1082
  conn.service_id == service_id &&
1042
- conn.generation < @generation_manager.generation(service_id: service_id)
1043
- end
1044
- if conn
1045
- @available_connections.delete(conn)
1046
- conn.disconnect!(reason: :stale, interrupted: true)
1047
- @populate_semaphore.signal
1048
- else
1049
- break
1083
+ conn.generation < @generation_manager.generation(service_id: service_id)
1050
1084
  end
1085
+ break unless conn
1086
+
1087
+ @available_connections.delete(conn)
1088
+ conn.disconnect!(reason: :stale, interrupted: true)
1089
+ @populate_semaphore.signal
1051
1090
  end
1052
1091
  else
1053
1092
  @available_connections.delete_if do |conn|
@@ -1067,7 +1106,7 @@ module Mongo
1067
1106
  def schedule_for_interruption(connections, service_id)
1068
1107
  @interrupt_connections += connections.select do |conn|
1069
1108
  (!server.load_balancer? || conn.service_id == service_id) &&
1070
- conn.generation < @generation_manager.generation(service_id: service_id)
1109
+ conn.generation < @generation_manager.generation(service_id: service_id)
1071
1110
  end
1072
1111
  end
1073
1112
 
@@ -1087,23 +1126,23 @@ module Mongo
1087
1126
  publish_cmap_event(
1088
1127
  Monitoring::Event::Cmap::ConnectionCheckOutFailed.new(
1089
1128
  @server.address,
1090
- Monitoring::Event::Cmap::ConnectionCheckOutFailed::TIMEOUT,
1091
- ),
1129
+ Monitoring::Event::Cmap::ConnectionCheckOutFailed::TIMEOUT
1130
+ )
1092
1131
  )
1093
1132
 
1094
1133
  connection_global_id_msg = if connection_global_id
1095
- " for connection #{connection_global_id}"
1096
- else
1097
- ''
1098
- end
1099
-
1100
- msg = "Timed out attempting to check out a connection " +
1101
- "from pool for #{@server.address}#{connection_global_id_msg} after #{wait_timeout} sec. " +
1102
- "Connections in pool: #{@available_connections.length} available, " +
1103
- "#{@checked_out_connections.length} checked out, " +
1104
- "#{@pending_connections.length} pending, " +
1105
- "#{@connection_requests} connections requests " +
1106
- "(max size: #{max_size})"
1134
+ " for connection #{connection_global_id}"
1135
+ else
1136
+ ''
1137
+ end
1138
+
1139
+ msg = 'Timed out attempting to check out a connection ' +
1140
+ "from pool for #{@server.address}#{connection_global_id_msg} after #{wait_timeout} sec. " +
1141
+ "Connections in pool: #{@available_connections.length} available, " +
1142
+ "#{@checked_out_connections.length} checked out, " +
1143
+ "#{@pending_connections.length} pending, " +
1144
+ "#{@connection_requests} connections requests " +
1145
+ "(max size: #{max_size})"
1107
1146
  raise Error::ConnectionCheckOutTimeout.new(msg, address: @server.address)
1108
1147
  end
1109
1148
 
@@ -1114,31 +1153,31 @@ module Mongo
1114
1153
  end
1115
1154
 
1116
1155
  def raise_if_pool_closed!
1117
- if closed?
1118
- publish_cmap_event(
1119
- Monitoring::Event::Cmap::ConnectionCheckOutFailed.new(
1120
- @server.address,
1121
- Monitoring::Event::Cmap::ConnectionCheckOutFailed::POOL_CLOSED
1122
- ),
1156
+ return unless closed?
1157
+
1158
+ publish_cmap_event(
1159
+ Monitoring::Event::Cmap::ConnectionCheckOutFailed.new(
1160
+ @server.address,
1161
+ Monitoring::Event::Cmap::ConnectionCheckOutFailed::POOL_CLOSED
1123
1162
  )
1124
- raise Error::PoolClosedError.new(@server.address, self)
1125
- end
1163
+ )
1164
+ raise Error::PoolClosedError.new(@server.address, self)
1126
1165
  end
1127
1166
 
1128
1167
  def raise_if_pool_paused!
1129
1168
  raise_unless_locked!
1130
1169
 
1131
- if !@ready
1132
- publish_cmap_event(
1133
- Monitoring::Event::Cmap::ConnectionCheckOutFailed.new(
1134
- @server.address,
1135
- # CMAP spec decided to conflate pool paused with all the other
1136
- # possible non-timeout errors.
1137
- Monitoring::Event::Cmap::ConnectionCheckOutFailed::CONNECTION_ERROR,
1138
- ),
1170
+ return if @ready
1171
+
1172
+ publish_cmap_event(
1173
+ Monitoring::Event::Cmap::ConnectionCheckOutFailed.new(
1174
+ @server.address,
1175
+ # CMAP spec decided to conflate pool paused with all the other
1176
+ # possible non-timeout errors.
1177
+ Monitoring::Event::Cmap::ConnectionCheckOutFailed::CONNECTION_ERROR
1139
1178
  )
1140
- raise Error::PoolPausedError.new(@server.address, self)
1141
- end
1179
+ )
1180
+ raise Error::PoolPausedError.new(@server.address, self)
1142
1181
  end
1143
1182
 
1144
1183
  def raise_if_pool_paused_locked!
@@ -1155,12 +1194,12 @@ module Mongo
1155
1194
  end
1156
1195
 
1157
1196
  def raise_unless_locked!
1158
- unless @lock.owned?
1159
- raise ArgumentError, "the lock must be owned when calling this method"
1160
- end
1197
+ return if @lock.owned?
1198
+
1199
+ raise ArgumentError, 'the lock must be owned when calling this method'
1161
1200
  end
1162
1201
 
1163
- def valid_available_connection?(connection, pid, connection_global_id)
1202
+ def valid_available_connection?(connection, pid, _connection_global_id)
1164
1203
  if connection.pid != pid
1165
1204
  log_warn("Detected PID change - Mongo client should have been reconnected (old pid #{connection.pid}, new pid #{pid}")
1166
1205
  connection.disconnect!(reason: :stale)
@@ -1168,7 +1207,7 @@ module Mongo
1168
1207
  return false
1169
1208
  end
1170
1209
 
1171
- if !connection.pinned?
1210
+ unless connection.pinned?
1172
1211
  # If connection is marked as pinned, it is used by a transaction
1173
1212
  # or a series of cursor operations in a load balanced setup.
1174
1213
  # In this case connection should not be disconnected until
@@ -1184,8 +1223,7 @@ module Mongo
1184
1223
  end
1185
1224
 
1186
1225
  if max_idle_time && connection.last_checkin &&
1187
- Time.now - connection.last_checkin > max_idle_time
1188
- then
1226
+ Time.now - connection.last_checkin > max_idle_time
1189
1227
  connection.disconnect!(reason: :idle)
1190
1228
  @populate_semaphore.signal
1191
1229
  return false
@@ -1209,9 +1247,7 @@ module Mongo
1209
1247
  # and remains so for longer than the wait timeout.
1210
1248
  def get_connection(pid, connection_global_id)
1211
1249
  if connection = next_available_connection(connection_global_id)
1212
- unless valid_available_connection?(connection, pid, connection_global_id)
1213
- return nil
1214
- end
1250
+ return nil unless valid_available_connection?(connection, pid, connection_global_id)
1215
1251
 
1216
1252
  # We've got a connection, so we decrement the number of connection
1217
1253
  # requests.
@@ -1222,13 +1258,11 @@ module Mongo
1222
1258
  # If the connection is connected, it's not considered a
1223
1259
  # "pending connection". The pending_connections list represents
1224
1260
  # the set of connections that are awaiting connection.
1225
- unless connection.connected?
1226
- @pending_connections << connection
1227
- end
1228
- return connection
1261
+ @pending_connections << connection unless connection.connected?
1262
+ connection
1229
1263
  elsif connection_global_id && @server.load_balancer?
1230
1264
  # A particular connection is requested, but it is not available.
1231
- # If it is nether available not checked out, we should stop here.
1265
+ # If it is neither available nor checked out, we should stop here.
1232
1266
  @checked_out_connections.detect do |conn|
1233
1267
  conn.global_id == connection_global_id
1234
1268
  end.tap do |conn|
@@ -1237,7 +1271,7 @@ module Mongo
1237
1271
  Monitoring::Event::Cmap::ConnectionCheckOutFailed.new(
1238
1272
  @server.address,
1239
1273
  Monitoring::Event::Cmap::ConnectionCheckOutFailed::CONNECTION_ERROR
1240
- ),
1274
+ )
1241
1275
  )
1242
1276
  # We're going to raise, so we need to decrement the number of
1243
1277
  # connection requests.
@@ -1253,7 +1287,7 @@ module Mongo
1253
1287
  connection = create_connection
1254
1288
  @connection_requests -= 1
1255
1289
  @pending_connections << connection
1256
- return connection
1290
+ connection
1257
1291
  end
1258
1292
  end
1259
1293
 
@@ -1269,18 +1303,32 @@ module Mongo
1269
1303
  # @raise [ Error::PoolClosedError ] If the pool has been closed.
1270
1304
  # @raise [ Timeout::Error ] If the connection pool is at maximum size
1271
1305
  # and remains so for longer than the wait timeout.
1272
- def retrieve_and_connect_connection(connection_global_id, context = nil)
1306
+ def retrieve_and_connect_connection(connection_global_id, context = nil)
1273
1307
  deadline = Utils.monotonic_time + wait_timeout(context)
1274
1308
  connection = nil
1275
1309
 
1276
1310
  @lock.synchronize do
1277
- # The first gate to checking out a connection. Make sure the number of
1278
- # unavailable connections is less than the max pool size.
1279
- until max_size == 0 || unavailable_connections < max_size
1311
+ # RUBY-3364: if any thread is already waiting for a size slot,
1312
+ # join the queue even when the gate predicate is currently
1313
+ # satisfied. Without this, re-entering threads (those that just
1314
+ # checked a connection back in) barge past existing waiters and
1315
+ # the 195 blocked threads in a 200:5 scenario never wake.
1316
+ # Skip the gate for unlimited pools (max_size == 0) where there
1317
+ # is no size constraint to wait on.
1318
+ must_wait = max_size != 0 && @size_waiters > 0
1319
+ until (max_size == 0 || unavailable_connections < max_size) && !must_wait
1280
1320
  wait = deadline - Utils.monotonic_time
1281
1321
  raise_check_out_timeout!(connection_global_id) if wait <= 0
1282
- @size_cv.wait(wait)
1322
+ @size_waiters += 1
1323
+ begin
1324
+ @size_cv.wait(wait)
1325
+ ensure
1326
+ @size_waiters -= 1
1327
+ end
1283
1328
  raise_if_not_ready!
1329
+ # After one wait cycle we have served our "queue tax" and
1330
+ # compete for the slot on the next predicate check.
1331
+ must_wait = false
1284
1332
  end
1285
1333
  @connection_requests += 1
1286
1334
  connection = wait_for_connection(connection_global_id, deadline)
@@ -1290,15 +1338,28 @@ module Mongo
1290
1338
 
1291
1339
  @lock.synchronize do
1292
1340
  @checked_out_connections << connection
1293
- if @pending_connections.include?(connection)
1294
- @pending_connections.delete(connection)
1295
- end
1341
+ @pending_connections.delete(connection) if @pending_connections.include?(connection)
1296
1342
  @max_connecting_cv.signal
1297
- # no need to signal size_cv here since the number of unavailable
1298
- # connections is unchanged.
1343
+ # RUBY-3364: hand off the baton. A waiter that arrived during
1344
+ # our wake-up window (seeing our stale @size_waiters > 0) may be
1345
+ # parked on @size_cv with capacity already available. The
1346
+ # regular check-in path is the only other place that signals
1347
+ # @size_cv, so we wake the next waiter only when the predicate
1348
+ # is actually satisfied for them. Signaling unconditionally
1349
+ # would re-queue a waiter at the back of the FIFO and break
1350
+ # ordering.
1351
+ if @size_waiters > 0 && (max_size == 0 || unavailable_connections < max_size)
1352
+ @size_cv.signal
1353
+ end
1299
1354
  end
1300
1355
 
1301
1356
  connection
1357
+ rescue Error::ConnectionCheckOutTimeout
1358
+ # Per the CSOT spec, if a connection checkout fails because the CSOT
1359
+ # deadline expired (rather than a configured waitQueueTimeout), the
1360
+ # error must be a CSOT TimeoutError so that callers can distinguish it.
1361
+ context&.check_timeout!
1362
+ raise
1302
1363
  end
1303
1364
 
1304
1365
  # Waits for a connection to become available, or raises is no connection
@@ -1311,9 +1372,12 @@ module Mongo
1311
1372
  def wait_for_connection(connection_global_id, deadline)
1312
1373
  connection = nil
1313
1374
  while connection.nil?
1375
+ # RUBY-3364: as above, yield to any thread already queued for
1376
+ # a max_connecting slot before competing ourselves.
1377
+ must_wait = @max_connecting_waiters > 0
1314
1378
  # The second gate to checking out a connection. Make sure 1) there
1315
1379
  # exists an available connection and 2) we are under max_connecting.
1316
- until @available_connections.any? || @pending_connections.length < @max_connecting
1380
+ until (@available_connections.any? || @pending_connections.length < @max_connecting) && !must_wait
1317
1381
  wait = deadline - Utils.monotonic_time
1318
1382
  if wait <= 0
1319
1383
  # We are going to raise a timeout error, so the connection
@@ -1322,21 +1386,35 @@ module Mongo
1322
1386
  decrement_connection_requests_and_signal
1323
1387
  raise_check_out_timeout!(connection_global_id)
1324
1388
  end
1325
- @max_connecting_cv.wait(wait)
1389
+ @max_connecting_waiters += 1
1390
+ begin
1391
+ @max_connecting_cv.wait(wait)
1392
+ ensure
1393
+ @max_connecting_waiters -= 1
1394
+ end
1326
1395
  # We do not need to decrement the connection_requests counter
1327
1396
  # or signal here because the pool is not ready yet.
1328
1397
  raise_if_not_ready!
1398
+ must_wait = false
1329
1399
  end
1330
1400
 
1331
1401
  connection = get_connection(Process.pid, connection_global_id)
1332
1402
  wait = deadline - Utils.monotonic_time
1333
- if connection.nil? && wait <= 0
1334
- # connection is nil here, it means that get_connection method
1335
- # did not create a new connection; therefore, it did not decrease
1336
- # the connection_requests counter. We need to do it here.
1337
- decrement_connection_requests_and_signal
1338
- raise_check_out_timeout!(connection_global_id)
1339
- end
1403
+ next unless connection.nil? && wait <= 0
1404
+
1405
+ # connection is nil here, it means that get_connection method
1406
+ # did not create a new connection; therefore, it did not decrease
1407
+ # the connection_requests counter. We need to do it here.
1408
+ decrement_connection_requests_and_signal
1409
+ raise_check_out_timeout!(connection_global_id)
1410
+ end
1411
+
1412
+ # RUBY-3364: hand off the baton for max_connecting_cv. Signal
1413
+ # only if the gate predicate is satisfied for the next waiter, to
1414
+ # avoid re-queuing a waiter at the back of the FIFO.
1415
+ if @max_connecting_waiters > 0 &&
1416
+ (@available_connections.any? || @pending_connections.length < @max_connecting)
1417
+ @max_connecting_cv.signal
1340
1418
  end
1341
1419
 
1342
1420
  connection
@@ -1351,9 +1429,7 @@ module Mongo
1351
1429
  rescue Exception
1352
1430
  # Handshake or authentication failed
1353
1431
  @lock.synchronize do
1354
- if @pending_connections.include?(connection)
1355
- @pending_connections.delete(connection)
1356
- end
1432
+ @pending_connections.delete(connection) if @pending_connections.include?(connection)
1357
1433
  @max_connecting_cv.signal
1358
1434
  @size_cv.signal
1359
1435
  end
@@ -1362,12 +1438,11 @@ module Mongo
1362
1438
  Monitoring::Event::Cmap::ConnectionCheckOutFailed.new(
1363
1439
  @server.address,
1364
1440
  Monitoring::Event::Cmap::ConnectionCheckOutFailed::CONNECTION_ERROR
1365
- ),
1441
+ )
1366
1442
  )
1367
1443
  raise
1368
1444
  end
1369
1445
 
1370
-
1371
1446
  # Decrement connection requests counter and signal the condition
1372
1447
  # variables that the number of unavailable connections has decreased.
1373
1448
  def decrement_connection_requests_and_signal