Skip to content

Commit 058632f

Browse files
RUBY-3742 Fix backpressure tests (#2994)
1 parent 1f75079 commit 058632f

File tree

7 files changed

+267
-9
lines changed

7 files changed

+267
-9
lines changed

.evergreen/config.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1273,7 +1273,7 @@ buildvariants:
12731273
- matrix_name: CSOT
12741274
matrix_spec:
12751275
ruby: "ruby-4.0"
1276-
mongodb-version: "8.0"
1276+
mongodb-version: ["8.0", "rapid"]
12771277
topology: replica-set-single-node
12781278
os: ubuntu2204
12791279
display_name: "CSOT - ${mongodb-version}"
@@ -1532,7 +1532,7 @@ buildvariants:
15321532
auth-and-ssl: "noauth-and-nossl"
15331533
ruby: ["ruby-4.0", "ruby-3.4", "ruby-3.3", "ruby-3.2", "ruby-3.1"]
15341534
topology: [replica-set, sharded-cluster]
1535-
mongodb-version: [ '6.0', '7.0', '8.0' ]
1535+
mongodb-version: [ '6.0', '7.0', '8.0', 'rapid' ]
15361536
os: ubuntu2204
15371537
fle: helper
15381538
display_name: "FLE: ${mongodb-version} ${topology} ${ruby}"

.evergreen/config/standard.yml.erb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ buildvariants:
151151
- matrix_name: CSOT
152152
matrix_spec:
153153
ruby: <%= latest_ruby %>
154-
mongodb-version: <%= latest_stable_mdb %>
154+
mongodb-version: <%= stable_and_rapid %>
155155
topology: replica-set-single-node
156156
os: ubuntu2204
157157
display_name: "CSOT - ${mongodb-version}"
@@ -350,7 +350,7 @@ buildvariants:
350350
auth-and-ssl: "noauth-and-nossl"
351351
ruby: <%= supported_mri_rubies_3_ubuntu %>
352352
topology: [replica-set, sharded-cluster]
353-
mongodb-version: [ '6.0', '7.0', '8.0' ]
353+
mongodb-version: [ '6.0', '7.0', '8.0', 'rapid' ]
354354
os: ubuntu2204
355355
fle: helper
356356
display_name: "FLE: ${mongodb-version} ${topology} ${ruby}"

spec/integration/sdam_prose_spec.rb

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,120 @@
6464
configureFailPoint: 'failCommand', mode: 'off')
6565
end
6666
end
67+
68+
describe 'Connection Pool Backpressure' do
69+
min_server_fcv '8.2'
70+
require_topology :single
71+
72+
let(:subscriber) { Mrss::EventSubscriber.new }
73+
74+
let(:client) do
75+
new_local_client(
76+
SpecConfig.instance.addresses,
77+
SpecConfig.instance.all_test_options.merge(
78+
max_connecting: 100,
79+
max_pool_size: 100,
80+
),
81+
).tap do |client|
82+
client.subscribe(Mongo::Monitoring::CONNECTION_POOL, subscriber)
83+
end
84+
end
85+
86+
after do
87+
sleep 1
88+
admin_db = root_authorized_client.use('admin').database
89+
90+
if defined?(@prev_ingressConnectionEstablishmentRateLimiterEnabled) &&
91+
defined?(@prev_ingressConnectionEstablishmentRatePerSec) &&
92+
defined?(@prev_ingressConnectionEstablishmentBurstCapacitySecs) &&
93+
defined?(@prev_ingressConnectionEstablishmentMaxQueueDepth)
94+
admin_db.command(
95+
setParameter: 1,
96+
ingressConnectionEstablishmentRateLimiterEnabled: @prev_ingressConnectionEstablishmentRateLimiterEnabled,
97+
)
98+
admin_db.command(
99+
setParameter: 1,
100+
ingressConnectionEstablishmentRatePerSec: @prev_ingressConnectionEstablishmentRatePerSec,
101+
)
102+
admin_db.command(
103+
setParameter: 1,
104+
ingressConnectionEstablishmentBurstCapacitySecs: @prev_ingressConnectionEstablishmentBurstCapacitySecs,
105+
)
106+
admin_db.command(
107+
setParameter: 1,
108+
ingressConnectionEstablishmentMaxQueueDepth: @prev_ingressConnectionEstablishmentMaxQueueDepth,
109+
)
110+
else
111+
# Fallback: at least disable the limiter if previous values were not captured.
112+
admin_db.command(
113+
setParameter: 1,
114+
ingressConnectionEstablishmentRateLimiterEnabled: false,
115+
)
116+
end
117+
end
118+
119+
it 'generates checkout failures when the ingress connection rate limiter is active' do
120+
admin_db = root_authorized_client.use('admin').database
121+
122+
# Capture current ingress connection establishment parameters so they can be restored.
123+
current_params = admin_db.command(
124+
getParameter: 1,
125+
ingressConnectionEstablishmentRateLimiterEnabled: 1,
126+
ingressConnectionEstablishmentRatePerSec: 1,
127+
ingressConnectionEstablishmentBurstCapacitySecs: 1,
128+
ingressConnectionEstablishmentMaxQueueDepth: 1,
129+
).first
130+
131+
@prev_ingressConnectionEstablishmentRateLimiterEnabled =
132+
current_params['ingressConnectionEstablishmentRateLimiterEnabled']
133+
@prev_ingressConnectionEstablishmentRatePerSec =
134+
current_params['ingressConnectionEstablishmentRatePerSec']
135+
@prev_ingressConnectionEstablishmentBurstCapacitySecs =
136+
current_params['ingressConnectionEstablishmentBurstCapacitySecs']
137+
@prev_ingressConnectionEstablishmentMaxQueueDepth =
138+
current_params['ingressConnectionEstablishmentMaxQueueDepth']
139+
140+
# Enable the ingress rate limiter with test-specific values.
141+
admin_db.command(
142+
setParameter: 1,
143+
ingressConnectionEstablishmentRateLimiterEnabled: true,
144+
)
145+
admin_db.command(
146+
setParameter: 1,
147+
ingressConnectionEstablishmentRatePerSec: 20,
148+
)
149+
admin_db.command(
150+
setParameter: 1,
151+
ingressConnectionEstablishmentBurstCapacitySecs: 1,
152+
)
153+
admin_db.command(
154+
setParameter: 1,
155+
ingressConnectionEstablishmentMaxQueueDepth: 1,
156+
)
157+
158+
# Add a document so $where has something to process.
159+
client.use('test')['test'].delete_many
160+
client.use('test')['test'].insert_one({})
161+
162+
# Run 100 parallel find_one operations that contend for connections.
163+
threads = 100.times.map do
164+
Thread.new do
165+
begin
166+
client.use('test')['test'].find(
167+
'$where' => 'function() { sleep(2000); return true; }'
168+
).first
169+
rescue StandardError
170+
# Ignore connection errors (including checkout timeouts).
171+
end
172+
end
173+
end
174+
threads.each(&:join)
175+
176+
checkout_failed = subscriber.select_published_events(
177+
Mongo::Monitoring::Event::Cmap::ConnectionCheckOutFailed
178+
)
179+
180+
expect(checkout_failed.length).to be >= 10
181+
end
182+
end
67183
end

spec/integration/secondary_reads_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
end_stats = get_read_counters
3030

31-
end_stats[:secondary].should be_within(10).of(start_stats[:secondary])
31+
end_stats[:secondary].should be_within(50).of(start_stats[:secondary])
3232
end_stats[:primary].should >= start_stats[:primary] + 30
3333
end
3434
end
@@ -50,7 +50,7 @@
5050

5151
end_stats = get_read_counters
5252

53-
end_stats[:primary].should be_within(10).of(start_stats[:primary])
53+
end_stats[:primary].should be_within(50).of(start_stats[:primary])
5454
end_stats[:secondary].should >= start_stats[:secondary] + 30
5555
end
5656
end

spec/mongo/retryable/token_bucket_spec.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,16 @@
6767
end
6868

6969
describe 'thread safety' do
70-
let(:bucket) { described_class.new(capacity: 1000) }
70+
# Use capacity 2000, start at 1000 tokens.
71+
# With 500 consumes and 500 deposits, floor/ceiling cannot be hit:
72+
# min possible = 1000 - 500 = 500 > 0 (all consumes succeed)
73+
# max possible = 1000 + 500 = 1500 < 2000 (all deposits effective)
74+
# So the net change is guaranteed to be 0, making the assertion reliable.
75+
let(:bucket) do
76+
b = described_class.new(capacity: 2000)
77+
b.consume(1000)
78+
b
79+
end
7180

7281
def run_concurrent_operations(bucket)
7382
threads = []

spec/spec_tests/data/sdam_unified/minPoolSize-error.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ initialData: &initialData
2121
documents: []
2222

2323
tests:
24-
- description: Network error on minPoolSize background creation
24+
- description: Server error on minPoolSize background creation
2525
operations:
2626
# Configure the initial monitor handshake to succeed but the
2727
# first or second background minPoolSize establishments to fail.
@@ -38,7 +38,7 @@ tests:
3838
- hello
3939
- isMaster
4040
appName: SDAMminPoolSizeError
41-
closeConnection: true
41+
errorCode: 91
4242
- name: createEntities
4343
object: testRunner
4444
arguments:
@@ -54,6 +54,7 @@ tests:
5454
heartbeatFrequencyMS: 10000
5555
appname: SDAMminPoolSizeError
5656
minPoolSize: 10
57+
serverMonitoringMode: poll
5758
serverSelectionTimeoutMS: 1000
5859
- database:
5960
id: &database database
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
---
2+
description: pool-cleared-on-min-pool-size-population-error
3+
4+
schemaVersion: "1.4"
5+
6+
runOnRequirements:
7+
# failCommand appName requirements
8+
- minServerVersion: "4.4"
9+
serverless: forbid
10+
topologies: [ single ]
11+
12+
createEntities:
13+
- client:
14+
id: &setupClient setupClient
15+
useMultipleMongoses: false
16+
17+
tests:
18+
- description: Pool is cleared on authentication error during minPoolSize population
19+
runOnRequirements:
20+
# failCommand appName requirements
21+
- auth: true
22+
operations:
23+
- name: failPoint
24+
object: testRunner
25+
arguments:
26+
client: *setupClient
27+
failPoint:
28+
configureFailPoint: failCommand
29+
mode:
30+
times: 1
31+
data:
32+
failCommands:
33+
- saslContinue
34+
appName: authErrorTest
35+
errorCode: 18
36+
- name: createEntities
37+
object: testRunner
38+
arguments:
39+
entities:
40+
- client:
41+
id: &client client
42+
observeEvents:
43+
- poolReadyEvent
44+
- poolClearedEvent
45+
- connectionClosedEvent
46+
uriOptions:
47+
appname: authErrorTest
48+
minPoolSize: 1
49+
50+
- name: waitForEvent
51+
object: testRunner
52+
arguments:
53+
client: *client
54+
event:
55+
poolReadyEvent: {}
56+
count: 1
57+
58+
- name: waitForEvent
59+
object: testRunner
60+
arguments:
61+
client: *client
62+
event:
63+
poolClearedEvent: {}
64+
count: 1
65+
66+
- name: waitForEvent
67+
object: testRunner
68+
arguments:
69+
client: *client
70+
event:
71+
connectionClosedEvent: {}
72+
count: 1
73+
74+
- description: Pool is not cleared on handshake error during minPoolSize population
75+
operations:
76+
- name: failPoint
77+
object: testRunner
78+
arguments:
79+
client: *setupClient
80+
failPoint:
81+
configureFailPoint: failCommand
82+
mode:
83+
skip: 1 # skip one to let monitoring thread to move pool to ready state
84+
data:
85+
failCommands:
86+
- hello
87+
- isMaster
88+
appName: authErrorTest
89+
closeConnection: true
90+
91+
- name: createEntities
92+
object: testRunner
93+
arguments:
94+
entities:
95+
- client:
96+
id: &client client
97+
observeEvents:
98+
- poolReadyEvent
99+
- poolClearedEvent
100+
- connectionClosedEvent
101+
uriOptions:
102+
appname: authErrorTest
103+
minPoolSize: 5
104+
maxConnecting: 1
105+
# ensure that once we've connected to the server, the failCommand won't
106+
# be triggered by monitors and will only be triggered by handshakes
107+
serverMonitoringMode: poll
108+
heartbeatFrequencyMS: 1000000
109+
110+
- name: waitForEvent
111+
object: testRunner
112+
arguments:
113+
client: *client
114+
event:
115+
poolReadyEvent: {}
116+
count: 1
117+
118+
- name: waitForEvent
119+
object: testRunner
120+
arguments:
121+
client: *client
122+
event:
123+
connectionClosedEvent: {}
124+
count: 1
125+
126+
- name: assertEventCount
127+
object: testRunner
128+
arguments:
129+
client: *client
130+
event:
131+
poolClearedEvent: {}
132+
count: 0

0 commit comments

Comments
 (0)