mongo 2.22.0 → 2.24.0

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 (467) 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 +11 -16
  46. data/lib/mongo/bulk_write/result_combiner.rb +9 -12
  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 +267 -276
  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 +152 -184
  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/behavior.rb +1 -1
  71. data/lib/mongo/collection/view/aggregation.rb +10 -12
  72. data/lib/mongo/collection/view/builder/aggregation.rb +6 -9
  73. data/lib/mongo/collection/view/builder/map_reduce.rb +18 -17
  74. data/lib/mongo/collection/view/builder.rb +0 -1
  75. data/lib/mongo/collection/view/change_stream/retryable.rb +3 -8
  76. data/lib/mongo/collection/view/change_stream.rb +59 -58
  77. data/lib/mongo/collection/view/explainable.rb +11 -20
  78. data/lib/mongo/collection/view/immutable.rb +1 -3
  79. data/lib/mongo/collection/view/iterable.rb +44 -35
  80. data/lib/mongo/collection/view/map_reduce.rb +20 -25
  81. data/lib/mongo/collection/view/readable.rb +96 -94
  82. data/lib/mongo/collection/view/writable.rb +104 -114
  83. data/lib/mongo/collection/view.rb +11 -8
  84. data/lib/mongo/collection.rb +103 -106
  85. data/lib/mongo/condition_variable.rb +4 -4
  86. data/lib/mongo/config/options.rb +0 -3
  87. data/lib/mongo/config/validators/option.rb +3 -5
  88. data/lib/mongo/config.rb +6 -4
  89. data/lib/mongo/crypt/auto_decryption_context.rb +9 -3
  90. data/lib/mongo/crypt/auto_encrypter.rb +34 -43
  91. data/lib/mongo/crypt/auto_encryption_context.rb +0 -3
  92. data/lib/mongo/crypt/binary.rb +5 -9
  93. data/lib/mongo/crypt/binding.rb +150 -156
  94. data/lib/mongo/crypt/context.rb +20 -17
  95. data/lib/mongo/crypt/data_key_context.rb +2 -7
  96. data/lib/mongo/crypt/encryption_io.rb +29 -39
  97. data/lib/mongo/crypt/explicit_decryption_context.rb +9 -3
  98. data/lib/mongo/crypt/explicit_encrypter.rb +1 -1
  99. data/lib/mongo/crypt/explicit_encryption_context.rb +19 -30
  100. data/lib/mongo/crypt/explicit_encryption_expression_context.rb +0 -2
  101. data/lib/mongo/crypt/handle.rb +42 -48
  102. data/lib/mongo/crypt/hooks.rb +12 -15
  103. data/lib/mongo/crypt/kms/aws/credentials.rb +12 -16
  104. data/lib/mongo/crypt/kms/aws/master_document.rb +6 -9
  105. data/lib/mongo/crypt/kms/aws.rb +0 -2
  106. data/lib/mongo/crypt/kms/azure/credentials_retriever.rb +2 -7
  107. data/lib/mongo/crypt/kms/azure/master_document.rb +15 -19
  108. data/lib/mongo/crypt/kms/azure.rb +0 -1
  109. data/lib/mongo/crypt/kms/credentials.rb +13 -27
  110. data/lib/mongo/crypt/kms/gcp/credentials.rb +12 -14
  111. data/lib/mongo/crypt/kms/gcp/credentials_retriever.rb +7 -9
  112. data/lib/mongo/crypt/kms/gcp/master_document.rb +12 -16
  113. data/lib/mongo/crypt/kms/gcp.rb +0 -2
  114. data/lib/mongo/crypt/kms/kmip/credentials.rb +7 -8
  115. data/lib/mongo/crypt/kms/kmip/master_document.rb +3 -5
  116. data/lib/mongo/crypt/kms/kmip.rb +0 -1
  117. data/lib/mongo/crypt/kms/local/credentials.rb +7 -8
  118. data/lib/mongo/crypt/kms/local/master_document.rb +2 -6
  119. data/lib/mongo/crypt/kms/local.rb +0 -1
  120. data/lib/mongo/crypt/kms/master_key_document.rb +11 -15
  121. data/lib/mongo/crypt/kms.rb +14 -16
  122. data/lib/mongo/crypt/kms_context.rb +0 -2
  123. data/lib/mongo/crypt/rewrap_many_data_key_context.rb +2 -7
  124. data/lib/mongo/crypt/rewrap_many_data_key_result.rb +2 -4
  125. data/lib/mongo/crypt/status.rb +12 -14
  126. data/lib/mongo/crypt.rb +0 -1
  127. data/lib/mongo/csot_timeout_holder.rb +3 -2
  128. data/lib/mongo/cursor/kill_spec.rb +7 -10
  129. data/lib/mongo/cursor.rb +74 -64
  130. data/lib/mongo/cursor_host.rb +8 -10
  131. data/lib/mongo/database/view.rb +23 -39
  132. data/lib/mongo/database.rb +68 -65
  133. data/lib/mongo/dbref.rb +0 -1
  134. data/lib/mongo/deprecations.rb +98 -0
  135. data/lib/mongo/distinguishing_semaphore.rb +0 -1
  136. data/lib/mongo/error/auth_error.rb +0 -2
  137. data/lib/mongo/error/bad_load_balancer_target.rb +0 -2
  138. data/lib/mongo/error/bulk_write_error.rb +7 -10
  139. data/lib/mongo/error/change_stream_resumable.rb +0 -2
  140. data/lib/mongo/error/client_closed.rb +0 -2
  141. data/lib/mongo/error/closed_stream.rb +1 -4
  142. data/lib/mongo/error/connection_check_out_timeout.rb +3 -6
  143. data/lib/mongo/error/connection_perished.rb +0 -2
  144. data/lib/mongo/error/connection_unavailable.rb +0 -2
  145. data/lib/mongo/error/credential_check_error.rb +0 -2
  146. data/lib/mongo/error/crypt_error.rb +0 -2
  147. data/lib/mongo/error/extra_file_chunk.rb +1 -4
  148. data/lib/mongo/error/failed_string_prep_validation.rb +5 -6
  149. data/lib/mongo/error/file_not_found.rb +0 -3
  150. data/lib/mongo/error/handshake_error.rb +0 -2
  151. data/lib/mongo/error/insufficient_iteration_count.rb +1 -4
  152. data/lib/mongo/error/internal_driver_error.rb +0 -2
  153. data/lib/mongo/error/invalid_address.rb +0 -2
  154. data/lib/mongo/error/invalid_application_name.rb +0 -3
  155. data/lib/mongo/error/invalid_bulk_operation.rb +1 -4
  156. data/lib/mongo/error/invalid_bulk_operation_type.rb +1 -4
  157. data/lib/mongo/error/invalid_collection_name.rb +1 -4
  158. data/lib/mongo/error/invalid_config_option.rb +0 -3
  159. data/lib/mongo/error/invalid_cursor_operation.rb +0 -2
  160. data/lib/mongo/error/invalid_database_name.rb +1 -4
  161. data/lib/mongo/error/invalid_document.rb +1 -4
  162. data/lib/mongo/error/invalid_file.rb +0 -3
  163. data/lib/mongo/error/invalid_file_revision.rb +0 -3
  164. data/lib/mongo/error/invalid_min_pool_size.rb +0 -3
  165. data/lib/mongo/error/invalid_nonce.rb +0 -3
  166. data/lib/mongo/error/invalid_read_concern.rb +2 -4
  167. data/lib/mongo/error/invalid_read_option.rb +0 -3
  168. data/lib/mongo/error/invalid_replacement_document.rb +2 -5
  169. data/lib/mongo/error/invalid_server_auth_host.rb +0 -2
  170. data/lib/mongo/error/invalid_server_auth_response.rb +0 -2
  171. data/lib/mongo/error/invalid_server_preference.rb +7 -16
  172. data/lib/mongo/error/invalid_session.rb +1 -4
  173. data/lib/mongo/error/invalid_signature.rb +0 -3
  174. data/lib/mongo/error/invalid_transaction_operation.rb +5 -8
  175. data/lib/mongo/error/invalid_txt_record.rb +0 -2
  176. data/lib/mongo/error/invalid_update_document.rb +2 -5
  177. data/lib/mongo/error/invalid_uri.rb +1 -4
  178. data/lib/mongo/error/invalid_write_concern.rb +2 -5
  179. data/lib/mongo/error/kms_error.rb +0 -2
  180. data/lib/mongo/error/labelable.rb +0 -3
  181. data/lib/mongo/error/lint_error.rb +0 -2
  182. data/lib/mongo/error/max_bson_size.rb +8 -11
  183. data/lib/mongo/error/max_message_size.rb +2 -5
  184. data/lib/mongo/error/mismatched_domain.rb +0 -2
  185. data/lib/mongo/error/missing_connection.rb +0 -2
  186. data/lib/mongo/error/missing_file_chunk.rb +0 -3
  187. data/lib/mongo/error/missing_password.rb +0 -2
  188. data/lib/mongo/error/missing_resume_token.rb +1 -4
  189. data/lib/mongo/error/missing_scram_server_signature.rb +2 -4
  190. data/lib/mongo/error/missing_service_id.rb +0 -2
  191. data/lib/mongo/error/mongocryptd_spawn_error.rb +0 -2
  192. data/lib/mongo/error/multi_index_drop.rb +0 -3
  193. data/lib/mongo/error/need_primary_server.rb +0 -2
  194. data/lib/mongo/error/no_server_available.rb +3 -8
  195. data/lib/mongo/error/no_service_connection_available.rb +1 -3
  196. data/lib/mongo/error/no_srv_records.rb +0 -2
  197. data/lib/mongo/error/notable.rb +8 -16
  198. data/lib/mongo/error/operation_failure.rb +22 -35
  199. data/lib/mongo/error/parser.rb +33 -75
  200. data/lib/mongo/error/pool_cleared_error.rb +1 -3
  201. data/lib/mongo/error/pool_closed_error.rb +0 -3
  202. data/lib/mongo/error/pool_error.rb +0 -3
  203. data/lib/mongo/error/pool_paused_error.rb +0 -2
  204. data/lib/mongo/error/raise_original_error.rb +1 -3
  205. data/lib/mongo/error/read_write_retryable.rb +14 -17
  206. data/lib/mongo/error/sdam_error_detection.rb +3 -5
  207. data/lib/mongo/error/server_api_conflict.rb +0 -2
  208. data/lib/mongo/error/server_certificate_revoked.rb +0 -2
  209. data/lib/mongo/error/server_not_usable.rb +0 -2
  210. data/lib/mongo/error/session_ended.rb +1 -3
  211. data/lib/mongo/error/session_not_materialized.rb +1 -3
  212. data/lib/mongo/error/sessions_not_supported.rb +1 -4
  213. data/lib/mongo/error/snapshot_session_invalid_server_version.rb +1 -4
  214. data/lib/mongo/error/snapshot_session_transaction_prohibited.rb +1 -4
  215. data/lib/mongo/error/socket_error.rb +0 -2
  216. data/lib/mongo/error/socket_timeout_error.rb +0 -2
  217. data/lib/mongo/error/transactions_not_supported.rb +3 -6
  218. data/lib/mongo/error/unchangeable_collection_option.rb +1 -4
  219. data/lib/mongo/error/unexpected_chunk_length.rb +0 -3
  220. data/lib/mongo/error/unexpected_response.rb +1 -4
  221. data/lib/mongo/error/unknown_payload_type.rb +0 -3
  222. data/lib/mongo/error/unmet_dependency.rb +0 -2
  223. data/lib/mongo/error/unsupported_array_filters.rb +3 -24
  224. data/lib/mongo/error/unsupported_collation.rb +3 -24
  225. data/lib/mongo/error/unsupported_features.rb +0 -2
  226. data/lib/mongo/error/unsupported_message_type.rb +0 -2
  227. data/lib/mongo/error/unsupported_option.rb +19 -21
  228. data/lib/mongo/error/write_retryable.rb +0 -2
  229. data/lib/mongo/error.rb +10 -24
  230. data/lib/mongo/event/base.rb +0 -2
  231. data/lib/mongo/event/listeners.rb +0 -3
  232. data/lib/mongo/event/publisher.rb +0 -3
  233. data/lib/mongo/event/subscriber.rb +0 -4
  234. data/lib/mongo/event.rb +4 -6
  235. data/lib/mongo/grid/file/chunk.rb +7 -10
  236. data/lib/mongo/grid/file/info.rb +20 -24
  237. data/lib/mongo/grid/file.rb +7 -8
  238. data/lib/mongo/grid/fs_bucket.rb +40 -48
  239. data/lib/mongo/grid/stream/read.rb +25 -35
  240. data/lib/mongo/grid/stream/write.rb +17 -22
  241. data/lib/mongo/grid/stream.rb +2 -4
  242. data/lib/mongo/grid.rb +0 -1
  243. data/lib/mongo/id.rb +0 -1
  244. data/lib/mongo/index/view.rb +68 -58
  245. data/lib/mongo/index.rb +7 -10
  246. data/lib/mongo/lint.rb +31 -37
  247. data/lib/mongo/loggable.rb +5 -8
  248. data/lib/mongo/logger.rb +1 -7
  249. data/lib/mongo/monitoring/cmap_log_subscriber.rb +0 -2
  250. data/lib/mongo/monitoring/command_log_subscriber.rb +25 -33
  251. data/lib/mongo/monitoring/event/cmap/base.rb +0 -2
  252. data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +1 -4
  253. data/lib/mongo/monitoring/event/cmap/connection_check_out_started.rb +0 -3
  254. data/lib/mongo/monitoring/event/cmap/connection_checked_in.rb +1 -4
  255. data/lib/mongo/monitoring/event/cmap/connection_checked_out.rb +2 -5
  256. data/lib/mongo/monitoring/event/cmap/connection_closed.rb +1 -4
  257. data/lib/mongo/monitoring/event/cmap/connection_created.rb +1 -4
  258. data/lib/mongo/monitoring/event/cmap/connection_ready.rb +1 -4
  259. data/lib/mongo/monitoring/event/cmap/pool_cleared.rb +0 -3
  260. data/lib/mongo/monitoring/event/cmap/pool_closed.rb +1 -4
  261. data/lib/mongo/monitoring/event/cmap/pool_created.rb +1 -4
  262. data/lib/mongo/monitoring/event/cmap/pool_ready.rb +1 -4
  263. data/lib/mongo/monitoring/event/cmap.rb +0 -1
  264. data/lib/mongo/monitoring/event/command_failed.rb +5 -9
  265. data/lib/mongo/monitoring/event/command_started.rb +8 -12
  266. data/lib/mongo/monitoring/event/command_succeeded.rb +7 -15
  267. data/lib/mongo/monitoring/event/secure.rb +15 -20
  268. data/lib/mongo/monitoring/event/server_closed.rb +1 -4
  269. data/lib/mongo/monitoring/event/server_description_changed.rb +4 -8
  270. data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +5 -10
  271. data/lib/mongo/monitoring/event/server_heartbeat_started.rb +1 -4
  272. data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +3 -8
  273. data/lib/mongo/monitoring/event/server_opening.rb +1 -4
  274. data/lib/mongo/monitoring/event/topology_changed.rb +2 -5
  275. data/lib/mongo/monitoring/event/topology_closed.rb +1 -4
  276. data/lib/mongo/monitoring/event/topology_opening.rb +1 -4
  277. data/lib/mongo/monitoring/event.rb +0 -1
  278. data/lib/mongo/monitoring/publishable.rb +20 -30
  279. data/lib/mongo/monitoring/sdam_log_subscriber.rb +0 -2
  280. data/lib/mongo/monitoring/server_closed_log_subscriber.rb +0 -3
  281. data/lib/mongo/monitoring/server_description_changed_log_subscriber.rb +0 -3
  282. data/lib/mongo/monitoring/server_opening_log_subscriber.rb +0 -3
  283. data/lib/mongo/monitoring/topology_changed_log_subscriber.rb +5 -8
  284. data/lib/mongo/monitoring/topology_closed_log_subscriber.rb +0 -3
  285. data/lib/mongo/monitoring/topology_opening_log_subscriber.rb +0 -3
  286. data/lib/mongo/monitoring/unified_sdam_log_subscriber.rb +1 -3
  287. data/lib/mongo/monitoring.rb +38 -39
  288. data/lib/mongo/operation/aggregate/op_msg.rb +0 -2
  289. data/lib/mongo/operation/aggregate/result.rb +3 -6
  290. data/lib/mongo/operation/aggregate.rb +0 -2
  291. data/lib/mongo/operation/collections_info/result.rb +0 -3
  292. data/lib/mongo/operation/collections_info.rb +0 -2
  293. data/lib/mongo/operation/command/op_msg.rb +1 -4
  294. data/lib/mongo/operation/command.rb +0 -2
  295. data/lib/mongo/operation/context.rb +13 -16
  296. data/lib/mongo/operation/count/op_msg.rb +2 -4
  297. data/lib/mongo/operation/count.rb +0 -2
  298. data/lib/mongo/operation/create/op_msg.rb +2 -5
  299. data/lib/mongo/operation/create.rb +4 -2
  300. data/lib/mongo/operation/create_index/op_msg.rb +3 -7
  301. data/lib/mongo/operation/create_index.rb +0 -2
  302. data/lib/mongo/operation/create_user/op_msg.rb +2 -4
  303. data/lib/mongo/operation/create_user.rb +0 -2
  304. data/lib/mongo/operation/delete/bulk_result.rb +2 -3
  305. data/lib/mongo/operation/delete/op_msg.rb +3 -10
  306. data/lib/mongo/operation/delete/result.rb +0 -3
  307. data/lib/mongo/operation/delete.rb +1 -5
  308. data/lib/mongo/operation/distinct/op_msg.rb +2 -5
  309. data/lib/mongo/operation/distinct.rb +0 -2
  310. data/lib/mongo/operation/drop/op_msg.rb +0 -2
  311. data/lib/mongo/operation/drop.rb +0 -2
  312. data/lib/mongo/operation/drop_database/op_msg.rb +0 -2
  313. data/lib/mongo/operation/drop_database.rb +0 -2
  314. data/lib/mongo/operation/drop_index/op_msg.rb +4 -6
  315. data/lib/mongo/operation/drop_index.rb +0 -2
  316. data/lib/mongo/operation/explain/op_msg.rb +0 -2
  317. data/lib/mongo/operation/explain/result.rb +0 -3
  318. data/lib/mongo/operation/explain.rb +0 -2
  319. data/lib/mongo/operation/find/builder/command.rb +4 -12
  320. data/lib/mongo/operation/find/builder/flags.rb +9 -15
  321. data/lib/mongo/operation/find/builder/modifiers.rb +1 -4
  322. data/lib/mongo/operation/find/builder.rb +0 -1
  323. data/lib/mongo/operation/find/op_msg.rb +4 -12
  324. data/lib/mongo/operation/find/result.rb +0 -3
  325. data/lib/mongo/operation/find.rb +0 -2
  326. data/lib/mongo/operation/get_more/command_builder.rb +1 -6
  327. data/lib/mongo/operation/get_more/op_msg.rb +10 -4
  328. data/lib/mongo/operation/get_more/result.rb +0 -3
  329. data/lib/mongo/operation/get_more.rb +0 -2
  330. data/lib/mongo/operation/indexes/op_msg.rb +0 -2
  331. data/lib/mongo/operation/indexes/result.rb +1 -5
  332. data/lib/mongo/operation/indexes.rb +0 -2
  333. data/lib/mongo/operation/insert/bulk_result.rb +2 -6
  334. data/lib/mongo/operation/insert/op_msg.rb +7 -6
  335. data/lib/mongo/operation/insert/result.rb +0 -3
  336. data/lib/mongo/operation/insert.rb +2 -5
  337. data/lib/mongo/operation/kill_cursors/command_builder.rb +0 -3
  338. data/lib/mongo/operation/kill_cursors/op_msg.rb +1 -3
  339. data/lib/mongo/operation/kill_cursors.rb +0 -2
  340. data/lib/mongo/operation/list_collections/op_msg.rb +4 -6
  341. data/lib/mongo/operation/list_collections/result.rb +1 -4
  342. data/lib/mongo/operation/list_collections.rb +0 -2
  343. data/lib/mongo/operation/map_reduce/op_msg.rb +0 -2
  344. data/lib/mongo/operation/map_reduce/result.rb +3 -6
  345. data/lib/mongo/operation/map_reduce.rb +0 -2
  346. data/lib/mongo/operation/op_msg_base.rb +0 -1
  347. data/lib/mongo/operation/parallel_scan/op_msg.rb +4 -5
  348. data/lib/mongo/operation/parallel_scan/result.rb +2 -5
  349. data/lib/mongo/operation/parallel_scan.rb +0 -2
  350. data/lib/mongo/operation/remove_user/op_msg.rb +2 -4
  351. data/lib/mongo/operation/remove_user.rb +0 -2
  352. data/lib/mongo/operation/result.rb +38 -48
  353. data/lib/mongo/operation/shared/bypass_document_validation.rb +3 -7
  354. data/lib/mongo/operation/shared/causal_consistency_supported.rb +0 -3
  355. data/lib/mongo/operation/shared/executable.rb +29 -31
  356. data/lib/mongo/operation/shared/executable_no_validate.rb +0 -3
  357. data/lib/mongo/operation/shared/executable_transaction_label.rb +0 -2
  358. data/lib/mongo/operation/shared/idable.rb +3 -6
  359. data/lib/mongo/operation/shared/limited.rb +0 -3
  360. data/lib/mongo/operation/shared/object_id_generator.rb +0 -3
  361. data/lib/mongo/operation/shared/op_msg_executable.rb +0 -2
  362. data/lib/mongo/operation/shared/polymorphic_lookup.rb +0 -2
  363. data/lib/mongo/operation/shared/polymorphic_result.rb +2 -4
  364. data/lib/mongo/operation/shared/read_preference_supported.rb +10 -15
  365. data/lib/mongo/operation/shared/response_handling.rb +13 -26
  366. data/lib/mongo/operation/shared/result/aggregatable.rb +12 -13
  367. data/lib/mongo/operation/shared/sessions_supported.rb +87 -99
  368. data/lib/mongo/operation/shared/specifiable.rb +37 -59
  369. data/lib/mongo/operation/shared/write.rb +12 -17
  370. data/lib/mongo/operation/shared/write_concern_supported.rb +4 -7
  371. data/lib/mongo/operation/update/bulk_result.rb +13 -17
  372. data/lib/mongo/operation/update/op_msg.rb +2 -5
  373. data/lib/mongo/operation/update/result.rb +5 -5
  374. data/lib/mongo/operation/update.rb +1 -5
  375. data/lib/mongo/operation/update_user/op_msg.rb +2 -4
  376. data/lib/mongo/operation/update_user.rb +0 -2
  377. data/lib/mongo/operation/users_info/op_msg.rb +2 -4
  378. data/lib/mongo/operation/users_info/result.rb +1 -4
  379. data/lib/mongo/operation/users_info.rb +0 -2
  380. data/lib/mongo/operation/write_command/op_msg.rb +2 -10
  381. data/lib/mongo/operation/write_command.rb +0 -2
  382. data/lib/mongo/operation.rb +9 -14
  383. data/lib/mongo/options/mapper.rb +8 -15
  384. data/lib/mongo/options/redacted.rb +7 -9
  385. data/lib/mongo/options.rb +0 -1
  386. data/lib/mongo/protocol/bit_vector.rb +3 -5
  387. data/lib/mongo/protocol/caching_hash.rb +2 -7
  388. data/lib/mongo/protocol/compressed.rb +5 -10
  389. data/lib/mongo/protocol/get_more.rb +2 -8
  390. data/lib/mongo/protocol/kill_cursors.rb +2 -8
  391. data/lib/mongo/protocol/message.rb +103 -105
  392. data/lib/mongo/protocol/msg.rb +48 -63
  393. data/lib/mongo/protocol/query.rb +32 -41
  394. data/lib/mongo/protocol/registry.rb +2 -5
  395. data/lib/mongo/protocol/reply.rb +10 -16
  396. data/lib/mongo/protocol/serializers.rb +41 -59
  397. data/lib/mongo/protocol.rb +0 -1
  398. data/lib/mongo/query_cache.rb +7 -15
  399. data/lib/mongo/retryable/backpressure.rb +31 -0
  400. data/lib/mongo/retryable/base_worker.rb +39 -13
  401. data/lib/mongo/retryable/read_worker.rb +77 -21
  402. data/lib/mongo/retryable/retry_policy.rb +59 -0
  403. data/lib/mongo/retryable/write_worker.rb +155 -56
  404. data/lib/mongo/retryable.rb +70 -9
  405. data/lib/mongo/search_index/view.rb +30 -10
  406. data/lib/mongo/semaphore.rb +0 -1
  407. data/lib/mongo/server/app_metadata/environment.rb +3 -3
  408. data/lib/mongo/server/app_metadata/platform.rb +17 -4
  409. data/lib/mongo/server/app_metadata.rb +4 -5
  410. data/lib/mongo/server/connection.rb +79 -61
  411. data/lib/mongo/server/connection_base.rb +43 -53
  412. data/lib/mongo/server/connection_common.rb +41 -64
  413. data/lib/mongo/server/connection_pool/generation_manager.rb +6 -11
  414. data/lib/mongo/server/connection_pool/populator.rb +1 -4
  415. data/lib/mongo/server/connection_pool.rb +195 -167
  416. data/lib/mongo/server/description/features.rb +51 -59
  417. data/lib/mongo/server/description/load_balancer.rb +0 -2
  418. data/lib/mongo/server/description.rb +117 -138
  419. data/lib/mongo/server/monitor/app_metadata.rb +3 -4
  420. data/lib/mongo/server/monitor/connection.rb +28 -35
  421. data/lib/mongo/server/monitor.rb +65 -60
  422. data/lib/mongo/server/pending_connection.rb +70 -71
  423. data/lib/mongo/server/push_monitor/connection.rb +0 -3
  424. data/lib/mongo/server/push_monitor.rb +21 -29
  425. data/lib/mongo/server/round_trip_time_calculator.rb +11 -17
  426. data/lib/mongo/server.rb +62 -94
  427. data/lib/mongo/server_selector/base.rb +133 -157
  428. data/lib/mongo/server_selector/nearest.rb +2 -5
  429. data/lib/mongo/server_selector/primary.rb +1 -5
  430. data/lib/mongo/server_selector/primary_preferred.rb +2 -6
  431. data/lib/mongo/server_selector/secondary.rb +2 -6
  432. data/lib/mongo/server_selector/secondary_preferred.rb +1 -5
  433. data/lib/mongo/server_selector.rb +3 -4
  434. data/lib/mongo/session/server_session.rb +6 -7
  435. data/lib/mongo/session/session_pool.rb +20 -34
  436. data/lib/mongo/session.rb +334 -199
  437. data/lib/mongo/socket/ocsp_cache.rb +8 -13
  438. data/lib/mongo/socket/ocsp_verifier.rb +69 -70
  439. data/lib/mongo/socket/ssl.rb +44 -43
  440. data/lib/mongo/socket/tcp.rb +5 -8
  441. data/lib/mongo/socket/unix.rb +0 -4
  442. data/lib/mongo/socket.rb +80 -102
  443. data/lib/mongo/srv/monitor.rb +10 -11
  444. data/lib/mongo/srv/resolver.rb +15 -24
  445. data/lib/mongo/srv/result.rb +25 -21
  446. data/lib/mongo/srv.rb +0 -1
  447. data/lib/mongo/timeout.rb +4 -11
  448. data/lib/mongo/topology_version.rb +8 -13
  449. data/lib/mongo/tracing/open_telemetry/command_tracer.rb +320 -0
  450. data/lib/mongo/tracing/open_telemetry/operation_tracer.rb +227 -0
  451. data/lib/mongo/tracing/open_telemetry/tracer.rb +236 -0
  452. data/lib/mongo/{error/server_api_not_supported.rb → tracing/open_telemetry.rb} +10 -10
  453. data/lib/mongo/tracing.rb +42 -0
  454. data/lib/mongo/uri/options_mapper.rb +135 -126
  455. data/lib/mongo/uri/srv_protocol.rb +34 -42
  456. data/lib/mongo/uri.rb +95 -139
  457. data/lib/mongo/utils.rb +5 -12
  458. data/lib/mongo/version.rb +1 -1
  459. data/lib/mongo/write_concern/acknowledged.rb +0 -2
  460. data/lib/mongo/write_concern/base.rb +6 -6
  461. data/lib/mongo/write_concern/unacknowledged.rb +0 -2
  462. data/lib/mongo/write_concern.rb +14 -15
  463. data/lib/mongo.rb +4 -3
  464. data/mongo.gemspec +17 -17
  465. metadata +11 -5
  466. data/lib/mongo/operation/shared/result/use_legacy_error_parser.rb +0 -32
  467. data/lib/mongo/operation/shared/validatable.rb +0 -87
data/lib/mongo/session.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- # rubocop:todo all
3
2
 
4
3
  # Copyright (C) 2017-2020 MongoDB Inc.
5
4
  #
@@ -19,7 +18,6 @@ require 'mongo/session/session_pool'
19
18
  require 'mongo/session/server_session'
20
19
 
21
20
  module Mongo
22
-
23
21
  # A logical session representing a set of sequential operations executed
24
22
  # by an application that are related in some way.
25
23
  #
@@ -36,10 +34,9 @@ module Mongo
36
34
  # Initialize a Session.
37
35
  #
38
36
  # A session can be explicit or implicit. Lifetime of explicit sessions is
39
- # managed by the application - applications explicitry create such sessions
37
+ # managed by the application - applications explicitly create such sessions
40
38
  # and explicitly end them. Implicit sessions are created automatically by
41
- # the driver when sending operations to servers that support sessions
42
- # (3.6+), and their lifetime is managed by the driver.
39
+ # the driver, and their lifetime is managed by the driver.
43
40
  #
44
41
  # When an implicit session is created, it cannot have a server session
45
42
  # associated with it. The server session will be checked out of the
@@ -89,20 +86,24 @@ module Mongo
89
86
  unless server_session.nil?
90
87
  raise ArgumentError, 'Implicit session cannot reference server session during construction'
91
88
  end
92
- else
93
- if server_session.nil?
94
- raise ArgumentError, 'Explicit session must reference server session during construction'
95
- end
89
+ elsif server_session.nil?
90
+ raise ArgumentError, 'Explicit session must reference server session during construction'
96
91
  end
97
92
 
98
93
  @server_session = server_session
99
94
  options = options.dup
100
95
 
101
- @client = client.use(:admin)
96
+ # Implicit sessions only need the cluster and client options (never run
97
+ # transactions), so avoid creating a Mongo::Client clone to prevent
98
+ # memory leaks: use the original client directly instead.
99
+ @client = options[:implicit] ? client : client.use(:admin)
100
+ @cluster = @client.cluster
102
101
  @options = options.dup.freeze
103
102
  @cluster_time = nil
104
103
  @state = NO_TRANSACTION_STATE
105
104
  @with_transaction_deadline = nil
105
+ @with_transaction_timeout_ms = nil
106
+ @inside_with_transaction = false
106
107
  end
107
108
 
108
109
  # @return [ Hash ] The options for this session.
@@ -115,9 +116,7 @@ module Mongo
115
116
  # @since 2.5.1
116
117
  attr_reader :client
117
118
 
118
- def cluster
119
- @client.cluster
120
- end
119
+ attr_reader :cluster
121
120
 
122
121
  # @return [ true | false ] Whether the session is configured for snapshot
123
122
  # reads.
@@ -130,6 +129,8 @@ module Mongo
130
129
  # @since 2.5.0
131
130
  attr_reader :operation_time
132
131
 
132
+ def_delegators :client, :tracer
133
+
133
134
  # Sets the dirty state to the given value for the underlying server
134
135
  # session. If there is no server session, this does nothing.
135
136
  #
@@ -152,7 +153,7 @@ module Mongo
152
153
  #
153
154
  # @since 2.6.0
154
155
  def txn_options
155
- @txn_options or raise ArgumentError, "There is no active transaction"
156
+ @txn_options or raise ArgumentError, 'There is no active transaction'
156
157
  end
157
158
 
158
159
  # Is this session an implicit one (not user-created).
@@ -206,8 +207,8 @@ module Mongo
206
207
  #
207
208
  # @return [ true, false ] If writes will be retried.
208
209
  #
209
- # @note Retryable writes are only available on server versions at least 3.6
210
- # and with sharded clusters, replica sets, or load-balanced topologies.
210
+ # @note Retryable writes are only available with sharded clusters, replica
211
+ # sets, or load-balanced topologies.
211
212
  #
212
213
  # @since 2.5.0
213
214
  def retry_writes?
@@ -227,7 +228,7 @@ module Mongo
227
228
  # @since 2.6.0
228
229
  def txn_read_preference
229
230
  rp = txn_options[:read] ||
230
- @client.read_preference
231
+ @client.read_preference
231
232
  Mongo::Lint.validate_underscore_read_preference(rp)
232
233
  rp
233
234
  end
@@ -253,9 +254,7 @@ module Mongo
253
254
  #
254
255
  # @since 2.5.0
255
256
  def session_id
256
- if ended?
257
- raise Error::SessionEnded
258
- end
257
+ raise Error::SessionEnded if ended?
259
258
 
260
259
  # An explicit session will always have a session_id, because during
261
260
  # construction a server session must be provided. An implicit session
@@ -264,9 +263,7 @@ module Mongo
264
263
  # to experience this failure because an implicit session shouldn't be
265
264
  # accessible to applications due to its lifetime being constrained to
266
265
  # operation execution, which is done entirely by the driver.
267
- unless materialized?
268
- raise Error::SessionNotMaterialized
269
- end
266
+ raise Error::SessionNotMaterialized unless materialized?
270
267
 
271
268
  @server_session.session_id
272
269
  end
@@ -294,20 +291,20 @@ module Mongo
294
291
  #
295
292
  # @since 2.5.0
296
293
  MISMATCHED_CLUSTER_ERROR_MSG = 'The configuration of the client used to create this session does not match that ' +
297
- 'of the client owning this operation. Please only use this session for operations through its parent ' +
298
- 'client.'.freeze
294
+ 'of the client owning this operation. Please only use this session for operations through its parent ' +
295
+ 'client.'
299
296
 
300
297
  # Error message describing that the session cannot be used because it has already been ended.
301
298
  #
302
299
  # @since 2.5.0
303
- SESSION_ENDED_ERROR_MSG = 'This session has ended and cannot be used. Please create a new one.'.freeze
300
+ SESSION_ENDED_ERROR_MSG = 'This session has ended and cannot be used. Please create a new one.'
304
301
 
305
302
  # Error message describing that sessions are not supported by the server version.
306
303
  #
307
304
  # @since 2.5.0
308
305
  # @deprecated
309
- SESSIONS_NOT_SUPPORTED = 'Sessions are not supported by the connected servers.'.freeze
310
- # Note: SESSIONS_NOT_SUPPORTED is used by Mongoid - do not remove from driver.
306
+ SESSIONS_NOT_SUPPORTED = 'Sessions are not supported by the connected servers.'
307
+ # NOTE: SESSIONS_NOT_SUPPORTED is used by Mongoid 7.6 and earlier
311
308
 
312
309
  # The state of a session in which the last operation was not related to
313
310
  # any transaction or no operations have yet occurred.
@@ -382,13 +379,15 @@ module Mongo
382
379
  rescue Mongo::Error, Error::AuthError
383
380
  end
384
381
  end
385
- if @server_session
386
- @client.cluster.session_pool.checkin(@server_session)
387
- end
382
+ # Release any pinned connection (e.g. after a committed transaction
383
+ # in load-balanced mode).
384
+ unpin if pinned_connection_global_id
385
+ cluster.session_pool.checkin(@server_session) if @server_session
388
386
  end
389
387
  ensure
390
388
  @server_session = nil
391
389
  @ended = true
390
+ @client = nil
392
391
  end
393
392
 
394
393
  # Executes the provided block in a transaction, retrying as necessary.
@@ -446,44 +445,74 @@ module Mongo
446
445
  #
447
446
  # @since 2.7.0
448
447
  def with_transaction(options = nil)
449
- if timeout_ms = (options || {})[:timeout_ms]
450
- timeout_sec = timeout_ms / 1_000.0
451
- deadline = Utils.monotonic_time + timeout_sec
452
- @with_transaction_deadline = deadline
453
- elsif default_timeout_ms = @options[:default_timeout_ms]
454
- timeout_sec = default_timeout_ms / 1_000.0
455
- deadline = Utils.monotonic_time + timeout_sec
456
- @with_transaction_deadline = deadline
457
- elsif @client.timeout_sec
458
- deadline = Utils.monotonic_time + @client.timeout_sec
459
- @with_transaction_deadline = deadline
460
- else
461
- deadline = Utils.monotonic_time + 120
462
- end
448
+ @inside_with_transaction = true
449
+ @with_transaction_timeout_ms = options&.dig(:timeout_ms) || @options[:default_timeout_ms] || @client.timeout_ms
450
+ @with_transaction_deadline = calculate_with_transaction_deadline(options)
451
+ deadline = if @with_transaction_deadline
452
+ # CSOT enabled, so we have a customer defined deadline.
453
+ @with_transaction_deadline
454
+ else
455
+ # CSOT not enabled, so we use the default deadline, 120 seconds.
456
+ Utils.monotonic_time + 120
457
+ end
463
458
  transaction_in_progress = false
459
+ transaction_attempt = 0
460
+ last_error = nil
461
+ overload_error_count = 0
462
+ overload_encountered = false
463
+
464
464
  loop do
465
- commit_options = {}
466
- if options
467
- commit_options[:write_concern] = options[:write_concern]
465
+ if transaction_attempt > 0
466
+ if overload_encountered
467
+ delay = @client.retry_policy.backoff_delay(overload_error_count)
468
+ if backoff_would_exceed_deadline?(deadline, delay)
469
+ make_timeout_error_from(last_error, 'CSOT timeout expired waiting to retry withTransaction')
470
+ end
471
+ raise(last_error) unless @client.retry_policy.should_retry_overload?(overload_error_count, delay)
472
+
473
+ sleep(delay)
474
+ else
475
+ backoff = backoff_seconds_for_retry(transaction_attempt)
476
+ if backoff_would_exceed_deadline?(deadline, backoff)
477
+ make_timeout_error_from(last_error, 'CSOT timeout expired waiting to retry withTransaction')
478
+ end
479
+
480
+ sleep(backoff)
481
+ end
468
482
  end
483
+
484
+ commit_options = {}
485
+ commit_options[:write_concern] = options[:write_concern] if options
469
486
  start_transaction(options)
470
487
  transaction_in_progress = true
488
+ transaction_attempt += 1
489
+
471
490
  begin
472
491
  rv = yield self
473
492
  rescue Exception => e
474
493
  if within_states?(STARTING_TRANSACTION_STATE, TRANSACTION_IN_PROGRESS_STATE)
475
494
  log_warn("Aborting transaction due to #{e.class}: #{e}")
476
- @with_transaction_deadline = nil
495
+ # CSOT: if the deadline is already expired, clear it so that
496
+ # abort_transaction uses a fresh timeout (not the expired deadline).
497
+ # If the deadline is not yet expired, keep it so abort uses remaining time.
498
+ @with_transaction_deadline = nil if @with_transaction_deadline && deadline_expired?(deadline)
477
499
  abort_transaction
478
500
  transaction_in_progress = false
479
501
  end
480
502
 
481
- if Utils.monotonic_time >= deadline
503
+ if deadline_expired?(deadline)
482
504
  transaction_in_progress = false
483
- raise
505
+ make_timeout_error_from(e, 'CSOT timeout expired during withTransaction callback')
484
506
  end
485
507
 
486
508
  if e.is_a?(Mongo::Error) && e.label?('TransientTransactionError')
509
+ last_error = e
510
+ if e.label?('SystemOverloadedError')
511
+ overload_encountered = true
512
+ overload_error_count += 1
513
+ elsif overload_encountered
514
+ overload_error_count += 1
515
+ end
487
516
  next
488
517
  end
489
518
 
@@ -494,32 +523,71 @@ module Mongo
494
523
  return rv
495
524
  end
496
525
 
526
+ # CSOT: if the timeout has expired before we can commit, abort the
527
+ # transaction instead and raise a client-side timeout error.
528
+ if @with_transaction_deadline && deadline_expired?(deadline)
529
+ transaction_in_progress = false
530
+ @with_transaction_deadline = nil
531
+ abort_transaction
532
+ raise Mongo::Error::TimeoutError, 'CSOT timeout expired before transaction could be committed'
533
+ end
534
+
497
535
  begin
498
536
  commit_transaction(commit_options)
499
537
  transaction_in_progress = false
500
538
  return rv
501
539
  rescue Mongo::Error => e
502
540
  if e.label?('UnknownTransactionCommitResult')
503
- if Utils.monotonic_time >= deadline ||
504
- e.is_a?(Error::OperationFailure::Family) && e.max_time_ms_expired?
505
- then
541
+ if deadline_expired?(deadline) ||
542
+ (e.is_a?(Error::OperationFailure::Family) && e.max_time_ms_expired?)
506
543
  transaction_in_progress = false
507
- raise
544
+
545
+ raise unless @with_transaction_timeout_ms && deadline_expired?(deadline)
546
+
547
+ make_timeout_error_from(e, 'CSOT timeout expired during withTransaction commit')
508
548
  end
509
- wc_options = case v = commit_options[:write_concern]
510
- when WriteConcern::Base
511
- v.options
512
- when nil
513
- {}
514
- else
515
- v
549
+
550
+ if e.label?('SystemOverloadedError')
551
+ overload_encountered = true
552
+ overload_error_count += 1
553
+ elsif overload_encountered
554
+ overload_error_count += 1
555
+ end
556
+
557
+ if overload_encountered
558
+ delay = @client.retry_policy.backoff_delay(overload_error_count)
559
+ if backoff_would_exceed_deadline?(deadline, delay)
560
+ transaction_in_progress = false
561
+ make_timeout_error_from(e, 'CSOT timeout expired during withTransaction commit')
562
+ end
563
+ unless @client.retry_policy.should_retry_overload?(overload_error_count, delay)
564
+ transaction_in_progress = false
565
+ raise
516
566
  end
567
+ sleep(delay)
568
+ end
569
+
570
+ wc_options = case v = commit_options[:write_concern]
571
+ when WriteConcern::Base
572
+ v.options
573
+ when nil
574
+ {}
575
+ else
576
+ v
577
+ end
517
578
  commit_options[:write_concern] = wc_options.merge(w: :majority)
518
579
  retry
519
580
  elsif e.label?('TransientTransactionError')
520
581
  if Utils.monotonic_time >= deadline
521
582
  transaction_in_progress = false
522
- raise
583
+ make_timeout_error_from(e, 'CSOT timeout expired during withTransaction commit')
584
+ end
585
+ last_error = e
586
+ if e.label?('SystemOverloadedError')
587
+ overload_encountered = true
588
+ overload_error_count += 1
589
+ elsif overload_encountered
590
+ overload_error_count += 1
523
591
  end
524
592
  @state = NO_TRANSACTION_STATE
525
593
  next
@@ -546,6 +614,8 @@ module Mongo
546
614
  end
547
615
  end
548
616
  @with_transaction_deadline = nil
617
+ @with_transaction_timeout_ms = nil
618
+ @inside_with_transaction = false
549
619
  end
550
620
 
551
621
  # Places subsequent operations in this session into a new transaction.
@@ -560,7 +630,7 @@ module Mongo
560
630
  #
561
631
  # @option options [ Integer ] :max_commit_time_ms The maximum amount of
562
632
  # time to allow a single commitTransaction command to run, in milliseconds.
563
- # This options is deprecated, use :timeout_ms instead.
633
+ # This option is deprecated, use :timeout_ms instead.
564
634
  # @option options [ Hash ] :read_concern The read concern options hash,
565
635
  # with the following optional keys:
566
636
  # - *:level* -- the read preference level as a symbol; valid values
@@ -586,28 +656,25 @@ module Mongo
586
656
  if options
587
657
  Lint.validate_read_concern_option(options[:read_concern])
588
658
 
589
- =begin
590
- # It would be handy to detect invalid read preferences here, but
591
- # some of the spec tests require later detection of invalid read prefs.
592
- # Maybe we can do this when lint mode is on.
593
- mode = options[:read] && options[:read][:mode].to_s
594
- if mode && mode != 'primary'
595
- raise Mongo::Error::InvalidTransactionOperation.new(
596
- "read preference in a transaction must be primary (requested: #{mode})"
597
- )
598
- end
599
- =end
659
+ # # It would be handy to detect invalid read preferences here, but
660
+ # # some of the spec tests require later detection of invalid read prefs.
661
+ # # Maybe we can do this when lint mode is on.
662
+ # mode = options[:read] && options[:read][:mode].to_s
663
+ # if mode && mode != 'primary'
664
+ # raise Mongo::Error::InvalidTransactionOperation.new(
665
+ # "read preference in a transaction must be primary (requested: #{mode})"
666
+ # )
667
+ # end
600
668
  end
601
669
 
602
- if snapshot?
603
- raise Mongo::Error::SnapshotSessionTransactionProhibited
604
- end
670
+ raise Mongo::Error::SnapshotSessionTransactionProhibited if snapshot?
605
671
 
606
672
  check_if_ended!
607
673
 
608
674
  if within_states?(STARTING_TRANSACTION_STATE, TRANSACTION_IN_PROGRESS_STATE)
609
675
  raise Mongo::Error::InvalidTransactionOperation.new(
610
- Mongo::Error::InvalidTransactionOperation::TRANSACTION_ALREADY_IN_PROGRESS)
676
+ Mongo::Error::InvalidTransactionOperation::TRANSACTION_ALREADY_IN_PROGRESS
677
+ )
611
678
  end
612
679
 
613
680
  unpin
@@ -617,11 +684,13 @@ module Mongo
617
684
 
618
685
  if txn_write_concern && !WriteConcern.get(txn_write_concern).acknowledged?
619
686
  raise Mongo::Error::InvalidTransactionOperation.new(
620
- Mongo::Error::InvalidTransactionOperation::UNACKNOWLEDGED_WRITE_CONCERN)
687
+ Mongo::Error::InvalidTransactionOperation::UNACKNOWLEDGED_WRITE_CONCERN
688
+ )
621
689
  end
622
690
 
623
691
  @state = STARTING_TRANSACTION_STATE
624
692
  @already_committed = false
693
+ tracer.start_transaction_span(self)
625
694
 
626
695
  # This method has no explicit return value.
627
696
  # We could return nil here but true indicates to the user that the
@@ -645,7 +714,7 @@ module Mongo
645
714
  # @raise [ Error::InvalidTransactionOperation ] If there is no active transaction.
646
715
  #
647
716
  # @since 2.6.0
648
- def commit_transaction(options=nil)
717
+ def commit_transaction(options = nil)
649
718
  QueryCache.clear
650
719
  check_if_ended!
651
720
  check_if_no_transaction!
@@ -653,7 +722,9 @@ module Mongo
653
722
  if within_states?(TRANSACTION_ABORTED_STATE)
654
723
  raise Mongo::Error::InvalidTransactionOperation.new(
655
724
  Mongo::Error::InvalidTransactionOperation.cannot_call_after_msg(
656
- :abortTransaction, :commitTransaction))
725
+ :abortTransaction, :commitTransaction
726
+ )
727
+ )
657
728
  end
658
729
 
659
730
  options ||= {}
@@ -673,9 +744,7 @@ module Mongo
673
744
  @committing_transaction = true
674
745
 
675
746
  write_concern = options[:write_concern] || txn_options[:write_concern]
676
- if write_concern && !write_concern.is_a?(WriteConcern::Base)
677
- write_concern = WriteConcern.get(write_concern)
678
- end
747
+ write_concern = WriteConcern.get(write_concern) if write_concern && !write_concern.is_a?(WriteConcern::Base)
679
748
 
680
749
  context = Operation::Context.new(
681
750
  client: @client,
@@ -683,15 +752,14 @@ module Mongo
683
752
  operation_timeouts: operation_timeouts(options)
684
753
  )
685
754
  write_with_retry(write_concern, ending_transaction: true,
686
- context: context,
687
- ) do |connection, txn_num, context|
688
- if context.retry?
755
+ context: context) do |connection, txn_num, context|
756
+ if context.retry? && !context.overload_only_retry?
689
757
  if write_concern
690
758
  wco = write_concern.options.merge(w: :majority)
691
- wco[:wtimeout] ||= 10000
759
+ wco[:wtimeout] ||= 10_000
692
760
  write_concern = WriteConcern.get(wco)
693
761
  else
694
- write_concern = WriteConcern.get(w: :majority, wtimeout: 10000)
762
+ write_concern = WriteConcern.get(w: :majority, wtimeout: 10_000)
695
763
  end
696
764
  end
697
765
  spec = {
@@ -701,9 +769,14 @@ module Mongo
701
769
  txn_num: txn_num,
702
770
  write_concern: write_concern,
703
771
  }
704
- Operation::Command.new(spec).execute_with_connection(connection, context: context)
772
+ operation = Operation::Command.new(spec)
773
+ tracer.trace_operation(operation, context, op_name: 'commitTransaction') do
774
+ operation.execute_with_connection(connection, context: context)
775
+ end
705
776
  end
706
777
  end
778
+ # Finish the transaction span before changing state
779
+ tracer.finish_transaction_span(self)
707
780
  ensure
708
781
  @state = TRANSACTION_COMMITTED_STATE
709
782
  @committing_transaction = false
@@ -736,12 +809,15 @@ module Mongo
736
809
  if within_states?(TRANSACTION_COMMITTED_STATE)
737
810
  raise Mongo::Error::InvalidTransactionOperation.new(
738
811
  Mongo::Error::InvalidTransactionOperation.cannot_call_after_msg(
739
- :commitTransaction, :abortTransaction))
812
+ :commitTransaction, :abortTransaction
813
+ )
814
+ )
740
815
  end
741
816
 
742
817
  if within_states?(TRANSACTION_ABORTED_STATE)
743
818
  raise Mongo::Error::InvalidTransactionOperation.new(
744
- Mongo::Error::InvalidTransactionOperation.cannot_call_twice_msg(:abortTransaction))
819
+ Mongo::Error::InvalidTransactionOperation.cannot_call_twice_msg(:abortTransaction)
820
+ )
745
821
  end
746
822
 
747
823
  options ||= {}
@@ -755,27 +831,31 @@ module Mongo
755
831
  operation_timeouts: operation_timeouts(options)
756
832
  )
757
833
  write_with_retry(txn_options[:write_concern],
758
- ending_transaction: true, context: context,
759
- ) do |connection, txn_num, context|
760
- begin
761
- Operation::Command.new(
762
- selector: { abortTransaction: 1 },
763
- db_name: 'admin',
764
- session: self,
765
- txn_num: txn_num
766
- ).execute_with_connection(connection, context: context)
767
- ensure
768
- unpin
834
+ ending_transaction: true, context: context) do |connection, txn_num, context|
835
+ operation = Operation::Command.new(
836
+ selector: { abortTransaction: 1 },
837
+ db_name: 'admin',
838
+ session: self,
839
+ txn_num: txn_num
840
+ )
841
+ tracer.trace_operation(operation, context, op_name: 'abortTransaction') do
842
+ operation.execute_with_connection(connection, context: context)
769
843
  end
844
+ ensure
845
+ unpin
770
846
  end
771
847
  end
772
848
 
849
+ # Finish the transaction span before changing state
850
+ tracer.finish_transaction_span(self)
773
851
  @state = TRANSACTION_ABORTED_STATE
774
852
  rescue Mongo::Error::InvalidTransactionOperation
775
853
  raise
776
854
  rescue Mongo::Error
855
+ tracer.finish_transaction_span(self)
777
856
  @state = TRANSACTION_ABORTED_STATE
778
857
  rescue Exception
858
+ tracer.finish_transaction_span(self)
779
859
  @state = TRANSACTION_ABORTED_STATE
780
860
  raise
781
861
  ensure
@@ -826,14 +906,12 @@ module Mongo
826
906
  #
827
907
  # @api private
828
908
  def pin_to_server(server)
829
- if server.nil?
830
- raise ArgumentError, 'Cannot pin to a nil server'
831
- end
832
- if Lint.enabled?
833
- unless server.mongos?
834
- raise Error::LintError, "Attempted to pin the session to server #{server.summary} which is not a mongos"
835
- end
909
+ raise ArgumentError, 'Cannot pin to a nil server' if server.nil?
910
+
911
+ if Lint.enabled? && !server.mongos?
912
+ raise Error::LintError, "Attempted to pin the session to server #{server.summary} which is not a mongos"
836
913
  end
914
+
837
915
  @pinned_server = server
838
916
  end
839
917
 
@@ -841,13 +919,14 @@ module Mongo
841
919
  #
842
920
  # @param [ Integer ] connection_global_id The global id of connection to pin
843
921
  # this session to.
922
+ # @param [ Connection | nil ] connection The connection object to pin to.
844
923
  #
845
924
  # @api private
846
- def pin_to_connection(connection_global_id)
847
- if connection_global_id.nil?
848
- raise ArgumentError, 'Cannot pin to a nil connection id'
849
- end
925
+ def pin_to_connection(connection_global_id, connection: nil)
926
+ raise ArgumentError, 'Cannot pin to a nil connection id' if connection_global_id.nil?
927
+
850
928
  @pinned_connection_global_id = connection_global_id
929
+ @pinned_connection = connection
851
930
  end
852
931
 
853
932
  # Unpins this session from the pinned server or connection,
@@ -859,7 +938,16 @@ module Mongo
859
938
  def unpin(connection = nil)
860
939
  @pinned_server = nil
861
940
  @pinned_connection_global_id = nil
862
- connection.unpin unless connection.nil?
941
+ conn = connection || @pinned_connection
942
+ if conn
943
+ conn.unpin(:transaction)
944
+ # Only check the connection back into the pool if nothing else
945
+ # still holds a pin on it (e.g. an open cursor).
946
+ unless conn.pinned?
947
+ conn.connection_pool.check_in(conn)
948
+ end
949
+ end
950
+ @pinned_connection = nil
863
951
  end
864
952
 
865
953
  # Unpins this session from the pinned server or connection, if the session was pinned
@@ -875,14 +963,12 @@ module Mongo
875
963
  # @api private
876
964
  def unpin_maybe(error, connection = nil)
877
965
  if !within_states?(Session::NO_TRANSACTION_STATE) &&
878
- error.label?('TransientTransactionError')
879
- then
966
+ error.label?('TransientTransactionError')
880
967
  unpin(connection)
881
968
  end
882
969
 
883
970
  if committing_transaction? &&
884
- error.label?('UnknownTransactionCommitResult')
885
- then
971
+ error.label?('UnknownTransactionCommitResult')
886
972
  unpin(connection)
887
973
  end
888
974
  end
@@ -913,9 +999,7 @@ module Mongo
913
999
  # @api private
914
1000
  def add_start_transaction!(command)
915
1001
  command.tap do |c|
916
- if starting_transaction?
917
- c[:startTransaction] = true
918
- end
1002
+ c[:startTransaction] = true if starting_transaction?
919
1003
  end
920
1004
  end
921
1005
 
@@ -943,7 +1027,7 @@ module Mongo
943
1027
  #
944
1028
  # @since 2.6.0
945
1029
  # @api private
946
- def add_txn_opts!(command, read, context)
1030
+ def add_txn_opts!(command, _read, context)
947
1031
  command.tap do |c|
948
1032
  # The read concern should be added to any command that starts a transaction.
949
1033
  if starting_transaction?
@@ -966,27 +1050,23 @@ module Mongo
966
1050
  if rc.nil? || rc.empty?
967
1051
  c.delete(:readConcern)
968
1052
  else
969
- c[:readConcern ] = Options::Mapper.transform_values_to_strings(rc)
1053
+ c[:readConcern] = Options::Mapper.transform_values_to_strings(rc)
970
1054
  end
971
1055
  end
972
1056
 
973
1057
  # We need to send the read concern level as a string rather than a symbol.
974
- if c[:readConcern]
975
- c[:readConcern] = Options::Mapper.transform_values_to_strings(c[:readConcern])
976
- end
1058
+ c[:readConcern] = Options::Mapper.transform_values_to_strings(c[:readConcern]) if c[:readConcern]
977
1059
 
978
- if c[:commitTransaction]
979
- if max_time_ms = txn_options[:max_commit_time_ms]
980
- c[:maxTimeMS] = max_time_ms
981
- end
1060
+ if c[:commitTransaction] && (max_time_ms = txn_options[:max_commit_time_ms])
1061
+ c[:maxTimeMS] = max_time_ms
982
1062
  end
983
1063
 
984
1064
  # The write concern should be added to any abortTransaction or commitTransaction command.
985
- if (c[:abortTransaction] || c[:commitTransaction])
1065
+ if c[:abortTransaction] || c[:commitTransaction]
986
1066
  if @already_committed
987
1067
  wc = BSON::Document.new(c[:writeConcern] || txn_write_concern || {})
988
1068
  wc.merge!(w: :majority)
989
- wc[:wtimeout] ||= 10000
1069
+ wc[:wtimeout] ||= 10_000
990
1070
  c[:writeConcern] = wc
991
1071
  elsif txn_write_concern
992
1072
  c[:writeConcern] ||= txn_write_concern
@@ -999,12 +1079,10 @@ module Mongo
999
1079
  end
1000
1080
 
1001
1081
  # Ignore wtimeout if csot
1002
- if context&.csot?
1003
- c[:writeConcern]&.delete(:wtimeout)
1004
- end
1082
+ c[:writeConcern]&.delete(:wtimeout) if context&.csot?
1005
1083
 
1006
1084
  # We must not send an empty (server default) write concern.
1007
- c.delete(:writeConcern) if c[:writeConcern]&.empty?
1085
+ c.delete(:writeConcern) if c[:writeConcern] && c[:writeConcern].empty?
1008
1086
  end
1009
1087
  end
1010
1088
 
@@ -1026,7 +1104,7 @@ module Mongo
1026
1104
  end
1027
1105
  end
1028
1106
 
1029
- # Ensure that the read preference of a command primary.
1107
+ # Ensure that the read preference of a command is primary.
1030
1108
  #
1031
1109
  # @example
1032
1110
  # session.validate_read_preference!(command)
@@ -1042,11 +1120,21 @@ module Mongo
1042
1120
 
1043
1121
  mode = command['$readPreference']['mode'] || command['$readPreference'][:mode]
1044
1122
 
1045
- if mode && mode != 'primary'
1046
- raise Mongo::Error::InvalidTransactionOperation.new(
1047
- "read preference in a transaction must be primary (requested: #{mode})"
1048
- )
1049
- end
1123
+ return unless mode && mode != 'primary'
1124
+
1125
+ raise Mongo::Error::InvalidTransactionOperation.new(
1126
+ "read preference in a transaction must be primary (requested: #{mode})"
1127
+ )
1128
+ end
1129
+
1130
+ # Reverts the session state to STARTING_TRANSACTION_STATE.
1131
+ # Called before retrying the first command in a transaction so that
1132
+ # startTransaction: true is preserved on the retry.
1133
+ # @api private
1134
+ def revert_to_starting_transaction!
1135
+ return unless within_states?(TRANSACTION_IN_PROGRESS_STATE)
1136
+
1137
+ @state = STARTING_TRANSACTION_STATE
1050
1138
  end
1051
1139
 
1052
1140
  # Update the state of the session due to a (non-commit and non-abort) operation being run.
@@ -1076,8 +1164,8 @@ module Mongo
1076
1164
  # @since 2.5.0
1077
1165
  # @api private
1078
1166
  def validate!(client)
1079
- check_matching_cluster!(client)
1080
1167
  check_if_ended!
1168
+ check_matching_cluster!(client)
1081
1169
  self
1082
1170
  end
1083
1171
 
@@ -1101,10 +1189,8 @@ module Mongo
1101
1189
  end
1102
1190
  @server_session.set_last_use!
1103
1191
 
1104
- if doc = result.reply && result.reply.documents.first
1105
- if doc[:recoveryToken]
1106
- self.recovery_token = doc[:recoveryToken]
1107
- end
1192
+ if (doc = result.reply && result.reply.documents.first) && doc[:recoveryToken]
1193
+ self.recovery_token = doc[:recoveryToken]
1108
1194
  end
1109
1195
 
1110
1196
  result
@@ -1121,11 +1207,11 @@ module Mongo
1121
1207
  #
1122
1208
  # @since 2.5.0
1123
1209
  def advance_operation_time(new_operation_time)
1124
- if @operation_time
1125
- @operation_time = [ @operation_time, new_operation_time ].max
1126
- else
1127
- @operation_time = new_operation_time
1128
- end
1210
+ @operation_time = if @operation_time
1211
+ [ @operation_time, new_operation_time ].max
1212
+ else
1213
+ new_operation_time
1214
+ end
1129
1215
  end
1130
1216
 
1131
1217
  # If not already set, populate a session objects's server_session by
@@ -1135,9 +1221,7 @@ module Mongo
1135
1221
  #
1136
1222
  # @api private
1137
1223
  def materialize_if_needed
1138
- if ended?
1139
- raise Error::SessionEnded
1140
- end
1224
+ raise Error::SessionEnded if ended?
1141
1225
 
1142
1226
  return unless implicit? && !@server_session
1143
1227
 
@@ -1148,9 +1232,7 @@ module Mongo
1148
1232
 
1149
1233
  # @api private
1150
1234
  def materialized?
1151
- if ended?
1152
- raise Error::SessionEnded
1153
- end
1235
+ raise Error::SessionEnded if ended?
1154
1236
 
1155
1237
  !@server_session.nil?
1156
1238
  end
@@ -1165,9 +1247,7 @@ module Mongo
1165
1247
  # @since 2.5.0
1166
1248
  # @api private
1167
1249
  def next_txn_num
1168
- if ended?
1169
- raise Error::SessionEnded
1170
- end
1250
+ raise Error::SessionEnded if ended?
1171
1251
 
1172
1252
  @server_session.next_txn_num
1173
1253
  end
@@ -1181,9 +1261,7 @@ module Mongo
1181
1261
  #
1182
1262
  # @since 2.6.0
1183
1263
  def txn_num
1184
- if ended?
1185
- raise Error::SessionEnded
1186
- end
1264
+ raise Error::SessionEnded if ended?
1187
1265
 
1188
1266
  @server_session.txn_num
1189
1267
  end
@@ -1191,8 +1269,16 @@ module Mongo
1191
1269
  # @api private
1192
1270
  attr_accessor :snapshot_timestamp
1193
1271
 
1272
+ # @return [ Integer | nil ] The deadline for the current transaction, if any.
1273
+ # @api private
1194
1274
  attr_reader :with_transaction_deadline
1195
1275
 
1276
+ # @return [ Boolean ] Whether we are currently inside a with_transaction block.
1277
+ # @api private
1278
+ def inside_with_transaction?
1279
+ @inside_with_transaction
1280
+ end
1281
+
1196
1282
  private
1197
1283
 
1198
1284
  # Get the read concern the session will use when starting a transaction.
@@ -1218,7 +1304,8 @@ module Mongo
1218
1304
  return unless within_states?(NO_TRANSACTION_STATE)
1219
1305
 
1220
1306
  raise Mongo::Error::InvalidTransactionOperation.new(
1221
- Mongo::Error::InvalidTransactionOperation::NO_TRANSACTION_STARTED)
1307
+ Mongo::Error::InvalidTransactionOperation::NO_TRANSACTION_STARTED
1308
+ )
1222
1309
  end
1223
1310
 
1224
1311
  def txn_write_concern
@@ -1229,25 +1316,23 @@ module Mongo
1229
1316
  # Returns causal consistency document if the last operation time is
1230
1317
  # known and causal consistency is enabled, otherwise returns nil.
1231
1318
  def causal_consistency_doc
1232
- if operation_time && causal_consistency?
1233
- {:afterClusterTime => operation_time}
1234
- else
1235
- nil
1236
- end
1319
+ return unless operation_time && causal_consistency?
1320
+
1321
+ { afterClusterTime: operation_time }
1237
1322
  end
1238
1323
 
1239
1324
  def causal_consistency?
1240
- @causal_consistency ||= (if @options.key?(:causal_consistency)
1241
- !!@options[:causal_consistency]
1242
- else
1243
- true
1244
- end)
1325
+ @causal_consistency ||= if @options.key?(:causal_consistency)
1326
+ !!@options[:causal_consistency]
1327
+ else
1328
+ true
1329
+ end
1245
1330
  end
1246
1331
 
1247
1332
  def set_operation_time(result)
1248
- if result && result.operation_time
1249
- @operation_time = result.operation_time
1250
- end
1333
+ return unless result && result.operation_time
1334
+
1335
+ @operation_time = result.operation_time
1251
1336
  end
1252
1337
 
1253
1338
  def check_if_ended!
@@ -1255,36 +1340,86 @@ module Mongo
1255
1340
  end
1256
1341
 
1257
1342
  def check_matching_cluster!(client)
1258
- if @client.cluster != client.cluster
1259
- raise Mongo::Error::InvalidSession.new(MISMATCHED_CLUSTER_ERROR_MSG)
1260
- end
1343
+ return unless cluster != client.cluster
1344
+
1345
+ raise Mongo::Error::InvalidSession.new(MISMATCHED_CLUSTER_ERROR_MSG)
1261
1346
  end
1262
1347
 
1263
1348
  def check_transactions_supported!
1264
- raise Mongo::Error::TransactionsNotSupported, "standalone topology" if cluster.single?
1265
-
1266
- cluster.next_primary.with_connection do |conn|
1267
- if cluster.replica_set? && !conn.features.transactions_enabled?
1268
- raise Mongo::Error::TransactionsNotSupported, "server version is < 4.0"
1269
- end
1270
- if cluster.sharded? && !conn.features.sharded_transactions_enabled?
1271
- raise Mongo::Error::TransactionsNotSupported, "sharded transactions require server version >= 4.2"
1272
- end
1273
- end
1349
+ raise Mongo::Error::TransactionsNotSupported, 'standalone topology' if cluster.single?
1274
1350
  end
1275
1351
 
1276
1352
  def operation_timeouts(opts)
1277
1353
  {
1278
- inherited_timeout_ms: @client.timeout_ms
1354
+ inherited_timeout_ms: @with_transaction_timeout_ms || @client.timeout_ms
1279
1355
  }.tap do |result|
1280
- if @with_transaction_deadline.nil?
1281
- if timeout_ms = opts[:timeout_ms]
1282
- result[:operation_timeout_ms] = timeout_ms
1283
- elsif default_timeout_ms = options[:default_timeout_ms]
1284
- result[:operation_timeout_ms] = default_timeout_ms
1356
+ if @inside_with_transaction
1357
+ if opts[:timeout_ms]
1358
+ raise Mongo::Error::InvalidTransactionOperation,
1359
+ 'timeoutMS cannot be overridden inside a withTransaction callback'
1285
1360
  end
1361
+ elsif timeout_ms = opts[:timeout_ms]
1362
+ result[:operation_timeout_ms] = timeout_ms
1363
+ elsif default_timeout_ms = options[:default_timeout_ms]
1364
+ result[:operation_timeout_ms] = default_timeout_ms
1365
+ end
1366
+ end
1367
+ end
1368
+
1369
+ def calculate_with_transaction_deadline(opts)
1370
+ calc = lambda { |timeout|
1371
+ if timeout == 0
1372
+ 0
1373
+ else
1374
+ Utils.monotonic_time + (timeout / 1000.0)
1375
+ end
1376
+ }
1377
+ if timeout_ms = opts&.dig(:timeout_ms)
1378
+ calc.call(timeout_ms)
1379
+ elsif default_timeout_ms = @options[:default_timeout_ms]
1380
+ calc.call(default_timeout_ms)
1381
+ elsif @client.timeout_ms
1382
+ calc.call(@client.timeout_ms)
1383
+ end
1384
+ end
1385
+
1386
+ def deadline_expired?(deadline)
1387
+ if deadline.zero?
1388
+ false
1389
+ else
1390
+ Utils.monotonic_time >= deadline
1391
+ end
1392
+ end
1393
+
1394
+ # Exponential backoff settings for with_transaction retries.
1395
+ BACKOFF_INITIAL = 0.005
1396
+ BACKOFF_MAX = 0.5
1397
+ private_constant :BACKOFF_INITIAL, :BACKOFF_MAX
1398
+
1399
+ def backoff_seconds_for_retry(transaction_attempt)
1400
+ exponential = BACKOFF_INITIAL * (1.5**(transaction_attempt - 1))
1401
+ Random.rand * [ exponential, BACKOFF_MAX ].min
1402
+ end
1403
+
1404
+ def backoff_would_exceed_deadline?(deadline, backoff_seconds)
1405
+ return false if deadline.zero?
1406
+
1407
+ Utils.monotonic_time + backoff_seconds >= deadline
1408
+ end
1409
+
1410
+ # Implements makeTimeoutError(lastError) from the transactions-convenient-api spec.
1411
+ # In CSOT mode raises TimeoutError with last_error's message and labels copied.
1412
+ # In non-CSOT mode re-raises last_error directly.
1413
+ def make_timeout_error_from(last_error, timeout_message)
1414
+ if @with_transaction_timeout_ms
1415
+ timeout_error = Mongo::Error::TimeoutError.new("#{timeout_message}: #{last_error}")
1416
+ if last_error.respond_to?(:labels)
1417
+ last_error.labels.each { |label| timeout_error.add_label(label) }
1286
1418
  end
1419
+ raise timeout_error
1287
1420
  end
1421
+
1422
+ raise last_error
1288
1423
  end
1289
1424
  end
1290
1425
  end