mongo 2.20.1 → 2.21.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 (246) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -0
  3. data/Rakefile +2 -2
  4. data/lib/mongo/address.rb +22 -3
  5. data/lib/mongo/auth/aws/credentials_retriever.rb +70 -17
  6. data/lib/mongo/auth/base.rb +1 -1
  7. data/lib/mongo/bulk_write.rb +35 -2
  8. data/lib/mongo/client.rb +38 -6
  9. data/lib/mongo/client_encryption.rb +6 -3
  10. data/lib/mongo/cluster/reapers/cursor_reaper.rb +6 -1
  11. data/lib/mongo/cluster/sdam_flow.rb +20 -7
  12. data/lib/mongo/cluster.rb +14 -4
  13. data/lib/mongo/collection/helpers.rb +1 -1
  14. data/lib/mongo/collection/view/aggregation/behavior.rb +131 -0
  15. data/lib/mongo/collection/view/aggregation.rb +33 -99
  16. data/lib/mongo/collection/view/builder/aggregation.rb +1 -7
  17. data/lib/mongo/collection/view/change_stream.rb +80 -27
  18. data/lib/mongo/collection/view/iterable.rb +76 -60
  19. data/lib/mongo/collection/view/map_reduce.rb +25 -8
  20. data/lib/mongo/collection/view/readable.rb +79 -30
  21. data/lib/mongo/collection/view/writable.rb +109 -48
  22. data/lib/mongo/collection/view.rb +43 -3
  23. data/lib/mongo/collection.rb +158 -23
  24. data/lib/mongo/crypt/auto_encrypter.rb +4 -6
  25. data/lib/mongo/crypt/binding.rb +4 -4
  26. data/lib/mongo/crypt/context.rb +20 -14
  27. data/lib/mongo/crypt/encryption_io.rb +56 -26
  28. data/lib/mongo/crypt/explicit_encrypter.rb +49 -20
  29. data/lib/mongo/crypt/explicit_encryption_context.rb +17 -11
  30. data/lib/mongo/crypt/kms/azure/credentials_retriever.rb +22 -6
  31. data/lib/mongo/crypt/kms/gcp/credentials_retriever.rb +29 -4
  32. data/lib/mongo/csot_timeout_holder.rb +119 -0
  33. data/lib/mongo/cursor/kill_spec.rb +5 -2
  34. data/lib/mongo/cursor/nontailable.rb +27 -0
  35. data/lib/mongo/cursor.rb +86 -24
  36. data/lib/mongo/cursor_host.rb +82 -0
  37. data/lib/mongo/database/view.rb +81 -14
  38. data/lib/mongo/database.rb +88 -18
  39. data/lib/mongo/error/operation_failure.rb +209 -204
  40. data/lib/mongo/error/server_timeout_error.rb +12 -0
  41. data/lib/mongo/error/socket_timeout_error.rb +3 -1
  42. data/lib/mongo/error/timeout_error.rb +23 -0
  43. data/lib/mongo/error.rb +2 -0
  44. data/lib/mongo/grid/fs_bucket.rb +45 -12
  45. data/lib/mongo/grid/stream/read.rb +15 -1
  46. data/lib/mongo/grid/stream/write.rb +21 -4
  47. data/lib/mongo/index/view.rb +77 -16
  48. data/lib/mongo/operation/context.rb +40 -2
  49. data/lib/mongo/operation/create_search_indexes/op_msg.rb +2 -2
  50. data/lib/mongo/operation/delete/op_msg.rb +2 -1
  51. data/lib/mongo/operation/drop_search_index/op_msg.rb +2 -2
  52. data/lib/mongo/operation/find/op_msg.rb +45 -0
  53. data/lib/mongo/operation/get_more/op_msg.rb +33 -0
  54. data/lib/mongo/operation/insert/op_msg.rb +3 -2
  55. data/lib/mongo/operation/insert/result.rb +4 -2
  56. data/lib/mongo/operation/list_collections/result.rb +1 -1
  57. data/lib/mongo/operation/map_reduce/result.rb +1 -1
  58. data/lib/mongo/operation/op_msg_base.rb +3 -1
  59. data/lib/mongo/operation/result.rb +26 -5
  60. data/lib/mongo/operation/shared/executable.rb +12 -1
  61. data/lib/mongo/operation/shared/op_msg_executable.rb +4 -1
  62. data/lib/mongo/operation/shared/response_handling.rb +3 -3
  63. data/lib/mongo/operation/shared/sessions_supported.rb +1 -1
  64. data/lib/mongo/operation/shared/timed.rb +52 -0
  65. data/lib/mongo/operation/shared/write.rb +4 -1
  66. data/lib/mongo/operation/update/op_msg.rb +2 -1
  67. data/lib/mongo/operation/update_search_index/op_msg.rb +2 -2
  68. data/lib/mongo/operation.rb +1 -0
  69. data/lib/mongo/protocol/message.rb +1 -4
  70. data/lib/mongo/protocol/msg.rb +2 -2
  71. data/lib/mongo/retryable/read_worker.rb +69 -29
  72. data/lib/mongo/retryable/write_worker.rb +49 -18
  73. data/lib/mongo/retryable.rb +8 -2
  74. data/lib/mongo/server/connection.rb +11 -5
  75. data/lib/mongo/server/connection_base.rb +22 -2
  76. data/lib/mongo/server/connection_pool.rb +32 -14
  77. data/lib/mongo/server/description/features.rb +1 -1
  78. data/lib/mongo/server/description.rb +18 -5
  79. data/lib/mongo/server/monitor.rb +7 -4
  80. data/lib/mongo/server/pending_connection.rb +7 -3
  81. data/lib/mongo/server/{round_trip_time_averager.rb → round_trip_time_calculator.rb} +25 -7
  82. data/lib/mongo/server.rb +11 -6
  83. data/lib/mongo/server_selector/base.rb +25 -9
  84. data/lib/mongo/session.rb +78 -9
  85. data/lib/mongo/socket/ssl.rb +109 -17
  86. data/lib/mongo/socket/tcp.rb +40 -6
  87. data/lib/mongo/socket.rb +154 -25
  88. data/lib/mongo/uri/options_mapper.rb +1 -0
  89. data/lib/mongo/version.rb +1 -1
  90. data/lib/mongo.rb +1 -0
  91. data/spec/atlas/atlas_connectivity_spec.rb +4 -0
  92. data/spec/atlas/operations_spec.rb +4 -0
  93. data/spec/integration/client_side_encryption/auto_encryption_mongocryptd_spawn_spec.rb +2 -1
  94. data/spec/integration/client_side_encryption/auto_encryption_spec.rb +494 -487
  95. data/spec/integration/client_side_encryption/on_demand_aws_credentials_spec.rb +1 -1
  96. data/spec/integration/client_side_encryption/range_explicit_encryption_prose_spec.rb +66 -22
  97. data/spec/integration/client_side_operations_timeout/encryption_prose_spec.rb +131 -0
  98. data/spec/integration/connection_pool_populator_spec.rb +2 -0
  99. data/spec/integration/cursor_pinning_spec.rb +15 -60
  100. data/spec/integration/cursor_reaping_spec.rb +1 -1
  101. data/spec/integration/docs_examples_spec.rb +1 -1
  102. data/spec/integration/operation_failure_code_spec.rb +1 -1
  103. data/spec/integration/operation_failure_message_spec.rb +3 -3
  104. data/spec/integration/retryable_errors_spec.rb +2 -2
  105. data/spec/integration/sdam_error_handling_spec.rb +2 -1
  106. data/spec/integration/search_indexes_prose_spec.rb +4 -0
  107. data/spec/integration/server_spec.rb +4 -3
  108. data/spec/integration/transactions_api_examples_spec.rb +2 -0
  109. data/spec/kerberos/kerberos_spec.rb +4 -0
  110. data/spec/lite_spec_helper.rb +3 -1
  111. data/spec/mongo/auth/user/view_spec.rb +1 -1
  112. data/spec/mongo/caching_cursor_spec.rb +1 -1
  113. data/spec/mongo/client_encryption_spec.rb +1 -0
  114. data/spec/mongo/client_spec.rb +158 -4
  115. data/spec/mongo/collection/view/aggregation_spec.rb +14 -39
  116. data/spec/mongo/collection/view/change_stream_spec.rb +3 -3
  117. data/spec/mongo/collection_spec.rb +5 -6
  118. data/spec/mongo/crypt/auto_encrypter_spec.rb +14 -12
  119. data/spec/mongo/crypt/data_key_context_spec.rb +3 -1
  120. data/spec/mongo/crypt/explicit_encryption_context_spec.rb +2 -2
  121. data/spec/mongo/crypt/handle_spec.rb +1 -1
  122. data/spec/mongo/cursor_spec.rb +26 -9
  123. data/spec/mongo/error/operation_failure_heavy_spec.rb +2 -2
  124. data/spec/mongo/operation/context_spec.rb +79 -0
  125. data/spec/mongo/operation/create/op_msg_spec.rb +106 -110
  126. data/spec/mongo/operation/delete/op_msg_spec.rb +6 -5
  127. data/spec/mongo/operation/find/op_msg_spec.rb +66 -0
  128. data/spec/mongo/operation/get_more/op_msg_spec.rb +65 -0
  129. data/spec/mongo/operation/insert/op_msg_spec.rb +128 -131
  130. data/spec/mongo/operation/shared/csot/examples.rb +113 -0
  131. data/spec/mongo/query_cache_spec.rb +243 -225
  132. data/spec/mongo/retryable_spec.rb +1 -0
  133. data/spec/mongo/server/round_trip_time_calculator_spec.rb +120 -0
  134. data/spec/mongo/socket/ssl_spec.rb +0 -10
  135. data/spec/runners/change_streams/test.rb +2 -2
  136. data/spec/runners/crud/operation.rb +1 -1
  137. data/spec/runners/crud/verifier.rb +3 -1
  138. data/spec/runners/transactions/operation.rb +4 -6
  139. data/spec/runners/unified/ambiguous_operations.rb +13 -0
  140. data/spec/runners/unified/assertions.rb +4 -0
  141. data/spec/runners/unified/change_stream_operations.rb +14 -24
  142. data/spec/runners/unified/crud_operations.rb +82 -59
  143. data/spec/runners/unified/ddl_operations.rb +38 -7
  144. data/spec/runners/unified/grid_fs_operations.rb +37 -2
  145. data/spec/runners/unified/support_operations.rb +43 -4
  146. data/spec/runners/unified/test.rb +22 -10
  147. data/spec/runners/unified.rb +1 -1
  148. data/spec/solo/clean_exit_spec.rb +2 -0
  149. data/spec/spec_tests/client_side_operations_timeout_spec.rb +15 -0
  150. data/spec/spec_tests/data/change_streams_unified/change-streams-clusterTime.yml +3 -1
  151. data/spec/spec_tests/data/change_streams_unified/change-streams-disambiguatedPaths.yml +3 -1
  152. data/spec/spec_tests/data/change_streams_unified/change-streams-errors.yml +3 -1
  153. data/spec/spec_tests/data/change_streams_unified/change-streams-pre_and_post_images.yml +1 -1
  154. data/spec/spec_tests/data/change_streams_unified/change-streams-resume-allowlist.yml +1 -1
  155. data/spec/spec_tests/data/change_streams_unified/change-streams-resume-errorLabels.yml +1 -1
  156. data/spec/spec_tests/data/change_streams_unified/change-streams-showExpandedEvents.yml +1 -1
  157. data/spec/spec_tests/data/client_side_encryption/badQueries.yml +2 -1
  158. data/spec/spec_tests/data/client_side_encryption/timeoutMS.yml +67 -0
  159. data/spec/spec_tests/data/client_side_operations_timeout/bulkWrite.yml +87 -0
  160. data/spec/spec_tests/data/client_side_operations_timeout/change-streams.yml +358 -0
  161. data/spec/spec_tests/data/client_side_operations_timeout/close-cursors.yml +129 -0
  162. data/spec/spec_tests/data/client_side_operations_timeout/command-execution.yml +250 -0
  163. data/spec/spec_tests/data/client_side_operations_timeout/convenient-transactions.yml +113 -0
  164. data/spec/spec_tests/data/client_side_operations_timeout/cursors.yml +70 -0
  165. data/spec/spec_tests/data/client_side_operations_timeout/deprecated-options.yml +3982 -0
  166. data/spec/spec_tests/data/client_side_operations_timeout/error-transformations.yml +96 -0
  167. data/spec/spec_tests/data/client_side_operations_timeout/global-timeoutMS.yml +3236 -0
  168. data/spec/spec_tests/data/client_side_operations_timeout/gridfs-advanced.yml +207 -0
  169. data/spec/spec_tests/data/client_side_operations_timeout/gridfs-delete.yml +152 -0
  170. data/spec/spec_tests/data/client_side_operations_timeout/gridfs-download.yml +182 -0
  171. data/spec/spec_tests/data/client_side_operations_timeout/gridfs-find.yml +100 -0
  172. data/spec/spec_tests/data/client_side_operations_timeout/gridfs-upload.yml +249 -0
  173. data/spec/spec_tests/data/client_side_operations_timeout/legacy-timeouts.yml +204 -0
  174. data/spec/spec_tests/data/client_side_operations_timeout/non-tailable-cursors.yml +307 -0
  175. data/spec/spec_tests/data/client_side_operations_timeout/override-collection-timeoutMS.yml +1877 -0
  176. data/spec/spec_tests/data/client_side_operations_timeout/override-operation-timeoutMS.yml +1918 -0
  177. data/spec/spec_tests/data/client_side_operations_timeout/retryability-legacy-timeouts.yml +1676 -0
  178. data/spec/spec_tests/data/client_side_operations_timeout/retryability-timeoutMS.yml +2824 -0
  179. data/spec/spec_tests/data/client_side_operations_timeout/sessions-inherit-timeoutMS.yml +168 -0
  180. data/spec/spec_tests/data/client_side_operations_timeout/sessions-override-operation-timeoutMS.yml +171 -0
  181. data/spec/spec_tests/data/client_side_operations_timeout/sessions-override-timeoutMS.yml +168 -0
  182. data/spec/spec_tests/data/client_side_operations_timeout/tailable-awaitData.yml +247 -0
  183. data/spec/spec_tests/data/client_side_operations_timeout/tailable-non-awaitData.yml +181 -0
  184. data/spec/spec_tests/data/crud_unified/aggregate-write-readPreference.yml +4 -0
  185. data/spec/spec_tests/data/crud_unified/db-aggregate-write-readPreference.yml +4 -0
  186. data/spec/spec_tests/data/crud_unified/find-test-all-options.yml +29 -0
  187. data/spec/spec_tests/server_selection_rtt_spec.rb +6 -6
  188. data/spec/support/certificates/atlas-ocsp-ca.crt +81 -83
  189. data/spec/support/certificates/atlas-ocsp.crt +107 -107
  190. data/spec/support/cluster_tools.rb +3 -3
  191. data/spec/support/common_shortcuts.rb +2 -2
  192. data/spec/support/crypt/encrypted_fields/range-encryptedFields-Date.json +1 -1
  193. data/spec/support/crypt/encrypted_fields/range-encryptedFields-DecimalNoPrecision.json +1 -1
  194. data/spec/support/crypt/encrypted_fields/range-encryptedFields-DecimalPrecision.json +1 -1
  195. data/spec/support/crypt/encrypted_fields/range-encryptedFields-DoubleNoPrecision.json +1 -1
  196. data/spec/support/crypt/encrypted_fields/range-encryptedFields-DoublePrecision.json +1 -1
  197. data/spec/support/crypt/encrypted_fields/range-encryptedFields-Int.json +1 -1
  198. data/spec/support/crypt/encrypted_fields/range-encryptedFields-Long.json +1 -1
  199. data/spec/support/shared/session.rb +2 -2
  200. data/spec/support/spec_setup.rb +2 -2
  201. data/spec/support/utils.rb +3 -1
  202. metadata +78 -91
  203. data/spec/mongo/server/round_trip_time_averager_spec.rb +0 -48
  204. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Aggregate.yml +0 -242
  205. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Correctness.yml +0 -423
  206. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Delete.yml +0 -183
  207. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-FindOneAndUpdate.yml +0 -240
  208. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-InsertFind.yml +0 -236
  209. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Update.yml +0 -253
  210. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Aggregate.yml +0 -1688
  211. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Correctness.yml +0 -294
  212. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Delete.yml +0 -906
  213. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-FindOneAndUpdate.yml +0 -1685
  214. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-InsertFind.yml +0 -1681
  215. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Update.yml +0 -1698
  216. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Aggregate.yml +0 -330
  217. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Correctness.yml +0 -425
  218. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Delete.yml +0 -227
  219. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-FindOneAndUpdate.yml +0 -328
  220. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-InsertFind.yml +0 -320
  221. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Update.yml +0 -337
  222. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Aggregate.yml +0 -914
  223. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Correctness.yml +0 -293
  224. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Delete.yml +0 -519
  225. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-FindOneAndUpdate.yml +0 -912
  226. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-InsertFind.yml +0 -908
  227. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Update.yml +0 -925
  228. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Aggregate.yml +0 -326
  229. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Correctness.yml +0 -425
  230. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Delete.yml +0 -225
  231. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-FindOneAndUpdate.yml +0 -324
  232. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-InsertFind.yml +0 -320
  233. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Update.yml +0 -339
  234. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Aggregate.yml +0 -242
  235. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Correctness.yml +0 -424
  236. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Delete.yml +0 -183
  237. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-FindOneAndUpdate.yml +0 -240
  238. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-InsertFind.yml +0 -236
  239. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Update.yml +0 -255
  240. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Aggregate.yml +0 -242
  241. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Correctness.yml +0 -423
  242. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Delete.yml +0 -183
  243. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-FindOneAndUpdate.yml +0 -240
  244. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-InsertFind.yml +0 -236
  245. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Update.yml +0 -255
  246. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-WrongType.yml +0 -44
@@ -42,7 +42,7 @@ module Mongo
42
42
  # Adds error labels to exceptions raised in the yielded to block,
43
43
  # which should perform MongoDB operations and raise Mongo::Errors on
44
44
  # failure. This method handles network errors (Error::SocketError)
45
- # and server-side errors (Error::OperationFailure); it does not
45
+ # and server-side errors (Error::OperationFailure::Family); it does not
46
46
  # handle server selection errors (Error::NoServerAvailable), for which
47
47
  # labels are added in the server selection code.
48
48
  #
@@ -65,7 +65,7 @@ module Mongo
65
65
  rescue Mongo::Error::SocketTimeoutError => e
66
66
  maybe_add_retryable_write_error_label!(e, connection, context)
67
67
  raise e
68
- rescue Mongo::Error::OperationFailure => e
68
+ rescue Mongo::Error::OperationFailure::Family => e
69
69
  if context.committing_transaction?
70
70
  if e.write_retryable? || e.wtimeout? || (e.write_concern_error? &&
71
71
  !Session::UNLABELED_WRITE_CONCERN_CODES.include?(e.write_concern_error_code)
@@ -104,7 +104,7 @@ module Mongo
104
104
  # raised during execution of operations on servers.
105
105
  def add_server_diagnostics(connection)
106
106
  yield
107
- rescue Error::SocketError, Error::SocketTimeoutError
107
+ rescue Error::SocketError, Error::SocketTimeoutError, Error::TimeoutError
108
108
  # Diagnostics should have already been added by the connection code,
109
109
  # do not add them again.
110
110
  raise
@@ -114,7 +114,7 @@ module Mongo
114
114
  end
115
115
 
116
116
  def apply_txn_opts!(selector)
117
- session.add_txn_opts!(selector, read_command?(selector))
117
+ session.add_txn_opts!(selector, read_command?(selector), context)
118
118
  end
119
119
 
120
120
  def suppress_read_write_concern!(selector)
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mongo
4
+ module Operation
5
+ # Defines the behavior of operations that have the default timeout
6
+ # behavior described by the client-side operation timeouts (CSOT)
7
+ # spec.
8
+ #
9
+ # @api private
10
+ module Timed
11
+ # If a timeout is active (as defined by the current context), and it has
12
+ # not yet expired, add :maxTimeMS to the spec.
13
+ #
14
+ # @param [ Hash ] spec The spec to modify
15
+ # @param [ Connection ] connection The connection that will be used to
16
+ # execute the operation
17
+ #
18
+ # @return [ Hash ] the spec
19
+ #
20
+ # @raises [ Mongo::Error::TimeoutError ] if the current timeout has
21
+ # expired.
22
+ def apply_relevant_timeouts_to(spec, connection)
23
+ with_max_time(connection) do |max_time_sec|
24
+ return spec if max_time_sec.nil?
25
+ return spec if connection.description.mongocryptd?
26
+
27
+ spec.tap { spec[:maxTimeMS] = (max_time_sec * 1_000).to_i }
28
+ end
29
+ end
30
+
31
+ # A helper method that computes the remaining timeout (in seconds) and
32
+ # yields it to the associated block. If no timeout is present, yields
33
+ # nil. If the timeout has expired, raises Mongo::Error::TimeoutError.
34
+ #
35
+ # @param [ Connection ] connection The connection that will be used to
36
+ # execute the operation
37
+ #
38
+ # @return [ Hash ] the result of yielding to the block (which must be
39
+ # a Hash)
40
+ def with_max_time(connection)
41
+ if context&.timeout?
42
+ max_time_sec = context.remaining_timeout_sec - connection.server.minimum_round_trip_time
43
+ raise Mongo::Error::TimeoutError if max_time_sec <= 0
44
+
45
+ yield max_time_sec
46
+ else
47
+ yield nil
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -35,7 +35,10 @@ module Mongo
35
35
  #
36
36
  # @since 2.5.2
37
37
  def execute(server, context:)
38
- server.with_connection(connection_global_id: context.connection_global_id) do |connection|
38
+ server.with_connection(
39
+ connection_global_id: context.connection_global_id,
40
+ context: context
41
+ ) do |connection|
39
42
  execute_with_connection(connection, context: context)
40
43
  end
41
44
  end
@@ -45,7 +45,8 @@ module Mongo
45
45
  def message(connection)
46
46
  updates = validate_updates(connection, send(IDENTIFIER))
47
47
  section = Protocol::Msg::Section1.new(IDENTIFIER, updates)
48
- Protocol::Msg.new(flags, {}, command(connection), section)
48
+ cmd = apply_relevant_timeouts_to(command(connection), connection)
49
+ Protocol::Msg.new(flags, {}, cmd, section)
49
50
  end
50
51
  end
51
52
  end
@@ -14,11 +14,11 @@ module Mongo
14
14
  # Returns the command to send to the database, describing the
15
15
  # desired updateSearchIndex operation.
16
16
  #
17
- # @param [ Mongo::Server ] _server the server that will receive the
17
+ # @param [ Connection ] _connection the connection that will receive the
18
18
  # command
19
19
  #
20
20
  # @return [ Hash ] the selector
21
- def selector(_server)
21
+ def selector(_connection)
22
22
  {
23
23
  updateSearchIndex: coll_name,
24
24
  :$db => db_name,
@@ -22,6 +22,7 @@ require 'mongo/operation/shared/specifiable'
22
22
  require 'mongo/operation/shared/validatable'
23
23
  require 'mongo/operation/shared/object_id_generator'
24
24
  require 'mongo/operation/shared/op_msg_executable'
25
+ require 'mongo/operation/shared/timed'
25
26
 
26
27
  require 'mongo/operation/op_msg_base'
27
28
  require 'mongo/operation/command'
@@ -244,10 +244,7 @@ module Mongo
244
244
  # timeout option. For compatibility with whoever might call this
245
245
  # method with some other IO-like object, pass options only when they
246
246
  # are not empty.
247
- read_options = {}
248
- if timeout = options[:socket_timeout]
249
- read_options[:timeout] = timeout
250
- end
247
+ read_options = options.slice(:timeout, :socket_timeout)
251
248
 
252
249
  if read_options.empty?
253
250
  chunk = io.read(16)
@@ -226,7 +226,7 @@ module Mongo
226
226
 
227
227
  db_name = @main_document[DATABASE_IDENTIFIER]
228
228
  cmd = merge_sections
229
- enc_cmd = context.encrypter.encrypt(db_name, cmd)
229
+ enc_cmd = context.encrypt(db_name, cmd)
230
230
  if cmd.key?('$db') && !enc_cmd.key?('$db')
231
231
  enc_cmd['$db'] = cmd['$db']
232
232
  end
@@ -251,7 +251,7 @@ module Mongo
251
251
  def maybe_decrypt(context)
252
252
  if context.decrypt?
253
253
  cmd = merge_sections
254
- enc_cmd = context.encrypter.decrypt(cmd)
254
+ enc_cmd = context.decrypt(cmd)
255
255
  Msg.new(@flags, @options, enc_cmd)
256
256
  else
257
257
  self
@@ -60,19 +60,21 @@ module Mongo
60
60
  # @param [ Mongo::ServerSelector::Selectable ] server_selector Server
61
61
  # selector for the operation.
62
62
  # @param [ CollectionView ] view The +CollectionView+ defining the query.
63
+ # @param [ Operation::Context | nil ] context the operation context to use
64
+ # with the cursor.
63
65
  # @param [ Proc ] block The block to execute.
64
66
  #
65
67
  # @return [ Cursor ] The cursor for the result set.
66
- def read_with_retry_cursor(session, server_selector, view, &block)
67
- read_with_retry(session, server_selector) do |server|
68
+ def read_with_retry_cursor(session, server_selector, view, context: nil, &block)
69
+ read_with_retry(session, server_selector, context) do |server|
68
70
  result = yield server
69
71
 
70
72
  # RUBY-2367: This will be updated to allow the query cache to
71
73
  # cache cursors with multi-batch results.
72
74
  if QueryCache.enabled? && !view.collection.system_collection?
73
- CachingCursor.new(view, result, server, session: session)
75
+ CachingCursor.new(view, result, server, session: session, context: context)
74
76
  else
75
- Cursor.new(view, result, server, session: session)
77
+ Cursor.new(view, result, server, session: session, context: context)
76
78
  end
77
79
  end
78
80
  end
@@ -107,16 +109,18 @@ module Mongo
107
109
  # is being run on.
108
110
  # @param [ Mongo::ServerSelector::Selectable | nil ] server_selector
109
111
  # Server selector for the operation.
112
+ # @param [ Mongo::Operation::Context | nil ] context Context for the
113
+ # read operation.
110
114
  # @param [ Proc ] block The block to execute.
111
115
  #
112
116
  # @return [ Result ] The result of the operation.
113
- def read_with_retry(session = nil, server_selector = nil, &block)
117
+ def read_with_retry(session = nil, server_selector = nil, context = nil, &block)
114
118
  if session.nil? && server_selector.nil?
115
119
  deprecated_legacy_read_with_retry(&block)
116
120
  elsif session&.retry_reads?
117
- modern_read_with_retry(session, server_selector, &block)
121
+ modern_read_with_retry(session, server_selector, context, &block)
118
122
  elsif client.max_read_retries > 0
119
- legacy_read_with_retry(session, server_selector, &block)
123
+ legacy_read_with_retry(session, server_selector, context, &block)
120
124
  else
121
125
  read_without_retry(session, server_selector, &block)
122
126
  end
@@ -186,17 +190,24 @@ module Mongo
186
190
  # being run on.
187
191
  # @param [ Mongo::ServerSelector::Selectable ] server_selector Server
188
192
  # selector for the operation.
193
+ # @param [ Mongo::Operation::Context ] context Context for the
194
+ # read operation.
189
195
  # @param [ Proc ] block The block to execute.
190
196
  #
191
197
  # @return [ Result ] The result of the operation.
192
- def modern_read_with_retry(session, server_selector, &block)
193
- server = select_server(cluster, server_selector, session)
198
+ def modern_read_with_retry(session, server_selector, context, &block)
199
+ server = select_server(
200
+ cluster,
201
+ server_selector,
202
+ session,
203
+ timeout: context&.remaining_timeout_sec
204
+ )
194
205
  yield server
195
- rescue *retryable_exceptions, Error::OperationFailure, Auth::Unauthorized, Error::PoolError => e
206
+ rescue *retryable_exceptions, Error::OperationFailure::Family, Auth::Unauthorized, Error::PoolError => e
196
207
  e.add_notes('modern retry', 'attempt 1')
197
208
  raise e if session.in_transaction?
198
209
  raise e if !is_retryable_exception?(e) && !e.write_retryable?
199
- retry_read(e, session, server_selector, failed_server: server, &block)
210
+ retry_read(e, session, server_selector, context: context, failed_server: server, &block)
200
211
  end
201
212
 
202
213
  # Attempts to do a "legacy" read with retry. The operation will be
@@ -207,17 +218,19 @@ module Mongo
207
218
  # being run on.
208
219
  # @param [ Mongo::ServerSelector::Selectable ] server_selector Server
209
220
  # selector for the operation.
221
+ # @param [ Mongo::Operation::Context | nil ] context Context for the
222
+ # read operation.
210
223
  # @param [ Proc ] block The block to execute.
211
224
  #
212
225
  # @return [ Result ] The result of the operation.
213
- def legacy_read_with_retry(session, server_selector, &block)
226
+ def legacy_read_with_retry(session, server_selector, context = nil, &block)
227
+ context&.check_timeout!
214
228
  attempt = attempt ? attempt + 1 : 1
215
229
  yield select_server(cluster, server_selector, session)
216
- rescue *legacy_retryable_exceptions, Error::OperationFailure => e
230
+ rescue *legacy_retryable_exceptions, Error::OperationFailure::Family => e
217
231
  e.add_notes('legacy retry', "attempt #{attempt}")
218
232
 
219
233
  if is_legacy_retryable_exception?(e)
220
-
221
234
  raise e if attempt > client.max_read_retries || session&.in_transaction?
222
235
  elsif e.retryable? && !session&.in_transaction?
223
236
  raise e if attempt > client.max_read_retries
@@ -245,7 +258,7 @@ module Mongo
245
258
 
246
259
  begin
247
260
  yield server
248
- rescue *retryable_exceptions, Error::PoolError, Error::OperationFailure => e
261
+ rescue *retryable_exceptions, Error::PoolError, Error::OperationFailure::Family => e
249
262
  e.add_note('retries disabled')
250
263
  raise e
251
264
  end
@@ -259,40 +272,67 @@ module Mongo
259
272
  # being run on.
260
273
  # @param [ Mongo::ServerSelector::Selectable ] server_selector Server
261
274
  # selector for the operation.
262
- # @param [ Mongo::Server ] failed_server The server on which the original
275
+ # @param [ Mongo::Operation::Context | nil ] :context Context for the
276
+ # read operation.
277
+ # @param [ Mongo::Server | nil ] :failed_server The server on which the original
263
278
  # operation failed.
264
279
  # @param [ Proc ] block The block to execute.
265
280
  #
266
281
  # @return [ Result ] The result of the operation.
267
- def retry_read(original_error, session, server_selector, failed_server: nil, &block)
268
- begin
269
- server = select_server(cluster, server_selector, session, failed_server)
270
- rescue Error, Error::AuthError => e
271
- original_error.add_note("later retry failed: #{e.class}: #{e}")
272
- raise original_error
273
- end
282
+ def retry_read(original_error, session, server_selector, context: nil, failed_server: nil, &block)
283
+ server = select_server_for_retry(
284
+ original_error, session, server_selector, context, failed_server
285
+ )
274
286
 
275
287
  log_retry(original_error, message: 'Read retry')
276
288
 
277
289
  begin
290
+ context&.check_timeout!
291
+ attempt = attempt ? attempt + 1 : 2
278
292
  yield server, true
293
+ rescue Error::TimeoutError
294
+ raise
279
295
  rescue *retryable_exceptions => e
280
- e.add_notes('modern retry', 'attempt 2')
281
- raise e
282
- rescue Error::OperationFailure, Error::PoolError => e
296
+ e.add_notes('modern retry', "attempt #{attempt}")
297
+ if context&.csot?
298
+ failed_server = server
299
+ retry
300
+ else
301
+ raise e
302
+ end
303
+ rescue Error::OperationFailure::Family, Error::PoolError => e
283
304
  e.add_note('modern retry')
284
- unless e.write_retryable?
305
+ if e.write_retryable?
306
+ e.add_note("attempt #{attempt}")
307
+ if context&.csot?
308
+ failed_server = server
309
+ retry
310
+ else
311
+ raise e
312
+ end
313
+ else
285
314
  original_error.add_note("later retry failed: #{e.class}: #{e}")
286
315
  raise original_error
287
316
  end
288
- e.add_note("attempt 2")
289
- raise e
290
317
  rescue Error, Error::AuthError => e
291
318
  e.add_note('modern retry')
292
319
  original_error.add_note("later retry failed: #{e.class}: #{e}")
293
320
  raise original_error
294
321
  end
295
322
  end
323
+
324
+ def select_server_for_retry(original_error, session, server_selector, context, failed_server)
325
+ select_server(
326
+ cluster,
327
+ server_selector,
328
+ session,
329
+ failed_server,
330
+ timeout: context&.remaining_timeout_sec
331
+ )
332
+ rescue Error, Error::AuthError => e
333
+ original_error.add_note("later retry failed: #{e.class}: #{e}")
334
+ raise original_error
335
+ end
296
336
  end
297
337
  end
298
338
  end
@@ -74,7 +74,11 @@ module Mongo
74
74
  # If we are here, session is not nil. A session being nil would have
75
75
  # failed retry_write_allowed? check.
76
76
 
77
- server = select_server(cluster, ServerSelector.primary, session)
77
+ server = select_server(
78
+ cluster, ServerSelector.primary,
79
+ session,
80
+ timeout: context.remaining_timeout_sec
81
+ )
78
82
 
79
83
  unless ending_transaction || server.retry_writes?
80
84
  return legacy_write_with_retry(server, context: context, &block)
@@ -110,7 +114,7 @@ module Mongo
110
114
  server.with_connection(connection_global_id: context.connection_global_id) do |connection|
111
115
  yield connection, nil, context
112
116
  end
113
- rescue *retryable_exceptions, Error::PoolError, Error::OperationFailure => e
117
+ rescue *retryable_exceptions, Error::PoolError, Error::OperationFailure::Family => e
114
118
  e.add_note('retries disabled')
115
119
  raise e
116
120
  end
@@ -170,6 +174,7 @@ module Mongo
170
174
  # @api private
171
175
  def legacy_write_with_retry(server = nil, context:)
172
176
  session = context.session
177
+ context.check_timeout!
173
178
 
174
179
  # This is the pre-session retry logic, and is not subject to
175
180
  # current retryable write specifications.
@@ -177,12 +182,20 @@ module Mongo
177
182
  attempt = 0
178
183
  begin
179
184
  attempt += 1
180
- server ||= select_server(cluster, ServerSelector.primary, session)
181
- server.with_connection(connection_global_id: context.connection_global_id) do |connection|
185
+ server ||= select_server(
186
+ cluster,
187
+ ServerSelector.primary,
188
+ session,
189
+ timeout: context.remaining_timeout_sec
190
+ )
191
+ server.with_connection(
192
+ connection_global_id: context.connection_global_id,
193
+ context: context
194
+ ) do |connection|
182
195
  # Legacy retries do not use txn_num
183
196
  yield connection, nil, context.dup
184
197
  end
185
- rescue Error::OperationFailure => e
198
+ rescue Error::OperationFailure::Family => e
186
199
  e.add_note('legacy retry')
187
200
  e.add_note("attempt #{attempt}")
188
201
  server = nil
@@ -220,7 +233,10 @@ module Mongo
220
233
  txn_num = nil
221
234
  connection_succeeded = false
222
235
 
223
- server.with_connection(connection_global_id: context.connection_global_id) do |connection|
236
+ server.with_connection(
237
+ connection_global_id: context.connection_global_id,
238
+ context: context
239
+ ) do |connection|
224
240
  connection_succeeded = true
225
241
 
226
242
  session.materialize_if_needed
@@ -230,10 +246,10 @@ module Mongo
230
246
  # it later for the retry as well.
231
247
  yield connection, txn_num, context.dup
232
248
  end
233
- rescue *retryable_exceptions, Error::PoolError, Auth::Unauthorized, Error::OperationFailure => e
249
+ rescue *retryable_exceptions, Error::PoolError, Auth::Unauthorized, Error::OperationFailure::Family => e
234
250
  e.add_notes('modern retry', 'attempt 1')
235
251
 
236
- if e.is_a?(Error::OperationFailure)
252
+ if e.is_a?(Error::OperationFailure::Family)
237
253
  ensure_retryable!(e)
238
254
  else
239
255
  ensure_labeled_retryable!(e, connection_succeeded, session)
@@ -256,6 +272,8 @@ module Mongo
256
272
  #
257
273
  # @return [ Result ] The result of the operation.
258
274
  def retry_write(original_error, txn_num, context:, failed_server: nil, &block)
275
+ context&.check_timeout!
276
+
259
277
  session = context.session
260
278
 
261
279
  # We do not request a scan of the cluster here, because error handling
@@ -263,7 +281,13 @@ module Mongo
263
281
  # server description and/or topology as necessary (specifically,
264
282
  # a socket error or a not master error should have marked the respective
265
283
  # server unknown). Here we just need to wait for server selection.
266
- server = select_server(cluster, ServerSelector.primary, session, failed_server)
284
+ server = select_server(
285
+ cluster,
286
+ ServerSelector.primary,
287
+ session,
288
+ failed_server,
289
+ timeout: context.remaining_timeout_sec
290
+ )
267
291
 
268
292
  unless server.retry_writes?
269
293
  # Do not need to add "modern retry" here, it should already be on
@@ -279,14 +303,21 @@ module Mongo
279
303
  raise Error::RaiseOriginalError
280
304
  end
281
305
 
306
+ attempt = attempt ? attempt + 1 : 2
282
307
  log_retry(original_error, message: 'Write retry')
283
308
  server.with_connection(connection_global_id: context.connection_global_id) do |connection|
284
309
  yield(connection, txn_num, context)
285
310
  end
286
311
  rescue *retryable_exceptions, Error::PoolError => e
287
- fail_on_retryable!(e, original_error)
288
- rescue Error::OperationFailure => e
289
- fail_on_operation_failure!(e, original_error)
312
+ maybe_fail_on_retryable(e, original_error, context, attempt)
313
+ failed_server = server
314
+ retry
315
+ rescue Error::OperationFailure::Family => e
316
+ maybe_fail_on_operation_failure(e, original_error, context, attempt)
317
+ failed_server = server
318
+ retry
319
+ rescue Mongo::Error::TimeoutError
320
+ raise
290
321
  rescue Error, Error::AuthError => e
291
322
  fail_on_other_error!(e, original_error)
292
323
  rescue Error::RaiseOriginalError
@@ -332,10 +363,10 @@ module Mongo
332
363
 
333
364
  # Raise either e, or original_error, depending on whether e is
334
365
  # write_retryable.
335
- def fail_on_retryable!(e, original_error)
366
+ def maybe_fail_on_retryable(e, original_error, context, attempt)
336
367
  if e.write_retryable?
337
- e.add_notes('modern retry', 'attempt 2')
338
- raise e
368
+ e.add_notes('modern retry', "attempt #{attempt}")
369
+ raise e unless context&.deadline
339
370
  else
340
371
  original_error.add_note("later retry failed: #{e.class}: #{e}")
341
372
  raise original_error
@@ -344,11 +375,11 @@ module Mongo
344
375
 
345
376
  # Raise either e, or original_error, depending on whether e is
346
377
  # appropriately labeled.
347
- def fail_on_operation_failure!(e, original_error)
378
+ def maybe_fail_on_operation_failure(e, original_error, context, attempt)
348
379
  e.add_note('modern retry')
349
380
  if e.label?('RetryableWriteError') && !e.label?('NoWritesPerformed')
350
- e.add_note('attempt 2')
351
- raise e
381
+ e.add_note("attempt #{attempt}")
382
+ raise e unless context&.deadline
352
383
  else
353
384
  original_error.add_note("later retry failed: #{e.class}: #{e}")
354
385
  raise original_error
@@ -46,8 +46,14 @@ module Mongo
46
46
  # @api private
47
47
  #
48
48
  # @return [ Mongo::Server ] A server matching the server preference.
49
- def select_server(cluster, server_selector, session, failed_server = nil)
50
- server_selector.select_server(cluster, nil, session, deprioritized: [failed_server].compact)
49
+ def select_server(cluster, server_selector, session, failed_server = nil, timeout: nil)
50
+ server_selector.select_server(
51
+ cluster,
52
+ nil,
53
+ session,
54
+ deprioritized: [failed_server].compact,
55
+ timeout: timeout
56
+ )
51
57
  end
52
58
 
53
59
  # Returns the read worker for handling retryable reads.
@@ -226,11 +226,11 @@ module Mongo
226
226
  # @return [ true ] If the connection succeeded.
227
227
  #
228
228
  # @since 2.0.0
229
- def connect!
229
+ def connect!(context = nil)
230
230
  raise_if_closed!
231
231
 
232
232
  unless @socket
233
- @socket = create_socket
233
+ @socket = create_socket(context)
234
234
  @description, @compressor = do_connect
235
235
 
236
236
  if server.load_balancer?
@@ -256,10 +256,16 @@ module Mongo
256
256
  #
257
257
  #
258
258
  # @return [ Socket ] The created socket.
259
- private def create_socket
259
+ private def create_socket(context = nil)
260
260
  add_server_diagnostics do
261
- address.socket(socket_timeout, ssl_options.merge(
262
- connection_address: address, connection_generation: generation, pipe: options[:pipe]))
261
+ opts = ssl_options.merge(
262
+ connection_address: address,
263
+ connection_generation: generation,
264
+ pipe: options[:pipe],
265
+ connect_timeout: context&.remaining_timeout_sec,
266
+ csot: !!context&.csot?
267
+ )
268
+ address.socket(socket_timeout, opts)
263
269
  end
264
270
  end
265
271
 
@@ -169,6 +169,7 @@ module Mongo
169
169
  raise Error::LintError, "Trying to deliver a message over a disconnected connection (to #{address})"
170
170
  end
171
171
  buffer = serialize(message, context)
172
+ check_timeout!(context)
172
173
  ensure_connected do |socket|
173
174
  operation_id = Monitoring.next_operation_id
174
175
  started_event = command_started(address, operation_id, message.payload,
@@ -181,9 +182,10 @@ module Mongo
181
182
  result = nil
182
183
  begin
183
184
  result = add_server_diagnostics do
184
- socket.write(buffer.to_s)
185
+ socket.write(buffer.to_s, timeout: context.remaining_timeout_sec)
185
186
  if message.replyable?
186
- Protocol::Message.deserialize(socket, max_message_size, message.request_id, options)
187
+ check_timeout!(context)
188
+ Protocol::Message.deserialize(socket, max_message_size, message.request_id, options.merge(timeout: context.remaining_timeout_sec))
187
189
  else
188
190
  nil
189
191
  end
@@ -273,6 +275,24 @@ module Mongo
273
275
 
274
276
  buffer
275
277
  end
278
+
279
+ # If timeoutMS is set for the operation context, checks whether there is
280
+ # enough time left to send the corresponding message to the server
281
+ # (remaining timeout is bigger than minimum round trip time for
282
+ # the server)
283
+ #
284
+ # @param [ Mongo::Operation::Context ] context Context of the operation.
285
+ #
286
+ # @raise [ Mongo::Error::TimeoutError ] if timeout expired or there is
287
+ # not enough time to send the message to the server.
288
+ def check_timeout!(context)
289
+ return if [nil, 0].include?(context.deadline)
290
+
291
+ time_to_execute = context.remaining_timeout_sec - server.minimum_round_trip_time
292
+ if time_to_execute <= 0
293
+ raise Mongo::Error::TimeoutError
294
+ end
295
+ end
276
296
  end
277
297
  end
278
298
  end