mongo 2.20.0 → 2.20.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +37 -1
  3. data/Rakefile +57 -21
  4. data/lib/mongo/config.rb +2 -2
  5. data/lib/mongo/retryable/base_worker.rb +28 -3
  6. data/lib/mongo/retryable/read_worker.rb +9 -8
  7. data/lib/mongo/retryable/write_worker.rb +4 -4
  8. data/lib/mongo/server/pending_connection.rb +19 -6
  9. data/lib/mongo/socket/ssl.rb +22 -1
  10. data/lib/mongo/version.rb +1 -5
  11. data/mongo.gemspec +8 -11
  12. data/spec/integration/client_side_encryption/range_explicit_encryption_prose_spec.rb +3 -0
  13. data/spec/integration/retryable_reads_errors_spec.rb +35 -23
  14. data/spec/integration/sdam_error_handling_spec.rb +2 -0
  15. data/spec/lite_spec_helper.rb +0 -10
  16. data/spec/mongo/collection_crud_spec.rb +1 -0
  17. data/spec/mongo/server/connection_spec.rb +22 -0
  18. data/spec/spec_tests/data/client_side_encryption/fle2v2-BypassQueryAnalysis.yml +1 -0
  19. data/spec/spec_tests/data/client_side_encryption/fle2v2-Compact.yml +1 -0
  20. data/spec/spec_tests/data/client_side_encryption/fle2v2-CreateCollection.yml +1 -0
  21. data/spec/spec_tests/data/client_side_encryption/fle2v2-DecryptExistingData.yml +1 -0
  22. data/spec/spec_tests/data/client_side_encryption/fle2v2-Delete.yml +1 -0
  23. data/spec/spec_tests/data/client_side_encryption/fle2v2-EncryptedFields-vs-EncryptedFieldsMap.yml +1 -0
  24. data/spec/spec_tests/data/client_side_encryption/fle2v2-EncryptedFields-vs-jsonSchema.yml +1 -0
  25. data/spec/spec_tests/data/client_side_encryption/fle2v2-EncryptedFieldsMap-defaults.yml +1 -0
  26. data/spec/spec_tests/data/client_side_encryption/fle2v2-FindOneAndUpdate.yml +1 -0
  27. data/spec/spec_tests/data/client_side_encryption/fle2v2-InsertFind-Indexed.yml +1 -0
  28. data/spec/spec_tests/data/client_side_encryption/fle2v2-InsertFind-Unindexed.yml +1 -0
  29. data/spec/spec_tests/data/client_side_encryption/fle2v2-MissingKey.yml +1 -0
  30. data/spec/spec_tests/data/client_side_encryption/fle2v2-NoEncryption.yml +1 -0
  31. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Aggregate.yml +1 -0
  32. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Correctness.yml +1 -0
  33. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Delete.yml +1 -0
  34. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-FindOneAndUpdate.yml +1 -0
  35. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-InsertFind.yml +1 -0
  36. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Update.yml +1 -0
  37. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Aggregate.yml +1 -0
  38. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Correctness.yml +1 -0
  39. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Delete.yml +1 -0
  40. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-FindOneAndUpdate.yml +1 -0
  41. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-InsertFind.yml +1 -0
  42. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Update.yml +1 -0
  43. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Aggregate.yml +1 -0
  44. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Correctness.yml +1 -0
  45. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Delete.yml +1 -0
  46. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-FindOneAndUpdate.yml +1 -0
  47. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-InsertFind.yml +1 -0
  48. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Update.yml +1 -0
  49. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Aggregate.yml +1 -0
  50. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Correctness.yml +1 -0
  51. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Delete.yml +1 -0
  52. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-FindOneAndUpdate.yml +1 -0
  53. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-InsertFind.yml +1 -0
  54. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Update.yml +1 -0
  55. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Aggregate.yml +1 -0
  56. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Correctness.yml +1 -0
  57. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Delete.yml +1 -0
  58. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-FindOneAndUpdate.yml +1 -0
  59. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-InsertFind.yml +1 -0
  60. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Update.yml +1 -0
  61. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Aggregate.yml +1 -0
  62. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Correctness.yml +1 -0
  63. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Delete.yml +1 -0
  64. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-FindOneAndUpdate.yml +1 -0
  65. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-InsertFind.yml +1 -0
  66. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Update.yml +1 -0
  67. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Aggregate.yml +1 -0
  68. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Correctness.yml +1 -0
  69. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Delete.yml +1 -0
  70. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-FindOneAndUpdate.yml +1 -0
  71. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-InsertFind.yml +1 -0
  72. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Update.yml +1 -0
  73. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-WrongType.yml +1 -0
  74. data/spec/spec_tests/data/client_side_encryption/fle2v2-Update.yml +1 -0
  75. data/spec/spec_tests/data/client_side_encryption/fle2v2-validatorAndPartialFieldExpression.yml +2 -1
  76. data/spec/spec_tests/data/crud_unified/aggregate-write-readPreference.yml +2 -0
  77. data/spec/spec_tests/data/crud_unified/db-aggregate-write-readPreference.yml +2 -0
  78. data/spec/spec_tests/transactions_unified_spec.rb +2 -1
  79. data/spec/support/certificates/atlas-ocsp-ca.crt +89 -77
  80. data/spec/support/certificates/atlas-ocsp.crt +117 -122
  81. data/spec/support/certificates/retrieve-atlas-cert +1 -1
  82. metadata +12 -84
  83. checksums.yaml.gz.sig +0 -0
  84. data/spec/shared/LICENSE +0 -20
  85. data/spec/shared/bin/get-mongodb-download-url +0 -17
  86. data/spec/shared/bin/s3-copy +0 -45
  87. data/spec/shared/bin/s3-upload +0 -69
  88. data/spec/shared/lib/mrss/child_process_helper.rb +0 -80
  89. data/spec/shared/lib/mrss/cluster_config.rb +0 -231
  90. data/spec/shared/lib/mrss/constraints.rb +0 -378
  91. data/spec/shared/lib/mrss/docker_runner.rb +0 -298
  92. data/spec/shared/lib/mrss/eg_config_utils.rb +0 -51
  93. data/spec/shared/lib/mrss/event_subscriber.rb +0 -210
  94. data/spec/shared/lib/mrss/lite_constraints.rb +0 -238
  95. data/spec/shared/lib/mrss/server_version_registry.rb +0 -113
  96. data/spec/shared/lib/mrss/session_registry.rb +0 -69
  97. data/spec/shared/lib/mrss/session_registry_legacy.rb +0 -60
  98. data/spec/shared/lib/mrss/spec_organizer.rb +0 -179
  99. data/spec/shared/lib/mrss/utils.rb +0 -37
  100. data/spec/shared/share/Dockerfile.erb +0 -281
  101. data/spec/shared/share/haproxy-1.conf +0 -16
  102. data/spec/shared/share/haproxy-2.conf +0 -17
  103. data/spec/shared/shlib/config.sh +0 -27
  104. data/spec/shared/shlib/distro.sh +0 -74
  105. data/spec/shared/shlib/server.sh +0 -417
  106. data/spec/shared/shlib/set_env.sh +0 -146
  107. data/spec/support/faas/app/aws_lambda/mongodb/Gemfile.lock +0 -19
  108. data.tar.gz.sig +0 -0
  109. metadata.gz.sig +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 281c66c9e5d92b0eb8040c342bb0dfcf554c9b5420ec803bc68748472d74a1ae
4
- data.tar.gz: 423f31b24accb3ce674ea28a7aa8fcaf028730eb1370187c23f5304ba30698f6
3
+ metadata.gz: 1a641cd6128fd246b0ac9999f00167631548206573a22989968f2a7a132bc0ae
4
+ data.tar.gz: d5526d1ca34219ac644191f1e2c7177877c70063cc5a97992379e8e86065d0ca
5
5
  SHA512:
6
- metadata.gz: 4f414d30bf15f7c32ac9181e25e707caa80447975690fc6c0eb9d8fa37a784f8c4d21125b385d5a6d7efdb4bd751ee2c9c4bea313adbf78d20de3b019c25ac8a
7
- data.tar.gz: 7a83ba71f6a36d886769d755fb5ea45e6d1fae37bd92b490c58e9f643f51c25d5a489ed79928d67975f067d258c738f54cc3e5caecca6eb0bad2e2bf49d81613
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.5-3.0 and JRuby 9.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 :prepare do
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 :wait_for_sessions do
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 :config do
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 fiter is ignored.
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 paramters and raise an error if they are invalid.
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 *retryable_exceptions, Error::OperationFailure, Error::PoolError => e
216
+ rescue *legacy_retryable_exceptions, Error::OperationFailure => e
217
217
  e.add_notes('legacy retry', "attempt #{attempt}")
218
-
219
- if is_retryable_exception?(e)
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 = @server.round_trip_time_averager.measure do
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
@@ -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.scan(/-----BEGIN CERTIFICATE-----(?:.|\n)+?-----END CERTIFICATE-----/)
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 = ["The MongoDB Ruby Team"]
13
- s.email = "dbx-ruby@mongodb.com"
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
- it "retries on PoolClearedError" do
77
- # After the first find fails, the pool is paused and retry is triggered.
78
- # Now, a race is started between the second find acquiring a connection,
79
- # and the first retrying the read. Now, retry reads cause the cluster to
80
- # be rescanned and the pool to be unpaused, allowing the second checkout
81
- # to succeed (when it should fail). Therefore we want the second find's
82
- # check out to win the race. This gives the check out a little head start.
83
- allow_any_instance_of(Mongo::Server::ConnectionPool).to receive(:ready).and_wrap_original do |m, *args, &block|
84
- ::Utils.wait_for_condition(5) do
85
- # check_out_results should contain:
86
- # - find1 connection check out successful
87
- # - pool cleared
88
- # - find2 connection check out failed
89
- # We wait here for the third event to happen before we ready the pool.
90
- cmap_events.select do |e|
91
- event_types.include?(e.class)
92
- end.length >= 3
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
- m.call(*args, &block)
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
- threads.map(&:join)
97
- expect(check_out_results[0]).to be_a(Mongo::Monitoring::Event::Cmap::ConnectionCheckedOut)
98
- expect(check_out_results[1]).to be_a(Mongo::Monitoring::Event::Cmap::PoolCleared)
99
- expect(check_out_results[2]).to be_a(Mongo::Monitoring::Event::Cmap::ConnectionCheckOutFailed)
100
- expect(find_events.length).to eq(3)
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
@@ -8,6 +8,8 @@ describe 'SDAM error handling' do
8
8
 
9
9
  clean_slate
10
10
 
11
+ retry_test
12
+
11
13
  after do
12
14
  # Close all clients after every test to avoid leaking expectations into
13
15
  # subsequent tests because we set global assertions on sockets.
@@ -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?
@@ -4,6 +4,7 @@
4
4
  require 'spec_helper'
5
5
 
6
6
  describe Mongo::Collection do
7
+ retry_test
7
8
 
8
9
  let(:subscriber) { Mrss::EventSubscriber.new }
9
10
 
@@ -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" ]