mongo 2.20.0 → 2.20.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +37 -1
- data/Rakefile +57 -21
- data/lib/mongo/config.rb +2 -2
- data/lib/mongo/retryable/base_worker.rb +28 -3
- data/lib/mongo/retryable/read_worker.rb +9 -8
- data/lib/mongo/retryable/write_worker.rb +4 -4
- data/lib/mongo/server/pending_connection.rb +19 -6
- data/lib/mongo/socket/ssl.rb +22 -1
- data/lib/mongo/version.rb +1 -5
- data/mongo.gemspec +8 -11
- data/spec/integration/client_side_encryption/range_explicit_encryption_prose_spec.rb +3 -0
- data/spec/integration/retryable_reads_errors_spec.rb +35 -23
- data/spec/integration/sdam_error_handling_spec.rb +2 -0
- data/spec/lite_spec_helper.rb +0 -10
- data/spec/mongo/collection_crud_spec.rb +1 -0
- data/spec/mongo/server/connection_spec.rb +22 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-BypassQueryAnalysis.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Compact.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-CreateCollection.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-DecryptExistingData.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Delete.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-EncryptedFields-vs-EncryptedFieldsMap.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-EncryptedFields-vs-jsonSchema.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-EncryptedFieldsMap-defaults.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-FindOneAndUpdate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-InsertFind-Indexed.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-InsertFind-Unindexed.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-MissingKey.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-NoEncryption.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Aggregate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Correctness.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Delete.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-FindOneAndUpdate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-InsertFind.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Update.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Aggregate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Correctness.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Delete.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-FindOneAndUpdate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-InsertFind.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Update.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Aggregate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Correctness.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Delete.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-FindOneAndUpdate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-InsertFind.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Update.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Aggregate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Correctness.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Delete.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-FindOneAndUpdate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-InsertFind.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Update.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Aggregate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Correctness.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Delete.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-FindOneAndUpdate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-InsertFind.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Update.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Aggregate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Correctness.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Delete.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-FindOneAndUpdate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-InsertFind.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Update.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Aggregate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Correctness.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Delete.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-FindOneAndUpdate.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-InsertFind.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Update.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-WrongType.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Update.yml +1 -0
- data/spec/spec_tests/data/client_side_encryption/fle2v2-validatorAndPartialFieldExpression.yml +2 -1
- data/spec/spec_tests/data/crud_unified/aggregate-write-readPreference.yml +2 -0
- data/spec/spec_tests/data/crud_unified/db-aggregate-write-readPreference.yml +2 -0
- data/spec/spec_tests/transactions_unified_spec.rb +2 -1
- data/spec/support/certificates/atlas-ocsp-ca.crt +89 -77
- data/spec/support/certificates/atlas-ocsp.crt +117 -122
- data/spec/support/certificates/retrieve-atlas-cert +1 -1
- metadata +12 -84
- checksums.yaml.gz.sig +0 -0
- data/spec/shared/LICENSE +0 -20
- data/spec/shared/bin/get-mongodb-download-url +0 -17
- data/spec/shared/bin/s3-copy +0 -45
- data/spec/shared/bin/s3-upload +0 -69
- data/spec/shared/lib/mrss/child_process_helper.rb +0 -80
- data/spec/shared/lib/mrss/cluster_config.rb +0 -231
- data/spec/shared/lib/mrss/constraints.rb +0 -378
- data/spec/shared/lib/mrss/docker_runner.rb +0 -298
- data/spec/shared/lib/mrss/eg_config_utils.rb +0 -51
- data/spec/shared/lib/mrss/event_subscriber.rb +0 -210
- data/spec/shared/lib/mrss/lite_constraints.rb +0 -238
- data/spec/shared/lib/mrss/server_version_registry.rb +0 -113
- data/spec/shared/lib/mrss/session_registry.rb +0 -69
- data/spec/shared/lib/mrss/session_registry_legacy.rb +0 -60
- data/spec/shared/lib/mrss/spec_organizer.rb +0 -179
- data/spec/shared/lib/mrss/utils.rb +0 -37
- data/spec/shared/share/Dockerfile.erb +0 -281
- data/spec/shared/share/haproxy-1.conf +0 -16
- data/spec/shared/share/haproxy-2.conf +0 -17
- data/spec/shared/shlib/config.sh +0 -27
- data/spec/shared/shlib/distro.sh +0 -74
- data/spec/shared/shlib/server.sh +0 -417
- data/spec/shared/shlib/set_env.sh +0 -146
- data/spec/support/faas/app/aws_lambda/mongodb/Gemfile.lock +0 -19
- data.tar.gz.sig +0 -0
- metadata.gz.sig +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a641cd6128fd246b0ac9999f00167631548206573a22989968f2a7a132bc0ae
|
4
|
+
data.tar.gz: d5526d1ca34219ac644191f1e2c7177877c70063cc5a97992379e8e86065d0ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4c23fd9abff5c29517b2cf0c0094bedfbbe667a96f2494b36e6465d8bbfbe113cd08f05e72d15b6db17ef35a211c4aaa040c20dc99eb65417c3c3690b7712c59
|
7
|
+
data.tar.gz: 54ffbc332168455337aa4de863db0ca179084a35c271e9f21dbfe2425556bd00e02b2fd46a5c2581aa15d5e9a11b84d9a0c231889774adb129f2ad38b997f4f4
|
data/README.md
CHANGED
@@ -5,7 +5,43 @@ MongoDB Ruby Driver
|
|
5
5
|
|
6
6
|
The officially supported Ruby driver for [MongoDB](https://www.mongodb.org/).
|
7
7
|
|
8
|
-
The Ruby driver supports Ruby 2.
|
8
|
+
The Ruby driver supports Ruby 2.7-3.3 and JRuby 9.3-9.4.
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Install via RubyGems, either via the command-line for ad-hoc uses:
|
13
|
+
|
14
|
+
$ gem install mongo
|
15
|
+
|
16
|
+
Or via a Gemfile for more general use:
|
17
|
+
|
18
|
+
gem 'mongo'
|
19
|
+
|
20
|
+
### Release Integrity
|
21
|
+
|
22
|
+
Each release of the MongoDB Ruby driver after version 2.20.0 has been automatically built and signed using the team's GPG key.
|
23
|
+
|
24
|
+
To verify the driver's gem file:
|
25
|
+
|
26
|
+
1. [Download the GPG key](https://pgp.mongodb.com/ruby-driver.asc).
|
27
|
+
2. Import the key into your GPG keyring with `gpg --import ruby-driver.asc`.
|
28
|
+
3. Download the gem file (if you don't already have it). You can download it from RubyGems with `gem fetch mongo`, or you can download it from the [releases page](https://github.com/mongodb/mongo-ruby-driver/releases) on GitHub.
|
29
|
+
4. Download the corresponding detached signature file from the [same release](https://github.com/mongodb/mongo-ruby-driver/releases). Look at the bottom of the release that corresponds to the gem file, under the 'Assets' list, for a `.sig` file with the same version number as the gem you wish to install.
|
30
|
+
5. Verify the gem with `gpg --verify mongo-X.Y.Z.gem.sig mongo-X.Y.Z.gem` (replacing `X.Y.Z` with the actual version number).
|
31
|
+
|
32
|
+
You are looking for text like "Good signature from "MongoDB Ruby Driver Release Signing Key <packaging@mongodb.com>" in the output. If you see that, the signature was found to correspond to the given gem file.
|
33
|
+
|
34
|
+
(Note that other output, like "This key is not certified with a trusted signature!", is related to *web of trust* and depends on how strongly you, personally, trust the `ruby-driver.asc` key that you downloaded from us. To learn more, see https://www.gnupg.org/gph/en/manual/x334.html)
|
35
|
+
|
36
|
+
### Why not use RubyGems' gem-signing functionality?
|
37
|
+
|
38
|
+
RubyGems' own gem signing is problematic, most significantly because there is no established chain of trust related to the keys used to sign gems. RubyGems' own documentation admits that "this method of signing gems is not widely used" (see https://guides.rubygems.org/security/). Discussions about this in the RubyGems community have been off-and-on for more than a decade, and while a solution will eventually arrive, we have settled on using GPG instead for the following reasons:
|
39
|
+
|
40
|
+
1. Many of the other driver teams at MongoDB are using GPG to sign their product releases. Consistency with the other teams means that we can reuse existing tooling for our own product releases.
|
41
|
+
2. GPG is widely available and has existing tools and procedures for dealing with web of trust (though they are admittedly quite arcane and intimidating to the uninitiated, unfortunately).
|
42
|
+
|
43
|
+
Ultimately, most users do not bother to verify gems, and will not be impacted by our choice of GPG over RubyGems' native method.
|
44
|
+
|
9
45
|
|
10
46
|
## Documentation
|
11
47
|
|
data/Rakefile
CHANGED
@@ -2,17 +2,12 @@
|
|
2
2
|
# rubocop:todo all
|
3
3
|
|
4
4
|
require 'bundler'
|
5
|
-
require 'bundler/gem_tasks'
|
6
5
|
require 'rspec/core/rake_task'
|
7
|
-
# TODO move the mongo require into the individual tasks that actually need it
|
8
|
-
require 'mongo'
|
9
6
|
|
10
7
|
ROOT = File.expand_path(File.join(File.dirname(__FILE__)))
|
11
8
|
|
12
9
|
$: << File.join(ROOT, 'spec/shared/lib')
|
13
10
|
|
14
|
-
require 'mrss/spec_organizer'
|
15
|
-
|
16
11
|
CLASSIFIERS = [
|
17
12
|
[%r,^mongo/server,, :unit_server],
|
18
13
|
[%r,^mongo,, :unit],
|
@@ -33,18 +28,67 @@ RUN_PRIORITY = %i(
|
|
33
28
|
spec spec_sdam_integration
|
34
29
|
)
|
35
30
|
|
36
|
-
tasks = Rake.application.instance_variable_get('@tasks')
|
37
|
-
tasks['release:do'] = tasks.delete('release')
|
38
|
-
|
39
31
|
RSpec::Core::RakeTask.new(:spec) do |t|
|
40
32
|
#t.rspec_opts = "--profile 5" if ENV['CI']
|
41
33
|
end
|
42
34
|
|
43
35
|
task :default => ['spec:prepare', :spec]
|
44
36
|
|
37
|
+
# stands in for the Bundler-provided `build` task, which builds the
|
38
|
+
# gem for this project. Our release process builds the gems in a
|
39
|
+
# particular way, in a GitHub action. This task is just to help remind
|
40
|
+
# developers of that fact.
|
41
|
+
task :build do
|
42
|
+
abort <<~WARNING
|
43
|
+
`rake build` does nothing in this project. The gem must be built via
|
44
|
+
the `Driver Release` action on GitHub, which is triggered manually when
|
45
|
+
a new release is ready.
|
46
|
+
WARNING
|
47
|
+
end
|
48
|
+
|
49
|
+
# `rake version` is used by the deployment system so get the release version
|
50
|
+
# of the product beng deployed. It must do nothing more than just print the
|
51
|
+
# product version number.
|
52
|
+
#
|
53
|
+
# See the mongodb-labs/driver-github-tools/ruby/publish Github action.
|
54
|
+
desc "Print the current value of Mongo::VERSION"
|
55
|
+
task :version do
|
56
|
+
require 'mongo/version'
|
57
|
+
|
58
|
+
puts Mongo::VERSION
|
59
|
+
end
|
60
|
+
|
61
|
+
# overrides the default Bundler-provided `release` task, which also
|
62
|
+
# builds the gem. Our release process assumes the gem has already
|
63
|
+
# been built (and signed via GPG), so we just need `rake release` to
|
64
|
+
# push the gem to rubygems.
|
65
|
+
task :release do
|
66
|
+
require 'mongo/version'
|
67
|
+
|
68
|
+
if ENV['GITHUB_ACTION'].nil?
|
69
|
+
abort <<~WARNING
|
70
|
+
`rake release` must be invoked from the `Driver Release` GitHub action,
|
71
|
+
and must not be invoked locally. This ensures the gem is properly signed
|
72
|
+
and distributed by the appropriate user.
|
73
|
+
|
74
|
+
Note that it is the `rubygems/release-gem@v1` step in the `Driver Release`
|
75
|
+
action that invokes this task. Do not rename or remove this task, or the
|
76
|
+
release-gem step will fail. Reimplement this task with caution.
|
77
|
+
|
78
|
+
mongo-#{Mongo::VERSION}.gem was NOT pushed to RubyGems.
|
79
|
+
WARNING
|
80
|
+
end
|
81
|
+
|
82
|
+
system 'gem', 'push', "mongo-#{Mongo::VERSION}.gem"
|
83
|
+
end
|
84
|
+
|
85
|
+
task :mongo do
|
86
|
+
require 'mongo'
|
87
|
+
end
|
88
|
+
|
45
89
|
namespace :spec do
|
46
90
|
desc 'Creates necessary user accounts in the cluster'
|
47
|
-
task :
|
91
|
+
task prepare: :mongo do
|
48
92
|
$: << File.join(File.dirname(__FILE__), 'spec')
|
49
93
|
|
50
94
|
require 'support/utils'
|
@@ -53,7 +97,7 @@ namespace :spec do
|
|
53
97
|
end
|
54
98
|
|
55
99
|
desc 'Waits for sessions to be available in the deployment'
|
56
|
-
task :
|
100
|
+
task wait_for_sessions: :mongo do
|
57
101
|
$: << File.join(File.dirname(__FILE__), 'spec')
|
58
102
|
|
59
103
|
require 'support/utils'
|
@@ -77,7 +121,7 @@ namespace :spec do
|
|
77
121
|
end
|
78
122
|
|
79
123
|
desc 'Prints configuration used by the test suite'
|
80
|
-
task :
|
124
|
+
task config: :mongo do
|
81
125
|
$: << File.join(File.dirname(__FILE__), 'spec')
|
82
126
|
|
83
127
|
# Since this task is usually used for troubleshooting of test suite
|
@@ -90,6 +134,8 @@ namespace :spec do
|
|
90
134
|
end
|
91
135
|
|
92
136
|
def spec_organizer
|
137
|
+
require 'mrss/spec_organizer'
|
138
|
+
|
93
139
|
Mrss::SpecOrganizer.new(
|
94
140
|
root: ROOT,
|
95
141
|
classifiers: CLASSIFIERS,
|
@@ -109,16 +155,6 @@ namespace :spec do
|
|
109
155
|
end
|
110
156
|
end
|
111
157
|
|
112
|
-
namespace :release do
|
113
|
-
task :check_private_key do
|
114
|
-
unless File.exist?('gem-private_key.pem')
|
115
|
-
raise "No private key present, cannot release"
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
task :release => ['release:check_private_key', 'release:do']
|
121
|
-
|
122
158
|
desc 'Build and validate the evergreen config'
|
123
159
|
task eg: %w[ eg:build eg:validate ]
|
124
160
|
|
data/lib/mongo/config.rb
CHANGED
@@ -16,7 +16,7 @@ module Mongo
|
|
16
16
|
|
17
17
|
# When this flag is off, an aggregation done on a view will be executed over
|
18
18
|
# the documents included in that view, instead of all documents in the
|
19
|
-
# collection. When this flag is on, the view
|
19
|
+
# collection. When this flag is on, the view filter is ignored.
|
20
20
|
option :broken_view_aggregate, default: true
|
21
21
|
|
22
22
|
# When this flag is set to false, the view options will be correctly
|
@@ -24,7 +24,7 @@ module Mongo
|
|
24
24
|
option :broken_view_options, default: true
|
25
25
|
|
26
26
|
# When this flag is set to true, the update and replace methods will
|
27
|
-
# validate the
|
27
|
+
# validate the parameters and raise an error if they are invalid.
|
28
28
|
option :validate_update_replace, default: false
|
29
29
|
|
30
30
|
# Set the configuration options.
|
@@ -49,7 +49,8 @@ module Mongo
|
|
49
49
|
|
50
50
|
private
|
51
51
|
|
52
|
-
# Indicate which exception classes that are generally retryable
|
52
|
+
# Indicate which exception classes that are generally retryable
|
53
|
+
# when using modern retries mechanism.
|
53
54
|
#
|
54
55
|
# @return [ Array<Mongo:Error> ] Array of exception classes that are
|
55
56
|
# considered retryable.
|
@@ -58,18 +59,42 @@ module Mongo
|
|
58
59
|
Error::ConnectionPerished,
|
59
60
|
Error::ServerNotUsable,
|
60
61
|
Error::SocketError,
|
61
|
-
Error::SocketTimeoutError
|
62
|
+
Error::SocketTimeoutError,
|
62
63
|
].freeze
|
63
64
|
end
|
64
65
|
|
66
|
+
# Indicate which exception classes that are generally retryable
|
67
|
+
# when using legacy retries mechanism.
|
68
|
+
#
|
69
|
+
# @return [ Array<Mongo:Error> ] Array of exception classes that are
|
70
|
+
# considered retryable.
|
71
|
+
def legacy_retryable_exceptions
|
72
|
+
[
|
73
|
+
Error::ConnectionPerished,
|
74
|
+
Error::ServerNotUsable,
|
75
|
+
Error::SocketError,
|
76
|
+
Error::SocketTimeoutError,
|
77
|
+
Error::PoolClearedError,
|
78
|
+
Error::PoolPausedError,
|
79
|
+
].freeze
|
80
|
+
end
|
81
|
+
|
82
|
+
|
65
83
|
# Tests to see if the given exception instance is of a type that can
|
66
|
-
# be retried.
|
84
|
+
# be retried with modern retry mechanism.
|
67
85
|
#
|
68
86
|
# @return [ true | false ] true if the exception is retryable.
|
69
87
|
def is_retryable_exception?(e)
|
70
88
|
retryable_exceptions.any? { |klass| klass === e }
|
71
89
|
end
|
72
90
|
|
91
|
+
# Tests to see if the given exception instance is of a type that can
|
92
|
+
# be retried with legacy retry mechanism.
|
93
|
+
#
|
94
|
+
# @return [ true | false ] true if the exception is retryable.
|
95
|
+
def is_legacy_retryable_exception?(e)
|
96
|
+
legacy_retryable_exceptions.any? { |klass| klass === e }
|
97
|
+
end
|
73
98
|
# Logs the given deprecation warning the first time it is called for a
|
74
99
|
# given key; after that, it does nothing when given the same key.
|
75
100
|
def deprecation_warning(key, warning)
|
@@ -198,7 +198,7 @@ module Mongo
|
|
198
198
|
raise e if !is_retryable_exception?(e) && !e.write_retryable?
|
199
199
|
retry_read(e, session, server_selector, failed_server: server, &block)
|
200
200
|
end
|
201
|
-
|
201
|
+
|
202
202
|
# Attempts to do a "legacy" read with retry. The operation will be
|
203
203
|
# attempted multiple times, up to the client's `max_read_retries`
|
204
204
|
# setting.
|
@@ -213,17 +213,18 @@ module Mongo
|
|
213
213
|
def legacy_read_with_retry(session, server_selector, &block)
|
214
214
|
attempt = attempt ? attempt + 1 : 1
|
215
215
|
yield select_server(cluster, server_selector, session)
|
216
|
-
rescue *
|
216
|
+
rescue *legacy_retryable_exceptions, Error::OperationFailure => e
|
217
217
|
e.add_notes('legacy retry', "attempt #{attempt}")
|
218
|
-
|
219
|
-
if
|
218
|
+
|
219
|
+
if is_legacy_retryable_exception?(e)
|
220
|
+
|
220
221
|
raise e if attempt > client.max_read_retries || session&.in_transaction?
|
221
222
|
elsif e.retryable? && !session&.in_transaction?
|
222
223
|
raise e if attempt > client.max_read_retries
|
223
224
|
else
|
224
225
|
raise e
|
225
226
|
end
|
226
|
-
|
227
|
+
|
227
228
|
log_retry(e, message: 'Legacy read retry')
|
228
229
|
sleep(client.read_retry_interval) unless is_retryable_exception?(e)
|
229
230
|
retry
|
@@ -261,7 +262,7 @@ module Mongo
|
|
261
262
|
# @param [ Mongo::Server ] failed_server The server on which the original
|
262
263
|
# operation failed.
|
263
264
|
# @param [ Proc ] block The block to execute.
|
264
|
-
#
|
265
|
+
#
|
265
266
|
# @return [ Result ] The result of the operation.
|
266
267
|
def retry_read(original_error, session, server_selector, failed_server: nil, &block)
|
267
268
|
begin
|
@@ -270,9 +271,9 @@ module Mongo
|
|
270
271
|
original_error.add_note("later retry failed: #{e.class}: #{e}")
|
271
272
|
raise original_error
|
272
273
|
end
|
273
|
-
|
274
|
+
|
274
275
|
log_retry(original_error, message: 'Read retry')
|
275
|
-
|
276
|
+
|
276
277
|
begin
|
277
278
|
yield server, true
|
278
279
|
rescue *retryable_exceptions => e
|
@@ -104,7 +104,7 @@ module Mongo
|
|
104
104
|
session = context.session
|
105
105
|
server = select_server(cluster, ServerSelector.primary, session)
|
106
106
|
options = session&.client&.options || {}
|
107
|
-
|
107
|
+
|
108
108
|
if options[:retry_writes]
|
109
109
|
begin
|
110
110
|
server.with_connection(connection_global_id: context.connection_global_id) do |connection|
|
@@ -219,7 +219,7 @@ module Mongo
|
|
219
219
|
def modern_write_with_retry(session, server, context, &block)
|
220
220
|
txn_num = nil
|
221
221
|
connection_succeeded = false
|
222
|
-
|
222
|
+
|
223
223
|
server.with_connection(connection_global_id: context.connection_global_id) do |connection|
|
224
224
|
connection_succeeded = true
|
225
225
|
|
@@ -264,7 +264,7 @@ module Mongo
|
|
264
264
|
# a socket error or a not master error should have marked the respective
|
265
265
|
# server unknown). Here we just need to wait for server selection.
|
266
266
|
server = select_server(cluster, ServerSelector.primary, session, failed_server)
|
267
|
-
|
267
|
+
|
268
268
|
unless server.retry_writes?
|
269
269
|
# Do not need to add "modern retry" here, it should already be on
|
270
270
|
# the first exception.
|
@@ -278,7 +278,7 @@ module Mongo
|
|
278
278
|
# special marker class to bypass the ordinarily applicable rescues.
|
279
279
|
raise Error::RaiseOriginalError
|
280
280
|
end
|
281
|
-
|
281
|
+
|
282
282
|
log_retry(original_error, message: 'Write retry')
|
283
283
|
server.with_connection(connection_global_id: context.connection_global_id) do |connection|
|
284
284
|
yield(connection, txn_num, context)
|
@@ -110,6 +110,24 @@ module Mongo
|
|
110
110
|
|
111
111
|
private
|
112
112
|
|
113
|
+
# Sends the hello command to the server, then receive and deserialize
|
114
|
+
# the response.
|
115
|
+
#
|
116
|
+
# This method is extracted to be mocked in the tests.
|
117
|
+
#
|
118
|
+
# @param [ Protocol::Message ] Command that should be sent to a server
|
119
|
+
# for handshake purposes.
|
120
|
+
#
|
121
|
+
# @return [ Mongo::Protocol::Reply ] Deserialized server response.
|
122
|
+
def get_handshake_response(hello_command)
|
123
|
+
@server.round_trip_time_averager.measure do
|
124
|
+
add_server_diagnostics do
|
125
|
+
socket.write(hello_command.serialize.to_s)
|
126
|
+
Protocol::Message.deserialize(socket, Protocol::Message::MAX_MESSAGE_SIZE)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
113
131
|
# @param [ BSON::Document | nil ] speculative_auth_doc The document to
|
114
132
|
# provide in speculativeAuthenticate field of handshake command.
|
115
133
|
#
|
@@ -131,12 +149,7 @@ module Mongo
|
|
131
149
|
doc = nil
|
132
150
|
@server.handle_handshake_failure! do
|
133
151
|
begin
|
134
|
-
response =
|
135
|
-
add_server_diagnostics do
|
136
|
-
socket.write(hello_command.serialize.to_s)
|
137
|
-
Protocol::Message.deserialize(socket, Protocol::Message::MAX_MESSAGE_SIZE)
|
138
|
-
end
|
139
|
-
end
|
152
|
+
response = get_handshake_response(hello_command)
|
140
153
|
result = Operation::Result.new([response])
|
141
154
|
result.validate!
|
142
155
|
doc = result.documents.first
|
data/lib/mongo/socket/ssl.rb
CHANGED
@@ -287,7 +287,7 @@ module Mongo
|
|
287
287
|
# for instance, if there is no newline between two certificates
|
288
288
|
# this code will extract them both but OpenSSL fails in this situation.
|
289
289
|
if cert_text
|
290
|
-
certs = cert_text
|
290
|
+
certs = extract_certs(cert_text)
|
291
291
|
if certs.length > 1
|
292
292
|
context.cert = OpenSSL::X509::Certificate.new(certs.shift)
|
293
293
|
context.extra_chain_cert = certs.map do |cert|
|
@@ -390,6 +390,27 @@ module Mongo
|
|
390
390
|
hook.call(@context)
|
391
391
|
end
|
392
392
|
end
|
393
|
+
|
394
|
+
BEGIN_CERT = "-----BEGIN CERTIFICATE-----"
|
395
|
+
END_CERT = "-----END CERTIFICATE-----"
|
396
|
+
|
397
|
+
# This was originally a scan + regex, but the regex was particularly
|
398
|
+
# inefficient and was flagged as a concern by static analysis.
|
399
|
+
def extract_certs(text)
|
400
|
+
[].tap do |list|
|
401
|
+
pos = 0
|
402
|
+
|
403
|
+
while (begin_idx = text.index(BEGIN_CERT, pos))
|
404
|
+
end_idx = text.index(END_CERT, begin_idx)
|
405
|
+
break unless end_idx
|
406
|
+
|
407
|
+
end_idx += END_CERT.length
|
408
|
+
list.push(text[begin_idx...end_idx])
|
409
|
+
|
410
|
+
pos = end_idx
|
411
|
+
end
|
412
|
+
end
|
413
|
+
end
|
393
414
|
end
|
394
415
|
end
|
395
416
|
end
|
data/lib/mongo/version.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
# rubocop:todo all
|
3
2
|
|
4
3
|
# Copyright (C) 2014-2020 MongoDB Inc.
|
5
4
|
#
|
@@ -16,9 +15,6 @@
|
|
16
15
|
# limitations under the License.
|
17
16
|
|
18
17
|
module Mongo
|
19
|
-
|
20
18
|
# The current version of the driver.
|
21
|
-
|
22
|
-
# @since 2.0.0
|
23
|
-
VERSION = '2.20.0'.freeze
|
19
|
+
VERSION = '2.20.1'
|
24
20
|
end
|
data/mongo.gemspec
CHANGED
@@ -9,28 +9,25 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.name = 'mongo'
|
10
10
|
s.version = Mongo::VERSION
|
11
11
|
s.platform = Gem::Platform::RUBY
|
12
|
-
s.authors = [
|
13
|
-
s.email =
|
12
|
+
s.authors = [ 'The MongoDB Ruby Team' ]
|
13
|
+
s.email = 'dbx-ruby@mongodb.com'
|
14
14
|
s.homepage = 'https://mongodb.com/docs/ruby-driver/'
|
15
15
|
s.summary = 'Ruby driver for MongoDB'
|
16
|
-
s.description = 'A Ruby driver for MongoDB'
|
17
16
|
s.license = 'Apache-2.0'
|
17
|
+
s.description = <<~DESC
|
18
|
+
A pure-Ruby driver for connecting to, querying, and manipulating MongoDB
|
19
|
+
databases. Officially developed and supported by MongoDB, with love for
|
20
|
+
the Ruby community.
|
21
|
+
DESC
|
18
22
|
|
19
23
|
s.metadata = {
|
20
24
|
'bug_tracker_uri' => 'https://jira.mongodb.org/projects/RUBY',
|
21
25
|
'changelog_uri' => 'https://github.com/mongodb/mongo-ruby-driver/releases',
|
22
|
-
'documentation_uri' => 'https://mongodb.com/docs/ruby-driver/',
|
23
26
|
'homepage_uri' => 'https://mongodb.com/docs/ruby-driver/',
|
27
|
+
'documentation_uri' => 'https://mongodb.com/docs/ruby-driver/current/tutorials/quick-start/',
|
24
28
|
'source_code_uri' => 'https://github.com/mongodb/mongo-ruby-driver',
|
25
29
|
}
|
26
30
|
|
27
|
-
if File.exist?('gem-private_key.pem')
|
28
|
-
s.signing_key = 'gem-private_key.pem'
|
29
|
-
s.cert_chain = ['gem-public_cert.pem']
|
30
|
-
else
|
31
|
-
warn "[#{s.name}] Warning: No private key present, creating unsigned gem."
|
32
|
-
end
|
33
|
-
|
34
31
|
s.files = Dir.glob('{bin,lib,spec}/**/*')
|
35
32
|
s.files += %w[mongo.gemspec LICENSE README.md CONTRIBUTING.md Rakefile]
|
36
33
|
s.test_files = Dir.glob('spec/**/*')
|
@@ -7,6 +7,9 @@ require 'spec_helper'
|
|
7
7
|
# rubocop:disable RSpec/ExampleLength
|
8
8
|
describe 'Range Explicit Encryption' do
|
9
9
|
min_server_version '7.0.0-rc0'
|
10
|
+
# https://jira.mongodb.org/browse/RUBY-3457
|
11
|
+
max_server_version '7.99.99'
|
12
|
+
|
10
13
|
require_libmongocrypt
|
11
14
|
include_context 'define shared FLE helpers'
|
12
15
|
|
@@ -4,6 +4,7 @@
|
|
4
4
|
require 'spec_helper'
|
5
5
|
|
6
6
|
describe 'Retryable reads errors tests' do
|
7
|
+
retry_test
|
7
8
|
|
8
9
|
let(:client) { authorized_client.with(options.merge(retry_reads: true)) }
|
9
10
|
|
@@ -73,31 +74,42 @@ describe 'Retryable reads errors tests' do
|
|
73
74
|
client.subscribe(Mongo::Monitoring::CONNECTION_POOL, subscriber)
|
74
75
|
end
|
75
76
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
::
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
77
|
+
shared_examples_for 'retries on PoolClearedError' do
|
78
|
+
it "retries on PoolClearedError" do
|
79
|
+
# After the first find fails, the pool is paused and retry is triggered.
|
80
|
+
# Now, a race is started between the second find acquiring a connection,
|
81
|
+
# and the first retrying the read. Now, retry reads cause the cluster to
|
82
|
+
# be rescanned and the pool to be unpaused, allowing the second checkout
|
83
|
+
# to succeed (when it should fail). Therefore we want the second find's
|
84
|
+
# check out to win the race. This gives the check out a little head start.
|
85
|
+
allow_any_instance_of(Mongo::Server::ConnectionPool).to receive(:ready).and_wrap_original do |m, *args, &block|
|
86
|
+
::Utils.wait_for_condition(5) do
|
87
|
+
# check_out_results should contain:
|
88
|
+
# - find1 connection check out successful
|
89
|
+
# - pool cleared
|
90
|
+
# - find2 connection check out failed
|
91
|
+
# We wait here for the third event to happen before we ready the pool.
|
92
|
+
cmap_events.select do |e|
|
93
|
+
event_types.include?(e.class)
|
94
|
+
end.length >= 3
|
95
|
+
end
|
96
|
+
m.call(*args, &block)
|
93
97
|
end
|
94
|
-
|
98
|
+
threads.map(&:join)
|
99
|
+
expect(check_out_results[0]).to be_a(Mongo::Monitoring::Event::Cmap::ConnectionCheckedOut)
|
100
|
+
expect(check_out_results[1]).to be_a(Mongo::Monitoring::Event::Cmap::PoolCleared)
|
101
|
+
expect(check_out_results[2]).to be_a(Mongo::Monitoring::Event::Cmap::ConnectionCheckOutFailed)
|
102
|
+
expect(find_events.length).to eq(3)
|
95
103
|
end
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
104
|
+
end
|
105
|
+
|
106
|
+
it_behaves_like 'retries on PoolClearedError'
|
107
|
+
|
108
|
+
context 'legacy read retries' do
|
109
|
+
|
110
|
+
let(:client) { authorized_client.with(options.merge(retry_reads: false, max_read_retries: 1)) }
|
111
|
+
|
112
|
+
it_behaves_like 'retries on PoolClearedError'
|
101
113
|
end
|
102
114
|
|
103
115
|
after do
|
data/spec/lite_spec_helper.rb
CHANGED
@@ -168,16 +168,6 @@ RSpec.configure do |config|
|
|
168
168
|
end
|
169
169
|
end
|
170
170
|
|
171
|
-
if SpecConfig.instance.ci? && !%w(1 true yes).include?(ENV['INTERACTIVE']&.downcase)
|
172
|
-
# Tests should take under 10 seconds ideally but it seems
|
173
|
-
# we have some that run for more than 10 seconds in CI.
|
174
|
-
config.around(:each) do |example|
|
175
|
-
TimeoutInterrupt.timeout(example_timeout_seconds, ExampleTimeout) do
|
176
|
-
example.run
|
177
|
-
end
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
171
|
if SpecConfig.instance.ci?
|
182
172
|
if defined?(Rfc::Rif)
|
183
173
|
unless BSON::Environment.jruby?
|
@@ -583,6 +583,28 @@ describe Mongo::Server::Connection do
|
|
583
583
|
end
|
584
584
|
end
|
585
585
|
|
586
|
+
context 'when the server returns unknown saslSupportedMechs' do
|
587
|
+
min_server_version '4.0'
|
588
|
+
|
589
|
+
let(:connection) do
|
590
|
+
described_class.new(server, server.options.merge(connection_pool: pool))
|
591
|
+
end
|
592
|
+
|
593
|
+
before do
|
594
|
+
expect_any_instance_of(Mongo::Server::PendingConnection).to receive(:get_handshake_response).and_wrap_original do |original_method, *args|
|
595
|
+
original_method.call(*args).tap do |result|
|
596
|
+
if result.documents.first.fetch('saslSupportedMechs', nil).is_a?(Array)
|
597
|
+
result.documents.first['saslSupportedMechs'].append('unknownMechanism')
|
598
|
+
end
|
599
|
+
end
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
it 'does not raise an error' do
|
604
|
+
expect { connection.connect! }.not_to raise_error
|
605
|
+
end
|
606
|
+
end
|
607
|
+
|
586
608
|
end
|
587
609
|
|
588
610
|
describe '#disconnect!' do
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# Requires libmongocrypt 1.8.0.
|
2
2
|
runOn:
|
3
3
|
- minServerVersion: "7.0.0"
|
4
|
+
maxServerVersion: "7.99.99"
|
4
5
|
# Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol.
|
5
6
|
# FLE 2 Encrypted collections are not supported on standalone.
|
6
7
|
topology: [ "replicaset", "sharded", "load-balanced" ]
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# Requires libmongocrypt 1.8.0.
|
2
2
|
runOn:
|
3
3
|
- minServerVersion: "7.0.0"
|
4
|
+
maxServerVersion: "7.99.99"
|
4
5
|
# Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol.
|
5
6
|
# FLE 2 Encrypted collections are not supported on standalone.
|
6
7
|
topology: [ "replicaset", "sharded", "load-balanced" ]
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# Requires libmongocrypt 1.8.0.
|
2
2
|
runOn:
|
3
3
|
- minServerVersion: "7.0.0"
|
4
|
+
maxServerVersion: "7.99.99"
|
4
5
|
# Skip QEv2 (also referred to as FLE2v2) tests on Serverless. Unskip once Serverless enables the QEv2 protocol.
|
5
6
|
# FLE 2 Encrypted collections are not supported on standalone.
|
6
7
|
topology: [ "replicaset", "sharded", "load-balanced" ]
|