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
@@ -73,47 +73,66 @@ module Mongo
73
73
  # filter
74
74
  #
75
75
  # @param [ Hash ] filter
76
+ # @param [ Integer ] :timeout_ms The operation timeout in milliseconds.
77
+ # Must be a non-negative integer. An explicit value of 0 means infinite.
78
+ # The default value is unset which means the feature is not enabled.
76
79
  #
77
80
  # @return [ Array<BSON::Document> ] The query results
78
- def find_keys(filter)
79
- key_vault_collection.find(filter).to_a
81
+ def find_keys(filter, timeout_ms: nil)
82
+ key_vault_collection.find(filter, timeout_ms: timeout_ms).to_a
80
83
  end
81
84
 
82
85
  # Insert a document into the key vault collection
83
86
  #
84
87
  # @param [ Hash ] document
88
+ # @param [ Integer ] :timeout_ms The operation timeout in milliseconds.
89
+ # Must be a non-negative integer. An explicit value of 0 means infinite.
90
+ # The default value is unset which means the feature is not enabled.
85
91
  #
86
92
  # @return [ Mongo::Operation::Insert::Result ] The insertion result
87
- def insert_data_key(document)
88
- key_vault_collection.insert_one(document)
93
+ def insert_data_key(document, timeout_ms: nil)
94
+ key_vault_collection.insert_one(document, timeout_ms: timeout_ms)
89
95
  end
90
96
 
91
97
  # Get collection info for a collection matching the provided filter
92
98
  #
93
99
  # @param [ Hash ] filter
100
+ # @param [ Integer ] :timeout_ms The operation timeout in milliseconds.
101
+ # Must be a non-negative integer. An explicit value of 0 means infinite.
102
+ # The default value is unset which means the feature is not enabled.
94
103
  #
95
104
  # @return [ Hash ] The collection information
96
- def collection_info(db_name, filter)
105
+ def collection_info(db_name, filter, timeout_ms: nil)
97
106
  unless @metadata_client
98
107
  raise ArgumentError, 'collection_info requires metadata_client to have been passed to the constructor, but it was not'
99
108
  end
100
109
 
101
- @metadata_client.use(db_name).database.list_collections(filter: filter, deserialize_as_bson: true).first
110
+ @metadata_client
111
+ .use(db_name)
112
+ .database
113
+ .list_collections(filter: filter, deserialize_as_bson: true, timeout_ms: timeout_ms)
114
+ .first
102
115
  end
103
116
 
104
117
  # Send the command to mongocryptd to be marked with intent-to-encrypt markings
105
118
  #
106
119
  # @param [ Hash ] cmd
120
+ # @param [ Integer ] :timeout_ms The operation timeout in milliseconds.
121
+ # Must be a non-negative integer. An explicit value of 0 means infinite.
122
+ # The default value is unset which means the feature is not enabled.
107
123
  #
108
124
  # @return [ Hash ] The marked command
109
- def mark_command(cmd)
125
+ def mark_command(cmd, timeout_ms: nil)
110
126
  unless @mongocryptd_client
111
127
  raise ArgumentError, 'mark_command requires mongocryptd_client to have been passed to the constructor, but it was not'
112
128
  end
113
129
 
114
130
  # Ensure the response from mongocryptd is deserialized with { mode: :bson }
115
131
  # to prevent losing type information in commands
116
- options = { execution_options: { deserialize_as_bson: true } }
132
+ options = {
133
+ execution_options: { deserialize_as_bson: true },
134
+ timeout_ms: timeout_ms
135
+ }
117
136
 
118
137
  begin
119
138
  response = @mongocryptd_client.database.command(cmd, options)
@@ -136,9 +155,12 @@ module Mongo
136
155
  # to send on that connection.
137
156
  # @param [ Hash ] tls_options. TLS options to connect to KMS provider.
138
157
  # The options are same as for Mongo::Client.
139
- def feed_kms(kms_context, tls_options)
158
+ # @param [ Integer ] :timeout_ms The operation timeout in milliseconds.
159
+ # Must be a non-negative integer. An explicit value of 0 means infinite.
160
+ # The default value is unset which means the feature is not enabled.
161
+ def feed_kms(kms_context, tls_options, timeout_ms: nil)
140
162
  with_ssl_socket(kms_context.endpoint, tls_options) do |ssl_socket|
141
- Timeout.timeout(SOCKET_TIMEOUT, Error::SocketTimeoutError,
163
+ Timeout.timeout(timeout_ms || SOCKET_TIMEOUT, Error::SocketTimeoutError,
142
164
  'Socket write operation timed out'
143
165
  ) do
144
166
  ssl_socket.syswrite(kms_context.message)
@@ -146,7 +168,7 @@ module Mongo
146
168
 
147
169
  bytes_needed = kms_context.bytes_needed
148
170
  while bytes_needed > 0 do
149
- bytes = Timeout.timeout(SOCKET_TIMEOUT, Error::SocketTimeoutError,
171
+ bytes = Timeout.timeout(timeout_ms || SOCKET_TIMEOUT, Error::SocketTimeoutError,
150
172
  'Socket read operation timed out'
151
173
  ) do
152
174
  ssl_socket.sysread(bytes_needed)
@@ -160,38 +182,39 @@ module Mongo
160
182
 
161
183
  # Adds a key_alt_name to the key_alt_names array of the key document
162
184
  # in the key vault collection with the given id.
163
- def add_key_alt_name(id, key_alt_name)
185
+ def add_key_alt_name(id, key_alt_name, timeout_ms: nil)
164
186
  key_vault_collection.find_one_and_update(
165
187
  { _id: id },
166
188
  { '$addToSet' => { keyAltNames: key_alt_name } },
189
+ timeout_ms: timeout_ms
167
190
  )
168
191
  end
169
192
 
170
193
  # Removes the key document with the given id
171
194
  # from the key vault collection.
172
- def delete_key(id)
173
- key_vault_collection.delete_one(_id: id)
195
+ def delete_key(id, timeout_ms: nil)
196
+ key_vault_collection.delete_one(_id: id, timeout_ms: timeout_ms)
174
197
  end
175
198
 
176
199
  # Finds a single key document with the given id.
177
- def get_key(id)
178
- key_vault_collection.find(_id: id).first
200
+ def get_key(id, timeout_ms: nil)
201
+ key_vault_collection.find(_id: id, timeout_ms: timeout_ms).first
179
202
  end
180
203
 
181
204
  # Returns a key document in the key vault collection with
182
205
  # the given key_alt_name.
183
- def get_key_by_alt_name(key_alt_name)
184
- key_vault_collection.find(keyAltNames: key_alt_name).first
206
+ def get_key_by_alt_name(key_alt_name, timeout_ms: nil)
207
+ key_vault_collection.find(keyAltNames: key_alt_name, timeout_ms: timeout_ms).first
185
208
  end
186
209
 
187
210
  # Finds all documents in the key vault collection.
188
- def get_keys
189
- key_vault_collection.find
211
+ def get_keys(timeout_ms: nil)
212
+ key_vault_collection.find(nil, timeout_ms: timeout_ms)
190
213
  end
191
214
 
192
215
  # Removes a key_alt_name from the key_alt_names array of the key document
193
216
  # in the key vault collection with the given id.
194
- def remove_key_alt_name(id, key_alt_name)
217
+ def remove_key_alt_name(id, key_alt_name, timeout_ms: nil)
195
218
  key_vault_collection.find_one_and_update(
196
219
  { _id: id },
197
220
  [
@@ -211,7 +234,8 @@ module Mongo
211
234
  }
212
235
  }
213
236
  }
214
- ]
237
+ ],
238
+ timeout_ms: timeout_ms
215
239
  )
216
240
  end
217
241
 
@@ -220,8 +244,8 @@ module Mongo
220
244
  # @param [ Array<Hash> ] requests The bulk write requests.
221
245
  #
222
246
  # @return [ BulkWrite::Result ] The result of the operation.
223
- def update_data_keys(updates)
224
- key_vault_collection.bulk_write(updates)
247
+ def update_data_keys(updates, timeout_ms: nil)
248
+ key_vault_collection.bulk_write(updates, timeout_ms: timeout_ms)
225
249
  end
226
250
 
227
251
  private
@@ -322,15 +346,21 @@ module Mongo
322
346
  #
323
347
  # @note The socket is always closed when the provided block has finished
324
348
  # executing
325
- def with_ssl_socket(endpoint, tls_options)
349
+ def with_ssl_socket(endpoint, tls_options, timeout_ms: nil)
350
+ csot = !timeout_ms.nil?
326
351
  address = begin
327
352
  host, port = endpoint.split(':')
328
353
  port ||= 443 # All supported KMS APIs use this port by default.
329
354
  Address.new([host, port].join(':'))
330
355
  end
356
+ socket_options = { ssl: true, csot: csot }.tap do |opts|
357
+ if csot
358
+ opts[:connect_timeout] = (timeout_ms / 1_000.0)
359
+ end
360
+ end
331
361
  mongo_socket = address.socket(
332
362
  SOCKET_TIMEOUT,
333
- tls_options.merge(ssl: true)
363
+ tls_options.merge(socket_options)
334
364
  )
335
365
  yield(mongo_socket.socket)
336
366
  rescue => e
@@ -35,7 +35,9 @@ module Mongo
35
35
  # providers. Keys of the hash should be KSM provider names; values
36
36
  # should be hashes of TLS connection options. The options are equivalent
37
37
  # to TLS connection options of Mongo::Client.
38
- def initialize(key_vault_client, key_vault_namespace, kms_providers, kms_tls_options)
38
+ # @param [ Integer | nil ] timeout_ms Timeout for every operation executed
39
+ # on this object.
40
+ def initialize(key_vault_client, key_vault_namespace, kms_providers, kms_tls_options, timeout_ms = nil)
39
41
  Crypt.validate_ffi!
40
42
  @crypt_handle = Handle.new(
41
43
  kms_providers,
@@ -47,6 +49,7 @@ module Mongo
47
49
  metadata_client: nil,
48
50
  key_vault_namespace: key_vault_namespace
49
51
  )
52
+ @timeout_ms = timeout_ms
50
53
  end
51
54
 
52
55
  # Generates a data key used for encryption/decryption and stores
@@ -71,9 +74,11 @@ module Mongo
71
74
  master_key_document,
72
75
  key_alt_names,
73
76
  key_material
74
- ).run_state_machine
77
+ ).run_state_machine(timeout_holder)
75
78
 
76
- @encryption_io.insert_data_key(data_key_document).inserted_id
79
+ @encryption_io.insert_data_key(
80
+ data_key_document, timeout_ms: timeout_holder.remaining_timeout_ms!
81
+ ).inserted_id
77
82
  end
78
83
 
79
84
  # Encrypts a value using the specified encryption key and algorithm
@@ -111,7 +116,7 @@ module Mongo
111
116
  @encryption_io,
112
117
  { v: value },
113
118
  options
114
- ).run_state_machine['v']
119
+ ).run_state_machine(timeout_holder)['v']
115
120
  end
116
121
 
117
122
  # Encrypts a Match Expression or Aggregate Expression to query a range index.
@@ -125,7 +130,7 @@ module Mongo
125
130
  # {'$and' => [{'$gt' => ['$field', 10]}, {'$lt' => ['$field', 20]}}
126
131
  # )
127
132
  # {$and: [{$gt: [<fieldpath>, <value1>]}, {$lt: [<fieldpath>, <value2>]}]
128
- # Only supported when queryType is "rangePreview" and algorithm is "RangePreview".
133
+ # Only supported when queryType is "range" and algorithm is "Range".
129
134
  # @note: The Range algorithm is experimental only. It is not intended
130
135
  # for public use. It is subject to breaking changes.
131
136
  #
@@ -137,24 +142,25 @@ module Mongo
137
142
  # @option options [ String ] :key_alt_name The alternate name for the
138
143
  # encryption key.
139
144
  # @option options [ String ] :algorithm The algorithm used to encrypt the
140
- # expression. The only allowed value is "RangePreview"
145
+ # expression. The only allowed value is "Range"
141
146
  # @option options [ Integer | nil ] :contention_factor Contention factor
142
147
  # to be applied If not provided, it defaults to a value of 0.
143
148
  # @option options [ String | nil ] query_type Query type to be applied.
144
- # The only allowed value is "rangePreview".
149
+ # The only allowed value is "range".
145
150
  # @option options [ Hash | nil ] :range_opts Specifies index options for
146
- # a Queryable Encryption field supporting "rangePreview" queries.
151
+ # a Queryable Encryption field supporting "range" queries.
147
152
  # Allowed options are:
148
153
  # - :min
149
154
  # - :max
155
+ # - :trim_factor
150
156
  # - :sparsity
151
157
  # - :precision
152
- # min, max, sparsity, and range must match the values set in
158
+ # min, max, trim_factor, sparsity, and precision must match the values set in
153
159
  # the encryptedFields of the destination collection.
154
160
  # For double and decimal128, min/max/precision must all be set,
155
161
  # or all be unset.
156
162
  #
157
- # @note The RangePreview algorithm is experimental only. It is not
163
+ # @note The Range algorithm is experimental only. It is not
158
164
  # intended for public use.
159
165
  #
160
166
  # @note The :key_id and :key_alt_name options are mutually exclusive. Only
@@ -170,7 +176,7 @@ module Mongo
170
176
  @encryption_io,
171
177
  { v: expression },
172
178
  options
173
- ).run_state_machine['v']
179
+ ).run_state_machine(timeout_holder)['v']
174
180
  end
175
181
 
176
182
  # Decrypts a value that has already been encrypted
@@ -184,7 +190,7 @@ module Mongo
184
190
  @crypt_handle,
185
191
  @encryption_io,
186
192
  { v: value }
187
- ).run_state_machine['v']
193
+ ).run_state_machine(timeout_holder)['v']
188
194
  end
189
195
 
190
196
  # Adds a key_alt_name for the key in the key vault collection with the given id.
@@ -195,7 +201,7 @@ module Mongo
195
201
  # @return [ BSON::Document | nil ] Document describing the identified key
196
202
  # before adding the key alt name, or nil if no such key.
197
203
  def add_key_alt_name(id, key_alt_name)
198
- @encryption_io.add_key_alt_name(id, key_alt_name)
204
+ @encryption_io.add_key_alt_name(id, key_alt_name, timeout_ms: @timeout_ms)
199
205
  end
200
206
 
201
207
  # Removes the key with the given id from the key vault collection.
@@ -204,7 +210,9 @@ module Mongo
204
210
  #
205
211
  # @return [ Operation::Result ] The response from the database for the delete_one
206
212
  # operation that deletes the key.
207
- def_delegators :@encryption_io, :delete_key
213
+ def delete_key(id)
214
+ @encryption_io.delete_key(id, timeout_ms: @timeout_ms)
215
+ end
208
216
 
209
217
  # Finds a single key with the given id.
210
218
  #
@@ -212,7 +220,9 @@ module Mongo
212
220
  #
213
221
  # @return [ BSON::Document | nil ] The found key document or nil
214
222
  # if not found.
215
- def_delegators :@encryption_io, :get_key
223
+ def get_key(id)
224
+ @encryption_io.get_key(id, timeout_ms: @timeout_ms)
225
+ end
216
226
 
217
227
  # Returns a key in the key vault collection with the given key_alt_name.
218
228
  #
@@ -220,12 +230,19 @@ module Mongo
220
230
  #
221
231
  # @return [ BSON::Document | nil ] The found key document or nil
222
232
  # if not found.
223
- def_delegators :@encryption_io, :get_key_by_alt_name
233
+ def get_key_by_alt_name(key_alt_name)
234
+ @encryption_io.get_key_by_alt_name(key_alt_name, timeout_ms: @timeout_ms)
235
+ end
224
236
 
225
237
  # Returns all keys in the key vault collection.
226
238
  #
227
239
  # @return [ Collection::View ] Keys in the key vault collection.
228
- def_delegators :@encryption_io, :get_keys
240
+ # rubocop:disable Naming/AccessorMethodName
241
+ # Name of this method is defined in the FLE spec
242
+ def get_keys
243
+ @encryption_io.get_keys(timeout_ms: @timeout_ms)
244
+ end
245
+ # rubocop:enable Naming/AccessorMethodName
229
246
 
230
247
  # Removes a key_alt_name from a key in the key vault collection with the given id.
231
248
  #
@@ -234,7 +251,9 @@ module Mongo
234
251
  #
235
252
  # @return [ BSON::Document | nil ] Document describing the identified key
236
253
  # before removing the key alt name, or nil if no such key.
237
- def_delegators :@encryption_io, :remove_key_alt_name
254
+ def remove_key_alt_name(id, key_alt_name)
255
+ @encryption_io.remove_key_alt_name(id, key_alt_name, timeout_ms: @timeout_ms)
256
+ end
238
257
 
239
258
  # Decrypts multiple data keys and (re-)encrypts them with a new master_key,
240
259
  # or with their current master_key if a new one is not given.
@@ -257,12 +276,14 @@ module Mongo
257
276
  @encryption_io,
258
277
  filter,
259
278
  master_key_document
260
- ).run_state_machine
279
+ ).run_state_machine(timeout_holder)
261
280
 
262
281
  return RewrapManyDataKeyResult.new(nil) if rewrap_result.nil?
263
282
 
264
283
  updates = updates_from_data_key_documents(rewrap_result.fetch('v'))
265
- RewrapManyDataKeyResult.new(@encryption_io.update_data_keys(updates))
284
+ RewrapManyDataKeyResult.new(
285
+ @encryption_io.update_data_keys(updates, timeout_ms: @timeout_ms)
286
+ )
266
287
  end
267
288
 
268
289
  private
@@ -318,6 +339,14 @@ module Mongo
318
339
  }
319
340
  end
320
341
  end
342
+
343
+ def timeout_holder
344
+ CsotTimeoutHolder.new(
345
+ operation_timeouts: {
346
+ operation_timeout_ms: @timeout_ms
347
+ }
348
+ )
349
+ end
321
350
  end
322
351
  end
323
352
  end
@@ -39,27 +39,28 @@ module Mongo
39
39
  # that will be used to encrypt the value.
40
40
  # @option options [ String ] :algorithm The algorithm used to encrypt the
41
41
  # value. Valid algorithms are "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
42
- # "AEAD_AES_256_CBC_HMAC_SHA_512-Random", "Indexed", "Unindexed", "RangePreview".
42
+ # "AEAD_AES_256_CBC_HMAC_SHA_512-Random", "Indexed", "Unindexed", "Range".
43
43
  # @option options [ Integer | nil ] :contention_factor Contention factor
44
44
  # to be applied if encryption algorithm is set to "Indexed". If not
45
45
  # provided, it defaults to a value of 0. Contention factor should be set
46
46
  # only if encryption algorithm is set to "Indexed".
47
47
  # @option options [ String | nil ] query_type Query type to be applied
48
- # if encryption algorithm is set to "Indexed" or "RangePreview".
49
- # Allowed values are "equality" and "rangePreview".
48
+ # if encryption algorithm is set to "Indexed" or "Range".
49
+ # Allowed values are "equality" and "range".
50
50
  # @option options [ Hash | nil ] :range_opts Specifies index options for
51
- # a Queryable Encryption field supporting "rangePreview" queries.
51
+ # a Queryable Encryption field supporting "range" queries.
52
52
  # Allowed options are:
53
53
  # - :min
54
54
  # - :max
55
+ # - :trim_factor
55
56
  # - :sparsity
56
57
  # - :precision
57
- # min, max, sparsity, and range must match the values set in
58
+ # min, max, trim_factor, sparsity, and precision must match the values set in
58
59
  # the encryptedFields of the destination collection.
59
60
  # For double and decimal128, min/max/precision must all be set,
60
61
  # or all be unset.
61
62
  #
62
- # @note The RangePreview algorithm is experimental only. It is not intended for
63
+ # @note The Range algorithm is experimental only. It is not intended for
63
64
  # public use.
64
65
  #
65
66
  # @raise [ ArgumentError|Mongo::Error::CryptError ] If invalid options are provided
@@ -116,7 +117,7 @@ module Mongo
116
117
 
117
118
  def set_algorithm_opts(options)
118
119
  Binding.ctx_setopt_algorithm(self, options[:algorithm])
119
- if %w(Indexed RangePreview).include?(options[:algorithm])
120
+ if %w(Indexed Range).include?(options[:algorithm])
120
121
  if options[:contention_factor]
121
122
  Binding.ctx_setopt_contention_factor(self, options[:contention_factor])
122
123
  end
@@ -125,20 +126,25 @@ module Mongo
125
126
  end
126
127
  else
127
128
  if options[:contention_factor]
128
- raise ArgumentError.new(':contention_factor is allowed only for "Indexed" or "RangePreview" algorithms')
129
+ raise ArgumentError.new(':contention_factor is allowed only for "Indexed" or "Range" algorithms')
129
130
  end
130
131
  if options[:query_type]
131
- raise ArgumentError.new(':query_type is allowed only for "Indexed" or "RangePreview" algorithms')
132
+ raise ArgumentError.new(':query_type is allowed only for "Indexed" or "Range" algorithms')
132
133
  end
133
134
  end
134
- if options[:algorithm] == 'RangePreview'
135
+ if options[:algorithm] == 'Range'
135
136
  Binding.ctx_setopt_algorithm_range(self, convert_range_opts(options[:range_opts]))
136
137
  end
137
138
  end
138
139
 
139
140
  def convert_range_opts(range_opts)
140
141
  range_opts.dup.tap do |opts|
141
- opts[:sparsity] = BSON::Int64.new(opts[:sparsity]) unless opts[:sparsity].is_a?(BSON::Int64)
142
+ if opts[:sparsity] && !opts[:sparsity].is_a?(BSON::Int64)
143
+ opts[:sparsity] = BSON::Int64.new(opts[:sparsity])
144
+ end
145
+ if opts[:trim_factor]
146
+ opts[:trimFactor] = opts.delete(:trim_factor)
147
+ end
142
148
  end
143
149
  end
144
150
  end
@@ -34,13 +34,16 @@ module Mongo
34
34
  # request. This is used for testing.
35
35
  # @param [String | nil] metadata_host Azure metadata host. This
36
36
  # is used for testing.
37
+ # @param [ CsotTimeoutHolder | nil ] timeout_holder CSOT timeout.
37
38
  #
38
39
  # @return [ KMS::Azure::AccessToken ] Azure access token.
39
40
  #
40
41
  # @raise [KMS::CredentialsNotFound] If credentials could not be found.
41
- def self.fetch_access_token(extra_headers: {}, metadata_host: nil)
42
+ # @raise Error::TimeoutError if credentials cannot be retrieved within
43
+ # the timeout.
44
+ def self.fetch_access_token(extra_headers: {}, metadata_host: nil, timeout_holder: nil)
42
45
  uri, req = prepare_request(extra_headers, metadata_host)
43
- parsed_response = fetch_response(uri, req)
46
+ parsed_response = fetch_response(uri, req, timeout_holder)
44
47
  Azure::AccessToken.new(
45
48
  parsed_response.fetch('access_token'),
46
49
  Integer(parsed_response.fetch('expires_in'))
@@ -78,13 +81,16 @@ module Mongo
78
81
  #
79
82
  # @param [URI] uri URI to Azure metadata host.
80
83
  # @param [Net::HTTP::Get] req Request object.
84
+ # @param [ CsotTimeoutHolder | nil ] timeout_holder CSOT timeout.
81
85
  #
82
86
  # @return [Hash] Parsed response.
83
87
  #
84
88
  # @raise [KMS::CredentialsNotFound] If cannot fetch response or
85
89
  # response is invalid.
86
- def self.fetch_response(uri, req)
87
- resp = do_request(uri, req)
90
+ # @raise Error::TimeoutError if credentials cannot be retrieved within
91
+ # the timeout.
92
+ def self.fetch_response(uri, req, timeout_holder)
93
+ resp = do_request(uri, req, timeout_holder)
88
94
  if resp.code != '200'
89
95
  raise KMS::CredentialsNotFound,
90
96
  "Azure metadata host responded with code #{resp.code}"
@@ -100,12 +106,22 @@ module Mongo
100
106
  #
101
107
  # @param [URI] uri URI to Azure metadata host.
102
108
  # @param [Net::HTTP::Get] req Request object.
109
+ # @param [ CsotTimeoutHolder | nil ] timeout_holder CSOT timeout.
103
110
  #
104
111
  # @return [Net::HTTPResponse] Response object.
105
112
  #
106
113
  # @raise [KMS::CredentialsNotFound] If cannot execute request.
107
- def self.do_request(uri, req)
108
- ::Timeout.timeout(10) do
114
+ # @raise Error::TimeoutError if credentials cannot be retrieved within
115
+ # the timeout.
116
+ def self.do_request(uri, req, timeout_holder)
117
+ timeout_holder&.check_timeout!
118
+ timeout = timeout_holder&.remaining_timeout_sec || 10
119
+ exception_class = if timeout_holder&.csot?
120
+ Error::TimeoutError
121
+ else
122
+ nil
123
+ end
124
+ ::Timeout.timeout(timeout, exception_class) do
109
125
  Net::HTTP.start(uri.hostname, uri.port, use_ssl: false) do |http|
110
126
  http.request(req)
111
127
  end
@@ -29,14 +29,20 @@ module Mongo
29
29
 
30
30
  DEFAULT_HOST = 'metadata.google.internal'
31
31
 
32
- def self.fetch_access_token
32
+ # Fetch GCP access token.
33
+ #
34
+ # @param [ CsotTimeoutHolder | nil ] timeout_holder CSOT timeout.
35
+ #
36
+ # @return [ String ] GCP access token.
37
+ #
38
+ # @raise [ KMS::CredentialsNotFound ]
39
+ # @raise [ Error::TimeoutError ]
40
+ def self.fetch_access_token(timeout_holder = nil)
33
41
  host = ENV.fetch(METADATA_HOST_ENV) { DEFAULT_HOST }
34
42
  uri = URI("http://#{host}/computeMetadata/v1/instance/service-accounts/default/token")
35
43
  req = Net::HTTP::Get.new(uri)
36
44
  req['Metadata-Flavor'] = 'Google'
37
- resp = Net::HTTP.start(uri.hostname, uri.port, use_ssl: false) do |http|
38
- http.request(req)
39
- end
45
+ resp = fetch_response(uri, req, timeout_holder)
40
46
  if resp.code != '200'
41
47
  raise KMS::CredentialsNotFound,
42
48
  "GCE metadata host responded with code #{resp.code}"
@@ -50,6 +56,25 @@ module Mongo
50
56
  raise KMS::CredentialsNotFound,
51
57
  "Could not receive GCP metadata response; #{e.class}: #{e.message}"
52
58
  end
59
+
60
+ def self.fetch_response(uri, req, timeout_holder)
61
+ timeout_holder&.check_timeout!
62
+ if timeout_holder&.timeout?
63
+ ::Timeout.timeout(timeout_holder.remaining_timeout_sec, Error:TimeoutError) do
64
+ do_fetch(uri, req)
65
+ end
66
+ else
67
+ do_fetch(uri, req)
68
+ end
69
+ end
70
+ private_class_method :fetch_response
71
+
72
+ def self.do_fetch(uri, req)
73
+ Net::HTTP.start(uri.hostname, uri.port, use_ssl: false) do |http|
74
+ http.request(req)
75
+ end
76
+ end
77
+ private_class_method :do_fetch
53
78
  end
54
79
  end
55
80
  end