mongo 2.14.1 → 2.15.0.alpha

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 (230) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +4 -1
  4. data/Rakefile +8 -15
  5. data/lib/mongo/auth/aws/conversation.rb +1 -4
  6. data/lib/mongo/auth/base.rb +13 -7
  7. data/lib/mongo/auth/conversation_base.rb +32 -0
  8. data/lib/mongo/auth/cr/conversation.rb +6 -29
  9. data/lib/mongo/auth/gssapi/conversation.rb +4 -15
  10. data/lib/mongo/auth/ldap/conversation.rb +3 -14
  11. data/lib/mongo/auth/sasl_conversation_base.rb +1 -13
  12. data/lib/mongo/auth/scram_conversation_base.rb +7 -34
  13. data/lib/mongo/auth/user/view.rb +16 -9
  14. data/lib/mongo/auth/x509/conversation.rb +4 -25
  15. data/lib/mongo/bulk_write.rb +21 -18
  16. data/lib/mongo/client.rb +82 -6
  17. data/lib/mongo/cluster/reapers/cursor_reaper.rb +6 -2
  18. data/lib/mongo/cluster.rb +19 -2
  19. data/lib/mongo/collection/view/aggregation.rb +1 -1
  20. data/lib/mongo/collection/view/change_stream.rb +1 -1
  21. data/lib/mongo/collection/view/iterable.rb +7 -17
  22. data/lib/mongo/collection/view/map_reduce.rb +2 -2
  23. data/lib/mongo/collection/view/readable.rb +42 -20
  24. data/lib/mongo/collection/view/writable.rb +14 -14
  25. data/lib/mongo/collection.rb +6 -6
  26. data/lib/mongo/cursor.rb +2 -12
  27. data/lib/mongo/database/view.rb +1 -1
  28. data/lib/mongo/database.rb +8 -3
  29. data/lib/mongo/error/bulk_write_error.rb +17 -3
  30. data/lib/mongo/error/internal_driver_error.rb +22 -0
  31. data/lib/mongo/error/operation_failure.rb +21 -2
  32. data/lib/mongo/error/parser.rb +65 -12
  33. data/lib/mongo/error/server_api_conflict.rb +23 -0
  34. data/lib/mongo/error/server_api_not_supported.rb +24 -0
  35. data/lib/mongo/error/unmet_dependency.rb +21 -0
  36. data/lib/mongo/error.rb +9 -1
  37. data/lib/mongo/index/view.rb +21 -11
  38. data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +27 -16
  39. data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +26 -15
  40. data/lib/mongo/monitoring.rb +13 -4
  41. data/lib/mongo/operation/collections_info/command.rb +2 -2
  42. data/lib/mongo/operation/collections_info.rb +18 -1
  43. data/lib/mongo/operation/context.rb +99 -0
  44. data/lib/mongo/operation/indexes.rb +15 -1
  45. data/lib/mongo/operation/insert/command.rb +2 -2
  46. data/lib/mongo/operation/insert/legacy.rb +2 -2
  47. data/lib/mongo/operation/insert/op_msg.rb +2 -2
  48. data/lib/mongo/operation/list_collections/result.rb +4 -1
  49. data/lib/mongo/operation/parallel_scan/command.rb +2 -1
  50. data/lib/mongo/operation/result.rb +2 -0
  51. data/lib/mongo/operation/shared/executable.rb +24 -14
  52. data/lib/mongo/operation/shared/executable_no_validate.rb +2 -2
  53. data/lib/mongo/operation/shared/op_msg_or_command.rb +1 -7
  54. data/lib/mongo/operation/shared/op_msg_or_find_command.rb +1 -7
  55. data/lib/mongo/operation/shared/polymorphic_operation.rb +39 -0
  56. data/lib/mongo/operation/shared/read_preference_supported.rb +36 -38
  57. data/lib/mongo/operation/shared/response_handling.rb +23 -23
  58. data/lib/mongo/operation/shared/sessions_supported.rb +15 -5
  59. data/lib/mongo/operation/shared/write.rb +8 -18
  60. data/lib/mongo/operation.rb +2 -2
  61. data/lib/mongo/protocol/compressed.rb +51 -5
  62. data/lib/mongo/protocol/message.rb +20 -2
  63. data/lib/mongo/protocol/msg.rb +38 -13
  64. data/lib/mongo/protocol/query.rb +11 -11
  65. data/lib/mongo/query_cache.rb +30 -0
  66. data/lib/mongo/retryable.rb +1 -1
  67. data/lib/mongo/server/app_metadata.rb +52 -18
  68. data/lib/mongo/server/connection.rb +5 -0
  69. data/lib/mongo/server/connection_base.rb +13 -10
  70. data/lib/mongo/server/connection_pool.rb +6 -2
  71. data/lib/mongo/server/description/features.rb +9 -8
  72. data/lib/mongo/server/description.rb +4 -0
  73. data/lib/mongo/server/monitor/app_metadata.rb +1 -1
  74. data/lib/mongo/server/monitor/connection.rb +9 -10
  75. data/lib/mongo/server/monitor.rb +20 -1
  76. data/lib/mongo/server/pending_connection.rb +24 -6
  77. data/lib/mongo/server/push_monitor.rb +11 -1
  78. data/lib/mongo/server.rb +7 -1
  79. data/lib/mongo/server_selector/secondary_preferred.rb +7 -2
  80. data/lib/mongo/session/session_pool.rb +4 -2
  81. data/lib/mongo/session.rb +2 -2
  82. data/lib/mongo/socket/ssl.rb +8 -0
  83. data/lib/mongo/socket.rb +29 -4
  84. data/lib/mongo/uri/options_mapper.rb +38 -0
  85. data/lib/mongo/utils.rb +15 -0
  86. data/lib/mongo/version.rb +1 -1
  87. data/lib/mongo.rb +23 -0
  88. data/spec/README.md +24 -1
  89. data/spec/integration/auth_spec.rb +25 -15
  90. data/spec/integration/bulk_write_error_message_spec.rb +41 -0
  91. data/spec/integration/change_stream_spec.rb +4 -4
  92. data/spec/integration/command_monitoring_spec.rb +2 -2
  93. data/spec/integration/connection_spec.rb +2 -0
  94. data/spec/integration/docs_examples_spec.rb +8 -1
  95. data/spec/integration/fork_reconnect_spec.rb +4 -1
  96. data/spec/integration/ocsp_verifier_spec.rb +13 -7
  97. data/spec/integration/operation_failure_code_spec.rb +1 -1
  98. data/spec/integration/operation_failure_message_spec.rb +90 -0
  99. data/spec/integration/query_cache_spec.rb +0 -45
  100. data/spec/integration/reconnect_spec.rb +1 -1
  101. data/spec/integration/snappy_compression_spec.rb +25 -0
  102. data/spec/integration/srv_monitoring_spec.rb +1 -1
  103. data/spec/integration/transactions_examples_spec.rb +6 -0
  104. data/spec/integration/zlib_compression_spec.rb +1 -1
  105. data/spec/integration/zstd_compression_spec.rb +26 -0
  106. data/spec/lite_spec_helper.rb +7 -1
  107. data/spec/mongo/address_spec.rb +15 -11
  108. data/spec/mongo/auth/ldap/conversation_spec.rb +1 -1
  109. data/spec/mongo/auth/ldap_spec.rb +5 -1
  110. data/spec/mongo/auth/scram_negotiation_spec.rb +1 -1
  111. data/spec/mongo/auth/scram_spec.rb +1 -1
  112. data/spec/mongo/auth/x509/conversation_spec.rb +3 -3
  113. data/spec/mongo/client_construction_spec.rb +207 -33
  114. data/spec/mongo/client_spec.rb +17 -0
  115. data/spec/mongo/cluster_spec.rb +1 -0
  116. data/spec/mongo/collection/view/explainable_spec.rb +1 -1
  117. data/spec/mongo/collection/view/readable_spec.rb +33 -19
  118. data/spec/mongo/collection_crud_spec.rb +4357 -0
  119. data/spec/mongo/collection_ddl_spec.rb +534 -0
  120. data/spec/mongo/collection_spec.rb +5 -4859
  121. data/spec/mongo/database_spec.rb +66 -4
  122. data/spec/mongo/error/bulk_write_error_spec.rb +3 -3
  123. data/spec/mongo/error/parser_spec.rb +37 -6
  124. data/spec/mongo/index/view_spec.rb +4 -0
  125. data/spec/mongo/monitoring/event/server_heartbeat_failed_spec.rb +1 -1
  126. data/spec/mongo/monitoring/event/server_heartbeat_succeeded_spec.rb +1 -1
  127. data/spec/mongo/operation/aggregate_spec.rb +2 -1
  128. data/spec/mongo/operation/collections_info_spec.rb +4 -1
  129. data/spec/mongo/operation/command_spec.rb +6 -3
  130. data/spec/mongo/operation/create_index_spec.rb +6 -3
  131. data/spec/mongo/operation/create_user_spec.rb +6 -3
  132. data/spec/mongo/operation/delete/bulk_spec.rb +9 -6
  133. data/spec/mongo/operation/delete_spec.rb +11 -7
  134. data/spec/mongo/operation/drop_index_spec.rb +6 -2
  135. data/spec/mongo/operation/find/legacy_spec.rb +3 -1
  136. data/spec/mongo/operation/get_more_spec.rb +3 -1
  137. data/spec/mongo/operation/indexes_spec.rb +5 -1
  138. data/spec/mongo/operation/insert/bulk_spec.rb +10 -7
  139. data/spec/mongo/operation/insert_spec.rb +15 -12
  140. data/spec/mongo/operation/map_reduce_spec.rb +5 -2
  141. data/spec/mongo/operation/read_preference_legacy_spec.rb +19 -9
  142. data/spec/mongo/operation/read_preference_op_msg_spec.rb +3 -3
  143. data/spec/mongo/operation/remove_user_spec.rb +6 -3
  144. data/spec/mongo/operation/result_spec.rb +1 -1
  145. data/spec/mongo/operation/update/bulk_spec.rb +9 -6
  146. data/spec/mongo/operation/update_spec.rb +10 -7
  147. data/spec/mongo/operation/update_user_spec.rb +4 -1
  148. data/spec/mongo/protocol/compressed_spec.rb +26 -12
  149. data/spec/mongo/query_cache_middleware_spec.rb +55 -0
  150. data/spec/mongo/retryable_spec.rb +3 -2
  151. data/spec/mongo/server/app_metadata_shared.rb +7 -33
  152. data/spec/mongo/server/app_metadata_spec.rb +2 -0
  153. data/spec/mongo/server/connection_pool/populator_spec.rb +3 -1
  154. data/spec/mongo/server/connection_pool_spec.rb +1 -1
  155. data/spec/mongo/server/connection_spec.rb +24 -17
  156. data/spec/mongo/server/monitor/connection_spec.rb +17 -7
  157. data/spec/mongo/server/monitor_spec.rb +9 -1
  158. data/spec/mongo/server_selector/secondary_preferred_spec.rb +6 -6
  159. data/spec/mongo/server_spec.rb +15 -2
  160. data/spec/mongo/socket/ssl_spec.rb +40 -0
  161. data/spec/mongo/socket_spec.rb +2 -2
  162. data/spec/mongo/tls_context_hooks_spec.rb +37 -0
  163. data/spec/runners/connection_string.rb +0 -4
  164. data/spec/runners/crud/requirement.rb +40 -3
  165. data/spec/runners/crud/verifier.rb +8 -0
  166. data/spec/runners/transactions/operation.rb +1 -1
  167. data/spec/runners/transactions/test.rb +1 -0
  168. data/spec/runners/unified/assertions.rb +249 -0
  169. data/spec/runners/unified/change_stream_operations.rb +26 -0
  170. data/spec/runners/unified/crud_operations.rb +199 -0
  171. data/spec/runners/unified/ddl_operations.rb +96 -0
  172. data/spec/runners/unified/entity_map.rb +39 -0
  173. data/spec/runners/unified/error.rb +25 -0
  174. data/spec/runners/unified/event_subscriber.rb +91 -0
  175. data/spec/runners/unified/exceptions.rb +21 -0
  176. data/spec/runners/unified/grid_fs_operations.rb +55 -0
  177. data/spec/runners/unified/support_operations.rb +250 -0
  178. data/spec/runners/unified/test.rb +393 -0
  179. data/spec/runners/unified/test_group.rb +28 -0
  180. data/spec/runners/unified/using_hash.rb +31 -0
  181. data/spec/runners/unified.rb +96 -0
  182. data/spec/shared/lib/mrss/cluster_config.rb +0 -3
  183. data/spec/shared/lib/mrss/docker_runner.rb +0 -3
  184. data/spec/shared/lib/mrss/lite_constraints.rb +0 -16
  185. data/spec/shared/lib/mrss/server_version_registry.rb +0 -3
  186. data/spec/shared/lib/mrss/spec_organizer.rb +0 -3
  187. data/spec/shared/shlib/server.sh +1 -1
  188. data/spec/spec_helper.rb +4 -1
  189. data/spec/spec_tests/crud_unified_spec.rb +10 -0
  190. data/spec/spec_tests/data/change_streams/change-streams.yml +0 -1
  191. data/spec/spec_tests/data/crud_unified/estimatedDocumentCount.yml +267 -0
  192. data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount-4.9.yml +60 -0
  193. data/spec/spec_tests/data/retryable_reads/{estimatedDocumentCount.yml → estimatedDocumentCount-pre4.9.yml} +2 -0
  194. data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount-serverErrors-4.9.yml +146 -0
  195. data/spec/spec_tests/data/retryable_reads/{estimatedDocumentCount-serverErrors.yml → estimatedDocumentCount-serverErrors-pre4.9.yml} +2 -0
  196. data/spec/spec_tests/data/retryable_reads/listIndexNames.yml +1 -1
  197. data/spec/spec_tests/data/unified/valid-fail/operation-failure.yml +31 -0
  198. data/spec/spec_tests/data/unified/valid-pass/poc-change-streams.yml +220 -0
  199. data/spec/spec_tests/data/unified/valid-pass/poc-command-monitoring.yml +102 -0
  200. data/spec/spec_tests/data/unified/valid-pass/poc-crud.yml +184 -0
  201. data/spec/spec_tests/data/unified/valid-pass/poc-gridfs.yml +155 -0
  202. data/spec/spec_tests/data/unified/valid-pass/poc-retryable-reads.yml +193 -0
  203. data/spec/spec_tests/data/unified/valid-pass/poc-retryable-writes.yml +210 -0
  204. data/spec/spec_tests/data/unified/valid-pass/poc-sessions.yml +215 -0
  205. data/spec/spec_tests/data/unified/valid-pass/poc-transactions-convenient-api.yml +235 -0
  206. data/spec/spec_tests/data/unified/valid-pass/poc-transactions-mongos-pin-auto.yml +169 -0
  207. data/spec/spec_tests/data/unified/valid-pass/poc-transactions.yml +170 -0
  208. data/spec/spec_tests/data/uri_options/compression-options.yml +1 -1
  209. data/spec/spec_tests/data/versioned_api/crud-api-version-1-strict.yml +416 -0
  210. data/spec/spec_tests/data/versioned_api/crud-api-version-1.yml +409 -0
  211. data/spec/spec_tests/data/versioned_api/runcommand-helper-no-api-version-declared.yml +67 -0
  212. data/spec/spec_tests/data/versioned_api/test-commands-deprecation-errors.yml +47 -0
  213. data/spec/spec_tests/data/versioned_api/test-commands-strict-mode.yml +44 -0
  214. data/spec/spec_tests/data/versioned_api/transaction-handling.yml +180 -0
  215. data/spec/spec_tests/unified_spec.rb +15 -0
  216. data/spec/spec_tests/uri_options_spec.rb +16 -0
  217. data/spec/spec_tests/versioned_api_spec.rb +10 -0
  218. data/spec/support/client_registry.rb +4 -8
  219. data/spec/support/client_registry_macros.rb +4 -4
  220. data/spec/support/common_shortcuts.rb +15 -1
  221. data/spec/support/shared/session.rb +2 -2
  222. data/spec/support/spec_config.rb +42 -11
  223. data/spec/support/utils.rb +64 -3
  224. data.tar.gz.sig +0 -0
  225. metadata +1005 -915
  226. metadata.gz.sig +0 -0
  227. data/lib/mongo/operation/shared/collections_info_or_list_collections.rb +0 -58
  228. data/lib/mongo/operation/shared/op_msg_or_list_indexes_command.rb +0 -47
  229. data/spec/integration/secondary_reads_spec.rb +0 -102
  230. data/spec/support/cluster_config.rb +0 -207
@@ -1,3 +1,4 @@
1
+ require 'mongo/operation/context'
1
2
  require 'mongo/operation/result'
2
3
 
3
4
  require 'mongo/operation/shared/response_handling'
@@ -5,6 +6,7 @@ require 'mongo/operation/shared/executable'
5
6
  require 'mongo/operation/shared/executable_no_validate'
6
7
  require 'mongo/operation/shared/executable_transaction_label'
7
8
  require 'mongo/operation/shared/polymorphic_lookup'
9
+ require 'mongo/operation/shared/polymorphic_operation'
8
10
  require 'mongo/operation/shared/polymorphic_result'
9
11
  require 'mongo/operation/shared/read_preference_supported'
10
12
  require 'mongo/operation/shared/bypass_document_validation'
@@ -18,8 +20,6 @@ require 'mongo/operation/shared/specifiable'
18
20
  require 'mongo/operation/shared/object_id_generator'
19
21
  require 'mongo/operation/shared/op_msg_or_command'
20
22
  require 'mongo/operation/shared/op_msg_or_find_command'
21
- require 'mongo/operation/shared/op_msg_or_list_indexes_command'
22
- require 'mongo/operation/shared/collections_info_or_list_collections'
23
23
 
24
24
  require 'mongo/operation/op_msg_base'
25
25
  require 'mongo/operation/command'
@@ -18,12 +18,25 @@ module Mongo
18
18
  # MongoDB Wire protocol Compressed message.
19
19
  #
20
20
  # This is a bi-directional message that compresses another opcode.
21
+ # See https://github.com/mongodb/specifications/blob/master/source/compression/OP_COMPRESSED.rst
21
22
  #
22
23
  # @api semipublic
23
24
  #
24
25
  # @since 2.5.0
25
26
  class Compressed < Message
26
27
 
28
+ # The noop compressor identifier.
29
+ NOOP = 'noop'.freeze
30
+
31
+ # The byte signaling that the message has not been compressed (test mode).
32
+ NOOP_BYTE = 0.chr.force_encoding(BSON::BINARY).freeze
33
+
34
+ # The snappy compressor identifier.
35
+ SNAPPY = 'snappy'.freeze
36
+
37
+ # The byte signaling that the message has been compressed with snappy.
38
+ SNAPPY_BYTE = 1.chr.force_encoding(BSON::BINARY).freeze
39
+
27
40
  # The byte signaling that the message has been compressed with Zlib.
28
41
  #
29
42
  # @since 2.5.0
@@ -34,10 +47,20 @@ module Mongo
34
47
  # @since 2.5.0
35
48
  ZLIB = 'zlib'.freeze
36
49
 
50
+ # The zstd compressor identifier.
51
+ ZSTD = 'zstd'.freeze
52
+
53
+ # The byte signaling that the message has been compressed with zstd.
54
+ ZSTD_BYTE = 3.chr.force_encoding(BSON::BINARY).freeze
55
+
37
56
  # The compressor identifier to byte map.
38
57
  #
39
58
  # @since 2.5.0
40
- COMPRESSOR_ID_MAP = { ZLIB => ZLIB_BYTE }.freeze
59
+ COMPRESSOR_ID_MAP = {
60
+ SNAPPY => SNAPPY_BYTE,
61
+ ZSTD => ZSTD_BYTE,
62
+ ZLIB => ZLIB_BYTE
63
+ }.freeze
41
64
 
42
65
  # Creates a new OP_COMPRESSED message.
43
66
  #
@@ -68,9 +91,7 @@ module Mongo
68
91
  # @api private
69
92
  def maybe_inflate
70
93
  message = Registry.get(@original_op_code).allocate
71
- uncompressed_message = Zlib::Inflate.inflate(@compressed_message)
72
-
73
- buf = BSON::ByteBuffer.new(uncompressed_message)
94
+ buf = decompress(@compressed_message)
74
95
 
75
96
  message.send(:fields).each do |field|
76
97
  if field[:multi]
@@ -125,10 +146,35 @@ module Mongo
125
146
  buf = BSON::ByteBuffer.new
126
147
  @original_message.send(:serialize_fields, buf, max_bson_size)
127
148
  @uncompressed_size = buf.length
128
- @compressed_message = Zlib::Deflate.deflate(buf.to_s, @zlib_compression_level).force_encoding(BSON::BINARY)
149
+ @compressed_message = compress(buf)
129
150
  super
130
151
  end
131
152
 
153
+ def compress(buffer)
154
+ if @compressor_id == NOOP_BYTE
155
+ buffer.to_s.force_encoding(BSON::BINARY)
156
+ elsif @compressor_id == ZLIB_BYTE
157
+ Zlib::Deflate.deflate(buffer.to_s, @zlib_compression_level).force_encoding(BSON::BINARY)
158
+ elsif @compressor_id == SNAPPY_BYTE
159
+ Snappy.deflate(buffer.to_s).force_encoding(BSON::BINARY)
160
+ elsif @compressor_id == ZSTD_BYTE
161
+ # DRIVERS-600 will allow this to be configurable in the future
162
+ Zstd.compress(buffer.to_s).force_encoding(BSON::BINARY)
163
+ end
164
+ end
165
+
166
+ def decompress(compressed_message)
167
+ if @compressor_id == NOOP_BYTE
168
+ BSON::ByteBuffer.new(compressed_message)
169
+ elsif @compressor_id == ZLIB_BYTE
170
+ BSON::ByteBuffer.new(Zlib::Inflate.inflate(compressed_message))
171
+ elsif @compressor_id == SNAPPY_BYTE
172
+ BSON::ByteBuffer.new(Snappy.inflate(compressed_message))
173
+ elsif @compressor_id == ZSTD_BYTE
174
+ BSON::ByteBuffer.new(Zstd.decompress(compressed_message))
175
+ end
176
+ end
177
+
132
178
  Registry.register(OP_CODE, self)
133
179
  end
134
180
  end
@@ -140,7 +140,13 @@ module Mongo
140
140
  self
141
141
  end
142
142
 
143
- def maybe_decrypt(client)
143
+ # Possibly decrypt this message with libmongocrypt.
144
+ #
145
+ # @param [ Mongo::Operation::Context ] context The operation context.
146
+ #
147
+ # @return [ Mongo::Protocol::Msg ] The decrypted message, or the original
148
+ # message if decryption was not possible or necessary.
149
+ def maybe_decrypt(context)
144
150
  # TODO determine if we should be decrypting data coming from pre-4.2
145
151
  # servers, potentially using legacy wire protocols. If so we need
146
152
  # to implement decryption for those wire protocols as our current
@@ -148,11 +154,23 @@ module Mongo
148
154
  self
149
155
  end
150
156
 
151
- def maybe_encrypt(server, client)
157
+ # Possibly encrypt this message with libmongocrypt.
158
+ #
159
+ # @param [ Mongo::Server::Connection ] connection The connection on which
160
+ # the operation is performed.
161
+ # @param [ Mongo::Operation::Context ] context The operation context.
162
+ #
163
+ # @return [ Mongo::Protocol::Msg ] The encrypted message, or the original
164
+ # message if encryption was not possible or necessary.
165
+ def maybe_encrypt(connection, context)
152
166
  # Do nothing if the Message subclass has not implemented this method
153
167
  self
154
168
  end
155
169
 
170
+ def maybe_add_server_api(server_api)
171
+ raise Error::ServerApiNotSupported, "Server API parameters cannot be sent to pre-3.6 MongoDB servers. Please remove the :server_api parameter from Client options or use MongoDB 3.6 or newer"
172
+ end
173
+
156
174
  private def merge_sections
157
175
  cmd = if @sections.length > 1
158
176
  cmd = @sections.detect { |section| section[:type] == 0 }[:payload]
@@ -43,8 +43,8 @@ module Mongo
43
43
  # Msg.new([:more_to_come], {}, { ismaster: 1 },
44
44
  # { type: 1, payload: { identifier: 'documents', sequence: [..] } })
45
45
  #
46
- # @param [ Array<Symbol> ] flags The flag bits. Currently supported
47
- # values are :more_to_come and :checksum_present.
46
+ # @param [ Array<Symbol> ] flags The flag bits. Current supported values
47
+ # are :more_to_come and :checksum_present.
48
48
  # @param [ Hash ] options The options.
49
49
  # @param [ BSON::Document, Hash ] main_document The document that will
50
50
  # become the payload type 0 section. Can contain global args as they
@@ -199,15 +199,16 @@ module Mongo
199
199
  # represents one of the command types whitelisted by libmongocrypt and it
200
200
  # contains data that is required to be encrypted by a local or remote json schema.
201
201
  #
202
- # @param [ Mongo::Client | nil ] client The client used to make the original
203
- # command. This client may have auto-encryption options specified.
202
+ # @param [ Mongo::Server::Connection ] connection The connection on which
203
+ # the operation is performed.
204
+ # @param [ Mongo::Operation::Context ] context The operation context.
204
205
  #
205
206
  # @return [ Mongo::Protocol::Msg ] The encrypted message, or the original
206
207
  # message if encryption was not possible or necessary.
207
- def maybe_encrypt(connection, client)
208
+ def maybe_encrypt(connection, context)
208
209
  # TODO verify compression happens later, i.e. when this method runs
209
210
  # the message is not compressed.
210
- if client && client.encrypter && client.encrypter.encrypt?
211
+ if context.encrypt?
211
212
  if connection.description.max_wire_version < 8
212
213
  raise Error::CryptError.new(
213
214
  "Cannot perform encryption against a MongoDB server older than " +
@@ -219,7 +220,7 @@ module Mongo
219
220
 
220
221
  db_name = @main_document[DATABASE_IDENTIFIER]
221
222
  cmd = merge_sections
222
- enc_cmd = client.encrypter.encrypt(db_name, cmd)
223
+ enc_cmd = context.encrypter.encrypt(db_name, cmd)
223
224
  if cmd.key?('$db') && !enc_cmd.key?('$db')
224
225
  enc_cmd['$db'] = cmd['$db']
225
226
  end
@@ -237,15 +238,14 @@ module Mongo
237
238
  # types whitelisted by libmongocrypt and it contains data that is required
238
239
  # to be encrypted by a local or remote json schema.
239
240
  #
240
- # @param [ Mongo::Client | nil ] client The client used to make the original
241
- # command. This client may have auto-encryption options specified.
241
+ # @param [ Mongo::Operation::Context ] context The operation context.
242
242
  #
243
- # @return [ Mongo::Protocol::Msg ] The decryption message, or the original
243
+ # @return [ Mongo::Protocol::Msg ] The decrypted message, or the original
244
244
  # message if decryption was not possible or necessary.
245
- def maybe_decrypt(client)
246
- if client && client.encrypter
245
+ def maybe_decrypt(context)
246
+ if context.decrypt?
247
247
  cmd = merge_sections
248
- enc_cmd = client.encrypter.decrypt(cmd)
248
+ enc_cmd = context.encrypter.decrypt(cmd)
249
249
  Msg.new(@flags, @options, enc_cmd)
250
250
  else
251
251
  self
@@ -275,6 +275,31 @@ module Mongo
275
275
  num_inserts > 1 || num_updates > 1 || num_deletes > 1
276
276
  end
277
277
 
278
+ def maybe_add_server_api(server_api)
279
+ if @main_document[:getMore]
280
+ # getMore does not allow API parameters.
281
+ return self
282
+ end
283
+
284
+ conflicts = {}
285
+ %i(apiVersion apiStrict apiDeprecationErrors).each do |key|
286
+ if @main_document.key?(key)
287
+ conflicts[key] = @main_document[key]
288
+ end
289
+ if @main_document.key?(key.to_s)
290
+ conflicts[key] = @main_document[key.to_s]
291
+ end
292
+ end
293
+ unless conflicts.empty?
294
+ raise Error::ServerApiConflict, "The Client is configured with :server_api option but the operation provided the following conflicting parameters: #{conflicts.inspect}"
295
+ end
296
+
297
+ main_document = @main_document.merge(
298
+ Utils.transform_server_api(server_api)
299
+ )
300
+ Msg.new(@flags, @options, main_document, *@sequences)
301
+ end
302
+
278
303
  private
279
304
 
280
305
  # Validate that the documents in this message are all smaller than the
@@ -46,18 +46,18 @@ module Mongo
46
46
  # @example Find all user ids.
47
47
  # Query.new('xgen', 'users', {}, :fields => {:id => 1})
48
48
  #
49
- # @param [ String, Symbol ] database The database to query.
50
- # @param [ String, Symbol ] collection The collection to query.
51
- # @param [ Hash ] selector The query selector.
52
- # @param [ Hash ] options The additional query options.
49
+ # @param database [String, Symbol] The database to query.
50
+ # @param collection [String, Symbol] The collection to query.
51
+ # @param selector [Hash] The query selector.
52
+ # @param options [Hash] The additional query options.
53
53
  #
54
- # @option options [ Array<Symbol> ] :flags The flag bits.
55
- # Currently supported values are :await_data, :exhaust,
56
- # :no_cursor_timeout, :oplog_replay, :partial, :slave_ok,
57
- # :tailable_cursor.
58
- # @option options [ Integer ] :limit The number of documents to return.
59
- # @option options [ Hash ] :project The projection.
60
- # @option options [ Integer ] :skip The number of documents to skip.
54
+ # @option options :project [Hash] The projection.
55
+ # @option options :skip [Integer] The number of documents to skip.
56
+ # @option options :limit [Integer] The number of documents to return.
57
+ # @option options :flags [Array] The flags for the query message.
58
+ #
59
+ # Supported flags: +:tailable_cursor+, +:slave_ok+, +:oplog_replay+,
60
+ # +:no_cursor_timeout+, +:await_data+, +:exhaust+, +:partial+
61
61
  def initialize(database, collection, selector, options = {})
62
62
  @database = database
63
63
  @namespace = "#{database}.#{collection}"
@@ -238,5 +238,35 @@ module Mongo
238
238
  end
239
239
  end
240
240
  end
241
+
242
+ # Rack middleware that activates the query cache for each request.
243
+ class Middleware
244
+
245
+ # Instantiate the middleware.
246
+ #
247
+ # @example Create the new middleware.
248
+ # Middleware.new(app)
249
+ #
250
+ # @param [ Object ] app The rack application stack.
251
+ def initialize(app)
252
+ @app = app
253
+ end
254
+
255
+ # Enable query cache and execute the request.
256
+ #
257
+ # @example Execute the request.
258
+ # middleware.call(env)
259
+ #
260
+ # @param [ Object ] env The environment.
261
+ #
262
+ # @return [ Object ] The result of the call.
263
+ def call(env)
264
+ QueryCache.cache do
265
+ @app.call(env)
266
+ end
267
+ ensure
268
+ QueryCache.clear
269
+ end
270
+ end
241
271
  end
242
272
  end
@@ -477,7 +477,7 @@ module Mongo
477
477
  else
478
478
  "Retry"
479
479
  end
480
- Logger.logger.warn "#{message} due to: #{e.class.name} #{e.message}"
480
+ Logger.logger.warn "#{message} due to: #{e.class.name}: #{e.message}"
481
481
  end
482
482
 
483
483
  # Retry writes on MMAPv1 should raise an actionable error; append actionable
@@ -43,6 +43,11 @@ module Mongo
43
43
  # @api private
44
44
  AUTH_OPTION_KEYS = [:user, :auth_source, :auth_mech].freeze
45
45
 
46
+ # Possible connection purposes.
47
+ #
48
+ # @api private
49
+ PURPOSES = %i(application monitor push_monitor).freeze
50
+
46
51
  # Instantiate the new AppMetadata object.
47
52
  #
48
53
  # @api private
@@ -60,10 +65,16 @@ module Mongo
60
65
  # @option options [ Array<String> ] :compressors A list of potential
61
66
  # compressors to use, in order of preference. The driver chooses the
62
67
  # first compressor that is also supported by the server. Currently the
63
- # driver only supports 'zlib'.
68
+ # driver only supports 'zstd', 'snappy' and 'zlib'.
64
69
  # @option options [ String ] :platform Platform information to include in
65
70
  # the metadata printed to the mongod logs upon establishing a connection
66
71
  # in server versions >= 3.4.
72
+ # @option options [ Symbol ] :purpose The purpose of this connection.
73
+ # @option options [ Hash ] :server_api The requested server API version.
74
+ # This hash can have the following items:
75
+ # - *:version* -- string
76
+ # - *:strict* -- boolean
77
+ # - *:deprecation_errors* -- boolean
67
78
  # @option options [ String ] :user The user name.
68
79
  # @option options [ Array<Hash> ] :wrapping_libraries Information about
69
80
  # libraries such as ODMs that are wrapping the driver. Specify the
@@ -71,11 +82,17 @@ module Mongo
71
82
  # :platform.
72
83
  #
73
84
  # @since 2.4.0
74
- def initialize(options)
85
+ def initialize(options = {})
75
86
  @app_name = options[:app_name].to_s if options[:app_name]
76
87
  @platform = options[:platform]
88
+ if @purpose = options[:purpose]
89
+ unless PURPOSES.include?(@purpose)
90
+ raise ArgumentError, "Invalid purpose: #{@purpose}"
91
+ end
92
+ end
77
93
  @compressors = options[:compressors] || []
78
94
  @wrapping_libraries = options[:wrapping_libraries]
95
+ @server_api = options[:server_api]
79
96
 
80
97
  if options[:user] && !options[:auth_mech]
81
98
  auth_db = options[:auth_source] || 'admin'
@@ -83,6 +100,12 @@ module Mongo
83
100
  end
84
101
  end
85
102
 
103
+ # @return [ Symbol ] The purpose of the connection for which this
104
+ # app metadata is created.
105
+ #
106
+ # @api private
107
+ attr_reader :purpose
108
+
86
109
  # @return [ Array<Hash> | nil ] Information about libraries wrapping
87
110
  # the driver.
88
111
  attr_reader :wrapping_libraries
@@ -130,22 +153,29 @@ module Mongo
130
153
  end
131
154
 
132
155
  def document
133
- client_document = full_client_document
134
- while client_document.to_bson.to_s.size > MAX_DOCUMENT_SIZE do
135
- if client_document[:os][:name] || client_document[:os][:architecture]
136
- client_document[:os].delete(:name)
137
- client_document[:os].delete(:architecture)
138
- elsif client_document[:platform]
139
- client_document.delete(:platform)
140
- else
141
- client_document = nil
156
+ @document ||= begin
157
+ client_document = full_client_document
158
+ while client_document.to_bson.to_s.size > MAX_DOCUMENT_SIZE do
159
+ if client_document[:os][:name] || client_document[:os][:architecture]
160
+ client_document[:os].delete(:name)
161
+ client_document[:os].delete(:architecture)
162
+ elsif client_document[:platform]
163
+ client_document.delete(:platform)
164
+ else
165
+ client_document = nil
166
+ end
167
+ end
168
+ document = Server::Monitor::Connection::ISMASTER
169
+ document = document.merge(compression: @compressors)
170
+ document[:client] = client_document
171
+ document[:saslSupportedMechs] = @request_auth_mech if @request_auth_mech
172
+ if @server_api
173
+ document.update(
174
+ Utils.transform_server_api(@server_api)
175
+ )
142
176
  end
177
+ document
143
178
  end
144
- document = Server::Monitor::Connection::ISMASTER
145
- document = document.merge(compression: @compressors)
146
- document[:client] = client_document
147
- document[:saslSupportedMechs] = @request_auth_mech if @request_auth_mech
148
- document
149
179
  end
150
180
 
151
181
  def driver_doc
@@ -192,12 +222,16 @@ module Mongo
192
222
  ruby_versions = ["Ruby #{RUBY_VERSION}"]
193
223
  platforms = [RUBY_PLATFORM]
194
224
  end
195
- platform = [
225
+ platforms = [
196
226
  @platform,
197
227
  *ruby_versions,
198
228
  *platforms,
199
229
  RbConfig::CONFIG['build'],
200
- ].compact.join(', ')
230
+ ]
231
+ if @purpose
232
+ platforms << @purpose.to_s[0].upcase
233
+ end
234
+ platform = platforms.compact.join(', ')
201
235
  platforms = [platform]
202
236
  if wrapping_libraries
203
237
  wrapping_libraries.each do |library|
@@ -85,6 +85,11 @@ module Mongo
85
85
  #
86
86
  # @option options [ Integer ] :generation Connection pool's generation
87
87
  # for this connection.
88
+ # @option options [ Hash ] :server_api The requested server API version.
89
+ # This hash can have the following items:
90
+ # - *:version* -- string
91
+ # - *:strict* -- boolean
92
+ # - *:deprecation_errors* -- boolean
88
93
  #
89
94
  # @since 2.0.0
90
95
  def initialize(server, options = {})
@@ -105,7 +105,7 @@ module Mongo
105
105
  if same
106
106
  @server.app_metadata
107
107
  else
108
- AppMetadata.new(options)
108
+ AppMetadata.new(options.merge(purpose: @server.app_metadata.purpose))
109
109
  end
110
110
  end
111
111
  end
@@ -124,7 +124,7 @@ module Mongo
124
124
  #
125
125
  # @param [ Array<Message> ] messages A one-element array containing
126
126
  # the message to dispatch.
127
- # @param [ Integer ] operation_id The operation id to link messages.
127
+ # @param [ Operation::Context ] context The operation context.
128
128
  # @param [ Hash ] options
129
129
  #
130
130
  # @option options [ Boolean ] :deserialize_as_bson Whether to deserialize
@@ -134,24 +134,27 @@ module Mongo
134
134
  # @return [ Protocol::Message | nil ] The reply if needed.
135
135
  #
136
136
  # @since 2.0.0
137
- def dispatch(messages, operation_id = nil, client = nil, options = {})
137
+ def dispatch(messages, context, options = {})
138
138
  # The monitoring code does not correctly handle multiple messages,
139
139
  # and the driver internally does not send more than one message at
140
140
  # a time ever. Thus prohibit multiple message use for now.
141
141
  if messages.length != 1
142
142
  raise ArgumentError, 'Can only dispatch one message at a time'
143
143
  end
144
+ if description.unknown?
145
+ raise Error::InternalDriverError, "Cannot dispatch a message on a connection with unknown description: #{description.inspect}"
146
+ end
144
147
  message = messages.first
145
- deliver(message, client, options)
148
+ deliver(message, context, options)
146
149
  end
147
150
 
148
151
  private
149
152
 
150
- def deliver(message, client, options = {})
153
+ def deliver(message, context, options = {})
151
154
  if Lint.enabled? && !@socket
152
155
  raise Error::LintError, "Trying to deliver a message over a disconnected connection (to #{address})"
153
156
  end
154
- buffer = serialize(message, client)
157
+ buffer = serialize(message, context)
155
158
  ensure_connected do |socket|
156
159
  operation_id = Monitoring.next_operation_id
157
160
  command_started(address, operation_id, message.payload,
@@ -178,14 +181,14 @@ module Mongo
178
181
  total_duration = Time.now - start
179
182
  command_completed(result, address, operation_id, message.payload, total_duration)
180
183
  end
181
- if client && result
182
- result = result.maybe_decrypt(client)
184
+ if result && context.decrypt?
185
+ result = result.maybe_decrypt(context)
183
186
  end
184
187
  result
185
188
  end
186
189
  end
187
190
 
188
- def serialize(message, client, buffer = BSON::ByteBuffer.new)
191
+ def serialize(message, context, buffer = BSON::ByteBuffer.new)
189
192
  # Driver specifications only mandate the fixed 16MiB limit for
190
193
  # serialized BSON documents. However, the server returns its
191
194
  # active serialized BSON document size limit in the ismaster response,
@@ -194,7 +197,7 @@ module Mongo
194
197
  # only as the default if the server's ismaster did not contain
195
198
  # maxBsonObjectSize.
196
199
  max_bson_size = max_bson_object_size || DEFAULT_MAX_BSON_OBJECT_SIZE
197
- if client && client.encrypter && client.encrypter.encrypt?
200
+ if context.encrypt?
198
201
  # The client-side encryption specification requires bulk writes to
199
202
  # be split at a reduced maxBsonObjectSize. If this message is a bulk
200
203
  # write and its size exceeds the reduced size limit, the serializer
@@ -703,8 +703,12 @@ module Mongo
703
703
  private
704
704
 
705
705
  def create_connection
706
- connection = Connection.new(@server, options.merge(generation: generation,
707
- connection_pool: self))
706
+ connection = Connection.new(@server, options.merge(
707
+ generation: generation,
708
+ connection_pool: self,
709
+ # Do not pass app metadata - this will be retrieved by the connection
710
+ # based on the auth needs.
711
+ ))
708
712
  end
709
713
 
710
714
  # Create a connection, connect it, and add it to the pool.
@@ -23,14 +23,15 @@ module Mongo
23
23
  # List of features and the wire protocol version they appear in.
24
24
  #
25
25
  # Wire protocol versions map to server releases as follows:
26
- # - 2 => 2.6
27
- # - 3 => 3.0
28
- # - 4 => 3.2
29
- # - 5 => 3.4
30
- # - 6 => 3.6
31
- # - 7 => 4.0
32
- # - 8 => 4.2
33
- # - 9 => 4.4
26
+ # - 2 => 2.6
27
+ # - 3 => 3.0
28
+ # - 4 => 3.2
29
+ # - 5 => 3.4
30
+ # - 6 => 3.6
31
+ # - 7 => 4.0
32
+ # - 8 => 4.2
33
+ # - 9 => 4.4
34
+ # - 12 => 5.0
34
35
  #
35
36
  # @since 2.0.0
36
37
  MAPPINGS = {
@@ -790,6 +790,10 @@ module Mongo
790
790
  # @api private
791
791
  def server_version_gte?(version)
792
792
  required_wv = case version
793
+ when '5.0'
794
+ 12
795
+ when '4.4'
796
+ 9
793
797
  when '4.2'
794
798
  8
795
799
  when '4.0'
@@ -22,7 +22,7 @@ module Mongo
22
22
  #
23
23
  # @api private
24
24
  class AppMetadata < Server::AppMetadata
25
- def initialize(options)
25
+ def initialize(options = {})
26
26
  super
27
27
  if instance_variable_defined?(:@request_auth_mech)
28
28
  remove_instance_variable(:@request_auth_mech)
@@ -44,7 +44,7 @@ module Mongo
44
44
  # The constant for the ismaster command.
45
45
  #
46
46
  # @since 2.2.0
47
- ISMASTER_MESSAGE = Protocol::Query.new(Database::ADMIN, Database::COMMAND, ISMASTER, :limit => -1)
47
+ ISMASTER_MESSAGE = Protocol::Query.new(Database::ADMIN, Database::COMMAND, ISMASTER, limit: -1)
48
48
 
49
49
  # The constant for the ismaster command as an OP_MSG (server versions >= 3.6).
50
50
  #
@@ -83,7 +83,7 @@ module Mongo
83
83
  # @option options [ Array<String> ] :compressors A list of potential
84
84
  # compressors to use, in order of preference. The driver chooses the
85
85
  # first compressor that is also supported by the server. Currently the
86
- # driver only supports 'zlib'.
86
+ # driver only supports 'zstd', 'snappy' and 'zlib'.
87
87
  # @option options [ Float ] :connect_timeout The timeout, in seconds,
88
88
  # to use for network operations. This timeout is used for all
89
89
  # socket operations rather than connect calls only, contrary to
@@ -93,7 +93,9 @@ module Mongo
93
93
  def initialize(address, options = {})
94
94
  @address = address
95
95
  @options = options.dup.freeze
96
- @app_metadata = options[:app_metadata]
96
+ unless @app_metadata = options[:app_metadata]
97
+ raise ArgumentError, 'App metadata is required'
98
+ end
97
99
  @socket = nil
98
100
  @pid = Process.pid
99
101
  @compressor = nil
@@ -221,14 +223,11 @@ module Mongo
221
223
  end
222
224
 
223
225
  def handshake!
224
- payload = if @app_metadata
225
- @app_metadata.ismaster_bytes
226
- else
227
- log_warn("No app metadata provided for handshake with #{address}")
228
- ISMASTER_BYTES
229
- end
226
+ payload = @app_metadata.ismaster_bytes
230
227
  message = dispatch_bytes(payload)
231
- reply = message.documents.first
228
+ result = Operation::Result.new(message)
229
+ result.validate!
230
+ reply = result.documents.first
232
231
  set_compressor!(reply)
233
232
  @server_connection_id = reply['connectionId']
234
233
  reply