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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/mongo.rb +1 -0
- data/lib/mongo/auth/user/view.rb +4 -4
- data/lib/mongo/bulk_write.rb +14 -8
- data/lib/mongo/bulk_write/result.rb +1 -1
- data/lib/mongo/bulk_write/result_combiner.rb +2 -2
- data/lib/mongo/bulk_write/transformable.rb +17 -9
- data/lib/mongo/client.rb +107 -16
- data/lib/mongo/cluster.rb +47 -25
- data/lib/mongo/cluster/topology/replica_set_no_primary.rb +1 -1
- data/lib/mongo/cluster_time.rb +139 -0
- data/lib/mongo/collection.rb +84 -25
- data/lib/mongo/collection/view.rb +7 -3
- data/lib/mongo/collection/view/aggregation.rb +4 -4
- data/lib/mongo/collection/view/builder/aggregation.rb +31 -6
- data/lib/mongo/collection/view/builder/find_command.rb +4 -1
- data/lib/mongo/collection/view/builder/map_reduce.rb +4 -1
- data/lib/mongo/collection/view/change_stream.rb +54 -66
- data/lib/mongo/collection/view/iterable.rb +2 -2
- data/lib/mongo/collection/view/map_reduce.rb +6 -4
- data/lib/mongo/collection/view/readable.rb +36 -16
- data/lib/mongo/collection/view/writable.rb +68 -22
- data/lib/mongo/cursor.rb +87 -20
- data/lib/mongo/database.rb +47 -43
- data/lib/mongo/database/view.rb +54 -11
- data/lib/mongo/error.rb +13 -4
- data/lib/mongo/error/invalid_write_concern.rb +2 -2
- data/lib/mongo/error/operation_failure.rb +65 -11
- data/lib/mongo/error/parser.rb +41 -8
- data/lib/mongo/grid/fs_bucket.rb +26 -6
- data/lib/mongo/grid/stream/read.rb +9 -2
- data/lib/mongo/grid/stream/write.rb +21 -5
- data/lib/mongo/index/view.rb +3 -3
- data/lib/mongo/lint.rb +10 -3
- data/lib/mongo/operation.rb +2 -0
- data/lib/mongo/operation/aggregate/result.rb +19 -6
- data/lib/mongo/operation/collections_info.rb +1 -1
- data/lib/mongo/operation/get_more/result.rb +9 -0
- data/lib/mongo/operation/list_collections/command.rb +1 -3
- data/lib/mongo/operation/list_collections/op_msg.rb +1 -2
- data/lib/mongo/operation/parallel_scan/command.rb +4 -1
- data/lib/mongo/operation/parallel_scan/op_msg.rb +4 -1
- data/lib/mongo/operation/result.rb +27 -4
- data/lib/mongo/operation/shared/executable.rb +19 -5
- data/lib/mongo/operation/shared/executable_no_validate.rb +1 -2
- data/lib/mongo/operation/shared/executable_transaction_label.rb +0 -9
- data/lib/mongo/operation/shared/polymorphic_result.rb +9 -1
- data/lib/mongo/operation/shared/result/aggregatable.rb +2 -2
- data/lib/mongo/operation/shared/sessions_supported.rb +42 -32
- data/lib/mongo/operation/shared/specifiable.rb +40 -0
- data/lib/mongo/operation/shared/unpinnable.rb +39 -0
- data/lib/mongo/operation/shared/write.rb +1 -1
- data/lib/mongo/protocol/update.rb +6 -2
- data/lib/mongo/retryable.rb +79 -39
- data/lib/mongo/server/connection.rb +10 -3
- data/lib/mongo/server/description.rb +25 -1
- data/lib/mongo/server/monitor/connection.rb +1 -1
- data/lib/mongo/server_selector.rb +10 -0
- data/lib/mongo/server_selector/selectable.rb +172 -32
- data/lib/mongo/session.rb +654 -581
- data/lib/mongo/session/session_pool.rb +1 -1
- data/lib/mongo/socket.rb +7 -28
- data/lib/mongo/socket/ssl.rb +26 -1
- data/lib/mongo/socket/tcp.rb +3 -0
- data/lib/mongo/socket/unix.rb +3 -0
- data/lib/mongo/uri.rb +112 -265
- data/lib/mongo/uri/srv_protocol.rb +4 -1
- data/lib/mongo/version.rb +1 -1
- data/lib/mongo/write_concern.rb +10 -29
- data/lib/mongo/write_concern/acknowledged.rb +12 -0
- data/lib/mongo/write_concern/base.rb +17 -13
- data/lib/mongo/write_concern/unacknowledged.rb +12 -0
- data/spec/atlas/atlas_connectivity_spec.rb +7 -37
- data/spec/atlas/operations_spec.rb +25 -0
- data/spec/integration/change_stream_examples_spec.rb +45 -31
- data/spec/integration/change_stream_spec.rb +305 -5
- data/spec/integration/client_spec.rb +44 -0
- data/spec/integration/command_monitoring_spec.rb +1 -0
- data/spec/integration/command_spec.rb +7 -1
- data/spec/integration/mmapv1_spec.rb +28 -0
- data/spec/integration/mongos_pinning_spec.rb +34 -0
- data/spec/integration/operation_failure_code_spec.rb +2 -2
- data/spec/integration/{read_concern.rb → read_concern_spec.rb} +7 -1
- data/spec/integration/read_preference_spec.rb +485 -0
- data/spec/integration/retryable_writes_spec.rb +8 -19
- data/spec/integration/sdam_error_handling_spec.rb +1 -1
- data/spec/integration/sdam_events_spec.rb +2 -2
- data/spec/integration/server_description_spec.rb +14 -17
- data/spec/integration/server_selector_spec.rb +7 -3
- data/spec/integration/server_spec.rb +48 -0
- data/spec/integration/ssl_uri_options_spec.rb +1 -1
- data/spec/integration/step_down_spec.rb +10 -4
- data/spec/integration/transactions_examples_spec.rb +11 -10
- data/spec/lite_spec_helper.rb +19 -16
- data/spec/mongo/auth/scram/negotiation_spec.rb +11 -8
- data/spec/mongo/bulk_write/ordered_combiner_spec.rb +6 -6
- data/spec/mongo/bulk_write/unordered_combiner_spec.rb +4 -4
- data/spec/mongo/bulk_write_spec.rb +12 -2
- data/spec/mongo/client_construction_spec.rb +160 -8
- data/spec/mongo/client_spec.rb +5 -4
- data/spec/mongo/cluster_spec.rb +6 -6
- data/spec/mongo/cluster_time_spec.rb +148 -0
- data/spec/mongo/collection/view/aggregation_spec.rb +34 -15
- data/spec/mongo/collection/view/change_stream_spec.rb +62 -3
- data/spec/mongo/collection/view/map_reduce_spec.rb +7 -5
- data/spec/mongo/collection/view/readable_spec.rb +4 -4
- data/spec/mongo/collection_spec.rb +331 -14
- data/spec/mongo/cursor_spec.rb +117 -5
- data/spec/mongo/database_spec.rb +240 -8
- data/spec/mongo/error/operation_failure_spec.rb +47 -1
- data/spec/mongo/error/parser_spec.rb +160 -23
- data/spec/mongo/operation/insert/bulk_spec.rb +2 -1
- data/spec/mongo/operation/result_spec.rb +27 -0
- data/spec/mongo/operation/update/bulk_spec.rb +1 -0
- data/spec/mongo/retryable_spec.rb +2 -0
- data/spec/mongo/server/app_metadata_spec.rb +2 -2
- data/spec/mongo/server/connection_spec.rb +13 -17
- data/spec/mongo/server/monitor/connection_spec.rb +13 -10
- data/spec/mongo/server_selector_spec.rb +34 -2
- data/spec/mongo/session/session_pool_spec.rb +14 -3
- data/spec/mongo/session_spec.rb +3 -3
- data/spec/mongo/session_transaction_spec.rb +4 -3
- data/spec/mongo/socket/ssl_spec.rb +19 -5
- data/spec/mongo/socket_spec.rb +1 -62
- data/spec/mongo/uri/srv_protocol_spec.rb +14 -20
- data/spec/mongo/uri_option_parsing_spec.rb +94 -8
- data/spec/mongo/uri_spec.rb +23 -10
- data/spec/mongo/write_concern_spec.rb +56 -3
- data/spec/spec_tests/change_streams_spec.rb +2 -1
- data/spec/spec_tests/cmap_spec.rb +1 -1
- data/spec/spec_tests/crud_spec.rb +12 -2
- data/spec/spec_tests/data/change_streams/change-streams-errors.yml +24 -1
- data/spec/spec_tests/data/change_streams/change-streams.yml +172 -3
- data/spec/spec_tests/data/command_monitoring/bulkWrite.yml +1 -1
- data/spec/spec_tests/data/command_monitoring/updateMany.yml +0 -2
- data/spec/spec_tests/data/command_monitoring/updateOne.yml +0 -5
- data/spec/spec_tests/data/crud/read/aggregate-out.yml +0 -6
- data/spec/spec_tests/data/crud/read/count-empty.yml +29 -0
- data/spec/spec_tests/data/crud/write/bulkWrite-arrayFilters.yml +1 -0
- data/spec/spec_tests/data/crud/write/bulkWrite-collation.yml +101 -0
- data/spec/spec_tests/data/crud/write/bulkWrite.yml +401 -0
- data/spec/spec_tests/data/crud/write/insertMany.yml +58 -2
- data/spec/spec_tests/data/crud/write/updateMany-arrayFilters.yml +3 -0
- data/spec/spec_tests/data/crud/write/updateOne-arrayFilters.yml +6 -1
- data/spec/spec_tests/data/crud_v2/aggregate-merge.yml +103 -0
- data/spec/spec_tests/data/crud_v2/aggregate-out-readConcern.yml +110 -0
- data/spec/spec_tests/data/crud_v2/bulkWrite-arrayFilters.yml +81 -0
- data/spec/spec_tests/data/crud_v2/db-aggregate.yml +38 -0
- data/spec/spec_tests/data/crud_v2/updateWithPipelines.yml +92 -0
- data/spec/spec_tests/data/retryable_writes/insertOne-serverErrors.yml +2 -2
- data/spec/spec_tests/data/transactions/abort.yml +3 -0
- data/spec/spec_tests/data/transactions/bulk.yml +3 -8
- data/spec/spec_tests/data/transactions/causal-consistency.yml +3 -8
- data/spec/spec_tests/data/transactions/commit.yml +3 -1
- data/spec/spec_tests/data/transactions/count.yml +3 -0
- data/spec/spec_tests/data/transactions/delete.yml +3 -0
- data/spec/spec_tests/data/transactions/error-labels.yml +4 -1
- data/spec/spec_tests/data/transactions/errors-client.yml +56 -0
- data/spec/spec_tests/data/transactions/errors.yml +3 -0
- data/spec/spec_tests/data/transactions/findOneAndDelete.yml +3 -0
- data/spec/spec_tests/data/transactions/findOneAndReplace.yml +3 -0
- data/spec/spec_tests/data/transactions/findOneAndUpdate.yml +3 -0
- data/spec/spec_tests/data/transactions/insert.yml +3 -0
- data/spec/spec_tests/data/transactions/isolation.yml +3 -0
- data/spec/spec_tests/data/transactions/mongos-pin-auto.yml +1671 -0
- data/spec/spec_tests/data/transactions/mongos-recovery-token.yml +347 -0
- data/spec/spec_tests/data/transactions/pin-mongos.yml +557 -0
- data/spec/spec_tests/data/transactions/read-concern.yml +3 -0
- data/spec/spec_tests/data/transactions/read-pref.yml +3 -0
- data/spec/spec_tests/data/transactions/reads.yml +3 -0
- data/spec/spec_tests/data/transactions/retryable-abort.yml +5 -2
- data/spec/spec_tests/data/transactions/retryable-commit.yml +4 -1
- data/spec/spec_tests/data/transactions/retryable-writes.yml +3 -0
- data/spec/spec_tests/data/transactions/run-command.yml +3 -0
- data/spec/spec_tests/data/transactions/transaction-options.yml +6 -0
- data/spec/spec_tests/data/transactions/update.yml +3 -8
- data/spec/spec_tests/data/transactions/write-concern.yml +348 -38
- data/spec/spec_tests/data/transactions_api/callback-aborts.yml +6 -0
- data/spec/spec_tests/data/transactions_api/callback-commits.yml +5 -0
- data/spec/spec_tests/data/transactions_api/callback-retry.yml +7 -2
- data/spec/spec_tests/data/transactions_api/commit-retry.yml +70 -15
- data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror-4.2.yml +3 -0
- data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror.yml +3 -0
- data/spec/spec_tests/data/transactions_api/commit-writeconcernerror.yml +59 -109
- data/spec/spec_tests/data/transactions_api/commit.yml +5 -0
- data/spec/spec_tests/data/transactions_api/transaction-options.yml +10 -0
- data/spec/spec_tests/retryable_reads_spec.rb +5 -2
- data/spec/spec_tests/retryable_writes_spec.rb +5 -2
- data/spec/spec_tests/sdam_monitoring_spec.rb +3 -3
- data/spec/spec_tests/sdam_spec.rb +2 -2
- data/spec/spec_tests/transactions_api_spec.rb +1 -67
- data/spec/spec_tests/transactions_spec.rb +2 -66
- data/spec/support/authorization.rb +4 -0
- data/spec/support/change_streams.rb +30 -10
- data/spec/support/change_streams/operation.rb +27 -0
- data/spec/support/client_registry.rb +44 -25
- data/spec/support/cluster_config.rb +25 -14
- data/spec/support/cluster_tools.rb +32 -10
- data/spec/support/command_monitoring.rb +1 -1
- data/spec/support/common_shortcuts.rb +30 -0
- data/spec/support/connection_string.rb +8 -3
- data/spec/support/constraints.rb +34 -0
- data/spec/support/crud.rb +31 -16
- data/spec/support/crud/context.rb +23 -0
- data/spec/support/crud/operation.rb +311 -14
- data/spec/support/crud/spec.rb +2 -1
- data/spec/support/crud/test.rb +24 -27
- data/spec/support/crud/test_base.rb +22 -0
- data/spec/support/crud/verifier.rb +15 -1
- data/spec/support/event_subscriber.rb +12 -0
- data/spec/support/sdam_formatter_integration.rb +12 -6
- data/spec/support/shared/server_selector.rb +10 -0
- data/spec/support/shared/session.rb +13 -12
- data/spec/support/spec_config.rb +32 -22
- data/spec/support/spec_setup.rb +2 -2
- data/spec/support/transactions.rb +87 -0
- data/spec/support/transactions/context.rb +33 -0
- data/spec/support/transactions/operation.rb +99 -349
- data/spec/support/transactions/spec.rb +1 -3
- data/spec/support/transactions/test.rb +110 -49
- data/spec/support/utils.rb +74 -1
- metadata +52 -10
- metadata.gz.sig +0 -0
- data/spec/support/crud/read.rb +0 -265
- data/spec/support/crud/write.rb +0 -284
@@ -103,7 +103,10 @@ module Mongo
|
|
103
103
|
|
104
104
|
records = get_records(hostname)
|
105
105
|
@txt_options = get_txt_opts(hostname) || {}
|
106
|
-
|
106
|
+
records.each do |record|
|
107
|
+
validate_host!(record)
|
108
|
+
end
|
109
|
+
@servers = records
|
107
110
|
end
|
108
111
|
|
109
112
|
# Validates the hostname used in an SRV URI.
|
data/lib/mongo/version.rb
CHANGED
data/lib/mongo/write_concern.rb
CHANGED
@@ -27,26 +27,31 @@ module Mongo
|
|
27
27
|
# The number of servers write concern.
|
28
28
|
#
|
29
29
|
# @since 2.0.0
|
30
|
+
# @deprecated
|
30
31
|
W = :w.freeze
|
31
32
|
|
32
33
|
# The journal write concern.
|
33
34
|
#
|
34
35
|
# @since 2.0.0
|
36
|
+
# @deprecated
|
35
37
|
J = :j.freeze
|
36
38
|
|
37
39
|
# The file sync write concern.
|
38
40
|
#
|
39
41
|
# @since 2.0.0
|
42
|
+
# @deprecated
|
40
43
|
FSYNC = :fsync.freeze
|
41
44
|
|
42
45
|
# The wtimeout write concern.
|
43
46
|
#
|
44
47
|
# @since 2.0.0
|
48
|
+
# @deprecated
|
45
49
|
WTIMEOUT = :wtimeout.freeze
|
46
50
|
|
47
51
|
# The GLE command name.
|
48
52
|
#
|
49
53
|
# @since 2.0.0
|
54
|
+
# @deprecated
|
50
55
|
GET_LAST_ERROR = :getlasterror.freeze
|
51
56
|
|
52
57
|
# The default write concern is to acknowledge on a single server.
|
@@ -56,6 +61,8 @@ module Mongo
|
|
56
61
|
|
57
62
|
# Create a write concern object for the provided options.
|
58
63
|
#
|
64
|
+
# If options are nil, returns nil.
|
65
|
+
#
|
59
66
|
# @example Get a write concern.
|
60
67
|
# Mongo::WriteConcern.get(:w => 1)
|
61
68
|
#
|
@@ -70,46 +77,20 @@ module Mongo
|
|
70
77
|
# @option options :wtimeout [ Integer ] The number of milliseconds to
|
71
78
|
# wait for acknowledgement before raising an error.
|
72
79
|
#
|
73
|
-
# @return [ Unacknowledged
|
80
|
+
# @return [ nil | Unacknowledged | Acknowledged ] The appropriate concern.
|
74
81
|
#
|
75
82
|
# @raise [ Error::InvalidWriteConcern ] If the options are invalid.
|
76
83
|
#
|
77
84
|
# @since 2.0.0
|
78
85
|
def get(options)
|
79
|
-
return options if options.is_a?(
|
86
|
+
return options if options.is_a?(Base)
|
80
87
|
if options
|
81
|
-
|
82
|
-
if unacknowledged?(options)
|
88
|
+
if (options[:w] || options['w']) == 0
|
83
89
|
Unacknowledged.new(options)
|
84
90
|
else
|
85
91
|
Acknowledged.new(options)
|
86
92
|
end
|
87
93
|
end
|
88
94
|
end
|
89
|
-
|
90
|
-
private
|
91
|
-
|
92
|
-
def validate!(options)
|
93
|
-
if options[W]
|
94
|
-
if options[W] == 0 && (options[J] || options[FSYNC])
|
95
|
-
raise Mongo::Error::InvalidWriteConcern.new
|
96
|
-
elsif options[W].is_a?(Integer) && options[W] < 0
|
97
|
-
raise Mongo::Error::InvalidWriteConcern.new
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
# Determine if the options are for an unacknowledged write concern.
|
103
|
-
#
|
104
|
-
# @api private
|
105
|
-
#
|
106
|
-
# @param [ Hash ] options The options to check.
|
107
|
-
#
|
108
|
-
# @return [ true, false ] If the options are unacknowledged.
|
109
|
-
#
|
110
|
-
# @since 2.0.0
|
111
|
-
def unacknowledged?(options)
|
112
|
-
options[W] == 0
|
113
|
-
end
|
114
95
|
end
|
115
96
|
end
|
@@ -34,6 +34,18 @@ module Mongo
|
|
34
34
|
)
|
35
35
|
end
|
36
36
|
|
37
|
+
# Is this write concern acknowledged.
|
38
|
+
#
|
39
|
+
# @example Whether this write concern object is acknowledged.
|
40
|
+
# write_concern.acknowledged?
|
41
|
+
#
|
42
|
+
# @return [ true, false ] Whether this write concern is acknowledged.
|
43
|
+
#
|
44
|
+
# @since 2.5.0
|
45
|
+
def acknowledged?
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
37
49
|
# Get a human-readable string representation of an acknowledged write concern.
|
38
50
|
#
|
39
51
|
# @example Inspect the write concern.
|
@@ -43,20 +43,24 @@ module Mongo
|
|
43
43
|
#
|
44
44
|
# @since 2.0.0
|
45
45
|
def initialize(options)
|
46
|
-
|
47
|
-
|
48
|
-
end
|
46
|
+
options = Options::Mapper.transform_keys_to_symbols(options)
|
47
|
+
options = Options::Mapper.transform_values_to_strings(options).freeze
|
49
48
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
49
|
+
if options[:w]
|
50
|
+
if options[:w] == 0 && options[:j]
|
51
|
+
raise Error::InvalidWriteConcern, "Invalid write concern options: :j cannot be true when :w is 0: #{options.inspect}"
|
52
|
+
elsif options[:w] == 0 && options[:fsync]
|
53
|
+
raise Error::InvalidWriteConcern, "Invalid write concern options: :fsync cannot be true when :w is 0: #{options.inspect}"
|
54
|
+
elsif options[:w].is_a?(Integer) && options[:w] < 0
|
55
|
+
raise Error::InvalidWriteConcern, "Invalid write concern options: :w cannot be negative (#{options[:w]}): #{options.inspect}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
if options[:journal]
|
60
|
+
raise Error::InvalidWriteConcern, "Invalid write concern options: use :j for journal: #{options.inspect}"
|
61
|
+
end
|
62
|
+
|
63
|
+
@options = options
|
60
64
|
end
|
61
65
|
end
|
62
66
|
end
|
@@ -37,6 +37,18 @@ module Mongo
|
|
37
37
|
NOOP
|
38
38
|
end
|
39
39
|
|
40
|
+
# Is this write concern acknowledged.
|
41
|
+
#
|
42
|
+
# @example Whether this write concern object is acknowledged.
|
43
|
+
# write_concern.acknowledged?
|
44
|
+
#
|
45
|
+
# @return [ true, false ] Whether this write concern is acknowledged.
|
46
|
+
#
|
47
|
+
# @since 2.5.0
|
48
|
+
def acknowledged?
|
49
|
+
false
|
50
|
+
end
|
51
|
+
|
40
52
|
# Get a human-readable string representation of an unacknowledged write concern.
|
41
53
|
#
|
42
54
|
# @example Inspect the write concern.
|
@@ -1,16 +1,16 @@
|
|
1
1
|
require 'lite_spec_helper'
|
2
2
|
|
3
3
|
describe 'Atlas connectivity' do
|
4
|
-
|
5
|
-
|
6
|
-
let(:client) { Mongo::Client.new(uri) }
|
4
|
+
let(:uri) { ENV['ATLAS_URI'] }
|
5
|
+
let(:client) { Mongo::Client.new(uri) }
|
7
6
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
7
|
+
before do
|
8
|
+
if uri.nil?
|
9
|
+
skip "ATLAS_URI not set in environment"
|
12
10
|
end
|
11
|
+
end
|
13
12
|
|
13
|
+
describe 'connection to Atlas' do
|
14
14
|
it 'runs ismaster successfully' do
|
15
15
|
result = client.database.command(:ismaster => 1)
|
16
16
|
expect(result.documents.first['ismaster']).to be true
|
@@ -21,34 +21,4 @@ describe 'Atlas connectivity' do
|
|
21
21
|
expect(result).to be_a(Array)
|
22
22
|
end
|
23
23
|
end
|
24
|
-
|
25
|
-
context 'Atlas replica set' do
|
26
|
-
let(:var) { 'ATLAS_REPLICA_SET_URI' }
|
27
|
-
|
28
|
-
it_behaves_like 'connects to Atlas'
|
29
|
-
end
|
30
|
-
|
31
|
-
context 'Atlas sharded cluster' do
|
32
|
-
let(:var) { 'ATLAS_SHARDED_URI' }
|
33
|
-
|
34
|
-
it_behaves_like 'connects to Atlas'
|
35
|
-
end
|
36
|
-
|
37
|
-
context 'Atlas free tier replica set' do
|
38
|
-
let(:var) { 'ATLAS_FREE_TIER_URI' }
|
39
|
-
|
40
|
-
it_behaves_like 'connects to Atlas'
|
41
|
-
end
|
42
|
-
|
43
|
-
context 'Atlas TLS 1.1 only replica set' do
|
44
|
-
let(:var) { 'ATLAS_TLS11_URI' }
|
45
|
-
|
46
|
-
it_behaves_like 'connects to Atlas'
|
47
|
-
end
|
48
|
-
|
49
|
-
context 'Atlas TLS 1.2 only replica set' do
|
50
|
-
let(:var) { 'ATLAS_TLS12_URI' }
|
51
|
-
|
52
|
-
it_behaves_like 'connects to Atlas'
|
53
|
-
end
|
54
24
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'lite_spec_helper'
|
2
|
+
|
3
|
+
describe 'Operations' do
|
4
|
+
let(:uri) { ENV['ATLAS_URI'] }
|
5
|
+
let(:client) { Mongo::Client.new(uri) }
|
6
|
+
|
7
|
+
before do
|
8
|
+
if uri.nil?
|
9
|
+
skip "ATLAS_URI not set in environment"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe 'list_collections' do
|
14
|
+
# Atlas free tier proxy enforces restrictions on list_collections
|
15
|
+
# arguments. This tests verifies that list_collections works on Atlas
|
16
|
+
|
17
|
+
it 'works' do
|
18
|
+
# We are not allowed to mutate the database, therefore the list of
|
19
|
+
# collections would generally be empty.
|
20
|
+
expect do
|
21
|
+
client.database.list_collections
|
22
|
+
end.not_to raise_error
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -3,6 +3,12 @@ require 'spec_helper'
|
|
3
3
|
describe 'change streams examples in Ruby' do
|
4
4
|
min_server_fcv '3.6'
|
5
5
|
require_topology :replica_set
|
6
|
+
require_wired_tiger
|
7
|
+
|
8
|
+
# On JRuby, change streams should be accessed using try_next on the
|
9
|
+
# change stream objects rather than using the Enumerable interface.
|
10
|
+
# https://jira.mongodb.org/browse/RUBY-1877
|
11
|
+
fails_on_jruby
|
6
12
|
|
7
13
|
let!(:inventory) do
|
8
14
|
client[:inventory]
|
@@ -17,7 +23,7 @@ describe 'change streams examples in Ruby' do
|
|
17
23
|
end
|
18
24
|
|
19
25
|
after do
|
20
|
-
client.close
|
26
|
+
client.close(true)
|
21
27
|
end
|
22
28
|
|
23
29
|
context 'example 1 - basic watching'do
|
@@ -103,10 +109,30 @@ describe 'change streams examples in Ruby' do
|
|
103
109
|
|
104
110
|
it 'returns the correct change when resuming' do
|
105
111
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
112
|
+
insert_thread = Thread.new do
|
113
|
+
sleep 2
|
114
|
+
inventory.insert_one(x: 1)
|
115
|
+
inventory.insert_one(x: 2)
|
116
|
+
end
|
117
|
+
|
118
|
+
next_change = nil
|
119
|
+
resume_stream_thread = Thread.new do
|
120
|
+
|
121
|
+
# Start Changestream Example 3
|
122
|
+
|
123
|
+
change_stream = inventory.watch
|
124
|
+
cursor = change_stream.to_enum
|
125
|
+
next_change = cursor.next
|
126
|
+
resume_token = change_stream.resume_token
|
127
|
+
|
128
|
+
new_cursor = inventory.watch([], resume_after: resume_token).to_enum
|
129
|
+
resumed_change = new_cursor.next
|
130
|
+
|
131
|
+
# End Changestream Example 3
|
132
|
+
end
|
133
|
+
|
134
|
+
insert_thread.value
|
135
|
+
resumed_change = resume_stream_thread.value
|
110
136
|
|
111
137
|
expect(next_change['_id']).not_to be_nil
|
112
138
|
expect(next_change['_id']['_data']).not_to be_nil
|
@@ -120,32 +146,20 @@ describe 'change streams examples in Ruby' do
|
|
120
146
|
expect(next_change['documentKey']).not_to be_nil
|
121
147
|
expect(next_change['documentKey']['_id']).to eq(next_change['fullDocument']['_id'])
|
122
148
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
expect(
|
128
|
-
expect(
|
129
|
-
expect(
|
130
|
-
expect(
|
131
|
-
expect(
|
132
|
-
expect(
|
133
|
-
expect(
|
134
|
-
|
135
|
-
expect(
|
136
|
-
expect(
|
137
|
-
expect(next_next_change['documentKey']['_id']).to eq(next_next_change['fullDocument']['_id'])
|
138
|
-
|
139
|
-
# Start Changestream Example 3
|
140
|
-
|
141
|
-
resume_token = next_change['_id']
|
142
|
-
cursor = inventory.watch([], resume_after: resume_token).to_enum
|
143
|
-
resumed_change = cursor.next
|
144
|
-
|
145
|
-
# End Changestream Example 3
|
146
|
-
|
147
|
-
expect(resumed_change.length).to eq(next_next_change.length)
|
148
|
-
resumed_change.each { |key| expect(resumed_change[key]).to eq(next_next_change[key]) }
|
149
|
+
expect(resumed_change['_id']).not_to be_nil
|
150
|
+
expect(resumed_change['_id']['_data']).not_to be_nil
|
151
|
+
expect(resumed_change['operationType']).to eq('insert')
|
152
|
+
expect(resumed_change['fullDocument']).not_to be_nil
|
153
|
+
expect(resumed_change['fullDocument']['_id']).not_to be_nil
|
154
|
+
expect(resumed_change['fullDocument']['x']).to eq(2)
|
155
|
+
expect(resumed_change['ns']).not_to be_nil
|
156
|
+
expect(resumed_change['ns']['db']).to eq(SpecConfig.instance.test_db)
|
157
|
+
expect(resumed_change['ns']['coll']).to eq(inventory.name)
|
158
|
+
expect(resumed_change['documentKey']).not_to be_nil
|
159
|
+
expect(resumed_change['documentKey']['_id']).to eq(resumed_change['fullDocument']['_id'])
|
160
|
+
|
161
|
+
expect(resumed_change.length).to eq(resumed_change.length)
|
162
|
+
resumed_change.each { |key| expect(resumed_change[key]).to eq(resumed_change[key]) }
|
149
163
|
end
|
150
164
|
end
|
151
165
|
|
@@ -5,6 +5,7 @@ describe 'Change stream integration', retry: 4 do
|
|
5
5
|
max_example_run_time 7
|
6
6
|
min_server_fcv '3.6'
|
7
7
|
require_topology :replica_set
|
8
|
+
require_wired_tiger
|
8
9
|
|
9
10
|
let(:fail_point_base_command) do
|
10
11
|
{ 'configureFailPoint' => "failCommand" }
|
@@ -45,6 +46,25 @@ describe 'Change stream integration', retry: 4 do
|
|
45
46
|
end
|
46
47
|
end
|
47
48
|
|
49
|
+
shared_examples_for 'raises an exception' do
|
50
|
+
it 'raises an exception and does not attempt to resume' do
|
51
|
+
change_stream
|
52
|
+
|
53
|
+
subscriber = EventSubscriber.new
|
54
|
+
authorized_client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
|
55
|
+
|
56
|
+
expect do
|
57
|
+
change_stream.to_enum.next
|
58
|
+
end.to raise_error(Mongo::Error::OperationFailure)
|
59
|
+
|
60
|
+
aggregate_commands = subscriber.started_events.select { |e| e.command_name == 'aggregate' }
|
61
|
+
expect(aggregate_commands.length).to be 0
|
62
|
+
|
63
|
+
get_more_commands = subscriber.started_events.select { |e| e.command_name == 'getMore' }
|
64
|
+
expect(get_more_commands.length).to be 1
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
48
68
|
context 'no errors' do
|
49
69
|
it 'next returns changes' do
|
50
70
|
change_stream
|
@@ -86,10 +106,36 @@ describe 'Change stream integration', retry: 4 do
|
|
86
106
|
before do
|
87
107
|
authorized_collection.client.use(:admin).command(fail_point_base_command.merge(
|
88
108
|
:mode => {:times => 1},
|
89
|
-
:data => {:failCommands => ['getMore'], errorCode:
|
109
|
+
:data => {:failCommands => ['getMore'], errorCode: errorCode}))
|
90
110
|
end
|
91
111
|
|
92
|
-
|
112
|
+
context 'when the error is resumable' do
|
113
|
+
let(:errorCode) do
|
114
|
+
100
|
115
|
+
end
|
116
|
+
it_behaves_like 'returns a change document'
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'when the error is Interrupted' do
|
120
|
+
let(:errorCode) do
|
121
|
+
11601
|
122
|
+
end
|
123
|
+
it_behaves_like 'raises an exception'
|
124
|
+
end
|
125
|
+
|
126
|
+
context 'when the error is CappedPositionLost' do
|
127
|
+
let(:errorCode) do
|
128
|
+
136
|
129
|
+
end
|
130
|
+
it_behaves_like 'raises an exception'
|
131
|
+
end
|
132
|
+
|
133
|
+
context 'when the error is CursorKilled' do
|
134
|
+
let(:errorCode) do
|
135
|
+
237
|
136
|
+
end
|
137
|
+
it_behaves_like 'raises an exception'
|
138
|
+
end
|
93
139
|
end
|
94
140
|
|
95
141
|
context 'error on a getMore other than first' do
|
@@ -105,10 +151,36 @@ describe 'Change stream integration', retry: 4 do
|
|
105
151
|
|
106
152
|
authorized_collection.client.use(:admin).command(fail_point_base_command.merge(
|
107
153
|
:mode => {:times => 1},
|
108
|
-
:data => {:failCommands => ['getMore'], errorCode:
|
154
|
+
:data => {:failCommands => ['getMore'], errorCode: errorCode}))
|
109
155
|
end
|
110
156
|
|
111
|
-
|
157
|
+
context 'when the error is resumable' do
|
158
|
+
let(:errorCode) do
|
159
|
+
100
|
160
|
+
end
|
161
|
+
it_behaves_like 'returns a change document'
|
162
|
+
end
|
163
|
+
|
164
|
+
context 'when the error is Interrupted' do
|
165
|
+
let(:errorCode) do
|
166
|
+
11601
|
167
|
+
end
|
168
|
+
it_behaves_like 'raises an exception'
|
169
|
+
end
|
170
|
+
|
171
|
+
context 'when the error is CappedPositionLost' do
|
172
|
+
let(:errorCode) do
|
173
|
+
136
|
174
|
+
end
|
175
|
+
it_behaves_like 'raises an exception'
|
176
|
+
end
|
177
|
+
|
178
|
+
context 'when the error is CursorKilled' do
|
179
|
+
let(:errorCode) do
|
180
|
+
237
|
181
|
+
end
|
182
|
+
it_behaves_like 'raises an exception'
|
183
|
+
end
|
112
184
|
end
|
113
185
|
end
|
114
186
|
|
@@ -369,7 +441,7 @@ describe 'Change stream integration', retry: 4 do
|
|
369
441
|
|
370
442
|
describe ':start_after option' do
|
371
443
|
require_topology :replica_set
|
372
|
-
|
444
|
+
min_server_fcv '4.2'
|
373
445
|
|
374
446
|
let(:start_after) do
|
375
447
|
stream = authorized_collection.watch([])
|
@@ -430,4 +502,232 @@ describe 'Change stream integration', retry: 4 do
|
|
430
502
|
end
|
431
503
|
end
|
432
504
|
end
|
505
|
+
|
506
|
+
describe 'resume_token' do
|
507
|
+
let(:stream) { authorized_collection.watch }
|
508
|
+
|
509
|
+
let(:events) do
|
510
|
+
subscriber = EventSubscriber.new
|
511
|
+
authorized_client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
|
512
|
+
use_stream
|
513
|
+
subscriber.succeeded_events.select { |e|
|
514
|
+
e.command_name == 'aggregate' || e.command_name === 'getMore'
|
515
|
+
}
|
516
|
+
end
|
517
|
+
|
518
|
+
let!(:sample_resume_token) do
|
519
|
+
cs = authorized_collection.watch
|
520
|
+
authorized_collection.insert_one(a: 1)
|
521
|
+
doc = cs.to_enum.next
|
522
|
+
cs.close
|
523
|
+
doc[:_id]
|
524
|
+
end
|
525
|
+
|
526
|
+
let(:use_stream) do
|
527
|
+
stream
|
528
|
+
authorized_collection.insert_one(x: 1)
|
529
|
+
stream.to_enum.next
|
530
|
+
end
|
531
|
+
|
532
|
+
context 'when batch has been emptied' do
|
533
|
+
context '4.2+' do
|
534
|
+
min_server_fcv '4.2'
|
535
|
+
it 'returns post batch resume token from current command response' do
|
536
|
+
expect(events.size).to eq(2)
|
537
|
+
|
538
|
+
aggregate_response = events.first.reply
|
539
|
+
get_more_response = events.last.reply
|
540
|
+
expect(aggregate_response['cursor'].key?('postBatchResumeToken')).to eq(true)
|
541
|
+
expect(get_more_response['cursor'].key?('postBatchResumeToken')).to eq(true)
|
542
|
+
|
543
|
+
res_tok = stream.resume_token
|
544
|
+
expect(res_tok).to eq(get_more_response['cursor']['postBatchResumeToken'])
|
545
|
+
expect(res_tok).to_not eq(aggregate_response['cursor']['postBatchResumeToken'])
|
546
|
+
end
|
547
|
+
end
|
548
|
+
|
549
|
+
context '4.0-' do
|
550
|
+
max_server_version '4.0'
|
551
|
+
|
552
|
+
it 'returns _id of previous document returned if one exists' do
|
553
|
+
doc = use_stream
|
554
|
+
expect(stream.resume_token).to eq(doc['_id'])
|
555
|
+
end
|
556
|
+
|
557
|
+
context 'when start_after is specified' do
|
558
|
+
min_server_fcv '4.2'
|
559
|
+
|
560
|
+
it 'must return startAfter from the initial aggregate if the option was specified' do
|
561
|
+
start_after = sample_resume_token
|
562
|
+
authorized_collection.insert_one(:a => 1)
|
563
|
+
stream = authorized_collection.watch([], { start_after: start_after })
|
564
|
+
|
565
|
+
expect(stream.resume_token).to eq(start_after)
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
it 'must return resumeAfter from the initial aggregate if the option was specified' do
|
570
|
+
resume_after = sample_resume_token
|
571
|
+
authorized_collection.insert_one(:a => 1)
|
572
|
+
stream = authorized_collection.watch([], { resume_after: resume_after })
|
573
|
+
|
574
|
+
expect(stream.resume_token).to eq(resume_after)
|
575
|
+
end
|
576
|
+
|
577
|
+
it 'must be empty if neither the startAfter nor resumeAfter options were specified' do
|
578
|
+
authorized_collection.insert_one(:a => 1)
|
579
|
+
stream = authorized_collection.watch
|
580
|
+
|
581
|
+
expect(stream.resume_token).to be(nil)
|
582
|
+
end
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
context 'before batch has been emptied' do
|
587
|
+
it 'returns _id of previous document returned' do
|
588
|
+
stream
|
589
|
+
|
590
|
+
authorized_collection.insert_one(:a => 1)
|
591
|
+
authorized_collection.insert_one(:a => 1)
|
592
|
+
authorized_collection.insert_one(:a => 1)
|
593
|
+
stream.to_enum.next
|
594
|
+
|
595
|
+
change = stream.to_enum.next
|
596
|
+
|
597
|
+
expect(stream.resume_token).to eq(change['_id'])
|
598
|
+
end
|
599
|
+
end
|
600
|
+
|
601
|
+
# Note that the watch method executes the initial aggregate command
|
602
|
+
context 'for non-empty, non-iterated batch, only the initial aggregate command executed' do
|
603
|
+
|
604
|
+
let (:use_stream) do
|
605
|
+
authorized_collection.insert_one(:a => 1)
|
606
|
+
stream
|
607
|
+
end
|
608
|
+
|
609
|
+
context 'if startAfter was specified' do
|
610
|
+
min_server_fcv '4.2'
|
611
|
+
|
612
|
+
let (:stream) do
|
613
|
+
authorized_collection.watch([], { start_after: sample_resume_token })
|
614
|
+
end
|
615
|
+
|
616
|
+
it 'must return startAfter from the initial aggregate' do
|
617
|
+
# Need to sample a doc id from the stream before we use the stream, so
|
618
|
+
# the events subscriber does not record these commands as part of the example.
|
619
|
+
sample_resume_token
|
620
|
+
|
621
|
+
# Verify that only the initial aggregate command was executed
|
622
|
+
expect(events.size).to eq(1)
|
623
|
+
expect(events.first.command_name).to eq('aggregate')
|
624
|
+
expect(stream.resume_token).to eq(sample_resume_token)
|
625
|
+
end
|
626
|
+
end
|
627
|
+
|
628
|
+
context 'if resumeAfter was specified' do
|
629
|
+
let (:stream) do
|
630
|
+
authorized_collection.watch([], { resume_after: sample_resume_token })
|
631
|
+
end
|
632
|
+
|
633
|
+
it 'must return resumeAfter from the initial aggregate' do
|
634
|
+
sample_resume_token
|
635
|
+
|
636
|
+
expect(events.size).to eq(1)
|
637
|
+
expect(events.first.command_name).to eq('aggregate')
|
638
|
+
expect(stream.resume_token).to eq(sample_resume_token)
|
639
|
+
end
|
640
|
+
end
|
641
|
+
|
642
|
+
context 'if neither the startAfter nor resumeAfter options were specified' do
|
643
|
+
it 'must be empty' do
|
644
|
+
expect(events.size).to eq(1)
|
645
|
+
expect(events.first.command_name).to eq('aggregate')
|
646
|
+
expect(stream.resume_token).to be(nil)
|
647
|
+
end
|
648
|
+
end
|
649
|
+
end
|
650
|
+
|
651
|
+
|
652
|
+
context 'for non-empty, non-iterated batch directly after get_more' do
|
653
|
+
let(:next_doc) do
|
654
|
+
authorized_collection.insert_one(:a => 1)
|
655
|
+
stream.to_enum.next
|
656
|
+
end
|
657
|
+
|
658
|
+
let(:do_get_more) do
|
659
|
+
authorized_collection.insert_one(:a => 1)
|
660
|
+
stream.instance_variable_get('@cursor').get_more
|
661
|
+
end
|
662
|
+
|
663
|
+
context '4.2+' do
|
664
|
+
min_server_fcv '4.2'
|
665
|
+
|
666
|
+
let(:use_stream) do
|
667
|
+
stream
|
668
|
+
next_doc
|
669
|
+
do_get_more
|
670
|
+
end
|
671
|
+
|
672
|
+
it 'returns post batch resume token from previous command response' do
|
673
|
+
expect(events.size).to eq(3)
|
674
|
+
|
675
|
+
expect(events.last.command_name).to eq('getMore')
|
676
|
+
|
677
|
+
first_get_more = events[1].reply
|
678
|
+
second_get_more = events[2].reply
|
679
|
+
expect(first_get_more['cursor'].key?('postBatchResumeToken')).to eq(true)
|
680
|
+
expect(second_get_more['cursor'].key?('postBatchResumeToken')).to eq(true)
|
681
|
+
|
682
|
+
res_tok = stream.resume_token
|
683
|
+
expect(res_tok).to eq(first_get_more['cursor']['postBatchResumeToken'])
|
684
|
+
expect(res_tok).not_to eq(second_get_more['cursor']['postBatchResumeToken'])
|
685
|
+
end
|
686
|
+
end
|
687
|
+
|
688
|
+
context '4.0-' do
|
689
|
+
max_server_version '4.0'
|
690
|
+
|
691
|
+
context 'if a document was returned' do
|
692
|
+
let(:use_stream) do
|
693
|
+
stream
|
694
|
+
next_doc
|
695
|
+
do_get_more
|
696
|
+
end
|
697
|
+
|
698
|
+
it 'returns _id of previous document' do
|
699
|
+
expect(events.last.command_name).to eq('getMore')
|
700
|
+
expect(stream.resume_token).to eq(next_doc['_id'])
|
701
|
+
end
|
702
|
+
end
|
703
|
+
|
704
|
+
context 'if a document was not returned' do
|
705
|
+
let(:use_stream) do
|
706
|
+
stream
|
707
|
+
do_get_more
|
708
|
+
end
|
709
|
+
|
710
|
+
context 'when resumeAfter is specified' do
|
711
|
+
let (:stream) do
|
712
|
+
authorized_collection.watch([], { resume_after: sample_resume_token })
|
713
|
+
end
|
714
|
+
|
715
|
+
it 'must return resumeAfter from the initial aggregate if the option was specified' do
|
716
|
+
sample_resume_token
|
717
|
+
|
718
|
+
expect(events.last.command_name).to eq('getMore')
|
719
|
+
expect(stream.resume_token).to eq(sample_resume_token)
|
720
|
+
end
|
721
|
+
end
|
722
|
+
|
723
|
+
context 'if neither the startAfter nor resumeAfter options were specified' do
|
724
|
+
it 'must be empty' do
|
725
|
+
expect(events.last.command_name).to eq('getMore')
|
726
|
+
expect(stream.resume_token).to be(nil)
|
727
|
+
end
|
728
|
+
end
|
729
|
+
end
|
730
|
+
end
|
731
|
+
end
|
732
|
+
end
|
433
733
|
end
|