mongo 2.19.2 → 2.20.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
- checksums.yaml.gz.sig +0 -0
- data/Rakefile +27 -154
- data/lib/mongo/cluster/topology/base.rb +16 -0
- data/lib/mongo/cluster.rb +27 -1
- data/lib/mongo/collection/view/iterable.rb +1 -0
- data/lib/mongo/collection.rb +27 -3
- data/lib/mongo/error/transactions_not_supported.rb +34 -0
- data/lib/mongo/error.rb +1 -0
- data/lib/mongo/grid/fs_bucket.rb +6 -0
- data/lib/mongo/monitoring/event/secure.rb +1 -1
- data/lib/mongo/operation/create_search_indexes/op_msg.rb +31 -0
- data/lib/mongo/operation/create_search_indexes.rb +15 -0
- data/lib/mongo/operation/drop_search_index/op_msg.rb +33 -0
- data/lib/mongo/operation/drop_search_index.rb +15 -0
- data/lib/mongo/operation/shared/executable.rb +43 -27
- data/lib/mongo/operation/shared/response_handling.rb +23 -25
- data/lib/mongo/operation/shared/specifiable.rb +7 -0
- data/lib/mongo/operation/update_search_index/op_msg.rb +34 -0
- data/lib/mongo/operation/update_search_index.rb +15 -0
- data/lib/mongo/operation.rb +3 -0
- data/lib/mongo/retryable/read_worker.rb +7 -6
- data/lib/mongo/retryable/write_worker.rb +7 -4
- data/lib/mongo/retryable.rb +2 -2
- data/lib/mongo/search_index/view.rb +232 -0
- data/lib/mongo/server/app_metadata/environment.rb +64 -9
- data/lib/mongo/server/app_metadata.rb +5 -4
- data/lib/mongo/server/description/features.rb +1 -0
- data/lib/mongo/server_selector/base.rb +32 -6
- data/lib/mongo/session/server_session/dirtyable.rb +52 -0
- data/lib/mongo/session/server_session.rb +3 -0
- data/lib/mongo/session/session_pool.rb +12 -18
- data/lib/mongo/session.rb +32 -0
- data/lib/mongo/uri.rb +0 -4
- data/lib/mongo/version.rb +1 -1
- data/lib/mongo.rb +1 -0
- data/mongo.gemspec +1 -7
- data/spec/atlas/atlas_connectivity_spec.rb +5 -9
- data/spec/atlas/operations_spec.rb +1 -5
- data/spec/faas/ruby-sam-app/Gemfile +9 -0
- data/spec/faas/ruby-sam-app/mongodb/Gemfile +4 -0
- data/spec/faas/ruby-sam-app/mongodb/app.rb +149 -0
- data/spec/faas/ruby-sam-app/template.yaml +48 -0
- data/spec/integration/client_side_encryption/corpus_spec.rb +10 -2
- data/spec/integration/retryable_reads_errors_spec.rb +161 -8
- data/spec/integration/retryable_writes_errors_spec.rb +156 -0
- data/spec/integration/search_indexes_prose_spec.rb +168 -0
- data/spec/lite_spec_helper.rb +32 -10
- data/spec/mongo/cluster_spec.rb +36 -0
- data/spec/mongo/collection/view/aggregation_spec.rb +6 -1
- data/spec/mongo/collection/view/explainable_spec.rb +2 -0
- data/spec/mongo/collection_crud_spec.rb +1 -1
- data/spec/mongo/operation/insert_spec.rb +1 -1
- data/spec/mongo/retryable/write_worker_spec.rb +39 -0
- data/spec/mongo/server/app_metadata/environment_spec.rb +135 -0
- data/spec/mongo/server/app_metadata_spec.rb +12 -2
- data/spec/mongo/server/connection_spec.rb +4 -0
- data/spec/mongo/session/session_pool_spec.rb +1 -16
- data/spec/mongo/session_transaction_spec.rb +15 -0
- data/spec/mongo/uri_spec.rb +0 -9
- data/spec/runners/crud/test.rb +0 -8
- data/spec/runners/crud.rb +1 -1
- data/spec/runners/transactions/test.rb +12 -3
- data/spec/runners/unified/assertions.rb +16 -3
- data/spec/runners/unified/crud_operations.rb +12 -0
- data/spec/runners/unified/search_index_operations.rb +63 -0
- data/spec/runners/unified/support_operations.rb +3 -5
- data/spec/runners/unified/test.rb +11 -2
- data/spec/shared/lib/mrss/docker_runner.rb +3 -0
- data/spec/shared/share/Dockerfile.erb +20 -69
- data/spec/shared/shlib/server.sh +1 -0
- data/spec/shared/shlib/set_env.sh +5 -28
- data/spec/spec_helper.rb +1 -1
- data/spec/spec_tests/data/client_side_encryption/explain.yml +2 -2
- data/spec/spec_tests/data/connection_string/invalid-uris.yml +0 -10
- data/spec/spec_tests/data/connection_string/valid-options.yml +13 -0
- data/spec/spec_tests/data/crud_unified/find-test-all-options.yml +348 -0
- data/spec/spec_tests/data/index_management/createSearchIndex.yml +64 -0
- data/spec/spec_tests/data/index_management/createSearchIndexes.yml +86 -0
- data/spec/spec_tests/data/index_management/dropSearchIndex.yml +43 -0
- data/spec/spec_tests/data/index_management/listSearchIndexes.yml +91 -0
- data/spec/spec_tests/data/index_management/updateSearchIndex.yml +46 -0
- data/spec/spec_tests/data/retryable_writes/unified/bulkWrite-serverErrors.yml +3 -6
- data/spec/spec_tests/data/retryable_writes/unified/insertOne-serverErrors.yml +3 -6
- data/spec/spec_tests/data/run_command_unified/runCommand.yml +319 -0
- data/spec/spec_tests/data/sessions_unified/driver-sessions-dirty-session-errors.yml +351 -0
- data/spec/spec_tests/data/unified/valid-pass/poc-crud.yml +1 -1
- data/spec/spec_tests/data/unified/valid-pass/poc-retryable-writes.yml +7 -7
- data/spec/spec_tests/data/unified/valid-pass/poc-sessions.yml +3 -4
- data/spec/spec_tests/data/unified/valid-pass/poc-transactions-convenient-api.yml +1 -1
- data/spec/spec_tests/data/unified/valid-pass/poc-transactions-mongos-pin-auto.yml +1 -1
- data/spec/spec_tests/data/unified/valid-pass/poc-transactions.yml +3 -3
- data/spec/spec_tests/index_management_unified_spec.rb +13 -0
- data/spec/spec_tests/run_command_unified_spec.rb +13 -0
- data/spec/spec_tests/sdam_unified_spec.rb +2 -0
- data/spec/support/constraints.rb +6 -0
- data/spec/support/faas/app/aws_lambda/mongodb/Gemfile.lock +19 -0
- data/spec/support/ocsp +1 -1
- data/spec/support/recording_logger.rb +27 -0
- data/spec/support/spec_config.rb +5 -0
- data.tar.gz.sig +0 -0
- metadata +1329 -1285
- metadata.gz.sig +3 -2
- data/spec/spec_tests/data/cmap/pool-clear-interrupt-immediately.yml +0 -49
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (C) 2024 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 Session
|
|
19
|
+
class ServerSession
|
|
20
|
+
# Functionality for manipulating and querying a session's
|
|
21
|
+
# "dirty" state, per the last paragraph at
|
|
22
|
+
# https://github.com/mongodb/specifications/blob/master/source/sessions/driver-sessions.rst#server-session-pool
|
|
23
|
+
#
|
|
24
|
+
# If a driver has a server session pool and a network error is
|
|
25
|
+
# encountered when executing any command with a ClientSession, the
|
|
26
|
+
# driver MUST mark the associated ServerSession as dirty. Dirty server
|
|
27
|
+
# sessions are discarded when returned to the server session pool. It is
|
|
28
|
+
# valid for a dirty session to be used for subsequent commands (e.g. an
|
|
29
|
+
# implicit retry attempt, a later command in a bulk write, or a later
|
|
30
|
+
# operation on an explicit session), however, it MUST remain dirty for
|
|
31
|
+
# the remainder of its lifetime regardless if later commands succeed.
|
|
32
|
+
#
|
|
33
|
+
# @api private
|
|
34
|
+
module Dirtyable
|
|
35
|
+
# Query whether the server session has been marked dirty or not.
|
|
36
|
+
#
|
|
37
|
+
# @return [ true | false ] the server session's dirty state
|
|
38
|
+
def dirty?
|
|
39
|
+
@dirty
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Mark the server session as dirty (the default) or clean.
|
|
43
|
+
#
|
|
44
|
+
# @param [ true | false ] mark whether the mark the server session
|
|
45
|
+
# dirty or not.
|
|
46
|
+
def dirty!(mark = true)
|
|
47
|
+
@dirty = mark
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
# See the License for the specific language governing permissions and
|
|
16
16
|
# limitations under the License.
|
|
17
17
|
|
|
18
|
+
require 'mongo/session/server_session/dirtyable'
|
|
19
|
+
|
|
18
20
|
module Mongo
|
|
19
21
|
|
|
20
22
|
class Session
|
|
@@ -25,6 +27,7 @@ module Mongo
|
|
|
25
27
|
#
|
|
26
28
|
# @since 2.5.0
|
|
27
29
|
class ServerSession
|
|
30
|
+
include Dirtyable
|
|
28
31
|
|
|
29
32
|
# Regex for removing dashes from the UUID string.
|
|
30
33
|
#
|
|
@@ -25,21 +25,6 @@ module Mongo
|
|
|
25
25
|
#
|
|
26
26
|
# @since 2.5.0
|
|
27
27
|
class SessionPool
|
|
28
|
-
|
|
29
|
-
# Create a SessionPool.
|
|
30
|
-
#
|
|
31
|
-
# @example
|
|
32
|
-
# SessionPool.create(cluster)
|
|
33
|
-
#
|
|
34
|
-
# @param [ Mongo::Cluster ] cluster The cluster that will be associated with this
|
|
35
|
-
# session pool.
|
|
36
|
-
#
|
|
37
|
-
# @since 2.5.0
|
|
38
|
-
def self.create(cluster)
|
|
39
|
-
pool = new(cluster)
|
|
40
|
-
cluster.instance_variable_set(:@session_pool, pool)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
28
|
# Initialize a SessionPool.
|
|
44
29
|
#
|
|
45
30
|
# @example
|
|
@@ -105,9 +90,7 @@ module Mongo
|
|
|
105
90
|
|
|
106
91
|
@mutex.synchronize do
|
|
107
92
|
prune!
|
|
108
|
-
|
|
109
|
-
@queue.unshift(session)
|
|
110
|
-
end
|
|
93
|
+
@queue.unshift(session) if return_to_queue?(session)
|
|
111
94
|
end
|
|
112
95
|
end
|
|
113
96
|
|
|
@@ -136,6 +119,17 @@ module Mongo
|
|
|
136
119
|
|
|
137
120
|
private
|
|
138
121
|
|
|
122
|
+
# Query whether the given session is okay to return to the
|
|
123
|
+
# pool's queue.
|
|
124
|
+
#
|
|
125
|
+
# @param [ Session::ServerSession ] session the session to query
|
|
126
|
+
#
|
|
127
|
+
# @return [ true | false ] whether to return the session to the
|
|
128
|
+
# queue.
|
|
129
|
+
def return_to_queue?(session)
|
|
130
|
+
!session.dirty? && !about_to_expire?(session)
|
|
131
|
+
end
|
|
132
|
+
|
|
139
133
|
def about_to_expire?(session)
|
|
140
134
|
if session.nil?
|
|
141
135
|
raise ArgumentError, 'session cannot be nil'
|
data/lib/mongo/session.rb
CHANGED
|
@@ -123,6 +123,23 @@ module Mongo
|
|
|
123
123
|
# @since 2.5.0
|
|
124
124
|
attr_reader :operation_time
|
|
125
125
|
|
|
126
|
+
# Sets the dirty state to the given value for the underlying server
|
|
127
|
+
# session. If there is no server session, this does nothing.
|
|
128
|
+
#
|
|
129
|
+
# @param [ true | false ] mark whether to mark the server session as
|
|
130
|
+
# dirty, or not.
|
|
131
|
+
def dirty!(mark = true)
|
|
132
|
+
@server_session&.dirty!(mark)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# @return [ true | false | nil ] whether the underlying server session is
|
|
136
|
+
# dirty. If no server session exists for this session, returns nil.
|
|
137
|
+
#
|
|
138
|
+
# @api private
|
|
139
|
+
def dirty?
|
|
140
|
+
@server_session&.dirty?
|
|
141
|
+
end
|
|
142
|
+
|
|
126
143
|
# @return [ Hash ] The options for the transaction currently being executed
|
|
127
144
|
# on this session.
|
|
128
145
|
#
|
|
@@ -538,6 +555,8 @@ module Mongo
|
|
|
538
555
|
#
|
|
539
556
|
# @since 2.6.0
|
|
540
557
|
def start_transaction(options = nil)
|
|
558
|
+
check_transactions_supported!
|
|
559
|
+
|
|
541
560
|
if options
|
|
542
561
|
Lint.validate_read_concern_option(options[:read_concern])
|
|
543
562
|
|
|
@@ -1185,5 +1204,18 @@ module Mongo
|
|
|
1185
1204
|
raise Mongo::Error::InvalidSession.new(MISMATCHED_CLUSTER_ERROR_MSG)
|
|
1186
1205
|
end
|
|
1187
1206
|
end
|
|
1207
|
+
|
|
1208
|
+
def check_transactions_supported!
|
|
1209
|
+
raise Mongo::Error::TransactionsNotSupported, "standalone topology" if cluster.single?
|
|
1210
|
+
|
|
1211
|
+
cluster.next_primary.with_connection do |conn|
|
|
1212
|
+
if cluster.replica_set? && !conn.features.transactions_enabled?
|
|
1213
|
+
raise Mongo::Error::TransactionsNotSupported, "server version is < 4.0"
|
|
1214
|
+
end
|
|
1215
|
+
if cluster.sharded? && !conn.features.sharded_transactions_enabled?
|
|
1216
|
+
raise Mongo::Error::TransactionsNotSupported, "sharded transactions require server version >= 4.2"
|
|
1217
|
+
end
|
|
1218
|
+
end
|
|
1219
|
+
end
|
|
1188
1220
|
end
|
|
1189
1221
|
end
|
data/lib/mongo/uri.rb
CHANGED
|
@@ -377,10 +377,6 @@ module Mongo
|
|
|
377
377
|
raise_invalid_error!("Options contain an unescaped question mark (?), or the database name contains a question mark and was not escaped")
|
|
378
378
|
end
|
|
379
379
|
|
|
380
|
-
if options && !hosts_and_db.index('/')
|
|
381
|
-
raise_invalid_error!("MongoDB URI must have a slash (/) after the hosts if options are given")
|
|
382
|
-
end
|
|
383
|
-
|
|
384
380
|
hosts, db = hosts_and_db.split('/', 2)
|
|
385
381
|
if db && db.index('/')
|
|
386
382
|
raise_invalid_error!("Database name contains an unescaped slash (/): #{db}")
|
data/lib/mongo/version.rb
CHANGED
data/lib/mongo.rb
CHANGED
data/mongo.gemspec
CHANGED
|
@@ -41,11 +41,5 @@ Gem::Specification.new do |s|
|
|
|
41
41
|
|
|
42
42
|
s.required_ruby_version = ">= 2.5"
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
# but in release version we want to depend on bson < 5.0.0.
|
|
46
|
-
if %w(1 yes true).include?(ENV['MONGO_RUBY_DRIVER_BSON_MASTER'])
|
|
47
|
-
s.add_dependency 'bson', '>=4.13.0', '<6.0.0'
|
|
48
|
-
else
|
|
49
|
-
s.add_dependency 'bson', '>=4.14.1', '<5.0.0'
|
|
50
|
-
end
|
|
44
|
+
s.add_dependency 'bson', '>=4.14.1', '<6.0.0'
|
|
51
45
|
end
|
|
@@ -7,21 +7,17 @@ describe 'Atlas connectivity' do
|
|
|
7
7
|
let(:uri) { ENV['ATLAS_URI'] }
|
|
8
8
|
let(:client) { Mongo::Client.new(uri) }
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
if uri.nil?
|
|
12
|
-
skip "ATLAS_URI not set in environment"
|
|
13
|
-
end
|
|
14
|
-
end
|
|
10
|
+
require_atlas
|
|
15
11
|
|
|
16
12
|
describe 'connection to Atlas' do
|
|
17
13
|
it 'runs ismaster successfully' do
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
expect { client.database.command(:hello => 1) }
|
|
15
|
+
.not_to raise_error
|
|
20
16
|
end
|
|
21
17
|
|
|
22
18
|
it 'runs findOne successfully' do
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
expect { client.use(:test)['test'].find.to_a }
|
|
20
|
+
.not_to raise_error
|
|
25
21
|
end
|
|
26
22
|
end
|
|
27
23
|
end
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'mongo'
|
|
4
|
+
require 'json'
|
|
5
|
+
|
|
6
|
+
class StatsAggregator
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@open_connections = 0
|
|
10
|
+
@heartbeats_count = 0
|
|
11
|
+
@total_heartbeat_time = 0
|
|
12
|
+
@commands_count = 0
|
|
13
|
+
@total_command_time = 0
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def add_command(duration)
|
|
17
|
+
@commands_count += 1
|
|
18
|
+
@total_command_time += duration
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def add_heartbeat(duration)
|
|
22
|
+
@heartbeats_count += 1
|
|
23
|
+
@total_heartbeat_time += duration
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def add_connection
|
|
27
|
+
@open_connections += 1
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def remove_connection
|
|
31
|
+
@open_connections -= 1
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def average_heartbeat_time
|
|
35
|
+
if @heartbeats_count == 0
|
|
36
|
+
0
|
|
37
|
+
else
|
|
38
|
+
@total_heartbeat_time / @heartbeats_count
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def average_command_time
|
|
43
|
+
if @commands_count == 0
|
|
44
|
+
0
|
|
45
|
+
else
|
|
46
|
+
@total_command_time / @commands_count
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def reset
|
|
51
|
+
@open_connections = 0
|
|
52
|
+
@heartbeats_count = 0
|
|
53
|
+
@total_heartbeat_time = 0
|
|
54
|
+
@commands_count = 0
|
|
55
|
+
@total_command_time = 0
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def result
|
|
59
|
+
{
|
|
60
|
+
average_heartbeat_time: average_heartbeat_time,
|
|
61
|
+
average_command_time: average_command_time,
|
|
62
|
+
heartbeats_count: @heartbeats_count,
|
|
63
|
+
open_connections: @open_connections,
|
|
64
|
+
}
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
class CommandMonitor
|
|
69
|
+
|
|
70
|
+
def initialize(stats_aggregator)
|
|
71
|
+
@stats_aggregator = stats_aggregator
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def started(event); end
|
|
75
|
+
|
|
76
|
+
def failed(event)
|
|
77
|
+
@stats_aggregator.add_command(event.duration)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def succeeded(event)
|
|
81
|
+
@stats_aggregator.add_command(event.duration)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
class HeartbeatMonitor
|
|
86
|
+
|
|
87
|
+
def initialize(stats_aggregator)
|
|
88
|
+
@stats_aggregator = stats_aggregator
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def started(event); end
|
|
92
|
+
|
|
93
|
+
def succeeded(event)
|
|
94
|
+
@stats_aggregator.add_heartbeat(event.duration)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def failed(event)
|
|
98
|
+
@stats_aggregator.add_heartbeat(event.duration)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
class PoolMonitor
|
|
103
|
+
|
|
104
|
+
def initialize(stats_aggregator)
|
|
105
|
+
@stats_aggregator = stats_aggregator
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def published(event)
|
|
109
|
+
case event
|
|
110
|
+
when Mongo::Monitoring::Event::Cmap::ConnectionCreated
|
|
111
|
+
@stats_aggregator.add_connection
|
|
112
|
+
when Mongo::Monitoring::Event::Cmap::ConnectionClosed
|
|
113
|
+
@stats_aggregator.remove_connection
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
$stats_aggregator = StatsAggregator.new
|
|
119
|
+
|
|
120
|
+
command_monitor = CommandMonitor.new($stats_aggregator)
|
|
121
|
+
heartbeat_monitor = HeartbeatMonitor.new($stats_aggregator)
|
|
122
|
+
pool_monitor = PoolMonitor.new($stats_aggregator)
|
|
123
|
+
|
|
124
|
+
sdam_proc = proc do |client|
|
|
125
|
+
client.subscribe(Mongo::Monitoring::COMMAND, command_monitor)
|
|
126
|
+
client.subscribe(Mongo::Monitoring::SERVER_HEARTBEAT, heartbeat_monitor)
|
|
127
|
+
client.subscribe(Mongo::Monitoring::CONNECTION_POOL, pool_monitor)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
puts 'Connecting'
|
|
131
|
+
$client = Mongo::Client.new(ENV['MONGODB_URI'], sdam_proc: sdam_proc)
|
|
132
|
+
# Populate the connection pool
|
|
133
|
+
$client.use('lambda_test').database.list_collections
|
|
134
|
+
puts 'Connected'
|
|
135
|
+
|
|
136
|
+
def lambda_handler(event:, context:)
|
|
137
|
+
db = $client.use('lambda_test')
|
|
138
|
+
collection = db[:test_collection]
|
|
139
|
+
result = collection.insert_one({ name: 'test' })
|
|
140
|
+
collection.delete_one({ _id: result.inserted_id })
|
|
141
|
+
response = $stats_aggregator.result.to_json
|
|
142
|
+
$stats_aggregator.reset
|
|
143
|
+
puts "Response: #{response}"
|
|
144
|
+
|
|
145
|
+
{
|
|
146
|
+
statusCode: 200,
|
|
147
|
+
body: response
|
|
148
|
+
}
|
|
149
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
AWSTemplateFormatVersion: '2010-09-09'
|
|
2
|
+
Transform: AWS::Serverless-2016-10-31
|
|
3
|
+
Description: >
|
|
4
|
+
Sample SAM Template for ruby-sam-app
|
|
5
|
+
|
|
6
|
+
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
|
|
7
|
+
Globals:
|
|
8
|
+
Function:
|
|
9
|
+
Timeout: 30
|
|
10
|
+
MemorySize: 128
|
|
11
|
+
|
|
12
|
+
Parameters:
|
|
13
|
+
MongoDbUri:
|
|
14
|
+
Type: String
|
|
15
|
+
Description: The MongoDB connection string.
|
|
16
|
+
|
|
17
|
+
Resources:
|
|
18
|
+
MongoDBFunction:
|
|
19
|
+
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
|
|
20
|
+
Properties:
|
|
21
|
+
CodeUri: mongodb/
|
|
22
|
+
Environment:
|
|
23
|
+
Variables:
|
|
24
|
+
MONGODB_URI: !Ref MongoDbUri
|
|
25
|
+
Handler: app.lambda_handler
|
|
26
|
+
Runtime: ruby3.2
|
|
27
|
+
Architectures:
|
|
28
|
+
- x86_64
|
|
29
|
+
Events:
|
|
30
|
+
MongoDB:
|
|
31
|
+
Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
|
|
32
|
+
Properties:
|
|
33
|
+
Path: /mongodb
|
|
34
|
+
Method: get
|
|
35
|
+
|
|
36
|
+
Outputs:
|
|
37
|
+
# ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
|
|
38
|
+
# Find out more about other implicit resources you can reference within SAM
|
|
39
|
+
# https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
|
|
40
|
+
MongoDBApi:
|
|
41
|
+
Description: "API Gateway endpoint URL for Prod stage for MongoDB function"
|
|
42
|
+
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/mongodb/"
|
|
43
|
+
MongoDBFunction:
|
|
44
|
+
Description: "MongoDB Lambda Function ARN"
|
|
45
|
+
Value: !GetAtt MongoDBFunction.Arn
|
|
46
|
+
MongoDBFunctionIamRole:
|
|
47
|
+
Description: "Implicit IAM Role created for MongoDB function"
|
|
48
|
+
Value: !GetAtt MongoDBFunctionRole.Arn
|
|
@@ -188,6 +188,15 @@ describe 'Client-Side Encryption' do
|
|
|
188
188
|
key_vault_collection.insert_one(kmip_data_key)
|
|
189
189
|
end
|
|
190
190
|
|
|
191
|
+
# This method compensates for an API change between BSON 4 and
|
|
192
|
+
# BSON 5.
|
|
193
|
+
def normalize_cse_value(a)
|
|
194
|
+
case a
|
|
195
|
+
when BSON::Decimal128 then a.to_d
|
|
196
|
+
else a
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
191
200
|
shared_context 'with jsonSchema collection validator' do
|
|
192
201
|
let(:local_schema_map) { nil }
|
|
193
202
|
|
|
@@ -228,12 +237,11 @@ describe 'Client-Side Encryption' do
|
|
|
228
237
|
.find(_id: corpus_encrypted_id)
|
|
229
238
|
.first
|
|
230
239
|
|
|
231
|
-
|
|
232
240
|
corpus_encrypted_actual.each do |key, value|
|
|
233
241
|
# If it was deterministically encrypted, test the encrypted values
|
|
234
242
|
# for equality.
|
|
235
243
|
if value['algo'] == 'det'
|
|
236
|
-
expect(value['value']).to eq(corpus_encrypted_expected[key]['value'])
|
|
244
|
+
expect(normalize_cse_value(value['value'])).to eq(normalize_cse_value(corpus_encrypted_expected[key]['value']))
|
|
237
245
|
else
|
|
238
246
|
# If the document was randomly encrypted, the two encrypted values
|
|
239
247
|
# will not be equal. Ensure that they are equal when decrypted.
|
|
@@ -20,14 +20,14 @@ describe 'Retryable reads errors tests' do
|
|
|
20
20
|
|
|
21
21
|
let(:failpoint) do
|
|
22
22
|
{
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
configureFailPoint: "failCommand",
|
|
24
|
+
mode: { times: 1 },
|
|
25
|
+
data: {
|
|
26
|
+
failCommands: [ "find" ],
|
|
27
|
+
errorCode: 91,
|
|
28
|
+
blockConnection: true,
|
|
29
|
+
blockTimeMS: 1000
|
|
30
|
+
}
|
|
31
31
|
}
|
|
32
32
|
end
|
|
33
33
|
|
|
@@ -107,4 +107,157 @@ describe 'Retryable reads errors tests' do
|
|
|
107
107
|
})
|
|
108
108
|
end
|
|
109
109
|
end
|
|
110
|
+
|
|
111
|
+
context 'Retries in a sharded cluster' do
|
|
112
|
+
require_topology :sharded
|
|
113
|
+
min_server_version '4.2'
|
|
114
|
+
require_no_auth
|
|
115
|
+
|
|
116
|
+
let(:subscriber) { Mrss::EventSubscriber.new }
|
|
117
|
+
|
|
118
|
+
let(:find_started_events) do
|
|
119
|
+
subscriber.started_events.select { |e| e.command_name == "find" }
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
let(:find_failed_events) do
|
|
123
|
+
subscriber.failed_events.select { |e| e.command_name == "find" }
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
let(:find_succeeded_events) do
|
|
127
|
+
subscriber.succeeded_events.select { |e| e.command_name == "find" }
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
context 'when another mongos is available' do
|
|
131
|
+
|
|
132
|
+
let(:first_mongos) do
|
|
133
|
+
Mongo::Client.new(
|
|
134
|
+
[SpecConfig.instance.addresses.first],
|
|
135
|
+
direct_connection: true,
|
|
136
|
+
database: 'admin'
|
|
137
|
+
)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
let(:second_mongos) do
|
|
141
|
+
Mongo::Client.new(
|
|
142
|
+
[SpecConfig.instance.addresses.last],
|
|
143
|
+
direct_connection: false,
|
|
144
|
+
database: 'admin'
|
|
145
|
+
)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
let(:client) do
|
|
149
|
+
new_local_client(
|
|
150
|
+
[
|
|
151
|
+
SpecConfig.instance.addresses.first,
|
|
152
|
+
SpecConfig.instance.addresses.last,
|
|
153
|
+
],
|
|
154
|
+
SpecConfig.instance.test_options.merge(retry_reads: true)
|
|
155
|
+
)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
let(:expected_servers) do
|
|
159
|
+
[
|
|
160
|
+
SpecConfig.instance.addresses.first.to_s,
|
|
161
|
+
SpecConfig.instance.addresses.last.to_s
|
|
162
|
+
].sort
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
before do
|
|
166
|
+
skip 'This test requires at least two mongos' if SpecConfig.instance.addresses.length < 2
|
|
167
|
+
|
|
168
|
+
first_mongos.database.command(
|
|
169
|
+
configureFailPoint: 'failCommand',
|
|
170
|
+
mode: { times: 1 },
|
|
171
|
+
data: {
|
|
172
|
+
failCommands: %w(find),
|
|
173
|
+
closeConnection: false,
|
|
174
|
+
errorCode: 6
|
|
175
|
+
}
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
second_mongos.database.command(
|
|
179
|
+
configureFailPoint: 'failCommand',
|
|
180
|
+
mode: { times: 1 },
|
|
181
|
+
data: {
|
|
182
|
+
failCommands: %w(find),
|
|
183
|
+
closeConnection: false,
|
|
184
|
+
errorCode: 6
|
|
185
|
+
}
|
|
186
|
+
)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
after do
|
|
190
|
+
[first_mongos, second_mongos].each do |admin_client|
|
|
191
|
+
admin_client.database.command(
|
|
192
|
+
configureFailPoint: 'failCommand',
|
|
193
|
+
mode: 'off'
|
|
194
|
+
)
|
|
195
|
+
admin_client.close
|
|
196
|
+
end
|
|
197
|
+
client.close
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
it 'retries on different mongos' do
|
|
201
|
+
client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
|
|
202
|
+
expect { collection.find.first }.to raise_error(Mongo::Error::OperationFailure)
|
|
203
|
+
expect(find_started_events.map { |e| e.address.to_s }.sort).to eq(expected_servers)
|
|
204
|
+
expect(find_failed_events.map { |e| e.address.to_s }.sort).to eq(expected_servers)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
context 'when no other mongos is available' do
|
|
209
|
+
let(:mongos) do
|
|
210
|
+
Mongo::Client.new(
|
|
211
|
+
[SpecConfig.instance.addresses.first],
|
|
212
|
+
direct_connection: true,
|
|
213
|
+
database: 'admin'
|
|
214
|
+
)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
let(:client) do
|
|
218
|
+
new_local_client(
|
|
219
|
+
[
|
|
220
|
+
SpecConfig.instance.addresses.first
|
|
221
|
+
],
|
|
222
|
+
SpecConfig.instance.test_options.merge(retry_reads: true)
|
|
223
|
+
)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
before do
|
|
227
|
+
mongos.database.command(
|
|
228
|
+
configureFailPoint: 'failCommand',
|
|
229
|
+
mode: { times: 1 },
|
|
230
|
+
data: {
|
|
231
|
+
failCommands: %w(find),
|
|
232
|
+
closeConnection: false,
|
|
233
|
+
errorCode: 6
|
|
234
|
+
}
|
|
235
|
+
)
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
after do
|
|
239
|
+
mongos.database.command(
|
|
240
|
+
configureFailPoint: 'failCommand',
|
|
241
|
+
mode: 'off'
|
|
242
|
+
)
|
|
243
|
+
mongos.close
|
|
244
|
+
client.close
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
it 'retries on the same mongos' do
|
|
248
|
+
client.subscribe(Mongo::Monitoring::COMMAND, subscriber)
|
|
249
|
+
expect { collection.find.first }.not_to raise_error
|
|
250
|
+
expect(find_started_events.map { |e| e.address.to_s }.sort).to eq([
|
|
251
|
+
SpecConfig.instance.addresses.first.to_s,
|
|
252
|
+
SpecConfig.instance.addresses.first.to_s
|
|
253
|
+
])
|
|
254
|
+
expect(find_failed_events.map { |e| e.address.to_s }.sort).to eq([
|
|
255
|
+
SpecConfig.instance.addresses.first.to_s
|
|
256
|
+
])
|
|
257
|
+
expect(find_succeeded_events.map { |e| e.address.to_s }.sort).to eq([
|
|
258
|
+
SpecConfig.instance.addresses.first.to_s
|
|
259
|
+
])
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
end
|
|
110
263
|
end
|