mongo 2.4.3 → 2.5.0.beta
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 +3 -2
- data.tar.gz.sig +0 -0
- data/lib/mongo.rb +3 -2
- data/lib/mongo/auth/cr.rb +6 -4
- data/lib/mongo/auth/cr/conversation.rb +33 -17
- data/lib/mongo/auth/ldap.rb +4 -2
- data/lib/mongo/auth/ldap/conversation.rb +19 -9
- data/lib/mongo/auth/scram.rb +7 -4
- data/lib/mongo/auth/scram/conversation.rb +62 -24
- data/lib/mongo/auth/user.rb +10 -0
- data/lib/mongo/auth/user/view.rb +44 -22
- data/lib/mongo/auth/x509.rb +4 -2
- data/lib/mongo/auth/x509/conversation.rb +19 -9
- data/lib/mongo/bulk_write.rb +33 -27
- data/lib/mongo/bulk_write/combineable.rb +5 -0
- data/lib/mongo/bulk_write/transformable.rb +2 -0
- data/lib/mongo/bulk_write/validatable.rb +4 -0
- data/lib/mongo/client.rb +123 -12
- data/lib/mongo/cluster.rb +52 -11
- data/lib/mongo/cluster/app_metadata.rb +8 -2
- data/lib/mongo/cluster/cursor_reaper.rb +0 -1
- data/lib/mongo/cluster/topology.rb +1 -1
- data/lib/mongo/collection.rb +114 -27
- data/lib/mongo/collection/view.rb +8 -2
- data/lib/mongo/collection/view/aggregation.rb +11 -7
- data/lib/mongo/collection/view/builder/aggregation.rb +5 -1
- data/lib/mongo/collection/view/builder/find_command.rb +5 -3
- data/lib/mongo/collection/view/builder/map_reduce.rb +11 -3
- data/lib/mongo/collection/view/builder/op_query.rb +1 -1
- data/lib/mongo/collection/view/change_stream.rb +160 -0
- data/lib/mongo/collection/view/change_stream/retryable.rb +57 -0
- data/lib/mongo/collection/view/iterable.rb +11 -10
- data/lib/mongo/collection/view/map_reduce.rb +22 -18
- data/lib/mongo/collection/view/readable.rb +51 -37
- data/lib/mongo/collection/view/writable.rb +72 -40
- data/lib/mongo/cursor.rb +25 -4
- data/lib/mongo/cursor/builder/get_more_command.rb +4 -2
- data/lib/mongo/database.rb +22 -11
- data/lib/mongo/database/view.rb +16 -12
- data/lib/mongo/error.rb +5 -0
- data/lib/mongo/error/invalid_session.rb +36 -0
- data/lib/mongo/error/missing_resume_token.rb +39 -0
- data/lib/mongo/error/operation_failure.rb +17 -0
- data/lib/mongo/error/parser.rb +3 -2
- data/lib/mongo/error/unknown_payload_type.rb +41 -0
- data/lib/mongo/error/unsupported_array_filters.rb +51 -0
- data/lib/mongo/error/unsupported_message_type.rb +23 -0
- data/lib/mongo/grid/fs_bucket.rb +5 -4
- data/lib/mongo/grid/stream/read.rb +3 -2
- data/lib/mongo/grid/stream/write.rb +2 -2
- data/lib/mongo/index/view.rb +35 -25
- data/lib/mongo/monitoring/event/secure.rb +14 -0
- data/lib/mongo/operation.rb +16 -0
- data/lib/mongo/operation/commands.rb +1 -0
- data/lib/mongo/operation/commands/aggregate.rb +9 -5
- data/lib/mongo/operation/commands/aggregate/result.rb +1 -1
- data/lib/mongo/operation/commands/collections_info.rb +6 -6
- data/lib/mongo/operation/commands/command.rb +2 -1
- data/lib/mongo/operation/commands/create.rb +6 -2
- data/lib/mongo/operation/commands/drop.rb +6 -2
- data/lib/mongo/operation/commands/drop_database.rb +6 -2
- data/lib/mongo/operation/commands/explain.rb +27 -0
- data/lib/mongo/operation/commands/explain/result.rb +52 -0
- data/lib/mongo/operation/commands/indexes.rb +1 -1
- data/lib/mongo/operation/commands/list_collections.rb +1 -1
- data/lib/mongo/operation/commands/list_collections/result.rb +1 -1
- data/lib/mongo/operation/commands/list_indexes.rb +1 -1
- data/lib/mongo/operation/commands/list_indexes/result.rb +1 -1
- data/lib/mongo/operation/commands/map_reduce.rb +8 -4
- data/lib/mongo/operation/commands/map_reduce/result.rb +13 -1
- data/lib/mongo/operation/commands/user_query.rb +1 -1
- data/lib/mongo/operation/commands/users_info.rb +6 -2
- data/lib/mongo/operation/executable.rb +4 -1
- data/lib/mongo/operation/read_preference.rb +10 -5
- data/lib/mongo/operation/result.rb +26 -2
- data/lib/mongo/operation/specifiable.rb +13 -1
- data/lib/mongo/operation/uses_command_op_msg.rb +47 -0
- data/lib/mongo/operation/write/bulk/bulkable.rb +4 -1
- data/lib/mongo/operation/write/bulk/insert/result.rb +4 -4
- data/lib/mongo/operation/write/command/create_index.rb +6 -1
- data/lib/mongo/operation/write/command/delete.rb +28 -4
- data/lib/mongo/operation/write/command/drop_index.rb +6 -1
- data/lib/mongo/operation/write/command/insert.rb +22 -18
- data/lib/mongo/operation/write/command/update.rb +24 -9
- data/lib/mongo/operation/write/command/writable.rb +14 -1
- data/lib/mongo/operation/write/insert.rb +4 -1
- data/lib/mongo/operation/write/insert/result.rb +2 -2
- data/lib/mongo/operation/write/update.rb +7 -1
- data/lib/mongo/operation/write/write_command_enabled.rb +20 -3
- data/lib/mongo/protocol.rb +3 -0
- data/lib/mongo/protocol/bit_vector.rb +2 -2
- data/lib/mongo/protocol/compressed.rb +135 -0
- data/lib/mongo/protocol/delete.rb +8 -6
- data/lib/mongo/protocol/get_more.rb +8 -6
- data/lib/mongo/protocol/insert.rb +8 -6
- data/lib/mongo/protocol/kill_cursors.rb +8 -6
- data/lib/mongo/protocol/message.rb +31 -3
- data/lib/mongo/protocol/msg.rb +172 -0
- data/lib/mongo/protocol/query.rb +26 -6
- data/lib/mongo/protocol/registry.rb +76 -0
- data/lib/mongo/protocol/reply.rb +10 -5
- data/lib/mongo/protocol/serializers.rb +224 -0
- data/lib/mongo/protocol/update.rb +8 -6
- data/lib/mongo/retryable.rb +4 -2
- data/lib/mongo/server.rb +6 -3
- data/lib/mongo/server/connectable.rb +1 -1
- data/lib/mongo/server/connection.rb +30 -8
- data/lib/mongo/server/description.rb +25 -1
- data/lib/mongo/server/description/features.rb +4 -1
- data/lib/mongo/server/monitor.rb +5 -0
- data/lib/mongo/server/monitor/connection.rb +50 -2
- data/lib/mongo/server_selector/nearest.rb +10 -4
- data/lib/mongo/server_selector/primary.rb +20 -0
- data/lib/mongo/server_selector/primary_preferred.rb +10 -4
- data/lib/mongo/server_selector/secondary.rb +10 -4
- data/lib/mongo/server_selector/secondary_preferred.rb +24 -4
- data/lib/mongo/session.rb +180 -0
- data/lib/mongo/session/server_session.rb +73 -0
- data/lib/mongo/session/session_pool.rb +161 -0
- data/lib/mongo/uri.rb +11 -0
- data/lib/mongo/version.rb +1 -1
- data/mongo.gemspec +2 -1
- data/spec/mongo/auth/cr_spec.rb +12 -0
- data/spec/mongo/auth/ldap_spec.rb +2 -0
- data/spec/mongo/auth/scram/conversation_spec.rb +6 -6
- data/spec/mongo/auth/scram_spec.rb +25 -1
- data/spec/mongo/auth/user/view_spec.rb +268 -76
- data/spec/mongo/auth/x509_spec.rb +2 -0
- data/spec/mongo/bulk_write_spec.rb +435 -5
- data/spec/mongo/client_spec.rb +356 -39
- data/spec/mongo/cluster/app_metadata_spec.rb +2 -2
- data/spec/mongo/cluster_spec.rb +176 -0
- data/spec/mongo/collection/view/aggregation_spec.rb +33 -12
- data/spec/mongo/collection/view/builder/find_command_spec.rb +46 -6
- data/spec/mongo/collection/view/change_stream_spec.rb +814 -0
- data/spec/mongo/collection/view/map_reduce_spec.rb +94 -17
- data/spec/mongo/collection/view/readable_spec.rb +3 -12
- data/spec/mongo/collection_spec.rb +1048 -42
- data/spec/mongo/cursor/builder/get_more_command_spec.rb +19 -0
- data/spec/mongo/cursor_spec.rb +2 -2
- data/spec/mongo/database_spec.rb +50 -1
- data/spec/mongo/grid/fs_bucket_spec.rb +225 -137
- data/spec/mongo/grid/stream/read_spec.rb +2 -2
- data/spec/mongo/index/view_spec.rb +146 -8
- data/spec/mongo/monitoring/event/secure_spec.rb +42 -0
- data/spec/mongo/operation/read/query_spec.rb +2 -1
- data/spec/mongo/operation/specifiable_spec.rb +2 -2
- data/spec/mongo/operation/write/command/delete_spec.rb +96 -13
- data/spec/mongo/operation/write/command/insert_spec.rb +111 -12
- data/spec/mongo/operation/write/command/update_spec.rb +93 -10
- data/spec/mongo/operation/write/delete_spec.rb +1 -1
- data/spec/mongo/operation/write/insert_spec.rb +1 -1
- data/spec/mongo/operation/write/update_spec.rb +1 -1
- data/spec/mongo/protocol/compressed_spec.rb +66 -0
- data/spec/mongo/protocol/delete_spec.rb +14 -0
- data/spec/mongo/protocol/get_more_spec.rb +14 -0
- data/spec/mongo/protocol/insert_spec.rb +14 -0
- data/spec/mongo/protocol/kill_cursors_spec.rb +14 -0
- data/spec/mongo/protocol/msg_spec.rb +499 -0
- data/spec/mongo/protocol/query_spec.rb +45 -0
- data/spec/mongo/protocol/registry_spec.rb +31 -0
- data/spec/mongo/protocol/reply_spec.rb +14 -0
- data/spec/mongo/protocol/update_spec.rb +14 -0
- data/spec/mongo/retryable_spec.rb +6 -2
- data/spec/mongo/sdam_spec.rb +4 -0
- data/spec/mongo/server/connection_spec.rb +4 -2
- data/spec/mongo/server/description_spec.rb +28 -1
- data/spec/mongo/session/server_session_spec.rb +16 -0
- data/spec/mongo/session/session_pool_spec.rb +194 -0
- data/spec/mongo/uri_spec.rb +31 -2
- data/spec/spec_helper.rb +104 -0
- data/spec/support/authorization.rb +6 -1
- data/spec/support/crud.rb +3 -1
- data/spec/support/crud/write.rb +6 -1
- data/spec/support/crud_tests/write/findOneAndUpdate-arrayFilters.yml +69 -0
- data/spec/support/crud_tests/write/updateMany-arrayFilters.yml +63 -0
- data/spec/support/crud_tests/write/updateOne-arrayFilters.yml +109 -0
- data/spec/support/sdam/rs/discover_arbiters.yml +1 -1
- data/spec/support/sdam/rs/discover_passives.yml +2 -2
- data/spec/support/sdam/rs/discover_primary.yml +1 -1
- data/spec/support/sdam/rs/discover_secondary.yml +1 -1
- data/spec/support/sdam/rs/discovery.yml +4 -4
- data/spec/support/sdam/rs/equal_electionids.yml +1 -0
- data/spec/support/sdam/rs/ghost_discovered.yml +1 -1
- data/spec/support/sdam/rs/hosts_differ_from_seeds.yml +1 -1
- data/spec/support/sdam/rs/ls_timeout.yml +88 -0
- data/spec/support/sdam/rs/member_reconfig.yml +2 -2
- data/spec/support/sdam/rs/member_standalone.yml +2 -2
- data/spec/support/sdam/rs/new_primary.yml +2 -2
- data/spec/support/sdam/rs/new_primary_new_electionid.yml +3 -0
- data/spec/support/sdam/rs/new_primary_new_setversion.yml +3 -0
- data/spec/support/sdam/rs/new_primary_wrong_set_name.yml +2 -2
- data/spec/support/sdam/rs/non_rs_member.yml +1 -1
- data/spec/support/sdam/rs/normalize_case.yml +1 -1
- data/spec/support/sdam/rs/null_election_id.yml +4 -0
- data/spec/support/sdam/rs/primary_becomes_standalone.yml +2 -2
- data/spec/support/sdam/rs/primary_changes_set_name.yml +2 -2
- data/spec/support/sdam/rs/primary_disconnect.yml +2 -2
- data/spec/support/sdam/rs/primary_disconnect_electionid.yml +5 -0
- data/spec/support/sdam/rs/primary_disconnect_setversion.yml +5 -0
- data/spec/support/sdam/rs/primary_hint_from_secondary_with_mismatched_me.yml +58 -0
- data/spec/support/sdam/rs/primary_reports_new_member.yml +4 -4
- data/spec/support/sdam/rs/primary_to_no_primary_mismatched_me.yml +2 -2
- data/spec/support/sdam/rs/primary_wrong_set_name.yml +1 -1
- data/spec/support/sdam/rs/response_from_removed.yml +2 -2
- data/spec/support/sdam/rs/rsother_discovered.yml +1 -1
- data/spec/support/sdam/rs/sec_not_auth.yml +1 -1
- data/spec/support/sdam/rs/secondary_wrong_set_name.yml +1 -1
- data/spec/support/sdam/rs/secondary_wrong_set_name_with_primary.yml +2 -2
- data/spec/support/sdam/rs/setversion_without_electionid.yml +2 -0
- data/spec/support/sdam/rs/stepdown_change_set_name.yml +2 -2
- data/spec/support/sdam/rs/unexpected_mongos.yml +1 -1
- data/spec/support/sdam/rs/use_setversion_without_electionid.yml +3 -0
- data/spec/support/sdam/rs/wrong_set_name.yml +1 -1
- data/spec/support/sdam/sharded/ls_timeout_mongos.yml +97 -0
- data/spec/support/sdam/sharded/mongos_disconnect.yml +3 -3
- data/spec/support/sdam/sharded/multiple_mongoses.yml +1 -1
- data/spec/support/sdam/sharded/non_mongos_removed.yml +1 -1
- data/spec/support/sdam/sharded/normalize_uri_case.yml +1 -1
- data/spec/support/sdam/single/direct_connection_external_ip.yml +1 -1
- data/spec/support/sdam/single/direct_connection_mongos.yml +1 -1
- data/spec/support/sdam/single/direct_connection_rsarbiter.yml +1 -1
- data/spec/support/sdam/single/direct_connection_rsprimary.yml +1 -1
- data/spec/support/sdam/single/direct_connection_rssecondary.yml +1 -1
- data/spec/support/sdam/single/direct_connection_slave.yml +1 -1
- data/spec/support/sdam/single/direct_connection_standalone.yml +1 -1
- data/spec/support/sdam/single/ls_timeout_standalone.yml +35 -0
- data/spec/support/sdam/single/not_ok_response.yml +1 -1
- data/spec/support/sdam/single/standalone_removed.yml +1 -1
- data/spec/support/sdam/single/unavailable_seed.yml +1 -1
- data/spec/support/server_discovery_and_monitoring.rb +4 -0
- data/spec/support/shared/session.rb +236 -0
- metadata +53 -15
- metadata.gz.sig +0 -0
@@ -17,6 +17,7 @@ require 'mongo/collection/view/immutable'
|
|
17
17
|
require 'mongo/collection/view/iterable'
|
18
18
|
require 'mongo/collection/view/explainable'
|
19
19
|
require 'mongo/collection/view/aggregation'
|
20
|
+
require 'mongo/collection/view/change_stream'
|
20
21
|
require 'mongo/collection/view/map_reduce'
|
21
22
|
require 'mongo/collection/view/readable'
|
22
23
|
require 'mongo/collection/view/writable'
|
@@ -59,8 +60,7 @@ module Mongo
|
|
59
60
|
def_delegators :collection,
|
60
61
|
:client,
|
61
62
|
:cluster,
|
62
|
-
:database
|
63
|
-
:read_preference
|
63
|
+
:database
|
64
64
|
|
65
65
|
# Delegate to the cluster for the next primary.
|
66
66
|
def_delegators :cluster, :next_primary
|
@@ -198,6 +198,12 @@ module Mongo
|
|
198
198
|
end
|
199
199
|
|
200
200
|
def view; self; end
|
201
|
+
|
202
|
+
def with_session
|
203
|
+
client.send(:with_session, @options) do |session|
|
204
|
+
yield(session)
|
205
|
+
end
|
206
|
+
end
|
201
207
|
end
|
202
208
|
end
|
203
209
|
end
|
@@ -37,7 +37,7 @@ module Mongo
|
|
37
37
|
def_delegators :view, :collection, :read, :cluster
|
38
38
|
|
39
39
|
# Delegate necessary operations to the collection.
|
40
|
-
def_delegators :collection, :database
|
40
|
+
def_delegators :collection, :database, :client
|
41
41
|
|
42
42
|
# The reroute message.
|
43
43
|
#
|
@@ -90,16 +90,20 @@ module Mongo
|
|
90
90
|
|
91
91
|
private
|
92
92
|
|
93
|
-
def
|
94
|
-
|
93
|
+
def server_selector
|
94
|
+
@view.send(:server_selector)
|
95
|
+
end
|
96
|
+
|
97
|
+
def aggregate_spec(session)
|
98
|
+
Builder::Aggregation.new(pipeline, view, options.merge(session: session)).specification
|
95
99
|
end
|
96
100
|
|
97
101
|
def new(options)
|
98
102
|
Aggregation.new(view, pipeline, options)
|
99
103
|
end
|
100
104
|
|
101
|
-
def initial_query_op
|
102
|
-
Operation::Commands::Aggregate.new(aggregate_spec)
|
105
|
+
def initial_query_op(session)
|
106
|
+
Operation::Commands::Aggregate.new(aggregate_spec(session))
|
103
107
|
end
|
104
108
|
|
105
109
|
def valid_server?(server)
|
@@ -110,13 +114,13 @@ module Mongo
|
|
110
114
|
pipeline.none? { |op| op.key?('$out') || op.key?(:$out) }
|
111
115
|
end
|
112
116
|
|
113
|
-
def send_initial_query(server)
|
117
|
+
def send_initial_query(server, session)
|
114
118
|
unless valid_server?(server)
|
115
119
|
log_warn(REROUTE)
|
116
120
|
server = cluster.next_primary(false)
|
117
121
|
end
|
118
122
|
validate_collation!(server)
|
119
|
-
initial_query_op.execute(server)
|
123
|
+
initial_query_op(session).execute(server)
|
120
124
|
end
|
121
125
|
|
122
126
|
def validate_collation!(server)
|
@@ -29,6 +29,9 @@ module Mongo
|
|
29
29
|
MAPPINGS = BSON::Document.new(
|
30
30
|
:allow_disk_use => 'allowDiskUse',
|
31
31
|
:max_time_ms => 'maxTimeMS',
|
32
|
+
# This is intentional; max_await_time_ms is an alias for maxTimeMS used on getmore
|
33
|
+
# commands for change streams.
|
34
|
+
:max_await_time_ms => 'maxTimeMS',
|
32
35
|
:explain => 'explain',
|
33
36
|
:bypass_document_validation => 'bypassDocumentValidation',
|
34
37
|
:collation => 'collation',
|
@@ -74,7 +77,8 @@ module Mongo
|
|
74
77
|
spec = {
|
75
78
|
selector: aggregation_command,
|
76
79
|
db_name: database.name,
|
77
|
-
read: read
|
80
|
+
read: read,
|
81
|
+
session: @options[:session]
|
78
82
|
}
|
79
83
|
write? ? spec.merge!(write_concern: write_concern) : spec
|
80
84
|
end
|
@@ -48,7 +48,6 @@ module Mongo
|
|
48
48
|
no_cursor_timeout: 'noCursorTimeout',
|
49
49
|
await_data: 'awaitData',
|
50
50
|
allow_partial_results: 'allowPartialResults',
|
51
|
-
read_concern: 'readConcern',
|
52
51
|
collation: 'collation'
|
53
52
|
).freeze
|
54
53
|
|
@@ -73,10 +72,12 @@ module Mongo
|
|
73
72
|
# FindCommandBuilder.new(view)
|
74
73
|
#
|
75
74
|
# @param [ Collection::View ] view The collection view.
|
75
|
+
# @param [ Session ] session The session.
|
76
76
|
#
|
77
77
|
# @since 2.2.2
|
78
|
-
def initialize(view)
|
78
|
+
def initialize(view, session)
|
79
79
|
@view = view
|
80
|
+
@session = session
|
80
81
|
end
|
81
82
|
|
82
83
|
# Get the specification to pass to the find command operation.
|
@@ -88,13 +89,14 @@ module Mongo
|
|
88
89
|
#
|
89
90
|
# @since 2.2.0
|
90
91
|
def specification
|
91
|
-
{ selector: find_command, db_name: database.name, read: read }
|
92
|
+
{ selector: find_command, db_name: database.name, read: read, session: @session }
|
92
93
|
end
|
93
94
|
|
94
95
|
private
|
95
96
|
|
96
97
|
def find_command
|
97
98
|
document = BSON::Document.new('find' => collection.name, 'filter' => filter)
|
99
|
+
document[:readConcern] = collection.read_concern if collection.read_concern
|
98
100
|
command = Options::Mapper.transform_documents(convert_flags(options), MAPPINGS, document)
|
99
101
|
convert_limit_and_batch_size(command)
|
100
102
|
command
|
@@ -81,7 +81,8 @@ module Mongo
|
|
81
81
|
{
|
82
82
|
selector: find_command,
|
83
83
|
db_name: query_database,
|
84
|
-
read: read
|
84
|
+
read: read,
|
85
|
+
session: options[:session]
|
85
86
|
}
|
86
87
|
end
|
87
88
|
|
@@ -109,7 +110,8 @@ module Mongo
|
|
109
110
|
spec = {
|
110
111
|
selector: map_reduce_command,
|
111
112
|
db_name: database.name,
|
112
|
-
read: read
|
113
|
+
read: read,
|
114
|
+
session: options[:session]
|
113
115
|
}
|
114
116
|
write?(spec) ? spec.merge!(write_concern: write_concern) : spec
|
115
117
|
end
|
@@ -138,7 +140,7 @@ module Mongo
|
|
138
140
|
:out => { inline: 1 }
|
139
141
|
)
|
140
142
|
command[:readConcern] = collection.read_concern if collection.read_concern
|
141
|
-
command.merge!(
|
143
|
+
command.merge!(view_options)
|
142
144
|
command.merge!(Options::Mapper.transform_documents(options, MAPPINGS))
|
143
145
|
command
|
144
146
|
end
|
@@ -152,6 +154,12 @@ module Mongo
|
|
152
154
|
options[:out][OUT_ACTIONS.find { |action| options[:out][action] }]
|
153
155
|
end || options[:out]
|
154
156
|
end
|
157
|
+
|
158
|
+
def view_options
|
159
|
+
@view_options ||= (opts = view.options.dup
|
160
|
+
opts.delete(:session)
|
161
|
+
opts)
|
162
|
+
end
|
155
163
|
end
|
156
164
|
end
|
157
165
|
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# Copyright (C) 2017 MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'mongo/collection/view/change_stream/retryable'
|
16
|
+
|
17
|
+
module Mongo
|
18
|
+
class Collection
|
19
|
+
class View
|
20
|
+
|
21
|
+
# Provides behaviour around a `$changeStream` pipeline stage in the
|
22
|
+
# aggregation framework. Specifying this stage allows users to request that
|
23
|
+
# notifications are sent for all changes to a particular collection or database.
|
24
|
+
#
|
25
|
+
# @note Only available in server versions 3.6 and higher.
|
26
|
+
# @note ChangeStreams do not work properly with JRuby because of the issue documented
|
27
|
+
# here: https://github.com/jruby/jruby/issues/4212
|
28
|
+
# Namely, JRuby eagerly evaluates #next on an Enumerator in a background green thread.
|
29
|
+
# So calling #next on the change stream will cause getmores to be called in a loop in the background.
|
30
|
+
#
|
31
|
+
#
|
32
|
+
# @since 2.5.0
|
33
|
+
class ChangeStream < Aggregation
|
34
|
+
include Retryable
|
35
|
+
|
36
|
+
# @return [ String ] The fullDocument option default value.
|
37
|
+
#
|
38
|
+
# @since 2.5.0
|
39
|
+
FULL_DOCUMENT_DEFAULT = 'default'.freeze
|
40
|
+
|
41
|
+
# @return [ BSON::Document ] The change stream options.
|
42
|
+
#
|
43
|
+
# @since 2.5.0
|
44
|
+
attr_reader :options
|
45
|
+
|
46
|
+
# Initialize the change stream for the provided collection view, pipeline
|
47
|
+
# and options.
|
48
|
+
#
|
49
|
+
# @example Create the new change stream view.
|
50
|
+
# ChangeStream.new(view, pipeline, options)
|
51
|
+
#
|
52
|
+
# @param [ Collection::View ] view The collection view.
|
53
|
+
# @param [ Array<Hash> ] pipeline The pipeline of operators to filter the change notifications.
|
54
|
+
# @param [ Hash ] opts The change stream options.
|
55
|
+
#
|
56
|
+
# @option options [ String ] :full_document Allowed values: ‘default’, ‘updateLookup’. Defaults to ‘default’.
|
57
|
+
# When set to ‘updateLookup’, the change notification for partial updates will include both a delta
|
58
|
+
# describing the changes to the document, as well as a copy of the entire document that was changed
|
59
|
+
# from some time after the change occurred.
|
60
|
+
# @option options [ BSON::Document, Hash ] :resume_after Specifies the logical starting point for the
|
61
|
+
# new change stream.
|
62
|
+
# @option options [ Integer ] :max_await_time_ms The maximum amount of time for the server to wait
|
63
|
+
# on new documents to satisfy a change stream query.
|
64
|
+
# @option options [ Integer ] :batch_size The number of documents to return per batch.
|
65
|
+
# @option options [ BSON::Document, Hash ] :collation The collation to use.
|
66
|
+
#
|
67
|
+
# @since 2.5.0
|
68
|
+
def initialize(view, pipeline, options = {})
|
69
|
+
@view = view
|
70
|
+
@change_stream_filters = pipeline && pipeline.dup
|
71
|
+
@options = options && options.dup.freeze
|
72
|
+
@resume_token = @options[:resume_after]
|
73
|
+
read_with_one_retry { create_cursor! }
|
74
|
+
end
|
75
|
+
|
76
|
+
# Iterate through documents returned by the change stream.
|
77
|
+
#
|
78
|
+
# @example Iterate through the stream of documents.
|
79
|
+
# stream.each do |document|
|
80
|
+
# p document
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# @return [ Enumerator ] The enumerator.
|
84
|
+
#
|
85
|
+
# @since 2.5.0
|
86
|
+
#
|
87
|
+
# @yieldparam [ BSON::Document ] Each change stream document.
|
88
|
+
def each
|
89
|
+
raise StopIteration.new if closed?
|
90
|
+
begin
|
91
|
+
@cursor.each do |doc|
|
92
|
+
cache_resume_token(doc)
|
93
|
+
yield doc
|
94
|
+
end if block_given?
|
95
|
+
@cursor.to_enum
|
96
|
+
rescue => e
|
97
|
+
close
|
98
|
+
if retryable?(e)
|
99
|
+
create_cursor!
|
100
|
+
retry
|
101
|
+
end
|
102
|
+
raise
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Close the change stream.
|
107
|
+
#
|
108
|
+
# @example Close the change stream.
|
109
|
+
# stream.close
|
110
|
+
#
|
111
|
+
# @return [ nil ] nil.
|
112
|
+
#
|
113
|
+
# @since 2.5.0
|
114
|
+
def close
|
115
|
+
unless closed?
|
116
|
+
begin; @cursor.send(:kill_cursors); rescue; end
|
117
|
+
@cursor = nil
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Is the change stream closed?
|
122
|
+
#
|
123
|
+
# @example Determine whether the change stream is closed.
|
124
|
+
# stream.closed?
|
125
|
+
#
|
126
|
+
# @return [ true, false ] If the change stream is closed.
|
127
|
+
#
|
128
|
+
# @since 2.5.0
|
129
|
+
def closed?
|
130
|
+
@cursor.nil?
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def cache_resume_token(doc)
|
136
|
+
unless @resume_token = (doc[:_id] && doc[:_id].dup)
|
137
|
+
raise Error::MissingResumeToken.new
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def create_cursor!
|
142
|
+
session = client.send(:get_session, @options)
|
143
|
+
server = server_selector.select_server(cluster, false)
|
144
|
+
result = send_initial_query(server, session)
|
145
|
+
@cursor = Cursor.new(view, result, server, disable_retry: true, session: session)
|
146
|
+
end
|
147
|
+
|
148
|
+
def pipeline
|
149
|
+
change_doc = { fullDocument: ( @options[:full_document] || FULL_DOCUMENT_DEFAULT ) }
|
150
|
+
change_doc[:resumeAfter] = @resume_token if @resume_token
|
151
|
+
[{ '$changeStream' => change_doc }] + @change_stream_filters
|
152
|
+
end
|
153
|
+
|
154
|
+
def send_initial_query(server, session)
|
155
|
+
initial_query_op(session).execute(server)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# Copyright (C) 2017 MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Mongo
|
16
|
+
class Collection
|
17
|
+
class View
|
18
|
+
class ChangeStream < Aggregation
|
19
|
+
|
20
|
+
# Behavior around resuming a change stream.
|
21
|
+
#
|
22
|
+
# @since 2.5.0
|
23
|
+
module Retryable
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
RETRY_MESSAGES = [
|
28
|
+
'not master',
|
29
|
+
'(43)' # cursor not found error code
|
30
|
+
].freeze
|
31
|
+
|
32
|
+
def read_with_one_retry
|
33
|
+
yield
|
34
|
+
rescue => e
|
35
|
+
if retryable?(e)
|
36
|
+
yield
|
37
|
+
else
|
38
|
+
raise(e)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def retryable?(error)
|
43
|
+
network_error?(error) || retryable_operation_failure?(error)
|
44
|
+
end
|
45
|
+
|
46
|
+
def network_error?(error)
|
47
|
+
[ Error::SocketError, Error::SocketTimeoutError].include?(error.class)
|
48
|
+
end
|
49
|
+
|
50
|
+
def retryable_operation_failure?(error)
|
51
|
+
error.is_a?(Error::OperationFailure) && RETRY_MESSAGES.any? { |m| error.message.include?(m) }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -36,10 +36,11 @@ module Mongo
|
|
36
36
|
# @yieldparam [ Hash ] Each matching document.
|
37
37
|
def each
|
38
38
|
@cursor = nil
|
39
|
+
session = client.send(:get_session, @options)
|
39
40
|
read_with_retry do
|
40
|
-
server =
|
41
|
-
result = send_initial_query(server)
|
42
|
-
@cursor = Cursor.new(view, result, server)
|
41
|
+
server = server_selector.select_server(cluster, false)
|
42
|
+
result = send_initial_query(server, session)
|
43
|
+
@cursor = Cursor.new(view, result, server, session: session)
|
43
44
|
end
|
44
45
|
@cursor.each do |doc|
|
45
46
|
yield doc
|
@@ -60,25 +61,25 @@ module Mongo
|
|
60
61
|
|
61
62
|
private
|
62
63
|
|
63
|
-
def initial_query_op(server)
|
64
|
+
def initial_query_op(server, session)
|
64
65
|
if server.features.find_command_enabled?
|
65
|
-
initial_command_op
|
66
|
+
initial_command_op(session)
|
66
67
|
else
|
67
68
|
Operation::Read::Query.new(Builder::OpQuery.new(self).specification)
|
68
69
|
end
|
69
70
|
end
|
70
71
|
|
71
|
-
def initial_command_op
|
72
|
+
def initial_command_op(session)
|
72
73
|
if explained?
|
73
|
-
Operation::Commands::
|
74
|
+
Operation::Commands::Explain.new(Builder::FindCommand.new(self, session).explain_specification)
|
74
75
|
else
|
75
|
-
Operation::Commands::Find.new(Builder::FindCommand.new(self).specification)
|
76
|
+
Operation::Commands::Find.new(Builder::FindCommand.new(self, session).specification)
|
76
77
|
end
|
77
78
|
end
|
78
79
|
|
79
|
-
def send_initial_query(server)
|
80
|
+
def send_initial_query(server, session = nil)
|
80
81
|
validate_collation!(server, collation)
|
81
|
-
initial_query_op(server).execute(server)
|
82
|
+
initial_query_op(server, session).execute(server)
|
82
83
|
end
|
83
84
|
end
|
84
85
|
end
|