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/timeout.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- # rubocop:todo all
3
2
 
4
3
  # Copyright (C) 2020 MongoDB Inc.
5
4
  #
@@ -16,10 +15,8 @@
16
15
  # limitations under the License.
17
16
 
18
17
  module Mongo
19
-
20
18
  # @api private
21
19
  module Timeout
22
-
23
20
  # A wrapper around Ruby core's Timeout::timeout method that provides
24
21
  # a standardized API for Ruby versions older and newer than 2.4.0,
25
22
  # which is when the third argument was introduced.
@@ -30,12 +27,10 @@ module Mongo
30
27
  # @param [ String ] message The error message passed to the exception raised
31
28
  # on timeout, optional. When no error message is provided, the default
32
29
  # error message for the exception class is used.
33
- def timeout(sec, klass=nil, message=nil)
30
+ def timeout(sec, klass = nil, message = nil, &block)
34
31
  if message && RUBY_VERSION < '2.94.0'
35
32
  begin
36
- ::Timeout.timeout(sec) do
37
- yield
38
- end
33
+ ::Timeout.timeout(sec, &block)
39
34
  rescue ::Timeout::Error
40
35
  raise klass, message
41
36
  end
@@ -43,10 +38,8 @@ module Mongo
43
38
  # Jruby Timeout::timeout method does not support passing nil arguments.
44
39
  # Remove the nil arguments before passing them along to the core
45
40
  # Timeout::timeout method.
46
- optional_args = [klass, message].compact
47
- ::Timeout.timeout(sec, *optional_args) do
48
- yield
49
- end
41
+ optional_args = [ klass, message ].compact
42
+ ::Timeout.timeout(sec, *optional_args, &block)
50
43
  end
51
44
  end
52
45
  module_function :timeout
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- # rubocop:todo all
3
2
 
4
3
  # Copyright (C) 2020 MongoDB Inc.
5
4
  #
@@ -23,12 +22,8 @@ module Mongo
23
22
  class TopologyVersion < BSON::Document
24
23
  def initialize(doc)
25
24
  if Lint.enabled?
26
- unless doc['processId']
27
- raise ArgumentError, 'Creating a topology version without processId field'
28
- end
29
- unless doc['counter']
30
- raise ArgumentError, 'Creating a topology version without counter field'
31
- end
25
+ raise ArgumentError, 'Creating a topology version without processId field' unless doc['processId']
26
+ raise ArgumentError, 'Creating a topology version without counter field' unless doc['counter']
32
27
  end
33
28
 
34
29
  super
@@ -55,10 +50,10 @@ module Mongo
55
50
  # @return [ true | false ] Whether this topology version is potentially newer.
56
51
  # @api private
57
52
  def gt?(other)
58
- if process_id != other.process_id
59
- true
60
- else
53
+ if process_id == other.process_id
61
54
  counter > other.counter
55
+ else
56
+ true
62
57
  end
63
58
  end
64
59
 
@@ -73,10 +68,10 @@ module Mongo
73
68
  # @return [ true | false ] Whether this topology version is potentially newer.
74
69
  # @api private
75
70
  def gte?(other)
76
- if process_id != other.process_id
77
- true
78
- else
71
+ if process_id == other.process_id
79
72
  counter >= other.counter
73
+ else
74
+ true
80
75
  end
81
76
  end
82
77
 
@@ -0,0 +1,320 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2025-present MongoDB Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the 'License');
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an 'AS IS' BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module Mongo
18
+ module Tracing
19
+ module OpenTelemetry
20
+ # CommandTracer is responsible for tracing MongoDB server commands using OpenTelemetry.
21
+ #
22
+ # @api private
23
+ class CommandTracer
24
+ # Initializes a new CommandTracer.
25
+ #
26
+ # @param otel_tracer [ OpenTelemetry::Trace::Tracer ] the OpenTelemetry tracer.
27
+ # @param parent_tracer [ Mongo::Tracing::OpenTelemetry::Tracer ] the parent tracer
28
+ # for accessing shared context maps.
29
+ # @param query_text_max_length [ Integer ] maximum length for captured query text.
30
+ # Defaults to 0 (no query text capture).
31
+ def initialize(otel_tracer, parent_tracer, query_text_max_length: 0)
32
+ @otel_tracer = otel_tracer
33
+ @parent_tracer = parent_tracer
34
+ @query_text_max_length = query_text_max_length
35
+ end
36
+
37
+ # Starts a span for a MongoDB command.
38
+ #
39
+ # @param message [ Mongo::Protocol::Message ] the command message.
40
+ # @param operation_context [ Mongo::Operation::Context ] the operation context.
41
+ # @param connection [ Mongo::Server::Connection ] the connection.
42
+ def start_span(message, operation_context, connection); end
43
+
44
+ # Trace a MongoDB command.
45
+ #
46
+ # Creates an OpenTelemetry span for the command, capturing attributes such as
47
+ # command name, database name, collection name, server address, connection IDs,
48
+ # and optionally query text. The span is automatically nested under the current
49
+ # operation span and is finished when the command completes or fails.
50
+ #
51
+ # @param message [ Mongo::Protocol::Message ] the command message to trace.
52
+ # @param _operation_context [ Mongo::Operation::Context ] the context of the operation.
53
+ # @param connection [ Mongo::Server::Connection ] the connection used to send the command.
54
+ #
55
+ # @yield the block representing the command to be traced.
56
+ #
57
+ # @return [ Object ] the result of the command.
58
+ # rubocop:disable Lint/RescueException
59
+ def trace_command(message, _operation_context, connection)
60
+ # Commands should always be nested under their operation span, not directly under
61
+ # the transaction span. Don't pass with_parent to use automatic parent resolution
62
+ # from the currently active span (the operation span).
63
+ span = create_command_span(message, connection)
64
+ ::OpenTelemetry::Trace.with_span(span) do |s, c|
65
+ yield.tap do |result|
66
+ process_command_result(result, cursor_id(message), c, s)
67
+ end
68
+ end
69
+ rescue Exception => e
70
+ handle_command_exception(span, e)
71
+ raise e
72
+ ensure
73
+ span&.finish
74
+ end
75
+ # rubocop:enable Lint/RescueException
76
+
77
+ private
78
+
79
+ # Creates a span for a command.
80
+ #
81
+ # @param message [ Mongo::Protocol::Message ] the command message.
82
+ # @param connection [ Mongo::Server::Connection ] the connection.
83
+ #
84
+ # @return [ OpenTelemetry::Trace::Span ] the created span.
85
+ def create_command_span(message, connection)
86
+ @otel_tracer.start_span(
87
+ command_name(message),
88
+ attributes: span_attributes(message, connection),
89
+ kind: :client
90
+ )
91
+ end
92
+
93
+ # Processes the command result and updates span attributes.
94
+ #
95
+ # @param result [ Object ] the command result.
96
+ # @param cursor_id [ Integer | nil ] the cursor ID.
97
+ # @param context [ OpenTelemetry::Context ] the context.
98
+ # @param span [ OpenTelemetry::Trace::Span ] the current span.
99
+ def process_command_result(result, cursor_id, context, span)
100
+ process_cursor_context(result, cursor_id, context, span)
101
+ maybe_trace_error(result, span)
102
+ end
103
+
104
+ # Handles exceptions that occur during command execution.
105
+ #
106
+ # @param span [ OpenTelemetry::Trace::Span | nil ] the span.
107
+ # @param exception [ Exception ] the exception that occurred.
108
+ def handle_command_exception(span, exception)
109
+ return unless span
110
+
111
+ if exception.is_a?(Mongo::Error::OperationFailure)
112
+ span.set_attribute('db.response.status_code', exception.code.to_s)
113
+ end
114
+ span.record_exception(exception)
115
+ span.status = ::OpenTelemetry::Trace::Status.error("Unhandled exception of type: #{exception.class}")
116
+ end
117
+
118
+ # Builds span attributes for the command.
119
+ #
120
+ # @param message [ Mongo::Protocol::Message ] the command message.
121
+ # @param connection [ Mongo::Server::Connection ] the connection.
122
+ #
123
+ # @return [ Hash ] OpenTelemetry span attributes following MongoDB semantic conventions.
124
+ def span_attributes(message, connection)
125
+ base_attributes(message)
126
+ .merge(connection_attributes(connection))
127
+ .merge(session_attributes(message))
128
+ .compact
129
+ end
130
+
131
+ # Returns base database and command attributes.
132
+ #
133
+ # @param message [ Mongo::Protocol::Message ] the command message.
134
+ #
135
+ # @return [ Hash ] base span attributes.
136
+ def base_attributes(message)
137
+ {
138
+ 'db.system.name' => 'mongodb',
139
+ 'db.namespace' => database(message),
140
+ 'db.collection.name' => collection_name(message),
141
+ 'db.command.name' => command_name(message),
142
+ 'db.query.summary' => query_summary(message),
143
+ 'db.query.text' => query_text(message)
144
+ }
145
+ end
146
+
147
+ # Returns connection-related attributes.
148
+ #
149
+ # @param connection [ Mongo::Server::Connection ] the connection.
150
+ #
151
+ # @return [ Hash ] connection span attributes.
152
+ def connection_attributes(connection)
153
+ {
154
+ 'server.port' => connection.address.port,
155
+ 'server.address' => connection.address.host,
156
+ 'network.transport' => connection.transport.to_s,
157
+ 'db.mongodb.server_connection_id' => connection.server.description.server_connection_id,
158
+ 'db.mongodb.driver_connection_id' => connection.id
159
+ }
160
+ end
161
+
162
+ # Returns session and transaction attributes.
163
+ #
164
+ # @param message [ Mongo::Protocol::Message ] the command message.
165
+ #
166
+ # @return [ Hash ] session span attributes.
167
+ def session_attributes(message)
168
+ {
169
+ 'db.mongodb.cursor_id' => cursor_id(message),
170
+ 'db.mongodb.lsid' => lsid(message),
171
+ 'db.mongodb.txn_number' => txn_number(message)
172
+ }
173
+ end
174
+
175
+ # Processes cursor context from the command result.
176
+ #
177
+ # @param result [ Object ] the command result.
178
+ # @param _cursor_id [ Integer | nil ] the cursor ID (unused).
179
+ # @param _context [ OpenTelemetry::Context ] the context (unused).
180
+ # @param span [ OpenTelemetry::Trace::Span ] the current span.
181
+ def process_cursor_context(result, _cursor_id, _context, span)
182
+ return unless result.has_cursor_id? && result.cursor_id.positive?
183
+
184
+ span.set_attribute('db.mongodb.cursor_id', result.cursor_id)
185
+ end
186
+
187
+ # Records error status code if the command failed.
188
+ #
189
+ # @param result [ Object ] the command result.
190
+ # @param span [ OpenTelemetry::Trace::Span ] the current span.
191
+ def maybe_trace_error(result, span)
192
+ return if result.successful?
193
+
194
+ span.set_attribute('db.response.status_code', result.error.code.to_s)
195
+ begin
196
+ result.validate!
197
+ rescue Mongo::Error::OperationFailure => e
198
+ span.record_exception(e)
199
+ end
200
+ end
201
+
202
+ # Generates a summary string for the query.
203
+ #
204
+ # @param message [ Mongo::Protocol::Message ] the command message.
205
+ #
206
+ # @return [ String ] summary in format "command_name db.collection" or "command_name db".
207
+ def query_summary(message)
208
+ if (coll_name = collection_name(message))
209
+ "#{command_name(message)} #{database(message)}.#{coll_name}"
210
+ else
211
+ "#{command_name(message)} #{database(message)}"
212
+ end
213
+ end
214
+
215
+ # Extracts the collection name from the command message.
216
+ #
217
+ # @param message [ Mongo::Protocol::Message ] the command message.
218
+ #
219
+ # @return [ String | nil ] the collection name, or nil if not applicable.
220
+ def collection_name(message)
221
+ case command_name(message)
222
+ when 'getMore'
223
+ message.documents.first['collection'].to_s
224
+ when 'listCollections', 'listDatabases', 'commitTransaction', 'abortTransaction'
225
+ nil
226
+ else
227
+ value = message.documents.first.values.first
228
+ # Return nil if the value is not a string (e.g., for admin commands that have numeric values)
229
+ value.is_a?(String) ? value : nil
230
+ end
231
+ end
232
+
233
+ # Extracts the command name from the message.
234
+ #
235
+ # @param message [ Mongo::Protocol::Message ] the command message.
236
+ #
237
+ # @return [ String ] the command name.
238
+ def command_name(message)
239
+ message.documents.first.keys.first.to_s
240
+ end
241
+
242
+ # Extracts the database name from the message.
243
+ #
244
+ # @param message [ Mongo::Protocol::Message ] the command message.
245
+ #
246
+ # @return [ String ] the database name.
247
+ def database(message)
248
+ message.documents.first['$db'].to_s
249
+ end
250
+
251
+ # Checks if query text capture is enabled.
252
+ #
253
+ # @return [ Boolean ] true if query text should be captured.
254
+ def query_text?
255
+ @query_text_max_length.positive?
256
+ end
257
+
258
+ # Extracts the cursor ID from getMore commands.
259
+ #
260
+ # @param message [ Mongo::Protocol::Message ] the command message.
261
+ #
262
+ # @return [ Integer | nil ] the cursor ID, or nil if not a getMore command.
263
+ def cursor_id(message)
264
+ return unless command_name(message) == 'getMore'
265
+
266
+ message.documents.first['getMore'].value
267
+ end
268
+
269
+ # Extracts the logical session ID from the command.
270
+ #
271
+ # @param message [ Mongo::Protocol::Message ] the command message.
272
+ #
273
+ # @return [ BSON::Binary | nil ] the session ID, or nil if not present.
274
+ def lsid(message)
275
+ lsid_doc = message.documents.first['lsid']
276
+ return unless lsid_doc
277
+
278
+ lsid_doc['id'].to_uuid
279
+ end
280
+
281
+ # Extracts the transaction number from the command.
282
+ #
283
+ # @param message [ Mongo::Protocol::Message ] the command message.
284
+ #
285
+ # @return [ Integer | nil ] the transaction number, or nil if not present.
286
+ def txn_number(message)
287
+ txn_num = message.documents.first['txnNumber']
288
+ return unless txn_num
289
+
290
+ txn_num.value
291
+ end
292
+
293
+ # Keys to exclude from query text capture.
294
+ EXCLUDED_KEYS = %w[lsid $db $clusterTime signature].freeze
295
+
296
+ # Ellipsis for truncated query text.
297
+ ELLIPSIS = '...'
298
+
299
+ # Extracts and formats the query text from the command.
300
+ #
301
+ # @param message [ Mongo::Protocol::Message ] the command message.
302
+ #
303
+ # @return [ String | nil ] JSON representation of the command, truncated if necessary, or nil if disabled.
304
+ def query_text(message)
305
+ return unless query_text?
306
+
307
+ text = message
308
+ .payload['command']
309
+ .reject { |key, _| EXCLUDED_KEYS.include?(key) }
310
+ .to_json
311
+ if text.length > @query_text_max_length
312
+ "#{text[0...@query_text_max_length]}#{ELLIPSIS}"
313
+ else
314
+ text
315
+ end
316
+ end
317
+ end
318
+ end
319
+ end
320
+ end
@@ -0,0 +1,227 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2025-present MongoDB Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the 'License');
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an 'AS IS' BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module Mongo
18
+ module Tracing
19
+ module OpenTelemetry
20
+ # OperationTracer is responsible for tracing MongoDB driver operations using OpenTelemetry.
21
+ #
22
+ # @api private
23
+ class OperationTracer
24
+ extend Forwardable
25
+
26
+ def_delegators :@parent_tracer,
27
+ :cursor_context_map,
28
+ :parent_context_for,
29
+ :transaction_context_map,
30
+ :transaction_map_key
31
+
32
+ # Initializes a new OperationTracer.
33
+ #
34
+ # @param otel_tracer [ OpenTelemetry::Trace::Tracer ] the OpenTelemetry tracer.
35
+ # @param parent_tracer [ Mongo::Tracing::OpenTelemetry::Tracer ] the parent tracer
36
+ # for accessing shared context maps.
37
+ def initialize(otel_tracer, parent_tracer)
38
+ @otel_tracer = otel_tracer
39
+ @parent_tracer = parent_tracer
40
+ end
41
+
42
+ # Trace a MongoDB operation.
43
+ #
44
+ # Creates an OpenTelemetry span for the operation, capturing attributes such as
45
+ # database name, collection name, operation name, and cursor ID. The span is finished
46
+ # automatically when the operation completes or fails.
47
+ #
48
+ # @param operation [ Mongo::Operation ] the MongoDB operation to trace.
49
+ # @param operation_context [ Mongo::Operation::Context ] the context of the operation.
50
+ # @param op_name [ String | nil ] an optional name for the operation. If nil, the
51
+ # operation class name is used.
52
+ #
53
+ # @yield the block representing the operation to be traced.
54
+ #
55
+ # @return [ Object ] the result of the operation.
56
+ #
57
+ # rubocop:disable Lint/RescueException
58
+ def trace_operation(operation, operation_context, op_name: nil, &block)
59
+ span = create_operation_span(operation, operation_context, op_name)
60
+ execute_with_span(span, operation, &block)
61
+ rescue Exception => e
62
+ handle_span_exception(span, e)
63
+ raise e
64
+ ensure
65
+ span&.finish
66
+ end
67
+ # rubocop:enable Lint/RescueException
68
+
69
+ private
70
+
71
+ # Creates an OpenTelemetry span for the operation.
72
+ #
73
+ # @param operation [ Mongo::Operation ] the operation.
74
+ # @param operation_context [ Mongo::Operation::Context ] the operation context.
75
+ # @param op_name [ String | nil ] optional operation name.
76
+ #
77
+ # @return [ OpenTelemetry::Trace::Span ] the created span.
78
+ def create_operation_span(operation, operation_context, op_name)
79
+ parent_context = parent_context_for(operation_context, operation.cursor_id)
80
+ @otel_tracer.start_span(
81
+ operation_span_name(operation, op_name),
82
+ attributes: span_attributes(operation, op_name),
83
+ with_parent: parent_context,
84
+ kind: :client
85
+ )
86
+ end
87
+
88
+ # Executes the operation block within the span context.
89
+ #
90
+ # @param span [ OpenTelemetry::Trace::Span ] the span.
91
+ # @param operation [ Mongo::Operation ] the operation.
92
+ #
93
+ # @yield the block to execute.
94
+ #
95
+ # @return [ Object ] the result of the block.
96
+ def execute_with_span(span, operation)
97
+ ::OpenTelemetry::Trace.with_span(span) do |s, c|
98
+ yield.tap do |result|
99
+ process_cursor_context(result, operation.cursor_id, c, s)
100
+ end
101
+ end
102
+ end
103
+
104
+ # Handles exception for the span.
105
+ #
106
+ # @param span [ OpenTelemetry::Trace::Span ] the span.
107
+ # @param exception [ Exception ] the exception.
108
+ def handle_span_exception(span, exception)
109
+ return unless span
110
+
111
+ span.record_exception(exception)
112
+ span.status = ::OpenTelemetry::Trace::Status.error(
113
+ "Unhandled exception of type: #{exception.class}"
114
+ )
115
+ end
116
+
117
+ # Returns the operation name from the provided name or operation class.
118
+ #
119
+ # @param operation [ Mongo::Operation ] the operation.
120
+ # @param op_name [ String | nil ] optional operation name.
121
+ #
122
+ # @return [ String ] the operation name in lowercase.
123
+ def operation_name(operation, op_name = nil)
124
+ op_name || operation.class.name.split('::').last.downcase
125
+ end
126
+
127
+ # Builds span attributes for the operation.
128
+ #
129
+ # @param operation [ Mongo::Operation ] the operation.
130
+ # @param op_name [ String | nil ] optional operation name.
131
+ #
132
+ # @return [ Hash ] OpenTelemetry span attributes following MongoDB semantic conventions.
133
+ def span_attributes(operation, op_name)
134
+ {
135
+ 'db.system.name' => 'mongodb',
136
+ 'db.namespace' => operation.db_name.to_s,
137
+ 'db.collection.name' => collection_name(operation),
138
+ 'db.operation.name' => operation_name(operation, op_name),
139
+ 'db.operation.summary' => operation_span_name(operation, op_name),
140
+ 'db.mongodb.cursor_id' => operation.cursor_id,
141
+ }.compact
142
+ end
143
+
144
+ # Processes cursor context after operation execution.
145
+ #
146
+ # Updates the cursor context map based on the result. Removes closed cursors
147
+ # and stores context for newly created cursors.
148
+ #
149
+ # @param result [ Object ] the operation result.
150
+ # @param cursor_id [ Integer | nil ] the cursor ID before the operation.
151
+ # @param context [ OpenTelemetry::Context ] the OpenTelemetry context.
152
+ # @param span [ OpenTelemetry::Trace::Span ] the current span.
153
+ def process_cursor_context(result, cursor_id, context, span)
154
+ return unless result.is_a?(Cursor)
155
+
156
+ if result.id.zero?
157
+ # If the cursor is closed, remove it from the context map.
158
+ cursor_context_map.delete(cursor_id)
159
+ elsif result.id && cursor_id.nil?
160
+ # New cursor created, store its context.
161
+ cursor_context_map[result.id] = context
162
+ span.set_attribute('db.mongodb.cursor_id', result.id)
163
+ end
164
+ end
165
+
166
+ # Extracts the collection name from the operation.
167
+ #
168
+ # @param operation [ Mongo::Operation ] the operation.
169
+ #
170
+ # @return [ String | nil ] the collection name, or nil if not applicable.
171
+ def collection_name(operation)
172
+ return operation.coll_name.to_s if operation.respond_to?(:coll_name) && operation.coll_name
173
+
174
+ extract_collection_from_spec(operation)
175
+ end
176
+
177
+ # Extracts collection name from operation spec based on operation type.
178
+ #
179
+ # @param operation [ Mongo::Operation ] the operation.
180
+ #
181
+ # @return [ String | nil ] the collection name, or nil if not found.
182
+ def extract_collection_from_spec(operation)
183
+ collection_key = collection_key_for_operation(operation)
184
+ return nil unless collection_key
185
+
186
+ value = if collection_key == :first_value
187
+ operation.spec[:selector].values.first
188
+ else
189
+ operation.spec[:selector][collection_key]
190
+ end
191
+ value&.to_s
192
+ end
193
+
194
+ # Returns the collection key for a given operation type.
195
+ #
196
+ # @param operation [ Mongo::Operation ] the operation.
197
+ #
198
+ # @return [ Symbol | nil ] the collection key symbol or nil.
199
+ def collection_key_for_operation(operation)
200
+ case operation
201
+ when Operation::Aggregate then :aggregate
202
+ when Operation::Count then :count
203
+ when Operation::Create then :create
204
+ when Operation::Distinct then :distinct
205
+ when Operation::Drop then :drop
206
+ when Operation::WriteCommand then :first_value
207
+ end
208
+ end
209
+
210
+ # Generates the span name for the operation.
211
+ #
212
+ # @param operation [ Mongo::Operation ] the operation.
213
+ # @param op_name [ String | nil ] optional operation name.
214
+ #
215
+ # @return [ String ] span name in format "operation_name db.collection" or "operation_name db".
216
+ def operation_span_name(operation, op_name = nil)
217
+ coll_name = collection_name(operation)
218
+ if coll_name && !coll_name.empty?
219
+ "#{operation_name(operation, op_name)} #{operation.db_name}.#{coll_name}"
220
+ else
221
+ "#{operation_name(operation, op_name)} #{operation.db_name}"
222
+ end
223
+ end
224
+ end
225
+ end
226
+ end
227
+ end