mongo 2.24.1 → 2.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/mongo_console +2 -2
- data/lib/mongo/address/validator.rb +1 -1
- data/lib/mongo/address.rb +1 -3
- data/lib/mongo/auth/aws/conversation.rb +0 -4
- data/lib/mongo/auth/aws/credentials_retriever.rb +3 -8
- data/lib/mongo/auth/base.rb +1 -1
- data/lib/mongo/auth/sasl_conversation_base.rb +1 -1
- data/lib/mongo/auth/scram_conversation_base.rb +1 -8
- data/lib/mongo/auth/stringprep.rb +1 -6
- data/lib/mongo/auth/user/view.rb +1 -1
- data/lib/mongo/bulk_write/result_combiner.rb +3 -3
- data/lib/mongo/bulk_write.rb +1 -1
- data/lib/mongo/client.rb +82 -12
- data/lib/mongo/cluster/reapers/cursor_reaper.rb +12 -5
- data/lib/mongo/cluster/sdam_flow.rb +4 -4
- data/lib/mongo/cluster/topology/base.rb +1 -1
- data/lib/mongo/cluster.rb +12 -28
- data/lib/mongo/collection/view/aggregation/behavior.rb +1 -1
- data/lib/mongo/collection/view/aggregation.rb +2 -4
- data/lib/mongo/collection/view/builder/aggregation.rb +3 -5
- data/lib/mongo/collection/view/builder/map_reduce.rb +1 -1
- data/lib/mongo/collection/view/change_stream.rb +14 -9
- data/lib/mongo/collection/view/map_reduce.rb +2 -11
- data/lib/mongo/collection/view/readable.rb +11 -7
- data/lib/mongo/collection/view/writable.rb +4 -4
- data/lib/mongo/collection/view.rb +8 -4
- data/lib/mongo/collection.rb +1 -1
- data/lib/mongo/crypt/auto_encrypter.rb +1 -1
- data/lib/mongo/crypt/binary.rb +7 -4
- data/lib/mongo/crypt/binding.rb +88 -10
- data/lib/mongo/crypt/context.rb +50 -19
- data/lib/mongo/crypt/encryption_io.rb +11 -5
- data/lib/mongo/crypt/handle.rb +17 -5
- data/lib/mongo/crypt/kms/credentials.rb +70 -31
- data/lib/mongo/crypt/kms/kmip/master_document.rb +30 -1
- data/lib/mongo/crypt/kms/master_key_document.rb +11 -6
- data/lib/mongo/crypt/kms.rb +12 -1
- data/lib/mongo/cursor.rb +8 -2
- data/lib/mongo/database/cursor_command_view.rb +95 -0
- data/lib/mongo/database.rb +144 -18
- data/lib/mongo/error/invalid_uri.rb +5 -1
- data/lib/mongo/error/operation_failure.rb +2 -2
- data/lib/mongo/error/parser.rb +3 -3
- data/lib/mongo/error/sdam_error_detection.rb +2 -3
- data/lib/mongo/grid/fs_bucket.rb +27 -21
- data/lib/mongo/grid/stream/write.rb +2 -2
- data/lib/mongo/index/view.rb +13 -7
- data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_check_out_started.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_checked_in.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_checked_out.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_closed.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_created.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/connection_ready.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/pool_cleared.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/pool_closed.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/pool_created.rb +1 -0
- data/lib/mongo/monitoring/event/cmap/pool_ready.rb +1 -0
- data/lib/mongo/monitoring/event/command_failed.rb +1 -0
- data/lib/mongo/monitoring/event/command_started.rb +1 -0
- data/lib/mongo/monitoring/event/command_succeeded.rb +1 -0
- data/lib/mongo/monitoring/event/secure.rb +12 -17
- data/lib/mongo/monitoring/event/server_closed.rb +1 -0
- data/lib/mongo/monitoring/event/server_description_changed.rb +1 -0
- data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +1 -0
- data/lib/mongo/monitoring/event/server_heartbeat_started.rb +1 -0
- data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +1 -0
- data/lib/mongo/monitoring/event/server_opening.rb +1 -0
- data/lib/mongo/monitoring/event/topology_changed.rb +1 -0
- data/lib/mongo/monitoring/event/topology_closed.rb +1 -0
- data/lib/mongo/monitoring/event/topology_opening.rb +1 -0
- data/lib/mongo/operation/create/op_msg.rb +1 -0
- data/lib/mongo/operation/create_index/op_msg.rb +2 -1
- data/lib/mongo/operation/cursor_command/op_msg.rb +37 -0
- data/lib/mongo/operation/cursor_command/result.rb +60 -0
- data/lib/mongo/operation/cursor_command.rb +33 -0
- data/lib/mongo/operation/delete/op_msg.rb +1 -0
- data/lib/mongo/operation/drop/op_msg.rb +1 -0
- data/lib/mongo/operation/drop_database/op_msg.rb +1 -0
- data/lib/mongo/operation/drop_index/op_msg.rb +1 -0
- data/lib/mongo/operation/find/builder/command.rb +1 -1
- data/lib/mongo/operation/insert/bulk_result.rb +2 -4
- data/lib/mongo/operation/insert/op_msg.rb +1 -0
- data/lib/mongo/operation/result.rb +12 -25
- data/lib/mongo/operation/shared/causal_consistency_supported.rb +4 -2
- data/lib/mongo/operation/shared/executable.rb +1 -9
- data/lib/mongo/operation/shared/result/aggregatable.rb +2 -2
- data/lib/mongo/operation/shared/sessions_supported.rb +3 -5
- data/lib/mongo/operation/update/bulk_result.rb +1 -1
- data/lib/mongo/operation/update/op_msg.rb +1 -0
- data/lib/mongo/operation/write_command/op_msg.rb +2 -0
- data/lib/mongo/operation.rb +1 -0
- data/lib/mongo/options/redacted.rb +2 -2
- data/lib/mongo/protocol/compressed.rb +15 -11
- data/lib/mongo/protocol/message.rb +16 -16
- data/lib/mongo/protocol/registry.rb +1 -1
- data/lib/mongo/protocol/serializers.rb +1 -3
- data/lib/mongo/query_cache.rb +3 -5
- data/lib/mongo/server/connection.rb +3 -1
- data/lib/mongo/server/connection_common.rb +9 -1
- data/lib/mongo/server/connection_pool/generation_manager.rb +1 -1
- data/lib/mongo/server/connection_pool.rb +24 -10
- data/lib/mongo/server/description/features.rb +1 -1
- data/lib/mongo/server/description.rb +1 -1
- data/lib/mongo/server/monitor/connection.rb +3 -2
- data/lib/mongo/server/monitor.rb +133 -30
- data/lib/mongo/server/pending_connection.rb +1 -0
- data/lib/mongo/server/push_monitor.rb +16 -2
- data/lib/mongo/server.rb +27 -7
- data/lib/mongo/server_selector/base.rb +3 -6
- data/lib/mongo/server_selector/secondary_preferred.rb +4 -1
- data/lib/mongo/session.rb +22 -23
- data/lib/mongo/socket/ssl.rb +3 -3
- data/lib/mongo/socket.rb +1 -7
- data/lib/mongo/srv/monitor.rb +11 -2
- data/lib/mongo/srv/resolver.rb +1 -1
- data/lib/mongo/srv/result.rb +5 -3
- data/lib/mongo/timeout.rb +10 -15
- data/lib/mongo/tracing/open_telemetry/command_tracer.rb +16 -2
- data/lib/mongo/uri/options_mapper.rb +21 -9
- data/lib/mongo/uri/srv_protocol.rb +4 -4
- data/lib/mongo/uri.rb +56 -8
- data/lib/mongo/utils.rb +1 -1
- data/lib/mongo/version.rb +1 -1
- metadata +6 -4
- data/lib/mongo/auth/stringprep/unicode_normalize/normalize.rb +0 -173
- data/lib/mongo/auth/stringprep/unicode_normalize/tables.rb +0 -1164
|
@@ -22,23 +22,28 @@ module Mongo
|
|
|
22
22
|
#
|
|
23
23
|
# @api private
|
|
24
24
|
class MasterKeyDocument
|
|
25
|
-
# Known KMS provider
|
|
25
|
+
# Known KMS provider types.
|
|
26
26
|
KMS_PROVIDERS = %w[aws azure gcp kmip local].freeze
|
|
27
27
|
|
|
28
|
-
# Creates a master key document object
|
|
28
|
+
# Creates a master key document object from a parameters hash.
|
|
29
29
|
#
|
|
30
|
-
# @param [ String ] kms_provider
|
|
30
|
+
# @param [ String ] kms_provider KMS provider identifier. May be a
|
|
31
|
+
# provider type (e.g. "aws") or a named provider (e.g. "aws:name1").
|
|
31
32
|
# @param [ Hash ] options A hash that contains master key options for
|
|
32
33
|
# the KMS provider.
|
|
33
34
|
# Required parameters for KMS providers are described in corresponding
|
|
34
35
|
# classes inside Mongo::Crypt::KMS module.
|
|
35
36
|
#
|
|
36
|
-
# @raise [ ArgumentError ] If required options are missing or incorrectly
|
|
37
|
+
# @raise [ ArgumentError ] If required options are missing or incorrectly
|
|
38
|
+
# formatted.
|
|
37
39
|
def initialize(kms_provider, options)
|
|
38
40
|
raise ArgumentError.new('Key document options must not be nil') if options.nil?
|
|
39
41
|
|
|
42
|
+
@provider = kms_provider.to_s
|
|
43
|
+
provider_type = KMS.provider_base_type(@provider)
|
|
44
|
+
|
|
40
45
|
master_key = options.fetch(:master_key, {})
|
|
41
|
-
@key_document = case
|
|
46
|
+
@key_document = case provider_type
|
|
42
47
|
when 'aws' then KMS::AWS::MasterKeyDocument.new(master_key)
|
|
43
48
|
when 'azure' then KMS::Azure::MasterKeyDocument.new(master_key)
|
|
44
49
|
when 'gcp' then KMS::GCP::MasterKeyDocument.new(master_key)
|
|
@@ -53,7 +58,7 @@ module Mongo
|
|
|
53
58
|
#
|
|
54
59
|
# @return [ BSON::Document ] Master key document as BSON document.
|
|
55
60
|
def to_document
|
|
56
|
-
@key_document.to_document
|
|
61
|
+
@key_document.to_document.merge(provider: @provider)
|
|
57
62
|
end
|
|
58
63
|
end
|
|
59
64
|
end
|
data/lib/mongo/crypt/kms.rb
CHANGED
|
@@ -16,13 +16,24 @@
|
|
|
16
16
|
|
|
17
17
|
module Mongo
|
|
18
18
|
module Crypt
|
|
19
|
-
module KMS
|
|
19
|
+
module KMS # rubocop:disable Style/Documentation
|
|
20
20
|
# This error indicates that we could not obtain credential for
|
|
21
21
|
# a KMS service.
|
|
22
22
|
#
|
|
23
23
|
# @api private
|
|
24
24
|
class CredentialsNotFound < RuntimeError; end
|
|
25
25
|
|
|
26
|
+
# Returns the base provider type from a KMS provider identifier.
|
|
27
|
+
# For example, "aws:name1" returns "aws" and :aws returns "aws".
|
|
28
|
+
#
|
|
29
|
+
# @param [ String | Symbol ] identifier The KMS provider identifier.
|
|
30
|
+
# @return [ String ] The base provider type.
|
|
31
|
+
#
|
|
32
|
+
# @api private
|
|
33
|
+
def self.provider_base_type(identifier)
|
|
34
|
+
identifier.to_s.split(':').first
|
|
35
|
+
end
|
|
36
|
+
|
|
26
37
|
# This module contains helper methods for validating KMS parameters.
|
|
27
38
|
#
|
|
28
39
|
# @api private
|
data/lib/mongo/cursor.rb
CHANGED
|
@@ -382,7 +382,7 @@ module Mongo
|
|
|
382
382
|
with_overload_retry(context: possibly_refreshed_context) do
|
|
383
383
|
process(execute_operation(get_more_operation))
|
|
384
384
|
end
|
|
385
|
-
rescue Error::SocketError, Error::SocketTimeoutError
|
|
385
|
+
rescue Error::SocketError, Error::SocketTimeoutError, Error::ConnectionPerished
|
|
386
386
|
@get_more_network_error = true
|
|
387
387
|
raise
|
|
388
388
|
rescue Error::OperationFailure => e
|
|
@@ -463,6 +463,12 @@ module Mongo
|
|
|
463
463
|
if view.respond_to?(:options) && view.options.is_a?(Hash) && !view.options[:comment].nil?
|
|
464
464
|
spec[:comment] = view.options[:comment]
|
|
465
465
|
end
|
|
466
|
+
# A cursor built from a runCursorCommand response carries a getMore-specific
|
|
467
|
+
# maxTimeMS that is sent on getMore commands. Regular find/aggregate views
|
|
468
|
+
# do not expose this, so their getMores are unaffected.
|
|
469
|
+
if view.respond_to?(:max_time_ms_for_get_more) && view.max_time_ms_for_get_more
|
|
470
|
+
spec[:max_time_ms] = view.max_time_ms_for_get_more
|
|
471
|
+
end
|
|
466
472
|
Operation::GetMore.new(spec)
|
|
467
473
|
end
|
|
468
474
|
|
|
@@ -501,7 +507,7 @@ module Mongo
|
|
|
501
507
|
end
|
|
502
508
|
|
|
503
509
|
def limit
|
|
504
|
-
@view.
|
|
510
|
+
@view.limit
|
|
505
511
|
end
|
|
506
512
|
|
|
507
513
|
def register
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (C) 2025 MongoDB Inc.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
module Mongo
|
|
18
|
+
class Database
|
|
19
|
+
# The minimal view a Cursor needs when it is built from an arbitrary
|
|
20
|
+
# command response rather than from a collection query.
|
|
21
|
+
#
|
|
22
|
+
# It carries the getMore-specific options (batchSize, maxTimeMS, comment)
|
|
23
|
+
# and the cursor type and timeout mode, and answers the few methods the
|
|
24
|
+
# Cursor reads from its view.
|
|
25
|
+
#
|
|
26
|
+
# @api private
|
|
27
|
+
class CursorCommandView
|
|
28
|
+
# @param [ Mongo::Database ] database The database the command ran on.
|
|
29
|
+
# @param [ Hash ] options The getMore and timeout options.
|
|
30
|
+
#
|
|
31
|
+
# @option options [ Integer ] :batch_size The batchSize for getMores.
|
|
32
|
+
# @option options [ Integer ] :max_time_ms The maxTimeMS for getMores.
|
|
33
|
+
# @option options [ Object ] :comment The comment for getMores.
|
|
34
|
+
# @option options [ Symbol ] :cursor_type :tailable or :tailable_await.
|
|
35
|
+
# @option options [ Symbol ] :timeout_mode :cursor_lifetime or :iteration.
|
|
36
|
+
def initialize(database, options = {})
|
|
37
|
+
@database = database
|
|
38
|
+
@options = options
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @return [ Mongo::Database ] The database the command ran on.
|
|
42
|
+
attr_reader :database
|
|
43
|
+
|
|
44
|
+
# @return [ Hash ] The view options. Used by the Cursor to read the
|
|
45
|
+
# getMore comment.
|
|
46
|
+
attr_reader :options
|
|
47
|
+
|
|
48
|
+
# @return [ Mongo::Client ] The client.
|
|
49
|
+
def client
|
|
50
|
+
database.client
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# A placeholder collection used only so the Cursor can reach the client
|
|
54
|
+
# and database. The actual namespace for getMore and killCursors is taken
|
|
55
|
+
# from the command response, not from this collection.
|
|
56
|
+
#
|
|
57
|
+
# @return [ Mongo::Collection ] The $cmd pseudo collection.
|
|
58
|
+
def collection
|
|
59
|
+
@collection ||= Collection.new(database, '$cmd')
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @return [ Integer | nil ] The batchSize sent on getMore commands.
|
|
63
|
+
def batch_size
|
|
64
|
+
options[:batch_size]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @return [ Integer | nil ] The maxTimeMS sent on getMore commands.
|
|
68
|
+
def max_time_ms_for_get_more
|
|
69
|
+
options[:max_time_ms]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# @return [ Symbol | nil ] The cursor type.
|
|
73
|
+
def cursor_type
|
|
74
|
+
options[:cursor_type]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# @return [ Symbol | nil ] The timeout mode.
|
|
78
|
+
def timeout_mode
|
|
79
|
+
options[:timeout_mode]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# @return [ Hash ] timeout values for the operation context.
|
|
83
|
+
def operation_timeouts(opts = {})
|
|
84
|
+
database.operation_timeouts(opts)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Cursors do not support a limit when built from a command response.
|
|
88
|
+
#
|
|
89
|
+
# @api private
|
|
90
|
+
def limit
|
|
91
|
+
nil
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
data/lib/mongo/database.rb
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
# limitations under the License.
|
|
16
16
|
|
|
17
17
|
require 'mongo/database/view'
|
|
18
|
+
require 'mongo/database/cursor_command_view'
|
|
18
19
|
|
|
19
20
|
module Mongo
|
|
20
21
|
# Represents a database on the db server and operations that can execute on
|
|
@@ -259,6 +260,119 @@ module Mongo
|
|
|
259
260
|
end
|
|
260
261
|
end
|
|
261
262
|
|
|
263
|
+
# Run a command that returns a cursor and parse the response as a cursor.
|
|
264
|
+
#
|
|
265
|
+
# The command is sent to the server unmodified; the driver MUST NOT inspect
|
|
266
|
+
# or alter it. If the response does not contain a cursor field an error is
|
|
267
|
+
# raised. The command is never retried.
|
|
268
|
+
#
|
|
269
|
+
# Note: if a +maxTimeMS+ field is already set on the command document it is
|
|
270
|
+
# left as-is. The +max_time_ms+ option below applies only to getMore
|
|
271
|
+
# commands. Setting both +timeout_ms+ and +max_time_ms+ is not supported
|
|
272
|
+
# and has undefined behavior.
|
|
273
|
+
#
|
|
274
|
+
# @example Run a cursor-returning command.
|
|
275
|
+
# database.cursor_command(checkMetadataConsistency: 1)
|
|
276
|
+
#
|
|
277
|
+
# @param [ Hash ] command The command to execute.
|
|
278
|
+
# @param [ Hash ] options The command options.
|
|
279
|
+
#
|
|
280
|
+
# @option options [ Hash ] :read The read preference for this command,
|
|
281
|
+
# used for server selection and reused for subsequent getMores.
|
|
282
|
+
# @option options [ Session ] :session The session to use. If none is
|
|
283
|
+
# given an implicit session is created and reused for the cursor's
|
|
284
|
+
# lifetime.
|
|
285
|
+
# @option options [ Integer ] :timeout_ms The operation timeout in
|
|
286
|
+
# milliseconds.
|
|
287
|
+
# @option options [ Integer ] :batch_size The batchSize to send on getMore
|
|
288
|
+
# commands.
|
|
289
|
+
# @option options [ Integer ] :max_time_ms The maxTimeMS to send on getMore
|
|
290
|
+
# commands.
|
|
291
|
+
# @option options [ Object ] :comment A comment to attach to getMore
|
|
292
|
+
# commands.
|
|
293
|
+
# @option options [ Symbol ] :cursor_type The cursor type, :tailable or
|
|
294
|
+
# :tailable_await. Must match the flags set on the command document.
|
|
295
|
+
# @option options [ Symbol ] :timeout_mode :cursor_lifetime or :iteration.
|
|
296
|
+
#
|
|
297
|
+
# @return [ Mongo::Cursor ] A cursor over the command results.
|
|
298
|
+
#
|
|
299
|
+
# @raise [ Error::InvalidCursorOperation ] If the response does not contain
|
|
300
|
+
# a cursor.
|
|
301
|
+
def cursor_command(command, options = {})
|
|
302
|
+
options = options.dup
|
|
303
|
+
execution_opts = options.delete(:execution_options) || {}
|
|
304
|
+
view_options = extract_cursor_command_view_options(options)
|
|
305
|
+
|
|
306
|
+
txn_read_pref = (options[:session].txn_read_preference if options[:session] && options[:session].in_transaction?)
|
|
307
|
+
txn_read_pref ||= options[:read] || ServerSelector::PRIMARY
|
|
308
|
+
Lint.validate_underscore_read_preference(txn_read_pref)
|
|
309
|
+
selector = ServerSelector.get(txn_read_pref)
|
|
310
|
+
|
|
311
|
+
# The session is intentionally not wrapped in #with_session: an implicit
|
|
312
|
+
# session must outlive this method and is ended by the cursor when it is
|
|
313
|
+
# exhausted or closed. Until the cursor takes ownership, the session and
|
|
314
|
+
# any load-balanced connection are cleaned up here on every exit path.
|
|
315
|
+
session = client.get_session(options)
|
|
316
|
+
context = Operation::Context.new(
|
|
317
|
+
client: client,
|
|
318
|
+
session: session,
|
|
319
|
+
operation_timeouts: operation_timeouts(options)
|
|
320
|
+
)
|
|
321
|
+
op = Operation::CursorCommand.new(
|
|
322
|
+
selector: command,
|
|
323
|
+
db_name: name,
|
|
324
|
+
read: selector,
|
|
325
|
+
session: session
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
# Per the client-backpressure spec, retrying a generic command on
|
|
329
|
+
# overload errors requires both retryable reads and writes to be
|
|
330
|
+
# enabled, same as Database#command.
|
|
331
|
+
retry_enabled = client.options[:retry_reads] != false &&
|
|
332
|
+
client.options[:retry_writes] != false
|
|
333
|
+
|
|
334
|
+
server = nil
|
|
335
|
+
connection = nil
|
|
336
|
+
cursor = nil
|
|
337
|
+
begin
|
|
338
|
+
result = with_overload_retry(context: context, retry_enabled: retry_enabled) do
|
|
339
|
+
server = selector.select_server(cluster, nil, session)
|
|
340
|
+
if server.load_balancer?
|
|
341
|
+
# The connection is checked in by the cursor when it is drained.
|
|
342
|
+
connection = check_out_cursor_command_connection(server, context)
|
|
343
|
+
begin
|
|
344
|
+
op.execute_with_connection(connection, context: context, options: execution_opts)
|
|
345
|
+
rescue StandardError
|
|
346
|
+
# Release the connection before the error propagates so that
|
|
347
|
+
# a retried attempt checks out a fresh one.
|
|
348
|
+
connection.connection_pool.check_in(connection) unless connection.pinned?
|
|
349
|
+
connection = nil
|
|
350
|
+
raise
|
|
351
|
+
end
|
|
352
|
+
else
|
|
353
|
+
op.execute(server, context: context, options: execution_opts)
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
unless result.cursor?
|
|
358
|
+
raise Error::InvalidCursorOperation,
|
|
359
|
+
'The command response did not include a cursor. ' \
|
|
360
|
+
'Use Database#command for commands that do not return a cursor.'
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
view = CursorCommandView.new(self, view_options)
|
|
364
|
+
cursor = Cursor.new(view, result, server, session: session, context: context)
|
|
365
|
+
ensure
|
|
366
|
+
# If the cursor was created it owns the session and connection;
|
|
367
|
+
# otherwise (error or no cursor in the response) release them here.
|
|
368
|
+
unless cursor
|
|
369
|
+
connection.connection_pool.check_in(connection) if connection && !connection.pinned?
|
|
370
|
+
session.end_session if session && session.implicit?
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
cursor
|
|
374
|
+
end
|
|
375
|
+
|
|
262
376
|
# Execute a read command on the database, retrying the read if necessary.
|
|
263
377
|
#
|
|
264
378
|
# @param [ Hash ] operation The command to execute.
|
|
@@ -530,24 +644,6 @@ module Mongo
|
|
|
530
644
|
)
|
|
531
645
|
end
|
|
532
646
|
|
|
533
|
-
# Create a database for the provided client, for use when we don't want the
|
|
534
|
-
# client's original database instance to be the same.
|
|
535
|
-
#
|
|
536
|
-
# @api private
|
|
537
|
-
#
|
|
538
|
-
# @example Create a database for the client.
|
|
539
|
-
# Database.create(client)
|
|
540
|
-
#
|
|
541
|
-
# @param [ Client ] client The client to create on.
|
|
542
|
-
#
|
|
543
|
-
# @return [ Database ] The database.
|
|
544
|
-
#
|
|
545
|
-
# @since 2.0.0
|
|
546
|
-
def self.create(client)
|
|
547
|
-
database = Database.new(client, client.options[:database], client.options)
|
|
548
|
-
client.instance_variable_set(:@database, database)
|
|
549
|
-
end
|
|
550
|
-
|
|
551
647
|
# @return [ Integer | nil ] Operation timeout that is for this database or
|
|
552
648
|
# for the corresponding client.
|
|
553
649
|
#
|
|
@@ -570,5 +666,35 @@ module Mongo
|
|
|
570
666
|
end
|
|
571
667
|
end
|
|
572
668
|
end
|
|
669
|
+
|
|
670
|
+
private
|
|
671
|
+
|
|
672
|
+
# Removes the getMore and cursor options from the options hash and returns
|
|
673
|
+
# them as a separate hash for the CursorCommandView. The remaining options
|
|
674
|
+
# (e.g. :session, :read, :timeout_ms) are left for command execution.
|
|
675
|
+
#
|
|
676
|
+
# @param [ Hash ] options The cursor_command options (mutated).
|
|
677
|
+
#
|
|
678
|
+
# @return [ Hash ] The view options.
|
|
679
|
+
def extract_cursor_command_view_options(options)
|
|
680
|
+
%i[ batch_size max_time_ms comment cursor_type timeout_mode ].each_with_object({}) do |key, view_options|
|
|
681
|
+
view_options[key] = options.delete(key) if options.key?(key)
|
|
682
|
+
end
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
# Checks out a load balanced connection for a cursor command. If the
|
|
686
|
+
# session is pinned to a connection (e.g. in a transaction), that
|
|
687
|
+
# connection is reused.
|
|
688
|
+
#
|
|
689
|
+
# @param [ Server ] server The load balancer server.
|
|
690
|
+
# @param [ Operation::Context ] context The operation context.
|
|
691
|
+
#
|
|
692
|
+
# @return [ Server::Connection ] The checked out connection.
|
|
693
|
+
def check_out_cursor_command_connection(server, context)
|
|
694
|
+
connection = if context.connection_global_id
|
|
695
|
+
server.pool.check_out_pinned_connection(context.connection_global_id)
|
|
696
|
+
end
|
|
697
|
+
connection || server.pool.check_out(context: context)
|
|
698
|
+
end
|
|
573
699
|
end
|
|
574
700
|
end
|
|
@@ -23,12 +23,16 @@ module Mongo
|
|
|
23
23
|
class InvalidURI < Error
|
|
24
24
|
# Instantiate the new exception.
|
|
25
25
|
#
|
|
26
|
+
# The URI is redacted via {Mongo::URI.redact} before being interpolated
|
|
27
|
+
# so that any cleartext credentials in the original input do not end up
|
|
28
|
+
# in logs, error reporters, or backtraces.
|
|
29
|
+
#
|
|
26
30
|
# @example Instantiate the exception.
|
|
27
31
|
# Mongo::Error::InvalidURI.new(uri, details, format)
|
|
28
32
|
#
|
|
29
33
|
# @since 2.0.0
|
|
30
34
|
def initialize(uri, details, format = nil)
|
|
31
|
-
message = "Bad URI: #{uri}\n" +
|
|
35
|
+
message = "Bad URI: #{Mongo::URI.redact(uri)}\n" +
|
|
32
36
|
"#{details}\n"
|
|
33
37
|
message += "MongoDB URI must be in the following format: #{format}\n" if format
|
|
34
38
|
message += "Please see the following URL for more information: #{Mongo::URI::HELP}\n"
|
|
@@ -233,9 +233,9 @@ module Mongo
|
|
|
233
233
|
def retrieve_details(document)
|
|
234
234
|
return nil unless document
|
|
235
235
|
|
|
236
|
-
if wce = document['writeConcernError']
|
|
236
|
+
if (wce = document['writeConcernError'])
|
|
237
237
|
wce['errInfo']
|
|
238
|
-
elsif we = document['writeErrors']&.first
|
|
238
|
+
elsif (we = document['writeErrors']&.first)
|
|
239
239
|
we['errInfo']
|
|
240
240
|
end
|
|
241
241
|
end
|
data/lib/mongo/error/parser.rb
CHANGED
|
@@ -180,13 +180,13 @@ module Mongo
|
|
|
180
180
|
end
|
|
181
181
|
|
|
182
182
|
def parse_single(message, key, doc = document)
|
|
183
|
-
return unless error = doc[key]
|
|
183
|
+
return unless (error = doc[key])
|
|
184
184
|
|
|
185
185
|
append(message, error)
|
|
186
186
|
end
|
|
187
187
|
|
|
188
188
|
def parse_multiple(message, key)
|
|
189
|
-
return unless errors = document[key]
|
|
189
|
+
return unless (errors = document[key])
|
|
190
190
|
|
|
191
191
|
errors.each do |error|
|
|
192
192
|
parse_single(message, 'errmsg', error)
|
|
@@ -228,7 +228,7 @@ module Mongo
|
|
|
228
228
|
return unless @code.nil? && @code_name.nil?
|
|
229
229
|
# If we have writeErrors, and all of their codes are the same,
|
|
230
230
|
# use that code. Otherwise don't set the code
|
|
231
|
-
return unless write_errors = document['writeErrors']
|
|
231
|
+
return unless (write_errors = document['writeErrors'])
|
|
232
232
|
|
|
233
233
|
codes = write_errors.map { |e| e['code'] }.compact
|
|
234
234
|
return unless codes.uniq.length == 1
|
|
@@ -29,10 +29,9 @@ module Mongo
|
|
|
29
29
|
# Require the error to be communicated at the top level of the response
|
|
30
30
|
# for it to influence SDAM state. See DRIVERS-1376 / RUBY-2516.
|
|
31
31
|
return false if document && document['ok'] == 1
|
|
32
|
+
return false if node_recovering?
|
|
32
33
|
|
|
33
|
-
if
|
|
34
|
-
false
|
|
35
|
-
elsif code
|
|
34
|
+
if code
|
|
36
35
|
NOT_MASTER_CODES.include?(code)
|
|
37
36
|
elsif message
|
|
38
37
|
message.include?('not master')
|
data/lib/mongo/grid/fs_bucket.rb
CHANGED
|
@@ -480,7 +480,7 @@ module Mongo
|
|
|
480
480
|
#
|
|
481
481
|
# @since 2.1.0
|
|
482
482
|
def write_concern
|
|
483
|
-
@write_concern ||= if wco = @options[:write_concern] || @options[:write]
|
|
483
|
+
@write_concern ||= if (wco = @options[:write_concern] || @options[:write])
|
|
484
484
|
WriteConcern.get(wco)
|
|
485
485
|
else
|
|
486
486
|
database.write_concern
|
|
@@ -494,6 +494,32 @@ module Mongo
|
|
|
494
494
|
chunks_collection.drop(timeout_ms: context.remaining_timeout_ms)
|
|
495
495
|
end
|
|
496
496
|
|
|
497
|
+
# Ensures the files and chunks collections have their required indexes,
|
|
498
|
+
# creating any that are missing.
|
|
499
|
+
#
|
|
500
|
+
# @param [ CsotTimeoutHolder | nil ] timeout_holder The timeout holder.
|
|
501
|
+
#
|
|
502
|
+
# @api private
|
|
503
|
+
def ensure_indexes!(timeout_holder = nil)
|
|
504
|
+
fc_idx = files_collection.find(
|
|
505
|
+
{},
|
|
506
|
+
limit: 1,
|
|
507
|
+
projection: { _id: 1 },
|
|
508
|
+
timeout_ms: timeout_holder&.remaining_timeout_ms
|
|
509
|
+
).first
|
|
510
|
+
create_index_if_missing!(files_collection, FSBucket::FILES_INDEX) if fc_idx.nil?
|
|
511
|
+
|
|
512
|
+
cc_idx = chunks_collection.find(
|
|
513
|
+
{},
|
|
514
|
+
limit: 1,
|
|
515
|
+
projection: { _id: 1 },
|
|
516
|
+
timeout_ms: timeout_holder&.remaining_timeout_ms
|
|
517
|
+
).first
|
|
518
|
+
return unless cc_idx.nil?
|
|
519
|
+
|
|
520
|
+
create_index_if_missing!(chunks_collection, FSBucket::CHUNKS_INDEX, unique: true)
|
|
521
|
+
end
|
|
522
|
+
|
|
497
523
|
private
|
|
498
524
|
|
|
499
525
|
# @param [ Hash ] opts The options.
|
|
@@ -516,26 +542,6 @@ module Mongo
|
|
|
516
542
|
"#{prefix}.#{Grid::File::Info::COLLECTION}"
|
|
517
543
|
end
|
|
518
544
|
|
|
519
|
-
def ensure_indexes!(timeout_holder = nil)
|
|
520
|
-
fc_idx = files_collection.find(
|
|
521
|
-
{},
|
|
522
|
-
limit: 1,
|
|
523
|
-
projection: { _id: 1 },
|
|
524
|
-
timeout_ms: timeout_holder&.remaining_timeout_ms
|
|
525
|
-
).first
|
|
526
|
-
create_index_if_missing!(files_collection, FSBucket::FILES_INDEX) if fc_idx.nil?
|
|
527
|
-
|
|
528
|
-
cc_idx = chunks_collection.find(
|
|
529
|
-
{},
|
|
530
|
-
limit: 1,
|
|
531
|
-
projection: { _id: 1 },
|
|
532
|
-
timeout_ms: timeout_holder&.remaining_timeout_ms
|
|
533
|
-
).first
|
|
534
|
-
return unless cc_idx.nil?
|
|
535
|
-
|
|
536
|
-
create_index_if_missing!(chunks_collection, FSBucket::CHUNKS_INDEX, unique: true)
|
|
537
|
-
end
|
|
538
|
-
|
|
539
545
|
def create_index_if_missing!(collection, index_spec, options = {})
|
|
540
546
|
indexes_view = collection.indexes
|
|
541
547
|
begin
|
|
@@ -148,7 +148,7 @@ module Mongo
|
|
|
148
148
|
#
|
|
149
149
|
# @since 2.1.0
|
|
150
150
|
def write_concern
|
|
151
|
-
@write_concern ||= if wco = @options[:write_concern] || @options[:write]
|
|
151
|
+
@write_concern ||= if (wco = @options[:write_concern] || @options[:write])
|
|
152
152
|
WriteConcern.get(wco)
|
|
153
153
|
else
|
|
154
154
|
fs.write_concern
|
|
@@ -212,7 +212,7 @@ module Mongo
|
|
|
212
212
|
end
|
|
213
213
|
|
|
214
214
|
def ensure_indexes!
|
|
215
|
-
fs.
|
|
215
|
+
fs.ensure_indexes!(@timeout_holder)
|
|
216
216
|
end
|
|
217
217
|
|
|
218
218
|
def ensure_open!
|
data/lib/mongo/index/view.rb
CHANGED
|
@@ -168,11 +168,11 @@ module Mongo
|
|
|
168
168
|
options = options.dup
|
|
169
169
|
|
|
170
170
|
create_options = {}
|
|
171
|
-
if session = @options[:session]
|
|
171
|
+
if (session = @options[:session])
|
|
172
172
|
create_options[:session] = session
|
|
173
173
|
end
|
|
174
174
|
%i[commit_quorum session comment timeout_ms max_time_ms].each do |key|
|
|
175
|
-
if value = options.delete(key)
|
|
175
|
+
if (value = options.delete(key))
|
|
176
176
|
create_options[key] = value
|
|
177
177
|
end
|
|
178
178
|
end
|
|
@@ -347,10 +347,20 @@ module Mongo
|
|
|
347
347
|
end
|
|
348
348
|
end
|
|
349
349
|
|
|
350
|
+
# The query limit for an index view. Always -1; present so a Cursor
|
|
351
|
+
# can treat an index view like a collection view.
|
|
352
|
+
#
|
|
353
|
+
# @api private
|
|
354
|
+
def limit
|
|
355
|
+
-1
|
|
356
|
+
end
|
|
357
|
+
|
|
350
358
|
private
|
|
351
359
|
|
|
352
360
|
def drop_by_name(name, opts = {})
|
|
353
|
-
|
|
361
|
+
session_options = @options.dup
|
|
362
|
+
session_options[:session] = opts[:session] if opts[:session]
|
|
363
|
+
client.with_session(session_options) do |session|
|
|
354
364
|
spec = {
|
|
355
365
|
db_name: database.name,
|
|
356
366
|
coll_name: collection.name,
|
|
@@ -394,10 +404,6 @@ module Mongo
|
|
|
394
404
|
Operation::Indexes.new(indexes_spec(session))
|
|
395
405
|
end
|
|
396
406
|
|
|
397
|
-
def limit
|
|
398
|
-
-1
|
|
399
|
-
end
|
|
400
|
-
|
|
401
407
|
def normalize_keys(spec)
|
|
402
408
|
return false if spec.is_a?(String)
|
|
403
409
|
|