Skip to content

Commit 15f8870

Browse files
authored
test(NODE-4165): sync maxConnecting spec tests and update runner (#3239)
1 parent c6a2ddd commit 15f8870

11 files changed

+845
-45
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { HostAddress, MongoClient } from '../../../src';
2+
import { shuffle } from '../../../src/utils';
3+
import { loadSpecTests } from '../../spec';
4+
import { CmapTest, runCmapTest, ThreadContext } from '../../tools/cmap_spec_runner';
5+
import { isAnyRequirementSatisfied } from '../../tools/unified-spec-runner/unified-utils';
6+
7+
// These tests rely on a simple "pool.clear()" command, which is not sufficient
8+
// to properly clear the pool in LB mode, since it requires a serviceId to be passed in
9+
const LB_SKIP_TESTS = [
10+
'must destroy checked in connection if it is stale',
11+
'must destroy and must not check out a stale connection if found while iterating available connections'
12+
];
13+
14+
describe('Connection Monitoring and Pooling Spec Tests (Integration)', function () {
15+
const suites: CmapTest[] = loadSpecTests('connection-monitoring-and-pooling');
16+
17+
for (const test of suites.filter(test => {
18+
// TODO(NODE-2993): unskip integration tests for maxConnecting
19+
return test.style === 'unit';
20+
})) {
21+
describe(test.description, function () {
22+
let hostAddress: HostAddress, threadContext: ThreadContext, client: MongoClient;
23+
24+
beforeEach(async function () {
25+
let utilClient: MongoClient;
26+
if (this.configuration.isLoadBalanced) {
27+
if (LB_SKIP_TESTS.some(testDescription => testDescription === test.description)) {
28+
this.currentTest.skipReason = 'cannot run against a load balanced environment';
29+
this.skip();
30+
}
31+
// The util client can always point at the single mongos LB frontend.
32+
utilClient = this.configuration.newClient(this.configuration.singleMongosLoadBalancerUri);
33+
} else {
34+
utilClient = this.configuration.newClient();
35+
}
36+
37+
await utilClient.connect();
38+
39+
const allRequirements = test.runOn || [];
40+
41+
const someRequirementMet =
42+
!allRequirements.length ||
43+
(await isAnyRequirementSatisfied(this.currentTest.ctx, allRequirements, utilClient));
44+
45+
if (!someRequirementMet) {
46+
await utilClient.close();
47+
this.skip();
48+
// NOTE: the rest of the code below won't execute after the skip is invoked
49+
}
50+
51+
try {
52+
const serverMap = utilClient.topology.s.description.servers;
53+
const hosts = shuffle(serverMap.keys());
54+
const selectedHostUri = hosts[0];
55+
hostAddress = serverMap.get(selectedHostUri).hostAddress;
56+
threadContext = new ThreadContext(
57+
hostAddress,
58+
this.configuration.isLoadBalanced ? { loadBalanced: true } : {}
59+
);
60+
61+
if (test.failPoint) {
62+
client = this.configuration.newClient(
63+
`mongodb://${hostAddress}/${
64+
this.configuration.isLoadBalanced ? '?loadBalanced=true' : '?directConnection=true'
65+
}`
66+
);
67+
await client.connect();
68+
await client.db('admin').command(test.failPoint);
69+
}
70+
} finally {
71+
await utilClient.close();
72+
}
73+
});
74+
75+
afterEach(async function () {
76+
await threadContext?.tearDown();
77+
if (!client) {
78+
return;
79+
}
80+
if (test.failPoint) {
81+
await client
82+
.db('admin')
83+
.command({ configureFailPoint: test.failPoint.configureFailPoint, mode: 'off' });
84+
}
85+
await client.close();
86+
});
87+
88+
it('should pass', async function () {
89+
await runCmapTest(test, threadContext);
90+
});
91+
});
92+
}
93+
});
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
{
2+
"version": 1,
3+
"style": "integration",
4+
"description": "maxConnecting is enforced",
5+
"runOn": [
6+
{
7+
"minServerVersion": "4.4.0"
8+
}
9+
],
10+
"failPoint": {
11+
"configureFailPoint": "failCommand",
12+
"mode": {
13+
"times": 50
14+
},
15+
"data": {
16+
"failCommands": [
17+
"isMaster",
18+
"hello"
19+
],
20+
"closeConnection": false,
21+
"blockConnection": true,
22+
"blockTimeMS": 750
23+
}
24+
},
25+
"poolOptions": {
26+
"maxPoolSize": 10,
27+
"waitQueueTimeoutMS": 5000
28+
},
29+
"operations": [
30+
{
31+
"name": "start",
32+
"target": "thread1"
33+
},
34+
{
35+
"name": "start",
36+
"target": "thread2"
37+
},
38+
{
39+
"name": "start",
40+
"target": "thread3"
41+
},
42+
{
43+
"name": "checkOut",
44+
"thread": "thread1"
45+
},
46+
{
47+
"name": "waitForEvent",
48+
"event": "ConnectionCreated",
49+
"count": 1
50+
},
51+
{
52+
"name": "wait",
53+
"ms": 100
54+
},
55+
{
56+
"name": "checkOut",
57+
"thread": "thread2"
58+
},
59+
{
60+
"name": "checkOut",
61+
"thread": "thread3"
62+
},
63+
{
64+
"name": "waitForEvent",
65+
"event": "ConnectionReady",
66+
"count": 3
67+
}
68+
],
69+
"events": [
70+
{
71+
"type": "ConnectionCreated",
72+
"address": 42,
73+
"connectionId": 1
74+
},
75+
{
76+
"type": "ConnectionCreated",
77+
"address": 42
78+
},
79+
{
80+
"type": "ConnectionReady",
81+
"address": 42,
82+
"connectionId": 1
83+
},
84+
{
85+
"type": "ConnectionCreated",
86+
"address": 42
87+
},
88+
{
89+
"type": "ConnectionReady",
90+
"address": 42
91+
},
92+
{
93+
"type": "ConnectionReady",
94+
"address": 42
95+
}
96+
],
97+
"ignore": [
98+
"ConnectionCheckOutStarted",
99+
"ConnectionCheckedIn",
100+
"ConnectionCheckedOut",
101+
"ConnectionClosed",
102+
"ConnectionPoolCreated"
103+
]
104+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
version: 1
2+
style: integration
3+
description: maxConnecting is enforced
4+
runOn:
5+
-
6+
# required for blockConnection in fail point
7+
minServerVersion: "4.4.0"
8+
failPoint:
9+
configureFailPoint: failCommand
10+
# high amount to ensure not interfered with by monitor checks.
11+
mode: { times: 50 }
12+
data:
13+
failCommands: ["isMaster","hello"]
14+
closeConnection: false
15+
blockConnection: true
16+
blockTimeMS: 750
17+
poolOptions:
18+
maxPoolSize: 10
19+
waitQueueTimeoutMS: 5000
20+
operations:
21+
# start 3 threads
22+
- name: start
23+
target: thread1
24+
- name: start
25+
target: thread2
26+
- name: start
27+
target: thread3
28+
# start creating a Connection. This will take a while
29+
# due to the fail point.
30+
- name: checkOut
31+
thread: thread1
32+
# wait for thread1 to actually start creating a Connection
33+
- name: waitForEvent
34+
event: ConnectionCreated
35+
count: 1
36+
# wait some more time to ensure thread1 has begun establishing a Connection
37+
- name: wait
38+
ms: 100
39+
# start 2 check out requests. Only one thread should
40+
# start creating a Connection and the other one should be
41+
# waiting for pendingConnectionCount to be less than maxConnecting,
42+
# only starting once thread1 finishes creating its Connection.
43+
- name: checkOut
44+
thread: thread2
45+
- name: checkOut
46+
thread: thread3
47+
# wait until all Connections have been created.
48+
- name: waitForEvent
49+
event: ConnectionReady
50+
count: 3
51+
events:
52+
# thread1 creates its connection
53+
- type: ConnectionCreated
54+
address: 42
55+
connectionId: 1
56+
# either thread2 or thread3 creates its connection
57+
# the other thread is stuck waiting for maxConnecting to come down
58+
- type: ConnectionCreated
59+
address: 42
60+
# thread1 finishes establishing its connection, freeing
61+
# up the blocked thread to start establishing
62+
- type: ConnectionReady
63+
address: 42
64+
connectionId: 1
65+
- type: ConnectionCreated
66+
address: 42
67+
# the remaining two Connections finish establishing
68+
- type: ConnectionReady
69+
address: 42
70+
- type: ConnectionReady
71+
address: 42
72+
ignore:
73+
- ConnectionCheckOutStarted
74+
- ConnectionCheckedIn
75+
- ConnectionCheckedOut
76+
- ConnectionClosed
77+
- ConnectionPoolCreated
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
{
2+
"version": 1,
3+
"style": "integration",
4+
"description": "waiting on maxConnecting is limited by WaitQueueTimeoutMS",
5+
"runOn": [
6+
{
7+
"minServerVersion": "4.4.0"
8+
}
9+
],
10+
"failPoint": {
11+
"configureFailPoint": "failCommand",
12+
"mode": {
13+
"times": 50
14+
},
15+
"data": {
16+
"failCommands": [
17+
"isMaster",
18+
"hello"
19+
],
20+
"closeConnection": false,
21+
"blockConnection": true,
22+
"blockTimeMS": 750
23+
}
24+
},
25+
"poolOptions": {
26+
"maxPoolSize": 10,
27+
"waitQueueTimeoutMS": 50
28+
},
29+
"operations": [
30+
{
31+
"name": "start",
32+
"target": "thread1"
33+
},
34+
{
35+
"name": "checkOut",
36+
"thread": "thread1"
37+
},
38+
{
39+
"name": "start",
40+
"target": "thread2"
41+
},
42+
{
43+
"name": "checkOut",
44+
"thread": "thread2"
45+
},
46+
{
47+
"name": "waitForEvent",
48+
"event": "ConnectionCreated",
49+
"count": 2
50+
},
51+
{
52+
"name": "start",
53+
"target": "thread3"
54+
},
55+
{
56+
"name": "checkOut",
57+
"thread": "thread3"
58+
},
59+
{
60+
"name": "waitForEvent",
61+
"event": "ConnectionCheckOutFailed",
62+
"count": 1
63+
},
64+
{
65+
"name": "waitForThread",
66+
"target": "thread3"
67+
}
68+
],
69+
"error": {
70+
"type": "WaitQueueTimeoutError",
71+
"message": "Timed out while checking out a connection from connection pool"
72+
},
73+
"events": [
74+
{
75+
"type": "ConnectionCheckOutStarted",
76+
"address": 42
77+
},
78+
{
79+
"type": "ConnectionCheckOutStarted",
80+
"address": 42
81+
},
82+
{
83+
"type": "ConnectionCheckOutStarted",
84+
"address": 42
85+
},
86+
{
87+
"type": "ConnectionCheckOutFailed",
88+
"reason": "timeout",
89+
"address": 42
90+
}
91+
],
92+
"ignore": [
93+
"ConnectionCreated",
94+
"ConnectionCheckedIn",
95+
"ConnectionCheckedOut",
96+
"ConnectionClosed",
97+
"ConnectionPoolCreated"
98+
]
99+
}

0 commit comments

Comments
 (0)