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
@@ -0,0 +1,99 @@
1
+ # Copyright (C) 2021 MongoDB Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Mongo
16
+ module Operation
17
+
18
+ # Context for operations.
19
+ #
20
+ # Holds various objects needed to make decisions about operation execution
21
+ # in a single container, and provides facade methods for the contained
22
+ # objects.
23
+ #
24
+ # @api private
25
+ class Context
26
+ def initialize(client: nil, session: nil, options: nil)
27
+ if options
28
+ if client
29
+ raise ArgumentError, 'Client and options cannot both be specified'
30
+ end
31
+
32
+ if session
33
+ raise ArgumentError, 'Session and options cannot both be specified'
34
+ end
35
+ end
36
+
37
+ @client = client
38
+ @session = session
39
+ @options = options
40
+ end
41
+
42
+ attr_reader :client
43
+ attr_reader :session
44
+ attr_reader :options
45
+
46
+ def in_transaction?
47
+ session&.in_transaction? || false
48
+ end
49
+
50
+ def starting_transaction?
51
+ session&.starting_transaction? || false
52
+ end
53
+
54
+ def committing_transaction?
55
+ in_transaction? && session.committing_transaction?
56
+ end
57
+
58
+ def aborting_transaction?
59
+ in_transaction? && session.aborting_transaction?
60
+ end
61
+
62
+ def modern_retry_writes?
63
+ client && client.options[:retry_writes]
64
+ end
65
+
66
+ def legacy_retry_writes?
67
+ client && !client.options[:retry_writes] && client.max_write_retries > 0
68
+ end
69
+
70
+ def any_retry_writes?
71
+ modern_retry_writes? || legacy_retry_writes?
72
+ end
73
+
74
+ def server_api
75
+ if client
76
+ client.options[:server_api]
77
+ elsif options
78
+ options[:server_api]
79
+ end
80
+ end
81
+
82
+ def encrypt?
83
+ client&.encrypter&.encrypt? || false
84
+ end
85
+
86
+ def decrypt?
87
+ !!client&.encrypter
88
+ end
89
+
90
+ def encrypter
91
+ if client&.encrypter
92
+ client.encrypter
93
+ else
94
+ raise Error::InternalDriverError, 'Encrypter should only be accessed when encryption is to be performed'
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -27,7 +27,21 @@ module Mongo
27
27
  # @since 2.0.0
28
28
  class Indexes
29
29
  include Specifiable
30
- include OpMsgOrListIndexesCommand
30
+ include PolymorphicOperation
31
+ include PolymorphicLookup
32
+
33
+ private
34
+
35
+ def final_operation(connection)
36
+ cls = if connection.features.op_msg_enabled?
37
+ polymorphic_class(self.class.name, :OpMsg)
38
+ elsif connection.features.list_indexes_enabled?
39
+ polymorphic_class(self.class.name, :Command)
40
+ else
41
+ polymorphic_class(self.class.name, :Legacy)
42
+ end
43
+ cls.new(spec)
44
+ end
31
45
  end
32
46
  end
33
47
  end
@@ -32,9 +32,9 @@ module Mongo
32
32
 
33
33
  private
34
34
 
35
- def get_result(connection, client, options = {})
35
+ def get_result(connection, context, options = {})
36
36
  # This is a Mongo::Operation::Insert::Result
37
- Result.new(*dispatch_message(connection, client), @ids)
37
+ Result.new(*dispatch_message(connection, context), @ids)
38
38
  end
39
39
 
40
40
  def selector(connection)
@@ -30,9 +30,9 @@ module Mongo
30
30
 
31
31
  private
32
32
 
33
- def get_result(connection, client, options = {})
33
+ def get_result(connection, context, options = {})
34
34
  # This is a Mongo::Operation::Insert::Result
35
- Result.new(*dispatch_message(connection, client), @ids)
35
+ Result.new(*dispatch_message(connection, context), @ids)
36
36
  end
37
37
 
38
38
  def selector
@@ -30,9 +30,9 @@ module Mongo
30
30
 
31
31
  private
32
32
 
33
- def get_result(connection, client, options = {})
33
+ def get_result(connection, context, options = {})
34
34
  # This is a Mongo::Operation::Insert::Result
35
- Result.new(*dispatch_message(connection, client), @ids)
35
+ Result.new(*dispatch_message(connection, context), @ids)
36
36
  end
37
37
 
38
38
  def selector(connection)
@@ -88,7 +88,10 @@ module Mongo
88
88
  code: parser.code,
89
89
  code_name: parser.code_name,
90
90
  labels: parser.labels,
91
- wtimeout: parser.wtimeout)
91
+ wtimeout: parser.wtimeout,
92
+ document: parser.document,
93
+ server_message: parser.server_message,
94
+ )
92
95
  end
93
96
  end
94
97
 
@@ -37,7 +37,8 @@ module Mongo
37
37
  read_concern)
38
38
  end
39
39
  sel[:maxTimeMS] = max_time_ms if max_time_ms
40
- add_read_preference_legacy(sel, connection)
40
+ update_selector_for_read_pref(sel, connection)
41
+ sel
41
42
  end
42
43
 
43
44
  def message(connection)
@@ -332,6 +332,8 @@ module Mongo
332
332
  labels: parser.labels,
333
333
  wtimeout: parser.wtimeout,
334
334
  connection_description: connection_description,
335
+ document: parser.document,
336
+ server_message: parser.server_message,
335
337
  )
336
338
  end
337
339
 
@@ -23,11 +23,11 @@ module Mongo
23
23
 
24
24
  include ResponseHandling
25
25
 
26
- def do_execute(connection, client, options = {})
26
+ def do_execute(connection, context, options = {})
27
27
  unpin_maybe(session) do
28
- add_error_labels(client, connection, session) do
28
+ add_error_labels(connection, context) do
29
29
  add_server_diagnostics(connection) do
30
- get_result(connection, client, options).tap do |result|
30
+ get_result(connection, context, options).tap do |result|
31
31
  process_result(result, connection)
32
32
  end
33
33
  end
@@ -35,15 +35,15 @@ module Mongo
35
35
  end
36
36
  end
37
37
 
38
- def execute(connection, client:, options: {})
38
+ def execute(connection, context:, options: {})
39
39
  if Lint.enabled?
40
40
  unless connection.is_a?(Mongo::Server::Connection)
41
41
  raise Error::LintError, "Connection argument is of wrong type: #{connection}"
42
42
  end
43
43
  end
44
44
 
45
- do_execute(connection, client, options).tap do |result|
46
- validate_result(result, client, connection)
45
+ do_execute(connection, context, options).tap do |result|
46
+ validate_result(result, connection, context)
47
47
  end
48
48
  end
49
49
 
@@ -53,20 +53,30 @@ module Mongo
53
53
  Result
54
54
  end
55
55
 
56
- def get_result(connection, client, options = {})
57
- result_class.new(*dispatch_message(connection, client, options))
56
+ def get_result(connection, context, options = {})
57
+ result_class.new(*dispatch_message(connection, context, options))
58
58
  end
59
59
 
60
60
  # Returns a Protocol::Message or nil as reply.
61
- def dispatch_message(connection, client, options = {})
62
- message = build_message(connection)
63
- message = message.maybe_encrypt(connection, client)
64
- reply = connection.dispatch([ message ], operation_id, client, options)
61
+ def dispatch_message(connection, context, options = {})
62
+ message = build_message(connection, context)
63
+ message = message.maybe_encrypt(connection, context)
64
+ reply = connection.dispatch([ message ], context, options)
65
65
  [reply, connection.description]
66
66
  end
67
67
 
68
- def build_message(connection)
69
- message(connection)
68
+ # @param [ Mongo::Server::Connection ] connection The connection on which
69
+ # the operation is performed.
70
+ # @param [ Mongo::Operation::Context ] context The operation context.
71
+ def build_message(connection, context)
72
+ msg = message(connection)
73
+ if (server_api = context.server_api) &&
74
+ # Commands in a transaction do not allow API parameters.
75
+ !(context.in_transaction? && !context.starting_transaction?)
76
+ then
77
+ msg = msg.maybe_add_server_api(server_api)
78
+ end
79
+ msg
70
80
  end
71
81
 
72
82
  def process_result(result, connection)
@@ -21,8 +21,8 @@ module Mongo
21
21
  # @api private
22
22
  module ExecutableNoValidate
23
23
 
24
- def execute(connection, client:)
25
- do_execute(connection, client)
24
+ def execute(connection, context:)
25
+ do_execute(connection, context)
26
26
  end
27
27
  end
28
28
  end
@@ -20,15 +20,9 @@ module Mongo
20
20
  #
21
21
  # @api private
22
22
  module OpMsgOrCommand
23
+ include PolymorphicOperation
23
24
  include PolymorphicLookup
24
25
 
25
- def execute(server, client:, options: {})
26
- server.with_connection do |connection|
27
- operation = final_operation(connection)
28
- operation.execute(connection, client: client, options: options)
29
- end
30
- end
31
-
32
26
  private
33
27
 
34
28
  def final_operation(connection)
@@ -21,15 +21,9 @@ module Mongo
21
21
  #
22
22
  # @api private
23
23
  module OpMsgOrFindCommand
24
+ include PolymorphicOperation
24
25
  include PolymorphicLookup
25
26
 
26
- def execute(server, client:)
27
- server.with_connection do |connection|
28
- operation = final_operation(connection)
29
- operation.execute(connection, client: client)
30
- end
31
- end
32
-
33
27
  private
34
28
 
35
29
  def final_operation(connection)
@@ -0,0 +1,39 @@
1
+ # Copyright (C) 2021 MongoDB Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Mongo
16
+ module Operation
17
+
18
+ # Shared behavior of implementing an operation differently based on
19
+ # the server that will be executing the operation.
20
+ #
21
+ # @api private
22
+ module PolymorphicOperation
23
+
24
+ # Execute the operation.
25
+ #
26
+ # @param [ Mongo::Server ] server The server to send the operation to.
27
+ # @param [ Operation::Context ] context The operation context.
28
+ # @param [ Hash ] options Operation execution options.
29
+ #
30
+ # @return [ Mongo::Operation::Result ] The operation result.
31
+ def execute(server, context:, options: {})
32
+ server.with_connection do |connection|
33
+ operation = final_operation(connection)
34
+ operation.execute(connection, context: context, options: options)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -36,44 +36,47 @@ module Mongo
36
36
  #
37
37
  # @since 2.0.0
38
38
  def options(connection)
39
- options = super
40
- if add_slave_ok_flag?(connection)
41
- flags = options[:flags]&.dup || []
42
- flags << :slave_ok
43
- options = options.merge(flags: flags)
44
- end
45
- options
39
+ add_slave_ok_flag_maybe(super, connection)
46
40
  end
47
41
 
48
- # Whether to add the :slave_ok flag to the request based on the
49
- # read preference specified in the operation or implied by the topology
50
- # that the connection's server is a part of.
42
+ # Adds :slave_ok flag to options based on the read preference specified
43
+ # in the operation or implied by the topology that the connection's
44
+ # server is a part of.
51
45
  #
46
+ # @param [ Hash ] options The options calculated so far.
52
47
  # @param [ Server::Connection ] connection The connection that the
53
48
  # operation will be executed on.
54
49
  #
55
- # @return [ true | false ] Whether the :slave_ok flag should be added.
56
- def add_slave_ok_flag?(connection)
57
- # https://github.com/mongodb/specifications/blob/master/source/server-selection/server-selection.rst#topology-type-single
58
- if connection.description.standalone?
59
- # Read preference is never sent to standalones.
60
- false
61
- elsif connection.server.cluster.single?
62
- # In Single topology the driver forces primaryPreferred read
63
- # preference mode (via the slave_ok flag, in case of old servers)
64
- # so that the query is satisfied.
65
- true
66
- else
67
- # In replica sets and sharded clusters, read preference is passed
68
- # to the server if one is specified by the application, and there
69
- # is no default.
70
- read && read.slave_ok? || false
50
+ # @return [ Hash ] The new options.
51
+ def add_slave_ok_flag_maybe(options, connection)
52
+ add_flag =
53
+ # https://github.com/mongodb/specifications/blob/master/source/server-selection/server-selection.rst#topology-type-single
54
+ if connection.description.standalone?
55
+ # Read preference is never sent to standalones.
56
+ false
57
+ elsif connection.server.cluster.single?
58
+ # In Single topology the driver forces primaryPreferred read
59
+ # preference mode (via the slave_ok flag, in case of old servers)
60
+ # so that the query is satisfied.
61
+ true
62
+ else
63
+ # In replica sets and sharded clusters, read preference is passed
64
+ # to the server if one is specified by the application, and there
65
+ # is no default.
66
+ read && read.slave_ok?
67
+ end
68
+
69
+ if add_flag
70
+ options= options.dup
71
+ (options[:flags] ||= []) << :slave_ok
71
72
  end
73
+
74
+ options
72
75
  end
73
76
 
74
77
  def command(connection)
75
78
  sel = super
76
- add_read_preference_legacy(sel, connection)
79
+ update_selector_for_read_pref(sel, connection)
77
80
  end
78
81
 
79
82
  # Adds $readPreference field to the command document.
@@ -92,19 +95,14 @@ module Mongo
92
95
  # operation will be executed on.
93
96
  #
94
97
  # @return [ Hash ] New command document to send to the server.
95
- def add_read_preference_legacy(sel, connection)
98
+ def update_selector_for_read_pref(sel, connection)
96
99
  if read && connection.description.mongos? && read_pref = read.to_mongos
97
- # If the read preference contains only mode and mode is secondary
98
- # preferred and we are sending to a pre-OP_MSG server, this read
99
- # preference is indicated by the :slave_ok wire protocol flag
100
- # and $readPreference command parameter isn't sent.
101
- if read_pref != {mode: 'secondaryPreferred'}
102
- Mongo::Lint.validate_camel_case_read_preference(read_pref)
103
- sel = sel[:$query] ? sel : {:$query => sel}
104
- sel = sel.merge(:$readPreference => read_pref)
105
- end
100
+ Mongo::Lint.validate_camel_case_read_preference(read_pref)
101
+ sel = sel[:$query] ? sel : {:$query => sel}
102
+ sel = sel.merge(:$readPreference => read_pref)
103
+ else
104
+ sel
106
105
  end
107
- sel
108
106
  end
109
107
  end
110
108
  end
@@ -22,9 +22,13 @@ module Mongo
22
22
 
23
23
  private
24
24
 
25
- def validate_result(result, client, connection)
26
- unpin_maybe(session) do
27
- add_error_labels(client, connection, session) do
25
+ # @param [ Mongo::Operation::Result ] result The operation result.
26
+ # @param [ Mongo::Server::Connection ] connection The connection on which
27
+ # the operation is performed.
28
+ # @param [ Mongo::Operation::Context ] context The operation context.
29
+ def validate_result(result, connection, context)
30
+ unpin_maybe(context.session) do
31
+ add_error_labels(connection, context) do
28
32
  add_server_diagnostics(connection) do
29
33
  result.validate!
30
34
  end
@@ -38,25 +42,29 @@ module Mongo
38
42
  # and server-side errors (Error::OperationFailure); it does not
39
43
  # handle server selection errors (Error::NoServerAvailable), for which
40
44
  # labels are added in the server selection code.
41
- def add_error_labels(client, connection, session)
45
+ #
46
+ # @param [ Mongo::Server::Connection ] connection The connection on which
47
+ # the operation is performed.
48
+ # @param [ Mongo::Operation::Context ] context The operation context.
49
+ def add_error_labels(connection, context)
42
50
  begin
43
51
  yield
44
52
  rescue Mongo::Error::SocketError => e
45
- if session && session.in_transaction? && !session.committing_transaction?
53
+ if context.in_transaction? && !context.committing_transaction?
46
54
  e.add_label('TransientTransactionError')
47
55
  end
48
- if session && session.committing_transaction?
56
+ if context.committing_transaction?
49
57
  e.add_label('UnknownTransactionCommitResult')
50
58
  end
51
59
 
52
- maybe_add_retryable_write_error_label!(e, connection, client, session)
60
+ maybe_add_retryable_write_error_label!(e, connection, context)
53
61
 
54
62
  raise e
55
63
  rescue Mongo::Error::SocketTimeoutError => e
56
- maybe_add_retryable_write_error_label!(e, connection, client, session)
64
+ maybe_add_retryable_write_error_label!(e, connection, context)
57
65
  raise e
58
66
  rescue Mongo::Error::OperationFailure => e
59
- if session && session.committing_transaction?
67
+ if context.committing_transaction?
60
68
  if e.write_retryable? || e.wtimeout? || (e.write_concern_error? &&
61
69
  !Session::UNLABELED_WRITE_CONCERN_CODES.include?(e.write_concern_error_code)
62
70
  ) || e.max_time_ms_expired?
@@ -64,7 +72,7 @@ module Mongo
64
72
  end
65
73
  end
66
74
 
67
- maybe_add_retryable_write_error_label!(e, connection, client, session)
75
+ maybe_add_retryable_write_error_label!(e, connection, context)
68
76
 
69
77
  raise e
70
78
  end
@@ -125,21 +133,12 @@ module Mongo
125
133
  # @param [ Mongo::Error ] error The error to which to add the label.
126
134
  # @param [ Mongo::Server::Connection ] connection The connection on which
127
135
  # the operation is performed.
128
- # @param [ Mongo::Client | nil ] client The client that is performing
129
- # the operation.
130
- # @param [ Mongo::Session ] session The operation's session.
136
+ # @param [ Mongo::Operation::Context ] context The operation context.
131
137
  #
132
138
  # @note The client argument is optional because some operations, such as
133
139
  # end_session, do not pass the client as an argument to the execute
134
140
  # method.
135
- def maybe_add_retryable_write_error_label!(error, connection, client, session)
136
- in_transaction = session && session.in_transaction?
137
- committing_transaction = in_transaction && session.committing_transaction?
138
- aborting_transaction = in_transaction && session.aborting_transaction?
139
- modern_retry_writes = client && client.options[:retry_writes]
140
- legacy_retry_writes = client && !client.options[:retry_writes] &&
141
- client.max_write_retries > 0
142
-
141
+ def maybe_add_retryable_write_error_label!(error, connection, context)
143
142
  # An operation is retryable if it meets one of the following criteria:
144
143
  # - It is a commitTransaction or abortTransaction
145
144
  # - It does not occur during a transaction and the client has enabled
@@ -147,8 +146,9 @@ module Mongo
147
146
  #
148
147
  # Note: any write operation within a transaction (excepting commit and
149
148
  # abort is NOT a retryable operation)
150
- retryable_operation = committing_transaction || aborting_transaction ||
151
- (!in_transaction && (modern_retry_writes || legacy_retry_writes))
149
+ retryable_operation = context.committing_transaction? ||
150
+ context.aborting_transaction? ||
151
+ !context.in_transaction? && context.any_retry_writes?
152
152
 
153
153
  # An operation should add the RetryableWriteError label if one of the
154
154
  # following conditions is met:
@@ -171,10 +171,9 @@ module Mongo
171
171
  elsif connection.description.mongos?
172
172
  # When server is a mongos:
173
173
  # - $readPreference is never sent when mode is 'primary'
174
+ # - When mode is 'secondaryPreferred' $readPreference is only sent
175
+ # when a non-mode field (i.e. tag_sets) is present
174
176
  # - Otherwise $readPreference is sent
175
- # When mode is 'secondaryPreferred' $readPreference is currently
176
- # required to only be sent when a non-mode field (i.e. tag_sets)
177
- # is present, but this causes wrong behavior (DRIVERS-1642).
178
177
  if read
179
178
  doc = read.to_mongos
180
179
  if doc
@@ -223,9 +222,20 @@ module Mongo
223
222
  end
224
223
  end
225
224
 
226
- def build_message(connection)
225
+ def build_message(connection, context)
226
+ if self.session != context.session
227
+ if self.session
228
+ raise Error::InternalDriverError, "Operation session #{self.session.inspect} does not match context session #{context.session.inspect}"
229
+ else
230
+ # Some operations are not constructed with sessions but are
231
+ # executed in a context where a session is available.
232
+ # This could be OK or a driver issue.
233
+ # TODO investigate.
234
+ end
235
+ end
236
+
227
237
  super.tap do |message|
228
- if session
238
+ if session = context.session
229
239
  # Serialize the message to detect client-side problems,
230
240
  # such as invalid BSON keys. The message will be serialized again
231
241
  # later prior to being sent to the connection.
@@ -25,18 +25,13 @@ module Mongo
25
25
 
26
26
  # Execute the operation.
27
27
  #
28
- # @example
29
- # operation.execute(server, client: nil)
30
- #
31
28
  # @param [ Mongo::Server ] server The server to send the operation to.
32
- # @param [ Mongo::Client ] client The client that will be used to
33
- # perform auto-encryption if it is necessary to encrypt the command
34
- # being executed (optional).
29
+ # @param [ Operation::Context ] context The operation context.
35
30
  #
36
31
  # @return [ Mongo::Operation::Result ] The operation result.
37
32
  #
38
33
  # @since 2.5.2
39
- def execute(server, client:)
34
+ def execute(server, context:)
40
35
  server.with_connection do |connection|
41
36
  validate!(connection)
42
37
  op = if connection.features.op_msg_enabled?
@@ -47,34 +42,29 @@ module Mongo
47
42
  self.class::Command.new(spec)
48
43
  end
49
44
 
50
- result = op.execute(connection, client: client)
51
- validate_result(result, client, connection)
45
+ result = op.execute(connection, context: context)
46
+ validate_result(result, connection, context)
52
47
  end
53
48
  end
54
49
 
55
50
  # Execute the bulk write operation.
56
51
  #
57
- # @example
58
- # operation.bulk_execute(connection, client: nil)
59
- #
60
52
  # @param [ Mongo::Server::Connection ] connection The connection over
61
53
  # which to send the operation.
62
- # @param [ Mongo::Client ] client The client that will be used to
63
- # perform auto-encryption if it is necessary to encrypt the command
64
- # being executed (optional).
54
+ # @param [ Operation::Context ] context The operation context.
65
55
  #
66
56
  # @return [ Mongo::Operation::Delete::BulkResult,
67
57
  # Mongo::Operation::Insert::BulkResult,
68
58
  # Mongo::Operation::Update::BulkResult ] The bulk result.
69
59
  #
70
60
  # @since 2.5.2
71
- def bulk_execute(connection, client:)
61
+ def bulk_execute(connection, context:)
72
62
  Lint.assert_type(connection, Server::Connection)
73
63
 
74
64
  if connection.features.op_msg_enabled?
75
- self.class::OpMsg.new(spec).execute(connection, client: client).bulk_result
65
+ self.class::OpMsg.new(spec).execute(connection, context: context).bulk_result
76
66
  else
77
- self.class::Command.new(spec).execute(connection, client: client).bulk_result
67
+ self.class::Command.new(spec).execute(connection, context: context).bulk_result
78
68
  end
79
69
  end
80
70