mongo 2.20.1 → 2.21.0

Sign up to get free protection for your applications and to get access to all the features.
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