mongo 2.9.2 → 2.10.0.rc0

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 (227) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongo.rb +1 -0
  5. data/lib/mongo/auth/user/view.rb +4 -4
  6. data/lib/mongo/bulk_write.rb +14 -8
  7. data/lib/mongo/bulk_write/result.rb +1 -1
  8. data/lib/mongo/bulk_write/result_combiner.rb +2 -2
  9. data/lib/mongo/bulk_write/transformable.rb +17 -9
  10. data/lib/mongo/client.rb +107 -16
  11. data/lib/mongo/cluster.rb +47 -25
  12. data/lib/mongo/cluster/topology/replica_set_no_primary.rb +1 -1
  13. data/lib/mongo/cluster_time.rb +139 -0
  14. data/lib/mongo/collection.rb +84 -25
  15. data/lib/mongo/collection/view.rb +7 -3
  16. data/lib/mongo/collection/view/aggregation.rb +4 -4
  17. data/lib/mongo/collection/view/builder/aggregation.rb +31 -6
  18. data/lib/mongo/collection/view/builder/find_command.rb +4 -1
  19. data/lib/mongo/collection/view/builder/map_reduce.rb +4 -1
  20. data/lib/mongo/collection/view/change_stream.rb +54 -66
  21. data/lib/mongo/collection/view/iterable.rb +2 -2
  22. data/lib/mongo/collection/view/map_reduce.rb +6 -4
  23. data/lib/mongo/collection/view/readable.rb +36 -16
  24. data/lib/mongo/collection/view/writable.rb +68 -22
  25. data/lib/mongo/cursor.rb +87 -20
  26. data/lib/mongo/database.rb +47 -43
  27. data/lib/mongo/database/view.rb +54 -11
  28. data/lib/mongo/error.rb +13 -4
  29. data/lib/mongo/error/invalid_write_concern.rb +2 -2
  30. data/lib/mongo/error/operation_failure.rb +65 -11
  31. data/lib/mongo/error/parser.rb +41 -8
  32. data/lib/mongo/grid/fs_bucket.rb +26 -6
  33. data/lib/mongo/grid/stream/read.rb +9 -2
  34. data/lib/mongo/grid/stream/write.rb +21 -5
  35. data/lib/mongo/index/view.rb +3 -3
  36. data/lib/mongo/lint.rb +10 -3
  37. data/lib/mongo/operation.rb +2 -0
  38. data/lib/mongo/operation/aggregate/result.rb +19 -6
  39. data/lib/mongo/operation/collections_info.rb +1 -1
  40. data/lib/mongo/operation/get_more/result.rb +9 -0
  41. data/lib/mongo/operation/list_collections/command.rb +1 -3
  42. data/lib/mongo/operation/list_collections/op_msg.rb +1 -2
  43. data/lib/mongo/operation/parallel_scan/command.rb +4 -1
  44. data/lib/mongo/operation/parallel_scan/op_msg.rb +4 -1
  45. data/lib/mongo/operation/result.rb +27 -4
  46. data/lib/mongo/operation/shared/executable.rb +19 -5
  47. data/lib/mongo/operation/shared/executable_no_validate.rb +1 -2
  48. data/lib/mongo/operation/shared/executable_transaction_label.rb +0 -9
  49. data/lib/mongo/operation/shared/polymorphic_result.rb +9 -1
  50. data/lib/mongo/operation/shared/result/aggregatable.rb +2 -2
  51. data/lib/mongo/operation/shared/sessions_supported.rb +42 -32
  52. data/lib/mongo/operation/shared/specifiable.rb +40 -0
  53. data/lib/mongo/operation/shared/unpinnable.rb +39 -0
  54. data/lib/mongo/operation/shared/write.rb +1 -1
  55. data/lib/mongo/protocol/update.rb +6 -2
  56. data/lib/mongo/retryable.rb +79 -39
  57. data/lib/mongo/server/connection.rb +10 -3
  58. data/lib/mongo/server/description.rb +25 -1
  59. data/lib/mongo/server/monitor/connection.rb +1 -1
  60. data/lib/mongo/server_selector.rb +10 -0
  61. data/lib/mongo/server_selector/selectable.rb +172 -32
  62. data/lib/mongo/session.rb +654 -581
  63. data/lib/mongo/session/session_pool.rb +1 -1
  64. data/lib/mongo/socket.rb +7 -28
  65. data/lib/mongo/socket/ssl.rb +26 -1
  66. data/lib/mongo/socket/tcp.rb +3 -0
  67. data/lib/mongo/socket/unix.rb +3 -0
  68. data/lib/mongo/uri.rb +112 -265
  69. data/lib/mongo/uri/srv_protocol.rb +4 -1
  70. data/lib/mongo/version.rb +1 -1
  71. data/lib/mongo/write_concern.rb +10 -29
  72. data/lib/mongo/write_concern/acknowledged.rb +12 -0
  73. data/lib/mongo/write_concern/base.rb +17 -13
  74. data/lib/mongo/write_concern/unacknowledged.rb +12 -0
  75. data/spec/atlas/atlas_connectivity_spec.rb +7 -37
  76. data/spec/atlas/operations_spec.rb +25 -0
  77. data/spec/integration/change_stream_examples_spec.rb +45 -31
  78. data/spec/integration/change_stream_spec.rb +305 -5
  79. data/spec/integration/client_spec.rb +44 -0
  80. data/spec/integration/command_monitoring_spec.rb +1 -0
  81. data/spec/integration/command_spec.rb +7 -1
  82. data/spec/integration/mmapv1_spec.rb +28 -0
  83. data/spec/integration/mongos_pinning_spec.rb +34 -0
  84. data/spec/integration/operation_failure_code_spec.rb +2 -2
  85. data/spec/integration/{read_concern.rb → read_concern_spec.rb} +7 -1
  86. data/spec/integration/read_preference_spec.rb +485 -0
  87. data/spec/integration/retryable_writes_spec.rb +8 -19
  88. data/spec/integration/sdam_error_handling_spec.rb +1 -1
  89. data/spec/integration/sdam_events_spec.rb +2 -2
  90. data/spec/integration/server_description_spec.rb +14 -17
  91. data/spec/integration/server_selector_spec.rb +7 -3
  92. data/spec/integration/server_spec.rb +48 -0
  93. data/spec/integration/ssl_uri_options_spec.rb +1 -1
  94. data/spec/integration/step_down_spec.rb +10 -4
  95. data/spec/integration/transactions_examples_spec.rb +11 -10
  96. data/spec/lite_spec_helper.rb +19 -16
  97. data/spec/mongo/auth/scram/negotiation_spec.rb +11 -8
  98. data/spec/mongo/bulk_write/ordered_combiner_spec.rb +6 -6
  99. data/spec/mongo/bulk_write/unordered_combiner_spec.rb +4 -4
  100. data/spec/mongo/bulk_write_spec.rb +12 -2
  101. data/spec/mongo/client_construction_spec.rb +160 -8
  102. data/spec/mongo/client_spec.rb +5 -4
  103. data/spec/mongo/cluster_spec.rb +6 -6
  104. data/spec/mongo/cluster_time_spec.rb +148 -0
  105. data/spec/mongo/collection/view/aggregation_spec.rb +34 -15
  106. data/spec/mongo/collection/view/change_stream_spec.rb +62 -3
  107. data/spec/mongo/collection/view/map_reduce_spec.rb +7 -5
  108. data/spec/mongo/collection/view/readable_spec.rb +4 -4
  109. data/spec/mongo/collection_spec.rb +331 -14
  110. data/spec/mongo/cursor_spec.rb +117 -5
  111. data/spec/mongo/database_spec.rb +240 -8
  112. data/spec/mongo/error/operation_failure_spec.rb +47 -1
  113. data/spec/mongo/error/parser_spec.rb +160 -23
  114. data/spec/mongo/operation/insert/bulk_spec.rb +2 -1
  115. data/spec/mongo/operation/result_spec.rb +27 -0
  116. data/spec/mongo/operation/update/bulk_spec.rb +1 -0
  117. data/spec/mongo/retryable_spec.rb +2 -0
  118. data/spec/mongo/server/app_metadata_spec.rb +2 -2
  119. data/spec/mongo/server/connection_spec.rb +13 -17
  120. data/spec/mongo/server/monitor/connection_spec.rb +13 -10
  121. data/spec/mongo/server_selector_spec.rb +34 -2
  122. data/spec/mongo/session/session_pool_spec.rb +14 -3
  123. data/spec/mongo/session_spec.rb +3 -3
  124. data/spec/mongo/session_transaction_spec.rb +4 -3
  125. data/spec/mongo/socket/ssl_spec.rb +19 -5
  126. data/spec/mongo/socket_spec.rb +1 -62
  127. data/spec/mongo/uri/srv_protocol_spec.rb +14 -20
  128. data/spec/mongo/uri_option_parsing_spec.rb +94 -8
  129. data/spec/mongo/uri_spec.rb +23 -10
  130. data/spec/mongo/write_concern_spec.rb +56 -3
  131. data/spec/spec_tests/change_streams_spec.rb +2 -1
  132. data/spec/spec_tests/cmap_spec.rb +1 -1
  133. data/spec/spec_tests/crud_spec.rb +12 -2
  134. data/spec/spec_tests/data/change_streams/change-streams-errors.yml +24 -1
  135. data/spec/spec_tests/data/change_streams/change-streams.yml +172 -3
  136. data/spec/spec_tests/data/command_monitoring/bulkWrite.yml +1 -1
  137. data/spec/spec_tests/data/command_monitoring/updateMany.yml +0 -2
  138. data/spec/spec_tests/data/command_monitoring/updateOne.yml +0 -5
  139. data/spec/spec_tests/data/crud/read/aggregate-out.yml +0 -6
  140. data/spec/spec_tests/data/crud/read/count-empty.yml +29 -0
  141. data/spec/spec_tests/data/crud/write/bulkWrite-arrayFilters.yml +1 -0
  142. data/spec/spec_tests/data/crud/write/bulkWrite-collation.yml +101 -0
  143. data/spec/spec_tests/data/crud/write/bulkWrite.yml +401 -0
  144. data/spec/spec_tests/data/crud/write/insertMany.yml +58 -2
  145. data/spec/spec_tests/data/crud/write/updateMany-arrayFilters.yml +3 -0
  146. data/spec/spec_tests/data/crud/write/updateOne-arrayFilters.yml +6 -1
  147. data/spec/spec_tests/data/crud_v2/aggregate-merge.yml +103 -0
  148. data/spec/spec_tests/data/crud_v2/aggregate-out-readConcern.yml +110 -0
  149. data/spec/spec_tests/data/crud_v2/bulkWrite-arrayFilters.yml +81 -0
  150. data/spec/spec_tests/data/crud_v2/db-aggregate.yml +38 -0
  151. data/spec/spec_tests/data/crud_v2/updateWithPipelines.yml +92 -0
  152. data/spec/spec_tests/data/retryable_writes/insertOne-serverErrors.yml +2 -2
  153. data/spec/spec_tests/data/transactions/abort.yml +3 -0
  154. data/spec/spec_tests/data/transactions/bulk.yml +3 -8
  155. data/spec/spec_tests/data/transactions/causal-consistency.yml +3 -8
  156. data/spec/spec_tests/data/transactions/commit.yml +3 -1
  157. data/spec/spec_tests/data/transactions/count.yml +3 -0
  158. data/spec/spec_tests/data/transactions/delete.yml +3 -0
  159. data/spec/spec_tests/data/transactions/error-labels.yml +4 -1
  160. data/spec/spec_tests/data/transactions/errors-client.yml +56 -0
  161. data/spec/spec_tests/data/transactions/errors.yml +3 -0
  162. data/spec/spec_tests/data/transactions/findOneAndDelete.yml +3 -0
  163. data/spec/spec_tests/data/transactions/findOneAndReplace.yml +3 -0
  164. data/spec/spec_tests/data/transactions/findOneAndUpdate.yml +3 -0
  165. data/spec/spec_tests/data/transactions/insert.yml +3 -0
  166. data/spec/spec_tests/data/transactions/isolation.yml +3 -0
  167. data/spec/spec_tests/data/transactions/mongos-pin-auto.yml +1671 -0
  168. data/spec/spec_tests/data/transactions/mongos-recovery-token.yml +347 -0
  169. data/spec/spec_tests/data/transactions/pin-mongos.yml +557 -0
  170. data/spec/spec_tests/data/transactions/read-concern.yml +3 -0
  171. data/spec/spec_tests/data/transactions/read-pref.yml +3 -0
  172. data/spec/spec_tests/data/transactions/reads.yml +3 -0
  173. data/spec/spec_tests/data/transactions/retryable-abort.yml +5 -2
  174. data/spec/spec_tests/data/transactions/retryable-commit.yml +4 -1
  175. data/spec/spec_tests/data/transactions/retryable-writes.yml +3 -0
  176. data/spec/spec_tests/data/transactions/run-command.yml +3 -0
  177. data/spec/spec_tests/data/transactions/transaction-options.yml +6 -0
  178. data/spec/spec_tests/data/transactions/update.yml +3 -8
  179. data/spec/spec_tests/data/transactions/write-concern.yml +348 -38
  180. data/spec/spec_tests/data/transactions_api/callback-aborts.yml +6 -0
  181. data/spec/spec_tests/data/transactions_api/callback-commits.yml +5 -0
  182. data/spec/spec_tests/data/transactions_api/callback-retry.yml +7 -2
  183. data/spec/spec_tests/data/transactions_api/commit-retry.yml +70 -15
  184. data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror-4.2.yml +3 -0
  185. data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror.yml +3 -0
  186. data/spec/spec_tests/data/transactions_api/commit-writeconcernerror.yml +59 -109
  187. data/spec/spec_tests/data/transactions_api/commit.yml +5 -0
  188. data/spec/spec_tests/data/transactions_api/transaction-options.yml +10 -0
  189. data/spec/spec_tests/retryable_reads_spec.rb +5 -2
  190. data/spec/spec_tests/retryable_writes_spec.rb +5 -2
  191. data/spec/spec_tests/sdam_monitoring_spec.rb +3 -3
  192. data/spec/spec_tests/sdam_spec.rb +2 -2
  193. data/spec/spec_tests/transactions_api_spec.rb +1 -67
  194. data/spec/spec_tests/transactions_spec.rb +2 -66
  195. data/spec/support/authorization.rb +4 -0
  196. data/spec/support/change_streams.rb +30 -10
  197. data/spec/support/change_streams/operation.rb +27 -0
  198. data/spec/support/client_registry.rb +44 -25
  199. data/spec/support/cluster_config.rb +25 -14
  200. data/spec/support/cluster_tools.rb +32 -10
  201. data/spec/support/command_monitoring.rb +1 -1
  202. data/spec/support/common_shortcuts.rb +30 -0
  203. data/spec/support/connection_string.rb +8 -3
  204. data/spec/support/constraints.rb +34 -0
  205. data/spec/support/crud.rb +31 -16
  206. data/spec/support/crud/context.rb +23 -0
  207. data/spec/support/crud/operation.rb +311 -14
  208. data/spec/support/crud/spec.rb +2 -1
  209. data/spec/support/crud/test.rb +24 -27
  210. data/spec/support/crud/test_base.rb +22 -0
  211. data/spec/support/crud/verifier.rb +15 -1
  212. data/spec/support/event_subscriber.rb +12 -0
  213. data/spec/support/sdam_formatter_integration.rb +12 -6
  214. data/spec/support/shared/server_selector.rb +10 -0
  215. data/spec/support/shared/session.rb +13 -12
  216. data/spec/support/spec_config.rb +32 -22
  217. data/spec/support/spec_setup.rb +2 -2
  218. data/spec/support/transactions.rb +87 -0
  219. data/spec/support/transactions/context.rb +33 -0
  220. data/spec/support/transactions/operation.rb +99 -349
  221. data/spec/support/transactions/spec.rb +1 -3
  222. data/spec/support/transactions/test.rb +110 -49
  223. data/spec/support/utils.rb +74 -1
  224. metadata +52 -10
  225. metadata.gz.sig +0 -0
  226. data/spec/support/crud/read.rb +0 -265
  227. data/spec/support/crud/write.rb +0 -284
@@ -46,7 +46,6 @@ module Mongo
46
46
  include Immutable
47
47
  include Iterable
48
48
  include Readable
49
- include Retryable
50
49
  include Explainable
51
50
  include Writable
52
51
 
@@ -60,7 +59,12 @@ module Mongo
60
59
  def_delegators :collection,
61
60
  :client,
62
61
  :cluster,
63
- :database
62
+ :database,
63
+ :read_with_retry,
64
+ :read_with_retry_cursor,
65
+ :write_with_retry,
66
+ :nro_write_with_retry,
67
+ :write_concern_with_session
64
68
 
65
69
  # Delegate to the cluster for the next primary.
66
70
  def_delegators :cluster, :next_primary
@@ -163,7 +167,7 @@ module Mongo
163
167
  #
164
168
  # @since 2.0.0
165
169
  def write_concern
166
- WriteConcern.get(options[:write] || options[:write_concern] || collection.write_concern)
170
+ WriteConcern.get(options[:write_concern] || options[:write] || collection.write_concern)
167
171
  end
168
172
 
169
173
  private
@@ -111,18 +111,18 @@ module Mongo
111
111
  server.standalone? || server.mongos? || server.primary? || secondary_ok?
112
112
  end
113
113
 
114
- def out?
115
- pipeline.any? { |op| op.key?('$out') || op.key?(:$out) }
114
+ def write?
115
+ pipeline.any? { |op| op.key?('$out') || op.key?(:$out) || op.key?('$merge') || op.key?(:$merge) }
116
116
  end
117
117
 
118
118
  def secondary_ok?
119
- !out?
119
+ !write?
120
120
  end
121
121
 
122
122
  def send_initial_query(server, session)
123
123
  unless valid_server?(server)
124
124
  log_warn("Rerouting the Aggregation operation to the primary server - #{server.summary} is not suitable")
125
- server = cluster.next_primary
125
+ server = cluster.next_primary(nil, session)
126
126
  end
127
127
  validate_collation!(server)
128
128
  initial_query_op(session).execute(server)
@@ -78,22 +78,40 @@ module Mongo
78
78
  spec = {
79
79
  selector: aggregation_command,
80
80
  db_name: database.name,
81
- read: read,
81
+ read: view.read_preference,
82
82
  session: @options[:session]
83
83
  }
84
- write? ? spec.merge!(write_concern: write_concern) : spec
84
+ if write?
85
+ spec.update(write_concern: write_concern)
86
+ end
87
+ spec
85
88
  end
86
89
 
87
90
  private
88
91
 
89
92
  def write?
90
- pipeline.any? { |operator| operator[:$out] || operator['$out'] }
93
+ pipeline.any? do |operator|
94
+ operator[:$out] || operator['$out'] ||
95
+ operator[:$merge] || operator['$merge']
96
+ end
91
97
  end
92
98
 
93
99
  def aggregation_command
94
- command = BSON::Document.new(:aggregate => collection.name, :pipeline => pipeline)
100
+ command = BSON::Document.new
101
+ # aggregate must be the first key in the command document
102
+ if view.is_a?(Collection::View)
103
+ command[:aggregate] = collection.name
104
+ elsif view.is_a?(Database::View)
105
+ command[:aggregate] = 1
106
+ else
107
+ raise ArgumentError, "Unknown view class: #{view}"
108
+ end
109
+ command[:pipeline] = pipeline
110
+ if read_concern = view.read_concern
111
+ command[:readConcern] = Options::Mapper.transform_values_to_strings(
112
+ read_concern)
113
+ end
95
114
  command[:cursor] = cursor if cursor
96
- command[:readConcern] = collection.read_concern if collection.read_concern
97
115
  command.merge!(Options::Mapper.transform_documents(options, MAPPINGS))
98
116
  command
99
117
  end
@@ -105,7 +123,14 @@ module Mongo
105
123
  end
106
124
 
107
125
  def batch_size_doc
108
- (value = options[:batch_size] || view.batch_size) ? { :batchSize => value } : {}
126
+ value = options[:batch_size] || view.batch_size
127
+ if value == 0 && write?
128
+ {}
129
+ elsif value
130
+ { :batchSize => value }
131
+ else
132
+ {}
133
+ end
109
134
  end
110
135
  end
111
136
  end
@@ -96,7 +96,10 @@ module Mongo
96
96
 
97
97
  def find_command
98
98
  document = BSON::Document.new('find' => collection.name, 'filter' => filter)
99
- document[:readConcern] = collection.read_concern if collection.read_concern
99
+ if collection.read_concern
100
+ document[:readConcern] = Options::Mapper.transform_values_to_strings(
101
+ collection.read_concern)
102
+ end
100
103
  command = Options::Mapper.transform_documents(convert_flags(options), MAPPINGS, document)
101
104
  convert_limit_and_batch_size(command)
102
105
  command
@@ -139,7 +139,10 @@ module Mongo
139
139
  :query => filter,
140
140
  :out => { inline: 1 }
141
141
  )
142
- command[:readConcern] = collection.read_concern if collection.read_concern
142
+ if collection.read_concern
143
+ command[:readConcern] = Options::Mapper.transform_values_to_strings(
144
+ collection.read_concern)
145
+ end
143
146
  command.merge!(view_options)
144
147
  command.merge!(Options::Mapper.transform_documents(options, MAPPINGS))
145
148
  command
@@ -94,8 +94,12 @@ module Mongo
94
94
  @changes_for = changes_for
95
95
  @change_stream_filters = pipeline && pipeline.dup
96
96
  @options = options && options.dup.freeze
97
- @resume_token = @options[:resume_after]
98
97
  @start_after = @options[:start_after]
98
+
99
+ # The resume token tracked by the change stream, used only
100
+ # when there is no cursor, or no cursor resume token
101
+ @resume_token = @start_after || @options[:resume_after]
102
+
99
103
  create_cursor!
100
104
 
101
105
  # We send different parameters when we resume a change stream
@@ -121,26 +125,12 @@ module Mongo
121
125
  # @yieldparam [ BSON::Document ] Each change stream document.
122
126
  def each
123
127
  raise StopIteration.new if closed?
124
- retried = false
125
- begin
126
- @cursor.each do |doc|
127
- cache_resume_token(doc)
128
- yield doc
129
- end if block_given?
130
- @cursor.to_enum
131
- rescue Mongo::Error => e
132
- if retried || !e.change_stream_resumable?
133
- raise
134
- end
135
-
136
- retried = true
137
- # Rerun initial aggregation.
138
- # Any errors here will stop iteration and break out of this
139
- # method
140
- close
141
- create_cursor!
142
- retry
128
+ loop do
129
+ document = try_next
130
+ yield document if document
143
131
  end
132
+ rescue StopIteration => e
133
+ return self
144
134
  end
145
135
 
146
136
  # Return one document from the change stream, if one is available.
@@ -153,10 +143,7 @@ module Mongo
153
143
  # for changes from the server, and if no changes are received
154
144
  # it will return nil.
155
145
  #
156
- # @note This method is experimental and subject to change.
157
- #
158
146
  # @return [ BSON::Document | nil ] A change stream document.
159
- # @api experimental
160
147
  # @since 2.6.0
161
148
  def try_next
162
149
  raise StopIteration.new if closed?
@@ -165,27 +152,28 @@ module Mongo
165
152
  begin
166
153
  doc = @cursor.try_next
167
154
  rescue Mongo::Error => e
168
- unless e.change_stream_resumable?
155
+ if retried || !e.change_stream_resumable?
169
156
  raise
170
157
  end
171
158
 
172
- if retried
173
- # Rerun initial aggregation.
174
- # Any errors here will stop iteration and break out of this
175
- # method
176
- close
177
- create_cursor!
178
- retried = false
179
- doc = @cursor.try_next
180
- else
181
- # Attempt to retry a getMore once
182
- retried = true
183
- retry
184
- end
159
+ retried = true
160
+ # Rerun initial aggregation.
161
+ # Any errors here will stop iteration and break out of this
162
+ # method
163
+
164
+ # Save cursor's resume token so we can use it
165
+ # to create a new cursor
166
+ @resume_token = @cursor.resume_token
167
+
168
+ close
169
+ create_cursor!
170
+ retry
185
171
  end
186
172
 
187
- if doc
188
- cache_resume_token(doc)
173
+ # We need to verify each doc has an _id, so we
174
+ # have a resume token to work with
175
+ if doc && doc['_id'].nil?
176
+ raise Error::MissingResumeToken
189
177
  end
190
178
  doc
191
179
  end
@@ -238,7 +226,21 @@ module Mongo
238
226
  # @since 2.5.0
239
227
  def inspect
240
228
  "#<Mongo::Collection::View:ChangeStream:0x#{object_id} filters=#{@change_stream_filters} " +
241
- "options=#{@options} resume_token=#{@resume_token}>"
229
+ "options=#{@options} resume_token=#{resume_token}>"
230
+ end
231
+
232
+ # Returns the resume token that the stream will
233
+ # use to automatically resume, if one exists.
234
+ #
235
+ # @example Get the change stream resume token.
236
+ # stream.resume_token
237
+ #
238
+ # @return [ BSON::Document | nil ] The change stream resume token.
239
+ #
240
+ # @since 2.10.0
241
+ def resume_token
242
+ cursor_resume_token = @cursor.resume_token if @cursor
243
+ cursor_resume_token || @resume_token
242
244
  end
243
245
 
244
246
  private
@@ -255,15 +257,6 @@ module Mongo
255
257
  !for_cluster? && !for_database?
256
258
  end
257
259
 
258
- def cache_resume_token(doc)
259
- # Always record both resume token and operation time,
260
- # in case we get an older or newer server during rolling
261
- # upgrades/downgrades
262
- unless @resume_token = (doc[:_id] && doc[:_id].dup)
263
- raise Error::MissingResumeToken
264
- end
265
- end
266
-
267
260
  def create_cursor!
268
261
  # clear the cache because we may get a newer or an older server
269
262
  # (rolling upgrades)
@@ -271,7 +264,10 @@ module Mongo
271
264
 
272
265
  session = client.send(:get_session, @options)
273
266
  start_at_operation_time = nil
267
+ start_at_operation_time_supported = nil
274
268
  @cursor = read_with_retry_cursor(session, server_selector, view) do |server|
269
+ start_at_operation_time_supported = server.description.server_version_gte?('4.0')
270
+
275
271
  result = send_initial_query(server, session)
276
272
  if doc = result.replies.first && result.replies.first.documents.first
277
273
  start_at_operation_time = doc['operationTime']
@@ -286,6 +282,7 @@ module Mongo
286
282
  result
287
283
  end
288
284
  @start_at_operation_time = start_at_operation_time
285
+ @start_at_operation_time_supported = start_at_operation_time_supported
289
286
  end
290
287
 
291
288
  def pipeline
@@ -305,14 +302,11 @@ module Mongo
305
302
  # However, if the first getMore fails and the user didn't pass
306
303
  # a resume token we won't have a resume token to use.
307
304
  # Use start_at_operation time in this case
308
- if @resume_token
309
- # Spec says we need to remove startAtOperationTime if
310
- # one was passed in by user, thus we won't forward it
311
- elsif @start_after
312
- # The spec says to set `resumeAfter` to the `startAfter` token and not to send
313
- # either `startAfter` or `startAtOperationTime`.
314
- @resume_token = @start_after
315
- elsif start_at_operation_time_supported? && @start_at_operation_time
305
+ if resume_token
306
+ # Spec says we need to remove both startAtOperationTime and startAfter if
307
+ # either was passed in by user, thus we won't forward them
308
+ doc[:resumeAfter] = resume_token
309
+ elsif @start_at_operation_time_supported && @start_at_operation_time
316
310
  # It is crucial to check @start_at_operation_time_supported
317
311
  # here - we may have switched to an older server that
318
312
  # does not support operation times and therefore shouldn't
@@ -327,6 +321,8 @@ module Mongo
327
321
  else
328
322
  if @start_after
329
323
  doc[:startAfter] = @start_after
324
+ elsif resume_token
325
+ doc[:resumeAfter] = resume_token
330
326
  end
331
327
 
332
328
  if options[:start_at_operation_time]
@@ -334,7 +330,7 @@ module Mongo
334
330
  options[:start_at_operation_time])
335
331
  end
336
332
  end
337
- doc[:resumeAfter] = @resume_token if @resume_token
333
+
338
334
  doc[:allChangesForCluster] = true if for_cluster?
339
335
  end
340
336
  end
@@ -357,14 +353,6 @@ module Mongo
357
353
  def resuming?
358
354
  !!@resuming
359
355
  end
360
-
361
- def start_at_operation_time_supported?
362
- if @start_at_operation_time_supported.nil?
363
- server = server_selector.select_server(cluster)
364
- @start_at_operation_time_supported = server.description.max_wire_version >= 7
365
- end
366
- @start_at_operation_time_supported
367
- end
368
356
  end
369
357
  end
370
358
  end
@@ -37,8 +37,8 @@ module Mongo
37
37
  def each
38
38
  @cursor = nil
39
39
  session = client.send(:get_session, @options)
40
- @cursor = if respond_to?(:out?, true) && out?
41
- server = server_selector.select_server(cluster)
40
+ @cursor = if respond_to?(:write?, true) && write?
41
+ server = server_selector.select_server(cluster, nil, session)
42
42
  result = send_initial_query(server, session)
43
43
  Cursor.new(view, result, server, session: session)
44
44
  else
@@ -68,7 +68,7 @@ module Mongo
68
68
  def each
69
69
  @cursor = nil
70
70
  session = client.send(:get_session, @options)
71
- server = cluster.next_primary
71
+ server = cluster.next_primary(nil, session)
72
72
  result = send_initial_query(server, session)
73
73
  result = send_fetch_query(server, session) unless inline?
74
74
  @cursor = Cursor.new(view, result, server, session: session)
@@ -195,7 +195,8 @@ module Mongo
195
195
  # @since 2.5.0
196
196
  def execute
197
197
  view.send(:with_session, @options) do |session|
198
- legacy_write_with_retry do |server|
198
+ write_concern = view.write_concern_with_session(session)
199
+ nro_write_with_retry(session, write_concern) do |server|
199
200
  send_initial_query(server, session)
200
201
  end
201
202
  end
@@ -233,8 +234,9 @@ module Mongo
233
234
 
234
235
  def send_initial_query(server, session)
235
236
  unless valid_server?(server)
236
- log_warn("Rerouting the MapReduce operation to the primary server - #{server.summary} is not suitable")
237
- server = cluster.next_primary
237
+ msg = "Rerouting the MapReduce operation to the primary server - #{server.summary} is not suitable"
238
+ log_warn(msg)
239
+ server = cluster.next_primary(nil, session)
238
240
  end
239
241
  validate_collation!(server)
240
242
  initial_query_op(session).execute(server)
@@ -138,7 +138,10 @@ module Mongo
138
138
  cmd[:skip] = opts[:skip] if opts[:skip]
139
139
  cmd[:hint] = opts[:hint] if opts[:hint]
140
140
  cmd[:limit] = opts[:limit] if opts[:limit]
141
- cmd[:readConcern] = read_concern if read_concern
141
+ if read_concern
142
+ cmd[:readConcern] = Options::Mapper.transform_values_to_strings(
143
+ read_concern)
144
+ end
142
145
  cmd[:maxTimeMS] = opts[:max_time_ms] if opts[:max_time_ms]
143
146
  Mongo::Lint.validate_underscore_read_preference(opts[:read])
144
147
  read_pref = opts[:read] || read_preference
@@ -205,7 +208,10 @@ module Mongo
205
208
  def estimated_document_count(opts = {})
206
209
  cmd = { count: collection.name }
207
210
  cmd[:maxTimeMS] = opts[:max_time_ms] if opts[:max_time_ms]
208
- cmd[:readConcern] = read_concern if read_concern
211
+ if read_concern
212
+ cmd[:readConcern] = Options::Mapper.transform_values_to_strings(
213
+ read_concern)
214
+ end
209
215
  Mongo::Lint.validate_underscore_read_preference(opts[:read])
210
216
  read_pref = opts[:read] || read_preference
211
217
  selector = ServerSelector.get(read_pref || server_selector)
@@ -242,7 +248,10 @@ module Mongo
242
248
  :key => field_name.to_s,
243
249
  :query => filter }
244
250
  cmd[:maxTimeMS] = opts[:max_time_ms] if opts[:max_time_ms]
245
- cmd[:readConcern] = read_concern if read_concern
251
+ if read_concern
252
+ cmd[:readConcern] = Options::Mapper.transform_values_to_strings(
253
+ read_concern)
254
+ end
246
255
  Mongo::Lint.validate_underscore_read_preference(opts[:read])
247
256
  read_pref = opts[:read] || read_preference
248
257
  selector = ServerSelector.get(read_pref || server_selector)
@@ -533,12 +542,7 @@ module Mongo
533
542
  configure(:cursor_type, type)
534
543
  end
535
544
 
536
- private
537
-
538
- def collation(doc = nil)
539
- configure(:collation, doc)
540
- end
541
-
545
+ # @api private
542
546
  def read_concern
543
547
  if options[:session] && options[:session].in_transaction?
544
548
  options[:session].send(:txn_read_concern) || collection.client.read_concern
@@ -547,14 +551,30 @@ module Mongo
547
551
  end
548
552
  end
549
553
 
554
+ # @api private
550
555
  def read_preference
551
- rp = if options[:session] && options[:session].in_transaction?
552
- options[:session].txn_read_preference || collection.client.read_preference
553
- else
554
- @read_preference ||= (options[:read] || collection.read_preference)
556
+ @read_preference ||= begin
557
+ # Operation read preference is always respected, and has the
558
+ # highest priority. If we are in a transaction, we look at
559
+ # transaction read preference and default to client, ignoring
560
+ # collection read preference. If we are not in transaction we
561
+ # look at collection read preference which defaults to client.
562
+ rp = if options[:read]
563
+ options[:read]
564
+ elsif options[:session] && options[:session].in_transaction?
565
+ options[:session].txn_read_preference || collection.client.read_preference
566
+ else
567
+ collection.read_preference
568
+ end
569
+ Lint.validate_underscore_read_preference(rp)
570
+ rp
555
571
  end
556
- Lint.validate_underscore_read_preference(rp)
557
- rp
572
+ end
573
+
574
+ private
575
+
576
+ def collation(doc = nil)
577
+ configure(:collation, doc)
558
578
  end
559
579
 
560
580
  def server_selector
@@ -571,7 +591,7 @@ module Mongo
571
591
  else
572
592
  session = nil
573
593
  end
574
- server = server_selector.select_server(cluster)
594
+ server = server_selector.select_server(cluster, nil, session)
575
595
  cmd = Operation::ParallelScan.new({
576
596
  :coll_name => collection.name,
577
597
  :db_name => database.name,