mongo 2.16.2 → 2.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +1 -1
  4. data/lib/mongo/auth/aws/request.rb +0 -1
  5. data/lib/mongo/client.rb +4 -0
  6. data/lib/mongo/collection/view/aggregation.rb +62 -17
  7. data/lib/mongo/collection/view/builder/aggregation.rb +11 -13
  8. data/lib/mongo/collection/view/builder/map_reduce.rb +5 -8
  9. data/lib/mongo/collection/view/change_stream.rb +7 -3
  10. data/lib/mongo/collection/view/iterable.rb +2 -3
  11. data/lib/mongo/collection/view/map_reduce.rb +3 -14
  12. data/lib/mongo/collection/view/readable.rb +24 -1
  13. data/lib/mongo/collection/view/writable.rb +23 -0
  14. data/lib/mongo/collection.rb +21 -1
  15. data/lib/mongo/database/view.rb +4 -2
  16. data/lib/mongo/database.rb +6 -6
  17. data/lib/mongo/error/snapshot_session_invalid_server_version.rb +31 -0
  18. data/lib/mongo/error/snapshot_session_transaction_prohibited.rb +30 -0
  19. data/lib/mongo/error.rb +2 -0
  20. data/lib/mongo/operation/delete/op_msg.rb +2 -1
  21. data/lib/mongo/operation/find/builder/command.rb +1 -0
  22. data/lib/mongo/operation/result.rb +6 -0
  23. data/lib/mongo/operation/shared/executable.rb +4 -0
  24. data/lib/mongo/operation/shared/sessions_supported.rb +18 -2
  25. data/lib/mongo/operation/update/op_msg.rb +2 -1
  26. data/lib/mongo/server/description/features.rb +3 -1
  27. data/lib/mongo/server_selector/base.rb +26 -4
  28. data/lib/mongo/session.rb +19 -0
  29. data/lib/mongo/socket/ocsp_cache.rb +2 -3
  30. data/lib/mongo/socket.rb +1 -5
  31. data/lib/mongo/utils.rb +0 -6
  32. data/lib/mongo/version.rb +1 -1
  33. data/mongo.gemspec +1 -1
  34. data/spec/integration/read_preference_spec.rb +16 -12
  35. data/spec/lite_spec_helper.rb +0 -7
  36. data/spec/mongo/collection/view/aggregation_spec.rb +71 -95
  37. data/spec/mongo/collection/view/change_stream_spec.rb +1 -1
  38. data/spec/mongo/collection/view/map_reduce_spec.rb +14 -17
  39. data/spec/mongo/operation/read_preference_op_msg_spec.rb +24 -1
  40. data/spec/mongo/server_selector_spec.rb +136 -15
  41. data/spec/mongo/socket/ssl_spec.rb +26 -58
  42. data/spec/mongo/utils_spec.rb +0 -14
  43. data/spec/runners/auth.rb +1 -1
  44. data/spec/runners/change_streams/spec.rb +1 -1
  45. data/spec/runners/cmap.rb +1 -1
  46. data/spec/runners/command_monitoring.rb +1 -1
  47. data/spec/runners/connection_string.rb +1 -1
  48. data/spec/runners/crud/spec.rb +3 -1
  49. data/spec/runners/crud/verifier.rb +1 -2
  50. data/spec/runners/gridfs.rb +1 -1
  51. data/spec/runners/read_write_concern_document.rb +1 -1
  52. data/spec/runners/sdam.rb +1 -1
  53. data/spec/runners/server_selection.rb +1 -1
  54. data/spec/runners/server_selection_rtt.rb +1 -1
  55. data/spec/runners/unified/assertions.rb +3 -1
  56. data/spec/runners/unified/crud_operations.rb +77 -23
  57. data/spec/runners/unified/ddl_operations.rb +29 -1
  58. data/spec/runners/unified/entity_map.rb +3 -3
  59. data/spec/runners/unified/support_operations.rb +6 -1
  60. data/spec/runners/unified/test.rb +15 -3
  61. data/spec/runners/unified/test_group.rb +1 -1
  62. data/spec/spec_tests/data/crud_unified/aggregate-let.yml +138 -0
  63. data/spec/spec_tests/data/crud_unified/aggregate-write-readPreference.yml +155 -0
  64. data/spec/spec_tests/data/crud_unified/db-aggregate-write-readPreference.yml +151 -0
  65. data/spec/spec_tests/data/crud_unified/deleteMany-let.yml +91 -0
  66. data/spec/spec_tests/data/crud_unified/deleteOne-let.yml +89 -0
  67. data/spec/spec_tests/data/crud_unified/find-let.yml +71 -0
  68. data/spec/spec_tests/data/crud_unified/findOneAndDelete-let.yml +88 -0
  69. data/spec/spec_tests/data/crud_unified/findOneAndReplace-let.yml +94 -0
  70. data/spec/spec_tests/data/crud_unified/findOneAndUpdate-let.yml +96 -0
  71. data/spec/spec_tests/data/crud_unified/updateMany-let.yml +103 -0
  72. data/spec/spec_tests/data/crud_unified/updateOne-let.yml +98 -0
  73. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/DefaultNoMaxStaleness.yml +2 -2
  74. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/LastUpdateTime.yml +3 -3
  75. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/Nearest.yml +3 -3
  76. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/Nearest2.yml +3 -3
  77. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/PrimaryPreferred.yml +2 -2
  78. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/PrimaryPreferred_tags.yml +2 -2
  79. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/Secondary.yml +4 -4
  80. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/SecondaryPreferred.yml +2 -2
  81. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/SecondaryPreferred_tags.yml +4 -4
  82. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/ZeroMaxStaleness.yml +2 -2
  83. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/DefaultNoMaxStaleness.yml +2 -2
  84. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/LastUpdateTime.yml +3 -3
  85. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/LongHeartbeat.yml +2 -2
  86. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/LongHeartbeat2.yml +2 -2
  87. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/MaxStalenessTooSmall.yml +2 -2
  88. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/MaxStalenessWithModePrimary.yml +2 -2
  89. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/Nearest.yml +3 -3
  90. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/Nearest2.yml +3 -3
  91. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/Nearest_tags.yml +2 -2
  92. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/PrimaryPreferred.yml +2 -2
  93. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred.yml +2 -2
  94. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred_tags.yml +5 -5
  95. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/SecondaryPreferred_tags2.yml +3 -3
  96. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/Secondary_tags.yml +5 -5
  97. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/Secondary_tags2.yml +3 -3
  98. data/spec/spec_tests/data/max_staleness/ReplicaSetWithPrimary/ZeroMaxStaleness.yml +2 -2
  99. data/spec/spec_tests/data/max_staleness/Sharded/SmallMaxStaleness.yml +2 -2
  100. data/spec/spec_tests/data/max_staleness/Single/SmallMaxStaleness.yml +1 -1
  101. data/spec/spec_tests/data/max_staleness/Unknown/SmallMaxStaleness.yml +1 -1
  102. data/spec/spec_tests/data/sessions_unified/snapshot-sessions-not-supported-client-error.yml +69 -0
  103. data/spec/spec_tests/data/sessions_unified/snapshot-sessions-not-supported-server-error.yml +102 -0
  104. data/spec/spec_tests/data/sessions_unified/snapshot-sessions-unsupported-ops.yml +258 -0
  105. data/spec/spec_tests/data/sessions_unified/snapshot-sessions.yml +482 -0
  106. data/spec/spec_tests/seed_list_discovery_spec.rb +1 -1
  107. data/spec/spec_tests/sessions_unified_spec.rb +13 -0
  108. data/spec/support/utils.rb +0 -31
  109. data.tar.gz.sig +3 -1
  110. metadata +1051 -1018
  111. metadata.gz.sig +0 -0
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ # Copyright (C) 2021 MongoDB Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ module Mongo
19
+ class Error
20
+
21
+ # Exception raised if a transaction is attempted on a snapshot session.
22
+ class SnapshotSessionTransactionProhibited < Error
23
+
24
+ # Instantiate the new exception.
25
+ def initialize
26
+ super("Transactions are not supported in snapshot sessions")
27
+ end
28
+ end
29
+ end
30
+ end
data/lib/mongo/error.rb CHANGED
@@ -223,6 +223,8 @@ require 'mongo/error/no_server_available'
223
223
  require 'mongo/error/no_srv_records'
224
224
  require 'mongo/error/session_ended'
225
225
  require 'mongo/error/sessions_not_supported'
226
+ require 'mongo/error/snapshot_session_invalid_server_version'
227
+ require 'mongo/error/snapshot_session_transaction_prohibited'
226
228
  require 'mongo/error/operation_failure'
227
229
  require 'mongo/error/pool_closed_error'
228
230
  require 'mongo/error/raise_original_error'
@@ -37,7 +37,8 @@ module Mongo
37
37
  { delete: coll_name,
38
38
  Protocol::Msg::DATABASE_IDENTIFIER => db_name,
39
39
  ordered: ordered?,
40
- }.tap do |selector|
40
+ let: spec[:let],
41
+ }.compact.tap do |selector|
41
42
  if hint = spec[:hint]
42
43
  validate_hint_on_update(connection, selector)
43
44
  selector[:hint] = hint
@@ -35,6 +35,7 @@ module Mongo
35
35
  comment: 'comment',
36
36
  filter: 'filter',
37
37
  hint: 'hint',
38
+ let: 'let',
38
39
  limit: 'limit',
39
40
  max_scan: 'maxScan',
40
41
  max_time_ms: 'maxTimeMS',
@@ -429,6 +429,12 @@ module Mongo
429
429
  !!(first_document && first_document['writeConcernError'])
430
430
  end
431
431
 
432
+ def snapshot_timestamp
433
+ if doc = reply.documents.first
434
+ doc['cursor']&.[]('atClusterTime') || doc['atClusterTime']
435
+ end
436
+ end
437
+
432
438
  private
433
439
 
434
440
  def aggregate_returned_count
@@ -43,6 +43,10 @@ module Mongo
43
43
  session.pin_to_service(connection.service_id)
44
44
  end
45
45
  end
46
+
47
+ if session.snapshot? && !session.snapshot_timestamp
48
+ session.snapshot_timestamp = result.snapshot_timestamp
49
+ end
46
50
  end
47
51
  process_result(result, connection)
48
52
  end
@@ -199,8 +199,13 @@ module Mongo
199
199
  read_doc
200
200
  else
201
201
  # In replica sets, read preference is passed to the server if one
202
- # is specified by the application, and there is no default.
203
- read&.to_doc
202
+ # is specified by the application, except for primary read preferences.
203
+ read_doc = BSON::Document.new(read&.to_doc || {})
204
+ if [nil, 'primary'].include?(read_doc['mode'])
205
+ nil
206
+ else
207
+ read_doc
208
+ end
204
209
  end
205
210
 
206
211
  if read_doc
@@ -224,6 +229,17 @@ module Mongo
224
229
  then
225
230
  sel[:recoveryToken] = session.recovery_token
226
231
  end
232
+
233
+ if session.snapshot?
234
+ unless connection.description.server_version_gte?('5.0')
235
+ raise Error::SnapshotSessionInvalidServerVersion
236
+ end
237
+
238
+ sel[:readConcern] = {level: 'snapshot'}
239
+ if session.snapshot_timestamp
240
+ sel[:readConcern][:atClusterTime] = session.snapshot_timestamp
241
+ end
242
+ end
227
243
  end
228
244
 
229
245
  def build_message(connection, context)
@@ -37,7 +37,8 @@ module Mongo
37
37
  {
38
38
  update: coll_name,
39
39
  ordered: ordered?,
40
- }
40
+ let: spec[:let]
41
+ }.compact
41
42
  end
42
43
 
43
44
  def message(connection)
@@ -35,9 +35,11 @@ module Mongo
35
35
  # - 8 => 4.2
36
36
  # - 9 => 4.4
37
37
  # - 13 => 5.0
38
+ # - 14 => 5.1
38
39
  #
39
40
  # @since 2.0.0
40
41
  MAPPINGS = {
42
+ merge_out_on_secondary: 13,
41
43
  retryable_write_error_label: 9,
42
44
  commit_quorum: 9,
43
45
  # Server versions older than 4.2 do not reliably validate options
@@ -78,7 +80,7 @@ module Mongo
78
80
  # The wire protocol versions that this version of the driver supports.
79
81
  #
80
82
  # @since 2.0.0
81
- DRIVER_WIRE_VERSIONS = (2..13).freeze
83
+ DRIVER_WIRE_VERSIONS = (6..14).freeze
82
84
 
83
85
  # Create the methods for each mapping to tell if they are supported.
84
86
  #
@@ -162,6 +162,8 @@ module Mongo
162
162
  # Deprecated and ignored.
163
163
  # @param [ Session | nil ] session Optional session to take into account
164
164
  # for mongos pinning. Added in version 2.10.0.
165
+ # @param [ true | false ] write_aggregation Whether we need a server that
166
+ # supports writing aggregations (e.g. with $merge/$out) on secondaries.
165
167
  #
166
168
  # @return [ Mongo::Server ] A server matching the server preference.
167
169
  #
@@ -172,7 +174,7 @@ module Mongo
172
174
  # lint mode is enabled.
173
175
  #
174
176
  # @since 2.0.0
175
- def select_server(cluster, ping = nil, session = nil)
177
+ def select_server(cluster, ping = nil, session = nil, write_aggregation: false)
176
178
  if cluster.topology.is_a?(Cluster::Topology::LoadBalanced)
177
179
  return cluster.servers.first
178
180
  end
@@ -243,7 +245,7 @@ module Mongo
243
245
  =end
244
246
 
245
247
  loop do
246
- server = try_select_server(cluster)
248
+ server = try_select_server(cluster, write_aggregation: write_aggregation)
247
249
 
248
250
  if server
249
251
  unless cluster.topology.compatible?
@@ -294,11 +296,31 @@ module Mongo
294
296
  # Tries to find a suitable server, returns the server if one is available
295
297
  # or nil if there isn't a suitable server.
296
298
  #
299
+ # @param [ Mongo::Cluster ] cluster The cluster from which to select
300
+ # an eligible server.
301
+ # @param [ true | false ] write_aggregation Whether we need a server that
302
+ # supports writing aggregations (e.g. with $merge/$out) on secondaries.
303
+ #
297
304
  # @return [ Server | nil ] A suitable server, if one exists.
298
305
  #
299
306
  # @api private
300
- def try_select_server(cluster)
301
- servers = suitable_servers(cluster)
307
+ def try_select_server(cluster, write_aggregation: false)
308
+ servers = if write_aggregation && cluster.replica_set?
309
+ # 1. Check if ALL servers in cluster support secondary writes.
310
+ is_write_supported = cluster.servers.reduce(true) do |res, server|
311
+ res && server.features.merge_out_on_secondary_enabled?
312
+ end
313
+
314
+ if is_write_supported
315
+ # 2. If all servers support secondary writes, we respect read preference.
316
+ suitable_servers(cluster)
317
+ else
318
+ # 3. Otherwise we fallback to primary for replica set.
319
+ [cluster.servers.detect(&:primary?)]
320
+ end
321
+ else
322
+ suitable_servers(cluster)
323
+ end
302
324
 
303
325
  # This list of servers may be ordered in a specific way
304
326
  # by the selector (e.g. for secondary preferred, the first
data/lib/mongo/session.rb CHANGED
@@ -56,10 +56,16 @@ module Mongo
56
56
  # - *:mode* -- the read preference as a string or symbol; valid values are
57
57
  # *:primary*, *:primary_preferred*, *:secondary*, *:secondary_preferred*
58
58
  # and *:nearest*.
59
+ # @option options [ true | false ] :snapshot Set up the session for
60
+ # snapshot reads.
59
61
  #
60
62
  # @since 2.5.0
61
63
  # @api private
62
64
  def initialize(server_session, client, options = {})
65
+ if options[:causal_consistency] && options[:snapshot]
66
+ raise ArgumentError, ':causal_consistency and :snapshot options cannot be both set on a session'
67
+ end
68
+
63
69
  @server_session = server_session
64
70
  options = options.dup
65
71
 
@@ -83,6 +89,12 @@ module Mongo
83
89
  @client.cluster
84
90
  end
85
91
 
92
+ # @return [ true | false ] Whether the session is configured for snapshot
93
+ # reads.
94
+ def snapshot?
95
+ !!options[:snapshot]
96
+ end
97
+
86
98
  # @return [ BSON::Timestamp ] The latest seen operation time for this session.
87
99
  #
88
100
  # @since 2.5.0
@@ -506,6 +518,10 @@ module Mongo
506
518
  =end
507
519
  end
508
520
 
521
+ if snapshot?
522
+ raise Mongo::Error::SnapshotSessionTransactionProhibited
523
+ end
524
+
509
525
  check_if_ended!
510
526
 
511
527
  if within_states?(STARTING_TRANSACTION_STATE, TRANSACTION_IN_PROGRESS_STATE)
@@ -1024,6 +1040,9 @@ module Mongo
1024
1040
  @server_session.txn_num
1025
1041
  end
1026
1042
 
1043
+ # @api private
1044
+ attr_accessor :snapshot_timestamp
1045
+
1027
1046
  private
1028
1047
 
1029
1048
  # Get the read concern the session will use when starting a transaction.
@@ -21,8 +21,7 @@ module Mongo
21
21
  # This module caches OCSP responses for their indicated validity time.
22
22
  #
23
23
  # The key is the CertificateId used for the OCSP request.
24
- # The value is the SingleResponse on Ruby 2.4+, or the OpenStruct
25
- # emulation of it on Ruby 2.3.
24
+ # The value is the SingleResponse.
26
25
  #
27
26
  # @api private
28
27
  module OcspCache
@@ -40,7 +39,7 @@ module Mongo
40
39
  # expire by the time caller uses them. The caller should not perform
41
40
  # update time checks on the returned response.
42
41
  #
43
- # @return [ OpenSSL::OCSP::SingleResponse | OpenStruct ] The previously
42
+ # @return [ OpenSSL::OCSP::SingleResponse ] The previously
44
43
  # retrieved response.
45
44
  module_function def get(cert_id)
46
45
  resp = responses.detect do |resp|
data/lib/mongo/socket.rb CHANGED
@@ -362,11 +362,7 @@ module Mongo
362
362
  end
363
363
 
364
364
  def allocate_string(capacity)
365
- if RUBY_VERSION >= '2.4.0'
366
- String.new('', :capacity => capacity, :encoding => 'BINARY')
367
- else
368
- ('x'*capacity).force_encoding('BINARY')
369
- end
365
+ String.new('', :capacity => capacity, :encoding => 'BINARY')
370
366
  end
371
367
 
372
368
  def read_buffer_size
data/lib/mongo/utils.rb CHANGED
@@ -101,11 +101,5 @@ module Mongo
101
101
  module_function def monotonic_time
102
102
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
103
103
  end
104
-
105
- # Hash#compact implementation for Ruby 2.3/2.4
106
- # Implementation based on activesupport 5.2.3
107
- module_function def slice_hash(hash, *keys)
108
- keys.each_with_object({}) { |k, res| res[k] = hash[k] if hash.key?(k) }
109
- end
110
104
  end
111
105
  end
data/lib/mongo/version.rb CHANGED
@@ -20,5 +20,5 @@ module Mongo
20
20
  # The current version of the driver.
21
21
  #
22
22
  # @since 2.0.0
23
- VERSION = '2.16.2'.freeze
23
+ VERSION = '2.17.0'.freeze
24
24
  end
data/mongo.gemspec CHANGED
@@ -36,7 +36,7 @@ Gem::Specification.new do |s|
36
36
  s.require_paths = ['lib']
37
37
  s.bindir = 'bin'
38
38
 
39
- s.required_ruby_version = ">= 2.4"
39
+ s.required_ruby_version = ">= 2.5"
40
40
 
41
41
  s.add_dependency 'bson', '>=4.8.2', '<5.0.0'
42
42
  end
@@ -45,16 +45,6 @@ describe 'Read preference' do
45
45
  {}
46
46
  end
47
47
 
48
- shared_examples_for 'sends expected read preference when reading' do
49
- it 'sends expected read preference when reading' do
50
- read_operation
51
-
52
- event = subscriber.single_command_started_event('find')
53
- actual_preference = event.command['$readPreference']
54
- expect(actual_preference).to eq(expected_read_preference)
55
- end
56
- end
57
-
58
48
  shared_examples_for 'does not send read preference when reading' do
59
49
  it 'does not send read preference when reading' do
60
50
  read_operation
@@ -95,7 +85,17 @@ describe 'Read preference' do
95
85
  context 'server supporting OP_MSG' do
96
86
  min_server_fcv '3.6'
97
87
 
98
- it_behaves_like 'sends expected read preference when reading'
88
+ it 'sends expected read preference when reading' do
89
+ read_operation
90
+
91
+ event = subscriber.single_command_started_event('find')
92
+ actual_preference = event.command['$readPreference']
93
+ if expected_read_preference&.[]("mode") == "primary"
94
+ expect(actual_preference).to be_nil
95
+ else
96
+ expect(actual_preference).to eq(expected_read_preference)
97
+ end
98
+ end
99
99
  end
100
100
  end
101
101
 
@@ -307,7 +307,11 @@ describe 'Read preference' do
307
307
 
308
308
  event = subscriber.single_command_started_event('find')
309
309
  actual_preference = event.command['$readPreference']
310
- expect(actual_preference).to eq(expected_read_preference)
310
+ if expected_read_preference&.[]("mode") == "primary"
311
+ expect(actual_preference).to be_nil
312
+ else
313
+ expect(actual_preference).to eq(expected_read_preference)
314
+ end
311
315
  end
312
316
  end
313
317
  end
@@ -157,13 +157,6 @@ RSpec.configure do |config|
157
157
  end
158
158
 
159
159
  if SpecConfig.instance.active_support?
160
- require "active_support/version"
161
- if ActiveSupport.version >= Gem::Version.new(7)
162
- # ActiveSupport wants us to require ALL of it all of the time.
163
- # See: https://github.com/rails/rails/issues/43851,
164
- # https://github.com/rails/rails/issues/43889, etc.
165
- require 'active_support'
166
- end
167
160
  require "active_support/time"
168
161
  require 'mongo/active_support'
169
162
  end
@@ -29,8 +29,16 @@ describe Mongo::Collection::View::Aggregation do
29
29
  described_class.new(view, pipeline, options)
30
30
  end
31
31
 
32
+ let(:server) do
33
+ double('server')
34
+ end
35
+
36
+ let(:session) do
37
+ double('session')
38
+ end
39
+
32
40
  let(:aggregation_spec) do
33
- aggregation.send(:aggregate_spec, double('session'))
41
+ aggregation.send(:aggregate_spec, server, session, nil)
34
42
  end
35
43
 
36
44
  before do
@@ -351,15 +359,15 @@ describe Mongo::Collection::View::Aggregation do
351
359
 
352
360
  describe '#aggregate_spec' do
353
361
 
354
- context 'when the collection has a read preference' do
362
+ context 'when a read preference is given' do
355
363
 
356
364
  let(:read_preference) do
357
- {mode: :secondary}
365
+ BSON::Document.new({mode: :secondary})
358
366
  end
359
367
 
360
368
  it 'includes the read preference in the spec' do
361
- allow(authorized_collection).to receive(:read_preference).and_return(read_preference)
362
- expect(aggregation_spec[:read]).to eq(read_preference)
369
+ spec = aggregation.send(:aggregate_spec, server, session, read_preference)
370
+ expect(spec[:read]).to eq(read_preference)
363
371
  end
364
372
  end
365
373
 
@@ -570,109 +578,77 @@ describe Mongo::Collection::View::Aggregation do
570
578
  end
571
579
 
572
580
  context 'when $out is in the pipeline' do
573
-
574
- let(:pipeline) do
575
- [{
576
- "$group" => {
577
- "_id" => "$city",
578
- "totalpop" => { "$sum" => "$pop" }
581
+ [['$out', 'string'], [:$out, 'symbol']].each do |op, type|
582
+ context "when #{op} is a #{type}" do
583
+ let(:pipeline) do
584
+ [{
585
+ "$group" => {
586
+ "_id" => "$city",
587
+ "totalpop" => { "$sum" => "$pop" }
588
+ }
589
+ },
590
+ {
591
+ op => 'output_collection'
579
592
  }
580
- },
581
- {
582
- '$out' => 'output_collection'
583
- }
584
- ]
585
- end
586
-
587
- before do
588
- authorized_client['output_collection'].delete_many
589
- end
590
-
591
- context 'when $out is a string' do
592
-
593
- it 'does not allow the operation on a secondary' do
594
- expect(aggregation.send(:secondary_ok?)).to be(false)
595
- end
596
- end
597
-
598
- context 'when $out is a symbol' do
599
-
600
- let(:pipeline) do
601
- [{
602
- "$group" => {
603
- "_id" => "$city",
604
- "totalpop" => { "$sum" => "$pop" }
605
- }
606
- },
607
- {
608
- :$out => 'output_collection'
609
- }
610
- ]
611
- end
612
-
613
- it 'does not allow the operation on a secondary' do
614
- expect(aggregation.send(:secondary_ok?)).to be(false)
615
- end
616
- end
593
+ ]
594
+ end
617
595
 
596
+ before do
597
+ authorized_client['output_collection'].delete_many
598
+ end
618
599
 
619
- context 'when the server is not a valid for writing' do
600
+ let(:features) do
601
+ double()
602
+ end
620
603
 
621
- it 'reroutes the operation to a primary' do
622
- allow(aggregation).to receive(:valid_server?).and_return(false)
623
- expect(Mongo::Logger.logger).to receive(:warn).and_call_original
624
- aggregation.to_a
625
- end
626
- end
604
+ let(:server) do
605
+ double().tap do |server|
606
+ allow(server).to receive(:features).and_return(features)
607
+ end
608
+ end
627
609
 
628
- context 'when the server is a valid for writing' do
610
+ context 'when the view has a write concern' do
629
611
 
630
- it 'does not reroute the operation to a primary' do
631
- expect(Mongo::Logger.logger).not_to receive(:warn)
632
- aggregation.to_a
633
- end
612
+ let(:collection) do
613
+ authorized_collection.with(write: INVALID_WRITE_CONCERN)
614
+ end
634
615
 
635
- context 'when the view has a write concern' do
616
+ let(:view) do
617
+ Mongo::Collection::View.new(collection, selector, view_options)
618
+ end
636
619
 
637
- let(:collection) do
638
- authorized_collection.with(write: INVALID_WRITE_CONCERN)
639
- end
620
+ context 'when the server supports write concern on the aggregate command' do
621
+ min_server_fcv '3.4'
640
622
 
641
- let(:view) do
642
- Mongo::Collection::View.new(collection, selector, view_options)
643
- end
623
+ it 'uses the write concern' do
624
+ expect {
625
+ aggregation.to_a
626
+ }.to raise_exception(Mongo::Error::OperationFailure)
627
+ end
628
+ end
644
629
 
645
- context 'when the server supports write concern on the aggregate command' do
646
- min_server_fcv '3.4'
630
+ context 'when the server does not support write concern on the aggregation command' do
631
+ max_server_version '3.2'
647
632
 
648
- it 'uses the write concern' do
649
- expect {
650
- aggregation.to_a
651
- }.to raise_exception(Mongo::Error::OperationFailure)
652
- end
653
- end
633
+ let(:documents) do
634
+ [
635
+ { city: "Berlin", pop: 18913, neighborhood: "Kreuzberg" },
636
+ { city: "Berlin", pop: 84143, neighborhood: "Mitte" },
637
+ { city: "New York", pop: 40270, neighborhood: "Brooklyn" }
638
+ ]
639
+ end
654
640
 
655
- context 'when the server does not support write concern on the aggregation command' do
656
- max_server_version '3.2'
641
+ before do
642
+ authorized_collection.insert_many(documents)
643
+ aggregation.to_a
644
+ end
657
645
 
658
- let(:documents) do
659
- [
660
- { city: "Berlin", pop: 18913, neighborhood: "Kreuzberg" },
661
- { city: "Berlin", pop: 84143, neighborhood: "Mitte" },
662
- { city: "New York", pop: 40270, neighborhood: "Brooklyn" }
663
- ]
664
- end
665
-
666
- before do
667
- authorized_collection.insert_many(documents)
668
- aggregation.to_a
669
- end
670
-
671
- it 'does not apply the write concern' do
672
- expect(authorized_client['output_collection'].find.count).to eq(2)
673
- end
674
- end
675
- end
646
+ it 'does not apply the write concern' do
647
+ expect(authorized_client['output_collection'].find.count).to eq(2)
648
+ end
649
+ end
650
+ end
651
+ end
676
652
  end
677
653
  end
678
654
  end
@@ -56,7 +56,7 @@ describe Mongo::Collection::View::ChangeStream do
56
56
 
57
57
  let(:command_spec) do
58
58
  change_stream.send(:instance_variable_set, '@resuming', false)
59
- change_stream.send(:aggregate_spec, double('session'))
59
+ change_stream.send(:aggregate_spec, double('server'), double('session'), nil)
60
60
  end
61
61
 
62
62
  let(:cursor) do
@@ -60,6 +60,14 @@ describe Mongo::Collection::View::MapReduce do
60
60
  described_class.new(view, map, reduce, options)
61
61
  end
62
62
 
63
+ describe '#initialize' do
64
+ it 'warns of deprecation' do
65
+ Mongo::Logger.logger.should receive(:warn).with('MONGODB | The map_reduce operation is deprecated, please use the aggregation pipeline instead')
66
+
67
+ map_reduce
68
+ end
69
+ end
70
+
63
71
  describe '#map_function' do
64
72
 
65
73
  it 'returns the map function' do
@@ -672,7 +680,12 @@ describe Mongo::Collection::View::MapReduce do
672
680
  end
673
681
 
674
682
  it 'does not reroute the operation to a primary' do
675
- expect(Mongo::Logger.logger).not_to receive(:warn)
683
+ # We produce a deprecation warning, but there shouldn't be
684
+ # the reroute warning.
685
+ expect(Mongo::Logger.logger).to receive(:warn).once do |msg|
686
+ expect(msg).not_to include('Rerouting the MapReduce operation to the primary server')
687
+ end
688
+
676
689
  map_reduce.to_a
677
690
  end
678
691
  end
@@ -865,20 +878,4 @@ describe Mongo::Collection::View::MapReduce do
865
878
  end
866
879
  end
867
880
  end
868
-
869
- describe '#map_reduce_spec' do
870
- context 'when read preference is given' do
871
- let(:view_options) do
872
- { read: {mode: :secondary} }
873
- end
874
-
875
- context 'selector' do
876
- # For compatibility with released versions of Mongoid, this method
877
- # must return read preference under the :read key.
878
- it 'contains read preference' do
879
- map_reduce_spec[:selector][:read].should == {'mode' => :secondary}
880
- end
881
- end
882
- end
883
- end
884
881
  end