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
@@ -20,6 +20,7 @@ module Mongo
|
|
20
20
|
#
|
21
21
|
# @since 2.0.0
|
22
22
|
module Specifiable
|
23
|
+
include Unpinnable
|
23
24
|
|
24
25
|
# The field for database name.
|
25
26
|
#
|
@@ -564,6 +565,45 @@ module Mongo
|
|
564
565
|
def acknowledged_write?
|
565
566
|
write_concern.nil? || write_concern.acknowledged?
|
566
567
|
end
|
568
|
+
|
569
|
+
private
|
570
|
+
|
571
|
+
def validate_result(result)
|
572
|
+
unpin_maybe(session) do
|
573
|
+
add_error_labels do
|
574
|
+
result.validate!
|
575
|
+
end
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
579
|
+
# Adds error labels to exceptions raised in the yielded to block,
|
580
|
+
# which should perform MongoDB operations and raise Mongo::Errors on
|
581
|
+
# failure. This method handles network errors (Error::SocketError)
|
582
|
+
# and server-side errors (Error::OperationFailure); it does not
|
583
|
+
# handle server selection errors (Error::NoServerAvailable), for which
|
584
|
+
# labels are added in the server selection code.
|
585
|
+
def add_error_labels
|
586
|
+
begin
|
587
|
+
yield
|
588
|
+
rescue Mongo::Error::SocketError => e
|
589
|
+
if session && session.in_transaction? && !session.committing_transaction?
|
590
|
+
e.add_label('TransientTransactionError')
|
591
|
+
end
|
592
|
+
if session && session.committing_transaction?
|
593
|
+
e.add_label('UnknownTransactionCommitResult')
|
594
|
+
end
|
595
|
+
raise e
|
596
|
+
rescue Mongo::Error::OperationFailure => e
|
597
|
+
if session && session.committing_transaction?
|
598
|
+
if e.write_retryable? || e.wtimeout? || (e.write_concern_error? &&
|
599
|
+
!Session::UNLABELED_WRITE_CONCERN_CODES.include?(e.write_concern_error_code)
|
600
|
+
) || e.max_time_ms_expired?
|
601
|
+
e.add_label('UnknownTransactionCommitResult')
|
602
|
+
end
|
603
|
+
end
|
604
|
+
raise e
|
605
|
+
end
|
606
|
+
end
|
567
607
|
end
|
568
608
|
end
|
569
609
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Copyright (C) 2014-2019 MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Mongo
|
16
|
+
module Operation
|
17
|
+
module Unpinnable
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
# Unpins the session if the session is pinned and the yielded to block
|
22
|
+
# raises errors that are required to unpin the session.
|
23
|
+
#
|
24
|
+
# @note This method takes the session as an argument because Unpinnable
|
25
|
+
# is included in BulkWrite which does not store the session in the
|
26
|
+
# receiver (despite Specifiable doing so).
|
27
|
+
#
|
28
|
+
# @param [ Session | nil ] Session to consider.
|
29
|
+
def unpin_maybe(session)
|
30
|
+
yield
|
31
|
+
rescue Mongo::Error => e
|
32
|
+
if session
|
33
|
+
session.unpin_maybe(e)
|
34
|
+
end
|
35
|
+
raise
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -192,8 +192,12 @@ module Mongo
|
|
192
192
|
updates = BSON::Document.new
|
193
193
|
updates.store(Message::Q, filter)
|
194
194
|
updates.store(U, update)
|
195
|
-
|
196
|
-
|
195
|
+
if flags.include?(:multi_update)
|
196
|
+
updates.store(MULTI, true)
|
197
|
+
end
|
198
|
+
if flags.include?(:upsert)
|
199
|
+
updates.store(UPSERT, true)
|
200
|
+
end
|
197
201
|
document.store(UPDATE, collection)
|
198
202
|
document.store(Message::ORDERED, true)
|
199
203
|
document.store(UPDATES, [ updates ])
|
data/lib/mongo/retryable.rb
CHANGED
@@ -118,7 +118,7 @@ module Mongo
|
|
118
118
|
elsif client.max_read_retries > 0
|
119
119
|
legacy_read_with_retry(session, server_selector, &block)
|
120
120
|
else
|
121
|
-
server = select_server(cluster, server_selector)
|
121
|
+
server = select_server(cluster, server_selector, session)
|
122
122
|
yield server
|
123
123
|
end
|
124
124
|
end
|
@@ -200,7 +200,7 @@ module Mongo
|
|
200
200
|
# If we are here, session is not nil. A session being nil would have
|
201
201
|
# failed retry_write_allowed? check.
|
202
202
|
|
203
|
-
server = cluster.
|
203
|
+
server = select_server(cluster, ServerSelector.primary, session)
|
204
204
|
|
205
205
|
unless ending_transaction || server.retry_writes?
|
206
206
|
return legacy_write_with_retry(server, session, &block)
|
@@ -213,12 +213,75 @@ module Mongo
|
|
213
213
|
if session.in_transaction? && !ending_transaction
|
214
214
|
raise
|
215
215
|
end
|
216
|
-
retry_write(e, txn_num, &block)
|
216
|
+
retry_write(e, session, txn_num, &block)
|
217
217
|
rescue Error::OperationFailure => e
|
218
218
|
if (session.in_transaction? && !ending_transaction) || !e.write_retryable?
|
219
219
|
raise
|
220
220
|
end
|
221
|
-
retry_write(e, txn_num, &block)
|
221
|
+
retry_write(e, session, txn_num, &block)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Retryable writes wrapper for operations not supporting modern retryable
|
226
|
+
# writes.
|
227
|
+
#
|
228
|
+
# If the driver is configured to use modern retryable writes, this method
|
229
|
+
# yields to the passed block exactly once, thus not retrying any writes.
|
230
|
+
#
|
231
|
+
# If the driver is configured to use legacy retryable writes, this method
|
232
|
+
# delegates to legacy_write_with_retry which performs write retries using
|
233
|
+
# legacy logic.
|
234
|
+
#
|
235
|
+
# @param [ nil | Session ] session Optional session to use with the operation.
|
236
|
+
# @param [ nil | Hash | WriteConcern::Base ] write_concern The write concern.
|
237
|
+
#
|
238
|
+
# @yieldparam [ Server ] server The server to which the write should be sent.
|
239
|
+
#
|
240
|
+
# @api private
|
241
|
+
def nro_write_with_retry(session, write_concern, &block)
|
242
|
+
if session && session.client.options[:retry_writes]
|
243
|
+
server = select_server(cluster, ServerSelector.primary, session)
|
244
|
+
yield server
|
245
|
+
else
|
246
|
+
legacy_write_with_retry(nil, session, &block)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
# Implements legacy write retrying functionality by yielding to the passed
|
251
|
+
# block one or more times.
|
252
|
+
#
|
253
|
+
# This method is used for operations which are not supported by modern
|
254
|
+
# retryable writes, such as delete_many and update_many.
|
255
|
+
#
|
256
|
+
# @param [ Server ] server The server which should be used for the
|
257
|
+
# operation. If not provided, the current primary will be retrieved from
|
258
|
+
# the cluster.
|
259
|
+
# @param [ nil | Session ] session Optional session to use with the operation.
|
260
|
+
#
|
261
|
+
# @yieldparam [ Server ] server The server to which the write should be sent.
|
262
|
+
#
|
263
|
+
# @api private
|
264
|
+
def legacy_write_with_retry(server = nil, session = nil)
|
265
|
+
# This is the pre-session retry logic, and is not subject to
|
266
|
+
# current retryable write specifications.
|
267
|
+
# In particular it does not retry on SocketError and SocketTimeoutError.
|
268
|
+
attempt = 0
|
269
|
+
begin
|
270
|
+
attempt += 1
|
271
|
+
server ||= select_server(cluster, ServerSelector.primary, session)
|
272
|
+
yield server
|
273
|
+
rescue Error::OperationFailure => e
|
274
|
+
server = nil
|
275
|
+
if attempt > client.max_write_retries
|
276
|
+
raise
|
277
|
+
end
|
278
|
+
if e.write_retryable? && !(session && session.in_transaction?)
|
279
|
+
log_retry(e, message: 'Legacy write retry')
|
280
|
+
cluster.scan!(false)
|
281
|
+
retry
|
282
|
+
else
|
283
|
+
raise
|
284
|
+
end
|
222
285
|
end
|
223
286
|
end
|
224
287
|
|
@@ -226,25 +289,25 @@ module Mongo
|
|
226
289
|
|
227
290
|
def modern_read_with_retry(session, server_selector, &block)
|
228
291
|
attempt = 0
|
229
|
-
server = select_server(cluster, server_selector)
|
292
|
+
server = select_server(cluster, server_selector, session)
|
230
293
|
begin
|
231
294
|
yield server
|
232
295
|
rescue Error::SocketError, Error::SocketTimeoutError => e
|
233
296
|
if session.in_transaction?
|
234
297
|
raise
|
235
298
|
end
|
236
|
-
retry_read(e, server_selector, &block)
|
299
|
+
retry_read(e, server_selector, session, &block)
|
237
300
|
rescue Error::OperationFailure => e
|
238
301
|
if session.in_transaction? || !e.write_retryable?
|
239
302
|
raise
|
240
303
|
end
|
241
|
-
retry_read(e, server_selector, &block)
|
304
|
+
retry_read(e, server_selector, session, &block)
|
242
305
|
end
|
243
306
|
end
|
244
307
|
|
245
308
|
def legacy_read_with_retry(session, server_selector)
|
246
309
|
attempt = 0
|
247
|
-
server = select_server(cluster, server_selector)
|
310
|
+
server = select_server(cluster, server_selector, session)
|
248
311
|
begin
|
249
312
|
attempt += 1
|
250
313
|
yield server
|
@@ -253,7 +316,7 @@ module Mongo
|
|
253
316
|
raise
|
254
317
|
end
|
255
318
|
log_retry(e, message: 'Legacy read retry')
|
256
|
-
server = select_server(cluster, server_selector)
|
319
|
+
server = select_server(cluster, server_selector, session)
|
257
320
|
retry
|
258
321
|
rescue Error::OperationFailure => e
|
259
322
|
if cluster.sharded? && e.retryable? && !(session && session.in_transaction?)
|
@@ -262,7 +325,7 @@ module Mongo
|
|
262
325
|
end
|
263
326
|
log_retry(e, message: 'Legacy read retry')
|
264
327
|
sleep(client.read_retry_interval)
|
265
|
-
server = select_server(cluster, server_selector)
|
328
|
+
server = select_server(cluster, server_selector, session)
|
266
329
|
retry
|
267
330
|
else
|
268
331
|
raise
|
@@ -285,9 +348,9 @@ module Mongo
|
|
285
348
|
end
|
286
349
|
end
|
287
350
|
|
288
|
-
def retry_read(original_error, server_selector, &block)
|
351
|
+
def retry_read(original_error, server_selector, session, &block)
|
289
352
|
begin
|
290
|
-
server = select_server(cluster, server_selector)
|
353
|
+
server = select_server(cluster, server_selector, session)
|
291
354
|
rescue
|
292
355
|
raise original_error
|
293
356
|
end
|
@@ -306,13 +369,13 @@ module Mongo
|
|
306
369
|
end
|
307
370
|
end
|
308
371
|
|
309
|
-
def retry_write(original_error, txn_num, &block)
|
372
|
+
def retry_write(original_error, session, txn_num, &block)
|
310
373
|
# We do not request a scan of the cluster here, because error handling
|
311
374
|
# for the error which triggered the retry should have updated the
|
312
375
|
# server description and/or topology as necessary (specifically,
|
313
376
|
# a socket error or a not master error should have marked the respective
|
314
377
|
# server unknown). Here we just need to wait for server selection.
|
315
|
-
server = cluster.
|
378
|
+
server = select_server(cluster, ServerSelector.primary, session)
|
316
379
|
raise original_error unless (server.retry_writes? && txn_num)
|
317
380
|
log_retry(original_error, message: 'Write retry')
|
318
381
|
yield(server, txn_num, true)
|
@@ -325,33 +388,10 @@ module Mongo
|
|
325
388
|
raise original_error
|
326
389
|
end
|
327
390
|
|
328
|
-
def legacy_write_with_retry(server = nil, session = nil)
|
329
|
-
# This is the pre-session retry logic, and is not subject to
|
330
|
-
# current retryable write specifications.
|
331
|
-
# In particular it does not retry on SocketError and SocketTimeoutError.
|
332
|
-
attempt = 0
|
333
|
-
begin
|
334
|
-
attempt += 1
|
335
|
-
yield(server || cluster.next_primary)
|
336
|
-
rescue Error::OperationFailure => e
|
337
|
-
server = nil
|
338
|
-
if attempt > client.max_write_retries
|
339
|
-
raise
|
340
|
-
end
|
341
|
-
if e.write_retryable? && !(session && session.in_transaction?)
|
342
|
-
log_retry(e, message: 'Legacy write retry')
|
343
|
-
cluster.scan!(false)
|
344
|
-
retry
|
345
|
-
else
|
346
|
-
raise
|
347
|
-
end
|
348
|
-
end
|
349
|
-
end
|
350
|
-
|
351
391
|
# This is a separate method to make it possible for the test suite to
|
352
392
|
# assert that server selection is performed during retry attempts.
|
353
|
-
def select_server(cluster, server_selector)
|
354
|
-
server_selector.select_server(cluster)
|
393
|
+
def select_server(cluster, server_selector, session)
|
394
|
+
server_selector.select_server(cluster, nil, session)
|
355
395
|
end
|
356
396
|
|
357
397
|
# Log a warning so that any application slow down is immediately obvious.
|
@@ -175,9 +175,16 @@ module Mongo
|
|
175
175
|
def do_connect
|
176
176
|
socket = address.socket(socket_timeout, ssl_options,
|
177
177
|
connect_timeout: address.connect_timeout)
|
178
|
-
|
179
|
-
|
180
|
-
|
178
|
+
|
179
|
+
begin
|
180
|
+
handshake!(socket)
|
181
|
+
pending_connection = PendingConnection.new(socket, @server, monitoring, options)
|
182
|
+
authenticate!(pending_connection)
|
183
|
+
rescue Exception
|
184
|
+
socket.close
|
185
|
+
raise
|
186
|
+
end
|
187
|
+
|
181
188
|
socket
|
182
189
|
end
|
183
190
|
private :do_connect
|
@@ -217,7 +217,7 @@ module Mongo
|
|
217
217
|
# @return [ Features ] features The features for the server.
|
218
218
|
def features
|
219
219
|
if unknown?
|
220
|
-
|
220
|
+
return Features.new(0..0, address.to_s)
|
221
221
|
end
|
222
222
|
@features
|
223
223
|
end
|
@@ -705,6 +705,30 @@ module Mongo
|
|
705
705
|
end
|
706
706
|
alias_method :eql?, :==
|
707
707
|
|
708
|
+
# @api private
|
709
|
+
def server_version_gte?(version)
|
710
|
+
required_wv = case version
|
711
|
+
when '4.2'
|
712
|
+
8
|
713
|
+
when '4.0'
|
714
|
+
7
|
715
|
+
when '3.6'
|
716
|
+
6
|
717
|
+
when '3.4'
|
718
|
+
5
|
719
|
+
when '3.2'
|
720
|
+
4
|
721
|
+
when '3.0'
|
722
|
+
3
|
723
|
+
when '2.6'
|
724
|
+
2
|
725
|
+
else
|
726
|
+
raise ArgumentError, "Bogus required version #{version}"
|
727
|
+
end
|
728
|
+
|
729
|
+
required_wv >= min_wire_version && required_wv <= max_wire_version
|
730
|
+
end
|
731
|
+
|
708
732
|
private
|
709
733
|
|
710
734
|
def compare_config(other)
|
@@ -74,5 +74,15 @@ module Mongo
|
|
74
74
|
Mongo::Lint.validate_underscore_read_preference(preference)
|
75
75
|
PREFERENCES.fetch((preference[:mode] || :primary).to_sym).new(preference)
|
76
76
|
end
|
77
|
+
|
78
|
+
# Returns the primary server selector.
|
79
|
+
#
|
80
|
+
# A call to this method is equivalent to `get(mode: :primary)`, except the
|
81
|
+
# resulting server selector object is cached and not recreated each time.
|
82
|
+
#
|
83
|
+
# @api private
|
84
|
+
def primary
|
85
|
+
@primary ||= get(mode: :primary)
|
86
|
+
end
|
77
87
|
end
|
78
88
|
end
|
@@ -92,17 +92,85 @@ module Mongo
|
|
92
92
|
"#<#{self.class.name}:0x#{object_id} tag_sets=#{tag_sets.inspect} max_staleness=#{max_staleness.inspect}>"
|
93
93
|
end
|
94
94
|
|
95
|
-
# Select a server from
|
96
|
-
#
|
97
|
-
#
|
98
|
-
#
|
99
|
-
#
|
100
|
-
#
|
95
|
+
# Select a server from the specified cluster, taking into account
|
96
|
+
# mongos pinning for the specified session.
|
97
|
+
#
|
98
|
+
# If the session is given and has a pinned server, this server is the
|
99
|
+
# only server considered for selection. If the server is of type mongos,
|
100
|
+
# it is returned immediately; otherwise monitoring checks on this
|
101
|
+
# server are initiated to update its status, and if the server becomes
|
102
|
+
# a mongos within the server selection timeout, it is returned.
|
103
|
+
#
|
104
|
+
# If no session is given or the session does not have a pinned server,
|
105
|
+
# normal server selection process is performed among all servers in the
|
106
|
+
# specified cluster matching the preference of this server selector
|
107
|
+
# object. Monitoring checks are initiated on servers in the cluster until
|
108
|
+
# a suitable server is found, up to the server selection timeout.
|
109
|
+
#
|
110
|
+
# If a suitable server is not found within the server selection timeout,
|
111
|
+
# this method raises Error::NoServerAvailable.
|
112
|
+
#
|
113
|
+
# @param [ Mongo::Cluster ] cluster The cluster from which to select
|
114
|
+
# an eligible server.
|
115
|
+
# @param [ true, false ] ping Whether to ping the server before selection.
|
116
|
+
# Deprecated and ignored.
|
117
|
+
# @param [ Session | nil ] session Optional session to take into account
|
118
|
+
# for mongos pinning. Added in version 2.10.0.
|
101
119
|
#
|
102
120
|
# @return [ Mongo::Server ] A server matching the server preference.
|
103
121
|
#
|
122
|
+
# @raise [ Error::NoServerAvailable ] No server was found matching the
|
123
|
+
# specified preference / pinning requirement in the server selection
|
124
|
+
# timeout.
|
125
|
+
# @raise [ Error::LintError ] An unexpected condition was detected, and
|
126
|
+
# lint mode is enabled.
|
127
|
+
#
|
104
128
|
# @since 2.0.0
|
105
|
-
def select_server(cluster, ping = nil)
|
129
|
+
def select_server(cluster, ping = nil, session = nil)
|
130
|
+
server_selection_timeout = cluster.options[:server_selection_timeout] || SERVER_SELECTION_TIMEOUT
|
131
|
+
|
132
|
+
# Special handling for zero timeout: if we have to select a server,
|
133
|
+
# and the timeout is zero, fail immediately (since server selection
|
134
|
+
# will take some non-zero amount of time in any case).
|
135
|
+
if server_selection_timeout == 0
|
136
|
+
msg = "Failing server selection due to zero timeout. " +
|
137
|
+
" Requested #{name} in cluster: #{cluster.summary}"
|
138
|
+
raise Error::NoServerAvailable.new(self, cluster, msg)
|
139
|
+
end
|
140
|
+
|
141
|
+
deadline = Time.now + server_selection_timeout
|
142
|
+
|
143
|
+
if session && session.pinned_server
|
144
|
+
if Mongo::Lint.enabled?
|
145
|
+
unless cluster.sharded?
|
146
|
+
raise Error::LintError, "Session has a pinned server in a non-sharded topology: #{topology}"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
if !session.in_transaction?
|
151
|
+
session.unpin
|
152
|
+
end
|
153
|
+
|
154
|
+
if server = session.pinned_server
|
155
|
+
# Here we assume that a mongos stays in the topology indefinitely.
|
156
|
+
# This will no longer be the case once SRV polling is implemented.
|
157
|
+
|
158
|
+
unless server.mongos?
|
159
|
+
while (time_remaining = deadline - Time.now) > 0
|
160
|
+
wait_for_server_selection(cluster, time_remaining)
|
161
|
+
end
|
162
|
+
|
163
|
+
unless server.mongos?
|
164
|
+
msg = "The session being used is pinned to the server which is not a mongos: #{server.summary} " +
|
165
|
+
"(after #{server_selection_timeout} seconds)"
|
166
|
+
raise Error::NoServerAvailable.new(self, cluster, msg)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
return server
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
106
174
|
if cluster.replica_set?
|
107
175
|
validate_max_staleness_value_early!
|
108
176
|
end
|
@@ -121,10 +189,7 @@ module Mongo
|
|
121
189
|
raise Error::NoServerAvailable.new(self, cluster, msg)
|
122
190
|
end
|
123
191
|
=end
|
124
|
-
|
125
|
-
@server_selection_timeout = cluster.options[:server_selection_timeout] || SERVER_SELECTION_TIMEOUT
|
126
|
-
deadline = Time.now + server_selection_timeout
|
127
|
-
while (time_remaining = deadline - Time.now) > 0
|
192
|
+
loop do
|
128
193
|
servers = candidates(cluster)
|
129
194
|
if Lint.enabled?
|
130
195
|
servers.each do |server|
|
@@ -152,36 +217,41 @@ module Mongo
|
|
152
217
|
raise Error::NoServerAvailable.new(self, cluster, msg)
|
153
218
|
end
|
154
219
|
|
220
|
+
if session && session.starting_transaction? && cluster.sharded?
|
221
|
+
session.pin(server)
|
222
|
+
end
|
223
|
+
|
155
224
|
return server
|
156
225
|
end
|
226
|
+
|
157
227
|
cluster.scan!(false)
|
158
|
-
|
159
|
-
|
228
|
+
|
229
|
+
time_remaining = deadline - Time.now
|
230
|
+
if time_remaining > 0
|
231
|
+
wait_for_server_selection(cluster, time_remaining)
|
232
|
+
|
233
|
+
# If we wait for server selection, perform another round of
|
234
|
+
# attempting to locate a suitable server. Otherwise server selection
|
235
|
+
# can raise NoServerAvailable message when the diagnostics
|
236
|
+
# reports an available server of the requested type.
|
160
237
|
else
|
161
|
-
|
162
|
-
raise Error::LintError, 'Waiting for server selection without having a server selection semaphore'
|
163
|
-
end
|
164
|
-
sleep 0.25
|
238
|
+
break
|
165
239
|
end
|
166
240
|
end
|
167
241
|
|
168
242
|
msg = "No #{name} server is available in cluster: #{cluster.summary} " +
|
169
243
|
"with timeout=#{server_selection_timeout}, " +
|
170
|
-
"LT=#{
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
end
|
177
|
-
end
|
178
|
-
if dead_monitors.any?
|
179
|
-
msg += ". The following servers have dead monitor threads: #{dead_monitors.map(&:summary).join(', ')}"
|
244
|
+
"LT=#{local_threshold_with_cluster(cluster)}"
|
245
|
+
msg += server_selection_diagnostic_message(cluster)
|
246
|
+
raise Error::NoServerAvailable.new(self, cluster, msg)
|
247
|
+
rescue Error::NoServerAvailable => e
|
248
|
+
if session && session.in_transaction? && !session.committing_transaction?
|
249
|
+
e.add_label('TransientTransactionError')
|
180
250
|
end
|
181
|
-
|
182
|
-
|
251
|
+
if session && session.committing_transaction?
|
252
|
+
e.add_label('UnknownTransactionCommitResult')
|
183
253
|
end
|
184
|
-
raise
|
254
|
+
raise e
|
185
255
|
end
|
186
256
|
|
187
257
|
# Get the timeout for server selection.
|
@@ -200,6 +270,10 @@ module Mongo
|
|
200
270
|
(options[:server_selection_timeout] || ServerSelector::SERVER_SELECTION_TIMEOUT)
|
201
271
|
end
|
202
272
|
|
273
|
+
def local_threshold_with_cluster(cluster)
|
274
|
+
options[:local_threshold] || cluster.options[:local_threshold] || LOCAL_THRESHOLD
|
275
|
+
end
|
276
|
+
|
203
277
|
# Get the local threshold boundary for nearest selection in seconds.
|
204
278
|
#
|
205
279
|
# @example Get the local threshold.
|
@@ -229,7 +303,10 @@ module Mongo
|
|
229
303
|
if cluster.single?
|
230
304
|
cluster.servers.each { |server| validate_max_staleness_support!(server) }
|
231
305
|
elsif cluster.sharded?
|
232
|
-
|
306
|
+
local_threshold = local_threshold_with_cluster(cluster)
|
307
|
+
near_servers(cluster.servers, local_threshold).each do |server|
|
308
|
+
validate_max_staleness_support!(server)
|
309
|
+
end
|
233
310
|
else
|
234
311
|
validate_max_staleness_value!(cluster) unless cluster.unknown?
|
235
312
|
select(cluster.servers)
|
@@ -276,13 +353,17 @@ module Mongo
|
|
276
353
|
#
|
277
354
|
# @param [ Array ] candidates List of candidate servers to select the
|
278
355
|
# near servers from.
|
356
|
+
# @param [ Integer ] local_threshold Local threshold. This parameter
|
357
|
+
# will be required in driver version 3.0.
|
279
358
|
#
|
280
359
|
# @return [ Array ] The near servers.
|
281
360
|
#
|
282
361
|
# @since 2.0.0
|
283
|
-
def near_servers(candidates = [])
|
362
|
+
def near_servers(candidates = [], local_threshold = nil)
|
284
363
|
return candidates if candidates.empty?
|
285
364
|
nearest_server = candidates.min_by(&:average_round_trip_time)
|
365
|
+
# Default for legacy signarure
|
366
|
+
local_threshold ||= self.local_threshold
|
286
367
|
threshold = nearest_server.average_round_trip_time + local_threshold
|
287
368
|
candidates.select { |server| server.average_round_trip_time <= threshold }.shuffle!
|
288
369
|
end
|
@@ -363,6 +444,65 @@ module Mongo
|
|
363
444
|
end
|
364
445
|
end
|
365
446
|
end
|
447
|
+
|
448
|
+
# Waits for server state changes in the specified cluster.
|
449
|
+
#
|
450
|
+
# If the cluster has a server selection semaphore, waits on that
|
451
|
+
# semaphore up to the specified remaining time. Any change in server
|
452
|
+
# state resulting from SDAM will immediately wake up this method and
|
453
|
+
# cause it to return.
|
454
|
+
#
|
455
|
+
# If the cluster des not have a server selection semaphore, waits
|
456
|
+
# the smaller of 0.25 seconds and the specified remaining time.
|
457
|
+
# This functionality is provided for backwards compatibilty only for
|
458
|
+
# applications directly invoking the server selection process.
|
459
|
+
# If lint mode is enabled and the cluster does not have a server
|
460
|
+
# selection semaphore, Error::LintError will be raised.
|
461
|
+
#
|
462
|
+
# @param [ Cluster ] cluster The cluster to wait for.
|
463
|
+
# @param [ Numeric ] time_remaining Maximum time to wait, in seconds.
|
464
|
+
def wait_for_server_selection(cluster, time_remaining)
|
465
|
+
if cluster.server_selection_semaphore
|
466
|
+
cluster.server_selection_semaphore.wait(time_remaining)
|
467
|
+
else
|
468
|
+
if Lint.enabled?
|
469
|
+
raise Error::LintError, 'Waiting for server selection without having a server selection semaphore'
|
470
|
+
end
|
471
|
+
sleep [time_remaining, 0.25].min
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
# Creates a diagnostic message when server selection fails.
|
476
|
+
#
|
477
|
+
# The diagnostic message includes the following information, as applicable:
|
478
|
+
#
|
479
|
+
# - Servers having dead monitor threads
|
480
|
+
# - Cluster is disconnected
|
481
|
+
#
|
482
|
+
# If none of the conditions for diagnostic messages apply, an empty string
|
483
|
+
# is returned.
|
484
|
+
#
|
485
|
+
# @param [ Cluster ] cluster The cluster on which server selection was
|
486
|
+
# performed.
|
487
|
+
#
|
488
|
+
# @return [ String ] The diagnostic message.
|
489
|
+
def server_selection_diagnostic_message(cluster)
|
490
|
+
msg = ''
|
491
|
+
dead_monitors = []
|
492
|
+
cluster.servers_list.each do |server|
|
493
|
+
thread = server.monitor.instance_variable_get('@thread')
|
494
|
+
if thread.nil? || !thread.alive?
|
495
|
+
dead_monitors << server
|
496
|
+
end
|
497
|
+
end
|
498
|
+
if dead_monitors.any?
|
499
|
+
msg += ". The following servers have dead monitor threads: #{dead_monitors.map(&:summary).join(', ')}"
|
500
|
+
end
|
501
|
+
unless cluster.connected?
|
502
|
+
msg += ". The cluster is disconnected (client may have been closed)"
|
503
|
+
end
|
504
|
+
msg
|
505
|
+
end
|
366
506
|
end
|
367
507
|
end
|
368
508
|
end
|