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
@@ -83,6 +83,12 @@ module Mongo
83
83
  @options.freeze
84
84
  @filename = @options[:filename]
85
85
  @open = true
86
+ @timeout_holder = CsotTimeoutHolder.new(
87
+ operation_timeouts: {
88
+ operation_timeout_ms: options[:timeout_ms],
89
+ inherited_timeout_ms: fs.database.timeout_ms
90
+ }
91
+ )
86
92
  end
87
93
 
88
94
  # Write to the GridFS bucket from the source stream or a string.
@@ -107,7 +113,12 @@ module Mongo
107
113
  end
108
114
  chunks = File::Chunk.split(io, file_info, @n)
109
115
  @n += chunks.size
110
- chunks_collection.insert_many(chunks) unless chunks.empty?
116
+ unless chunks.empty?
117
+ chunks_collection.insert_many(
118
+ chunks,
119
+ timeout_ms: @timeout_holder.remaining_timeout_ms!
120
+ )
121
+ end
111
122
  self
112
123
  end
113
124
 
@@ -124,7 +135,10 @@ module Mongo
124
135
  def close
125
136
  ensure_open!
126
137
  update_length
127
- files_collection.insert_one(file_info, @options)
138
+ files_collection.insert_one(
139
+ file_info,
140
+ @options.merge(timeout_ms: @timeout_holder.remaining_timeout_ms!)
141
+ )
128
142
  @open = false
129
143
  file_id
130
144
  end
@@ -166,7 +180,10 @@ module Mongo
166
180
  #
167
181
  # @since 2.1.0
168
182
  def abort
169
- fs.chunks_collection.find({ :files_id => file_id }, @options).delete_many
183
+ fs.chunks_collection.find(
184
+ { :files_id => file_id },
185
+ @options.merge(timeout_ms: @timeout_holder.remaining_timeout_ms!)
186
+ ).delete_many
170
187
  (@open = false) || true
171
188
  end
172
189
 
@@ -200,7 +217,7 @@ module Mongo
200
217
  end
201
218
 
202
219
  def ensure_indexes!
203
- fs.send(:ensure_indexes!)
220
+ fs.send(:ensure_indexes!, @timeout_holder)
204
221
  end
205
222
 
206
223
  def ensure_open!
@@ -15,6 +15,8 @@
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
17
 
18
+ require 'mongo/cursor/nontailable'
19
+
18
20
  module Mongo
19
21
  module Index
20
22
 
@@ -25,6 +27,8 @@ module Mongo
25
27
  extend Forwardable
26
28
  include Enumerable
27
29
  include Retryable
30
+ include Mongo::CursorHost
31
+ include Cursor::NonTailable
28
32
 
29
33
  # @return [ Collection ] collection The indexes collection.
30
34
  attr_reader :collection
@@ -33,6 +37,12 @@ module Mongo
33
37
  # when sending the listIndexes command.
34
38
  attr_reader :batch_size
35
39
 
40
+ # @return [ Integer | nil | The timeout_ms value that was passed as an
41
+ # option to the view.
42
+ #
43
+ # @api private
44
+ attr_reader :operation_timeout_ms
45
+
36
46
  def_delegators :@collection, :cluster, :database, :read_preference, :write_concern, :client
37
47
  def_delegators :cluster, :next_primary
38
48
 
@@ -90,7 +100,7 @@ module Mongo
90
100
  # @since 2.0.0
91
101
  def drop_one(name, options = {})
92
102
  raise Error::MultiIndexDrop.new if name == Index::ALL
93
- drop_by_name(name, comment: options[:comment])
103
+ drop_by_name(name, options)
94
104
  end
95
105
 
96
106
  # Drop all indexes on the collection.
@@ -107,7 +117,7 @@ module Mongo
107
117
  #
108
118
  # @since 2.0.0
109
119
  def drop_all(options = {})
110
- drop_by_name(Index::ALL, comment: options[:comment])
120
+ drop_by_name(Index::ALL, options)
111
121
  end
112
122
 
113
123
  # Creates an index on the collection.
@@ -161,7 +171,7 @@ module Mongo
161
171
  if session = @options[:session]
162
172
  create_options[:session] = session
163
173
  end
164
- %i(commit_quorum session comment).each do |key|
174
+ %i(commit_quorum session comment timeout_ms max_time_ms).each do |key|
165
175
  if value = options.delete(key)
166
176
  create_options[key] = value
167
177
  end
@@ -210,7 +220,7 @@ module Mongo
210
220
  options = models.pop
211
221
  end
212
222
 
213
- client.send(:with_session, @options.merge(options)) do |session|
223
+ client.with_session(@options.merge(options)) do |session|
214
224
  server = next_primary(nil, session)
215
225
 
216
226
  indexes = normalize_models(models, server)
@@ -229,8 +239,12 @@ module Mongo
229
239
  write_concern: write_concern,
230
240
  comment: options[:comment],
231
241
  }
232
-
233
- Operation::CreateIndex.new(spec).execute(server, context: Operation::Context.new(client: client, session: session))
242
+ context = Operation::Context.new(
243
+ client: client,
244
+ session: session,
245
+ operation_timeouts: operation_timeouts(options)
246
+ )
247
+ Operation::CreateIndex.new(spec).execute(server, context: context)
234
248
  end
235
249
  end
236
250
 
@@ -263,9 +277,15 @@ module Mongo
263
277
  #
264
278
  # @since 2.0.0
265
279
  def each(&block)
266
- session = client.send(:get_session, @options)
267
- cursor = read_with_retry_cursor(session, ServerSelector.primary, self) do |server|
268
- send_initial_query(server, session)
280
+ session = client.get_session(@options)
281
+ context = Operation::Context.new(
282
+ client: client,
283
+ session: session,
284
+ operation_timeouts: operation_timeouts(@options)
285
+ )
286
+
287
+ cursor = read_with_retry_cursor(session, ServerSelector.primary, self, context: context) do |server|
288
+ send_initial_query(server, session, context)
269
289
  end
270
290
  if block_given?
271
291
  cursor.each do |doc|
@@ -283,22 +303,53 @@ module Mongo
283
303
  #
284
304
  # @param [ Collection ] collection The collection.
285
305
  # @param [ Hash ] options Options for getting a list of indexes.
286
- # Only relevant for when the listIndexes command is used with server
287
- # versions >=2.8.
288
306
  #
289
307
  # @option options [ Integer ] :batch_size The batch size for results
290
308
  # returned from the listIndexes command.
309
+ # @option options [ :cursor_lifetime | :iteration ] :timeout_mode How to interpret
310
+ # :timeout_ms (whether it applies to the lifetime of the cursor, or per
311
+ # iteration).
312
+ # @option options [ Integer ] :timeout_ms The operation timeout in milliseconds.
313
+ # Must be a non-negative integer. An explicit value of 0 means infinite.
314
+ # The default value is unset which means the value is inherited from
315
+ # the collection or the database or the client.
291
316
  #
292
317
  # @since 2.0.0
293
318
  def initialize(collection, options = {})
294
319
  @collection = collection
320
+ @operation_timeout_ms = options.delete(:timeout_ms)
321
+
322
+ validate_timeout_mode!(options)
323
+
295
324
  @batch_size = options[:batch_size]
296
325
  @options = options
297
326
  end
298
327
 
328
+ # The timeout_ms value to use for this operation; either specified as an
329
+ # option to the view, or inherited from the collection.
330
+ #
331
+ # @return [ Integer | nil ] the timeout_ms for this operation
332
+ def timeout_ms
333
+ operation_timeout_ms || collection.timeout_ms
334
+ end
335
+
336
+ # @return [ Hash ] timeout_ms value set on the operation level (if any),
337
+ # and/or timeout_ms that is set on collection/database/client level (if any).
338
+ #
339
+ # @api private
340
+ def operation_timeouts(opts = {})
341
+ {}.tap do |result|
342
+ if opts[:timeout_ms] || operation_timeout_ms
343
+ result[:operation_timeout_ms] = opts.delete(:timeout_ms) || operation_timeout_ms
344
+ else
345
+ result[:inherited_timeout_ms] = collection.timeout_ms
346
+ end
347
+ end
348
+ end
349
+
299
350
  private
300
351
 
301
- def drop_by_name(name, comment: nil)
352
+ def drop_by_name(name, opts = {})
302
353
  client.send(:with_session, @options) do |session|
303
354
  spec = {
304
355
  db_name: database.name,
@@ -307,9 +358,14 @@ module Mongo
307
358
  session: session,
308
359
  write_concern: write_concern,
309
360
  }
310
- spec[:comment] = comment unless comment.nil?
361
+ spec[:comment] = opts[:comment] unless opts[:comment].nil?
311
362
  server = next_primary(nil, session)
312
- Operation::DropIndex.new(spec).execute(server, context: Operation::Context.new(client: client, session: session))
363
+ context = Operation::Context.new(
364
+ client: client,
365
+ session: session,
366
+ operation_timeouts: operation_timeouts(opts)
367
+ )
368
+ Operation::DropIndex.new(spec).execute(server, context: context)
313
369
  end
314
370
  end
315
371
 
@@ -347,8 +403,13 @@ module Mongo
347
403
  end
348
404
  end
349
405
 
350
- def send_initial_query(server, session)
351
- initial_query_op(session).execute(server, context: Operation::Context.new(client: client, session: session))
406
+ def send_initial_query(server, session, context)
407
+ if server.load_balancer?
408
+ connection = server.pool.check_out(context: context)
409
+ initial_query_op(session).execute_with_connection(connection, context: context)
410
+ else
411
+ initial_query_op(session).execute(server, context: context)
412
+ end
352
413
  end
353
414
  end
354
415
  end
@@ -34,8 +34,15 @@ module Mongo
34
34
  # operations.
35
35
  #
36
36
  # @api private
37
- class Context
38
- def initialize(client: nil, session: nil, connection_global_id: nil, options: nil)
37
+ class Context < CsotTimeoutHolder
38
+ def initialize(
39
+ client: nil,
40
+ session: nil,
41
+ connection_global_id: nil,
42
+ operation_timeouts: {},
43
+ view: nil,
44
+ options: nil
45
+ )
39
46
  if options
40
47
  if client
41
48
  raise ArgumentError, 'Client and options cannot both be specified'
@@ -52,14 +59,33 @@ module Mongo
52
59
 
53
60
  @client = client
54
61
  @session = session
62
+ @view = view
55
63
  @connection_global_id = connection_global_id
56
64
  @options = options
65
+ super(session: session, operation_timeouts: operation_timeouts)
57
66
  end
58
67
 
59
68
  attr_reader :client
60
69
  attr_reader :session
70
+ attr_reader :view
61
71
  attr_reader :options
62
72
 
73
+ # Returns a new Operation::Context with the deadline refreshed
74
+ # and relative to the current moment.
75
+ #
76
+ # @return [ Operation::Context ] the refreshed context
77
+ def refresh(connection_global_id: @connection_global_id, timeout_ms: nil, view: nil)
78
+ operation_timeouts = @operation_timeouts
79
+ operation_timeouts = operation_timeouts.merge(operation_timeout_ms: timeout_ms) if timeout_ms
80
+
81
+ self.class.new(client: client,
82
+ session: session,
83
+ connection_global_id: connection_global_id,
84
+ operation_timeouts: operation_timeouts,
85
+ view: view || self.view,
86
+ options: options)
87
+ end
88
+
63
89
  def connection_global_id
64
90
  @connection_global_id || session&.pinned_connection_global_id
65
91
  end
@@ -122,10 +148,18 @@ module Mongo
122
148
  client&.encrypter&.encrypt? || false
123
149
  end
124
150
 
151
+ def encrypt(db_name, cmd)
152
+ encrypter.encrypt(db_name, cmd, self)
153
+ end
154
+
125
155
  def decrypt?
126
156
  !!client&.encrypter
127
157
  end
128
158
 
159
+ def decrypt(cmd)
160
+ encrypter.decrypt(cmd, self)
161
+ end
162
+
129
163
  def encrypter
130
164
  if client&.encrypter
131
165
  client.encrypter
@@ -133,6 +167,10 @@ module Mongo
133
167
  raise Error::InternalDriverError, 'Encrypter should only be accessed when encryption is to be performed'
134
168
  end
135
169
  end
170
+
171
+ def inspect
172
+ "#<#{self.class} connection_global_id=#{connection_global_id.inspect} deadline=#{deadline.inspect} options=#{options.inspect} operation_timeouts=#{operation_timeouts.inspect}>"
173
+ end
136
174
  end
137
175
  end
138
176
  end
@@ -14,11 +14,11 @@ module Mongo
14
14
  # Returns the command to send to the database, describing the
15
15
  # desired createSearchIndexes 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
  createSearchIndexes: coll_name,
24
24
  :$db => db_name,
@@ -49,7 +49,8 @@ module Mongo
49
49
 
50
50
  def message(connection)
51
51
  section = Protocol::Msg::Section1.new(IDENTIFIER, send(IDENTIFIER))
52
- Protocol::Msg.new(flags, {}, command(connection), section)
52
+ cmd = apply_relevant_timeouts_to(command(connection), connection)
53
+ Protocol::Msg.new(flags, {}, cmd, section)
53
54
  end
54
55
  end
55
56
  end
@@ -14,11 +14,11 @@ module Mongo
14
14
  # Returns the command to send to the database, describing the
15
15
  # desired dropSearchIndex 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
  dropSearchIndex: coll_name,
24
24
  :$db => db_name,
@@ -31,6 +31,51 @@ module Mongo
31
31
 
32
32
  private
33
33
 
34
+ # Applies the relevant CSOT timeouts for a find command.
35
+ # Considers the cursor type and timeout mode and will add (or omit) a
36
+ # maxTimeMS field accordingly.
37
+ def apply_relevant_timeouts_to(spec, connection)
38
+ with_max_time(connection) do |max_time_sec|
39
+ timeout_ms = max_time_sec ? (max_time_sec * 1_000).to_i : nil
40
+ apply_find_timeouts_to(spec, timeout_ms) unless connection.description.mongocryptd?
41
+ end
42
+ end
43
+
44
+ def apply_find_timeouts_to(spec, timeout_ms)
45
+ view = context&.view
46
+ return spec unless view
47
+
48
+ case view.cursor_type
49
+ when nil # non-tailable
50
+ if view.timeout_mode == :cursor_lifetime
51
+ spec[:maxTimeMS] = timeout_ms || view.options[:max_time_ms]
52
+ else # timeout_mode == :iterable
53
+ # drivers MUST honor the timeoutMS option for the initial command
54
+ # but MUST NOT append a maxTimeMS field to the command sent to the
55
+ # server
56
+ if !timeout_ms && view.options[:max_time_ms]
57
+ spec[:maxTimeMS] = view.options[:max_time_ms]
58
+ end
59
+ end
60
+
61
+ when :tailable
62
+ # If timeoutMS is set, drivers...MUST NOT append a maxTimeMS field to any commands.
63
+ if !timeout_ms && view.options[:max_time_ms]
64
+ spec[:maxTimeMS] = view.options[:max_time_ms]
65
+ end
66
+
67
+ when :tailable_await
68
+ # The server supports the maxTimeMS option for the original command.
69
+ if timeout_ms || view.options[:max_time_ms]
70
+ spec[:maxTimeMS] = timeout_ms || view.options[:max_time_ms]
71
+ end
72
+ end
73
+
74
+ spec.tap do |spc|
75
+ spc.delete(:maxTimeMS) if spc[:maxTimeMS].nil?
76
+ end
77
+ end
78
+
34
79
  def selector(connection)
35
80
  # The mappings are BSON::Documents and as such store keys as
36
81
  # strings, the spec here has symbol keys.
@@ -28,6 +28,39 @@ module Mongo
28
28
  include ExecutableTransactionLabel
29
29
  include PolymorphicResult
30
30
  include CommandBuilder
31
+
32
+ private
33
+
34
+ # Applies the relevant CSOT timeouts for a getMore command.
35
+ # Considers the cursor type and timeout mode and will add (or omit) a
36
+ # maxTimeMS field accordingly.
37
+ def apply_relevant_timeouts_to(spec, connection)
38
+ with_max_time(connection) do |max_time_sec|
39
+ timeout_ms = max_time_sec ? (max_time_sec * 1_000).to_i : nil
40
+ apply_get_more_timeouts_to(spec, timeout_ms)
41
+ end
42
+ end
43
+
44
+ def apply_get_more_timeouts_to(spec, timeout_ms)
45
+ view = context&.view
46
+ return spec unless view
47
+
48
+ if view.cursor_type == :tailable_await
49
+ # If timeoutMS is set, drivers MUST apply it to the original operation.
50
+ # Drivers MUST also apply the original timeoutMS value to each next
51
+ # call on the resulting cursor but MUST NOT use it to derive a
52
+ # maxTimeMS value for getMore commands. Helpers for operations that
53
+ # create tailable awaitData cursors MUST also support the
54
+ # maxAwaitTimeMS option. Drivers MUST error if this option is set,
55
+ # timeoutMS is set to a non-zero value, and maxAwaitTimeMS is greater
56
+ # than or equal to timeoutMS. If this option is set, drivers MUST use
57
+ # it as the maxTimeMS field on getMore commands.
58
+ max_await_time_ms = view.respond_to?(:max_await_time_ms) ? view.max_await_time_ms : nil
59
+ spec[:maxTimeMS] = max_await_time_ms if max_await_time_ms
60
+ end
61
+
62
+ spec
63
+ end
31
64
  end
32
65
  end
33
66
  end
@@ -35,7 +35,7 @@ module Mongo
35
35
 
36
36
  def get_result(connection, context, options = {})
37
37
  # This is a Mongo::Operation::Insert::Result
38
- Result.new(*dispatch_message(connection, context), @ids)
38
+ Result.new(*dispatch_message(connection, context), @ids, context: context)
39
39
  end
40
40
 
41
41
  def selector(connection)
@@ -49,7 +49,8 @@ module Mongo
49
49
 
50
50
  def message(connection)
51
51
  section = Protocol::Msg::Section1.new(IDENTIFIER, send(IDENTIFIER))
52
- Protocol::Msg.new(flags, {}, command(connection), section)
52
+ cmd = apply_relevant_timeouts_to(command(connection), connection)
53
+ Protocol::Msg.new(flags, {}, cmd, section)
53
54
  end
54
55
  end
55
56
  end
@@ -47,11 +47,13 @@ module Mongo
47
47
  # Global id of the connection on which the operation that
48
48
  # this result is for was performed.
49
49
  # @param [ Array<Object> ] ids The ids of the inserted documents.
50
+ # @param [ Operation::Context | nil ] context the operation context that
51
+ # was active when this result was produced.
50
52
  #
51
53
  # @since 2.0.0
52
54
  # @api private
53
- def initialize(replies, connection_description, connection_global_id, ids)
54
- super(replies, connection_description, connection_global_id)
55
+ def initialize(replies, connection_description, connection_global_id, ids, context: nil)
56
+ super(replies, connection_description, connection_global_id, context: context)
55
57
  @inserted_ids = ids
56
58
  end
57
59
 
@@ -85,7 +85,7 @@ module Mongo
85
85
  if successful?
86
86
  self
87
87
  else
88
- raise Error::OperationFailure.new(
88
+ raise operation_failure_class.new(
89
89
  parser.message,
90
90
  self,
91
91
  code: parser.code,
@@ -108,7 +108,7 @@ module Mongo
108
108
  # @example Validate the result.
109
109
  # result.validate!
110
110
  #
111
- # @raise [ Error::OperationFailure ] If an error is in the result.
111
+ # @raise [ Error::OperationFailure::Family ] If an error is in the result.
112
112
  #
113
113
  # @return [ Result ] The result if verification passed.
114
114
  #
@@ -22,11 +22,13 @@ module Mongo
22
22
  include Specifiable
23
23
  include Executable
24
24
  include SessionsSupported
25
+ include Timed
25
26
 
26
27
  private
27
28
 
28
29
  def message(connection)
29
- Protocol::Msg.new(flags, options(connection), command(connection))
30
+ cmd = apply_relevant_timeouts_to(command(connection), connection)
31
+ Protocol::Msg.new(flags, options(connection), cmd)
30
32
  end
31
33
  end
32
34
  end
@@ -100,9 +100,13 @@ module Mongo
100
100
  # @param [ Integer ] connection_global_id
101
101
  # Global id of the connection on which the operation that
102
102
  # this result is for was performed.
103
+ # @param [ Operation::Context | nil ] context the context that was active
104
+ # when this result was produced.
103
105
  #
104
106
  # @api private
105
- def initialize(replies, connection_description = nil, connection_global_id = nil)
107
+ def initialize(replies, connection_description = nil, connection_global_id = nil, context: nil, connection: nil)
108
+ @context = context
109
+
106
110
  if replies
107
111
  if replies.is_a?(Array)
108
112
  if replies.length != 1
@@ -118,6 +122,7 @@ module Mongo
118
122
  @replies = [ reply ]
119
123
  @connection_description = connection_description
120
124
  @connection_global_id = connection_global_id
125
+ @connection = connection
121
126
  end
122
127
  end
123
128
 
@@ -138,6 +143,14 @@ module Mongo
138
143
  # @api private
139
144
  attr_reader :connection_global_id
140
145
 
146
+ # @return [ Operation::Context | nil ] the operation context (if any)
147
+ # that was active when this result was produced.
148
+ #
149
+ # @api private
150
+ attr_reader :context
151
+
152
+ attr_reader :connection
153
+
141
154
  # @api private
142
155
  def_delegators :parser,
143
156
  :not_master?, :node_recovering?, :node_shutting_down?
@@ -320,7 +333,7 @@ module Mongo
320
333
  # @example Validate the result.
321
334
  # result.validate!
322
335
  #
323
- # @raise [ Error::OperationFailure ] If an error is in the result.
336
+ # @raise [ Error::OperationFailure::Family ] If an error is in the result.
324
337
  #
325
338
  # @return [ Result ] The result if verification passed.
326
339
  #
@@ -330,16 +343,16 @@ module Mongo
330
343
  !successful? ? raise_operation_failure : self
331
344
  end
332
345
 
333
- # The exception instance (of the Error::OperationFailure class)
346
+ # The exception instance (of Error::OperationFailure::Family)
334
347
  # that would be raised during processing of this result.
335
348
  #
336
349
  # This method should only be called when result is not successful.
337
350
  #
338
- # @return [ Error::OperationFailure ] The exception.
351
+ # @return [ Error::OperationFailure::Family ] The exception.
339
352
  #
340
353
  # @api private
341
354
  def error
342
- @error ||= Error::OperationFailure.new(
355
+ @error ||= operation_failure_class.new(
343
356
  parser.message,
344
357
  self,
345
358
  code: parser.code,
@@ -453,6 +466,14 @@ module Mongo
453
466
 
454
467
  private
455
468
 
469
+ def operation_failure_class
470
+ if context&.csot? && parser.code == 50
471
+ Error::ServerTimeoutError
472
+ else
473
+ Error::OperationFailure
474
+ end
475
+ end
476
+
456
477
  def aggregate_returned_count
457
478
  replies.reduce(0) do |n, reply|
458
479
  n += reply.number_returned
@@ -28,7 +28,18 @@ module Mongo
28
28
 
29
29
  include ResponseHandling
30
30
 
31
+ # @return [ Operation::Context | nil ] the operation context used to
32
+ # execute this operation.
33
+ attr_accessor :context
34
+
31
35
  def do_execute(connection, context, options = {})
36
+ # Save the context on the instance, to avoid having to pass it as a
37
+ # parameter to every single method. There are many legacy methods that
38
+ # still accept it as a parameter, which are left as-is for now to
39
+ # minimize the impact of this change. Moving forward, it may be
40
+ # reasonable to refactor things so this saved reference is used instead.
41
+ @context = context
42
+
32
43
  session&.materialize_if_needed
33
44
  unpin_maybe(session, connection) do
34
45
  add_error_labels(connection, context) do
@@ -93,7 +104,7 @@ module Mongo
93
104
  end
94
105
 
95
106
  def get_result(connection, context, options = {})
96
- result_class.new(*dispatch_message(connection, context, options))
107
+ result_class.new(*dispatch_message(connection, context, options), context: context, connection: connection)
97
108
  end
98
109
 
99
110
  # Returns a Protocol::Message or nil as reply.
@@ -32,7 +32,10 @@ module Mongo
32
32
  #
33
33
  # @return [ Mongo::Operation::Result ] The operation result.
34
34
  def execute(server, context:, options: {})
35
- server.with_connection(connection_global_id: context.connection_global_id) do |connection|
35
+ server.with_connection(
36
+ connection_global_id: context.connection_global_id,
37
+ context: context
38
+ ) do |connection|
36
39
  execute_with_connection(connection, context: context, options: options)
37
40
  end
38
41
  end