Skip to content

Commit 8adca00

Browse files
refactor for table
1 parent 4798bc2 commit 8adca00

File tree

4 files changed

+122
-136
lines changed

4 files changed

+122
-136
lines changed

test/integration/client-close/client_close.test.ts renamed to test/integration/node-specific/client_close.test.ts

Lines changed: 72 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,165 +1,93 @@
1-
import { fork } from 'child_process';
2-
import {
3-
MongoClient,
4-
} from '../../mongodb';
5-
import { on, once } from 'node:events';
6-
import { readFile, unlink, writeFile } from 'node:fs/promises';
71
import { TestConfiguration } from '../../tools/runner/config';
8-
import { expect } from 'chai';
9-
import { StringOrPlaceholder } from '../../tools/unified-spec-runner/schema';
10-
11-
/*
12-
export async function testScriptFactory(
13-
name: string,
14-
uri: string,
15-
iterations: number,
16-
func: Function
17-
) {
18-
let resourceScript = await readFile(RESOURCE_SCRIPT_PATH, { encoding: 'utf8' });
19-
20-
resourceScript = resourceScript.replace('DRIVER_SOURCE_PATH', DRIVER_SRC_PATH);
21-
resourceScript = resourceScript.replace('FUNCTION_STRING', `(${func.toString()})`);
22-
resourceScript = resourceScript.replace('NAME_STRING', JSON.stringify(name));
23-
resourceScript = resourceScript.replace('URI_STRING', JSON.stringify(uri));
24-
resourceScript = resourceScript.replace('ITERATIONS_STRING', `${iterations}`);
25-
26-
return resourceScript;
27-
}
28-
29-
export async function runScriptAndReturnResourceInfo(
30-
name: string,
31-
config: TestConfiguration,
32-
func: Function
33-
) {
34-
35-
const pathName = `scripts/${name}.cjs`;
36-
const scriptContent = await testScriptFactory(name, config.url(), func);
37-
await writeFile(name, func.toString(), { encoding: 'utf8' });
38-
39-
const processDiedController = new AbortController();
40-
const script = fork(name, { execArgv: ['--expose-gc'] });
41-
42-
// Interrupt our awaiting of messages if the process crashed
43-
script.once('close', exitCode => {
44-
if (exitCode !== 0) {
45-
processDiedController.abort(new Error(`process exited with: ${exitCode}`));
46-
}
47-
});
48-
49-
const willClose = once(script, 'close');
50-
51-
// make sure the process ended
52-
const [exitCode] = await willClose;
53-
expect(exitCode, 'process should have exited with zero').to.equal(0);
54-
55-
return process.report.getReport().libuv;
56-
}
57-
*/
2+
import { runScriptAndReturnResourceInfo } from './resource_tracking_script_builder';
583

594
describe.only('client.close() Integration', () => {
60-
let client: MongoClient;
615
let config: TestConfiguration;
626
beforeEach(function () {
637
config = this.configuration;
64-
client = this.configuration.newClient();
658
});
669

67-
describe('File System', () => {
68-
describe('when client is connected and reading a TLS long file', () => {
69-
it('the file read is interrupted by client.close', () => {
70-
71-
});
72-
});
73-
describe('when client is created and reading a long docker file', () => {
10+
describe('MongoClient', () => {
11+
describe('when client is being instantiated and reads a long docker file', () => {
7412
// our docker env detection uses fs.access which will not be aborted until after it runs
7513
// fs.access does not support abort signals
7614
it('the file read is not interrupted by client.close', () => {
7715
});
7816
});
79-
80-
describe('when FLE is enabled and the client has made a KMS request that is reading a long TLS file', () => {
17+
describe('when client is connecting and reads a TLS long file', () => {
8118
it('the file read is interrupted by client.close', () => {
82-
8319
});
8420
});
8521
});
8622

87-
describe('Connection Creation and Socket Lifetime', () => {
88-
describe('after client is connected', () => {
89-
it('no sockets remain after client.close', () => {
90-
91-
});
92-
it('no server-side connection threads remain after client.close', () => {
93-
23+
describe('MongoClientAuthProviders', () => {
24+
describe('when MongoClientAuthProviders is instantiated', () => {
25+
it('the token cache is cleaned up by client.close', () => {
9426
});
9527
});
28+
});
9629

97-
describe('after a connection is checked out', () => {
98-
it('no sockets remain after client.close', () => {
30+
describe('Topology', () => {
31+
describe('after a Topology is explicitly created', () => {
32+
it('timers are cleaned up by client.close()', () => {
9933

10034
});
101-
it('no server-side connection threads remain after client.close', () => {
35+
});
36+
describe('after a Topology is created through client.connect()', () => {
37+
it('timers are cleaned up by client.close()', () => {
10238

10339
});
10440
});
41+
});
10542

106-
describe('after a minPoolSize has been set on the ConnectionPool', () => {
107-
it('no sockets remain after client.close', () => {
108-
109-
});
110-
it('no server-side connection threads remain after client.close', () => {
43+
describe('SRVPoller', () => {
44+
describe('after SRVPoller is explicitly created', () => {
45+
it('timers are cleaned up by client.close()', () => {
11146

11247
});
11348
});
11449

115-
describe('when connection monitoring is turned on', () => {
116-
it('no sockets remain after client.close', () => {
117-
118-
});
119-
it('no server-side connection threads remain after client.close', () => {
50+
// SRVPoller is implicitly created after an SRV string's topology transitions to sharded
51+
describe('after SRVPoller is implicitly created', () => {
52+
it('timers are cleaned up by client.close()', () => {
12053

12154
});
12255
});
56+
});
12357

124-
describe('when rtt monitoring is turned on', () => {
125-
it('no sockets remain after client.close', () => {
126-
127-
});
128-
it('no server-side connection threads remain after client.close', () => {
58+
describe('ClientSession', () => {
59+
describe('after a clientSession is created', () => {
60+
it('the server-side ServerSession and transaction are cleaned up by client.close()', () => {
12961

13062
});
13163
});
64+
});
13265

66+
describe('StateMachine', () => {
13367
describe('when FLE is enabled and the client has made a KMS request', () => {
13468
it('no sockets remain after client.close', () => {
13569

13670
});
13771
it('no server-side connection threads remain after client.close', () => {
13872

13973
});
140-
});
141-
});
142-
143-
describe('Timers', () => {
144-
describe('after SRVPoller is explicitly created', () => {
145-
it('timers are cleaned up by client.close()', () => {
146-
147-
});
148-
});
149-
150-
// SRVPoller is implicitly created after an SRV string's topology transitions to sharded
151-
describe('after SRVPoller is implicitly created', () => {
152-
it('timers are cleaned up by client.close()', () => {
74+
describe('when the TLS file read hangs', () => {
75+
it('the file read is interrupted by client.close', () => {
15376

77+
});
15478
});
15579
});
80+
});
15681

82+
describe('ConnectionPool', () => {
15783
describe('after new connection pool is created', () => {
15884
it('minPoolSize timer is cleaned up by client.close()', () => {
15985

16086
});
16187
});
88+
});
16289

90+
describe('MonitorInterval', () => {
16391
describe('after a new monitor is made', () => {
16492
it('monitor interval timer is cleaned up by client.close()', () => {
16593

@@ -171,25 +99,57 @@ describe.only('client.close() Integration', () => {
17199

172100
});
173101
});
102+
});
174103

104+
describe('RTTPinger', () => {
175105
describe('after helloReply has a topologyVersion defined fails', () => {
176106
it('rtt pinger timer is cleaned up by client.close()', () => {
177107

178108
});
179109
});
180110
});
181111

182-
describe('Cursor Clean-up', () => {
183-
describe('after cursors are created', () => {
184-
it('all active server-side cursors are closed by client.close()', () => {
112+
describe('Connection', () => {
113+
describe('when connection monitoring is turned on', () => {
114+
it('no sockets remain after client.close', () => {
115+
116+
});
117+
it('no server-side connection threads remain after client.close', () => {
118+
119+
});
120+
});
121+
122+
describe('when rtt monitoring is turned on', () => {
123+
it('no sockets remain after client.close', () => {
124+
125+
});
126+
it('no server-side connection threads remain after client.close', () => {
127+
128+
});
129+
});
130+
131+
describe('after a connection is checked out', () => {
132+
it('no sockets remain after client.close', () => {
133+
134+
});
135+
it('no server-side connection threads remain after client.close', () => {
136+
137+
});
138+
});
139+
140+
describe('after a minPoolSize has been set on the ConnectionPool', () => {
141+
it('no sockets remain after client.close', () => {
142+
143+
});
144+
it('no server-side connection threads remain after client.close', () => {
185145

186146
});
187147
});
188148
});
189149

190-
describe('Sessions', () => {
191-
describe('after a clientSession is created', () => {
192-
it('the server-side ServerSession is cleaned up by client.close()', () => {
150+
describe('Cursor', () => {
151+
describe('after cursors are created', () => {
152+
it('all active server-side cursors are closed by client.close()', () => {
193153

194154
});
195155
});

test/integration/node-specific/resource_clean_up.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as v8 from 'node:v8';
33
import { expect } from 'chai';
44

55
import { sleep } from '../../tools/utils';
6-
import { runScript } from './resource_tracking_script_builder';
6+
import { runScriptAndReturnHeapInfo } from './resource_tracking_script_builder';
77

88
/**
99
* This 5MB range is selected arbitrarily and should likely be raised if failures are seen intermittently.
@@ -38,7 +38,7 @@ describe('Driver Resources', () => {
3838
return;
3939
}
4040
try {
41-
const res = await runScript(
41+
const res = await runScriptAndReturnHeapInfo(
4242
'no_resource_leak_connect_close',
4343
this.configuration,
4444
async function run({ MongoClient, uri }) {

test/integration/node-specific/resource_tracking_script_builder.ts

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,26 @@ export type ResourceTestFunction = (options: {
1515
iteration: number;
1616
}) => Promise<void>;
1717

18-
const RESOURCE_SCRIPT_PATH = path.resolve(__dirname, '../../tools/fixtures/resource_script.in.js');
18+
const HEAP_RESOURCE_SCRIPT_PATH = path.resolve(__dirname, '../../tools/fixtures/resource_script.in.js');
19+
const REPORT_RESOURCE_SCRIPT_PATH = path.resolve(__dirname, '../../tools/fixtures/close_resource_script.in.js');
1920
const DRIVER_SRC_PATH = JSON.stringify(path.resolve(__dirname, '../../../lib'));
2021

2122
export async function testScriptFactory(
2223
name: string,
2324
uri: string,
24-
iterations: number,
25-
func: ResourceTestFunction
25+
resourceScriptPath: string,
26+
func: ResourceTestFunction,
27+
iterations?: number,
2628
) {
27-
let resourceScript = await readFile(RESOURCE_SCRIPT_PATH, { encoding: 'utf8' });
29+
let resourceScript = await readFile(resourceScriptPath, { encoding: 'utf8' });
2830

2931
resourceScript = resourceScript.replace('DRIVER_SOURCE_PATH', DRIVER_SRC_PATH);
3032
resourceScript = resourceScript.replace('FUNCTION_STRING', `(${func.toString()})`);
3133
resourceScript = resourceScript.replace('NAME_STRING', JSON.stringify(name));
3234
resourceScript = resourceScript.replace('URI_STRING', JSON.stringify(uri));
33-
resourceScript = resourceScript.replace('ITERATIONS_STRING', `${iterations}`);
35+
if (resourceScriptPath === HEAP_RESOURCE_SCRIPT_PATH) {
36+
resourceScript = resourceScript.replace('ITERATIONS_STRING', `${iterations}`);
37+
}
3438

3539
return resourceScript;
3640
}
@@ -57,7 +61,7 @@ export async function testScriptFactory(
5761
* @param options - settings for the script
5862
* @throws Error - if the process exits with failure
5963
*/
60-
export async function runScript(
64+
export async function runScriptAndReturnHeapInfo(
6165
name: string,
6266
config: TestConfiguration,
6367
func: ResourceTestFunction,
@@ -66,7 +70,7 @@ export async function runScript(
6670
const scriptName = `${name}.cjs`;
6771
const heapsnapshotFile = `${name}.heapsnapshot.json`;
6872

69-
const scriptContent = await testScriptFactory(name, config.url(), iterations, func);
73+
const scriptContent = await testScriptFactory(name, config.url(), HEAP_RESOURCE_SCRIPT_PATH, func, iterations);
7074
await writeFile(scriptName, scriptContent, { encoding: 'utf8' });
7175

7276
const processDiedController = new AbortController();
@@ -106,3 +110,34 @@ export async function runScript(
106110
heap
107111
};
108112
}
113+
114+
export async function runScriptAndReturnResourceInfo(
115+
name: string,
116+
config: TestConfiguration,
117+
func: ResourceTestFunction
118+
) {
119+
120+
const scriptName = `scripts/${name}.cjs`;
121+
const scriptContent = await testScriptFactory(name, config.url(), REPORT_RESOURCE_SCRIPT_PATH, func);
122+
await writeFile(scriptName, scriptContent, { encoding: 'utf8' });
123+
124+
const processDiedController = new AbortController();
125+
const script = fork(name);
126+
127+
// Interrupt our awaiting of messages if the process crashed
128+
script.once('close', exitCode => {
129+
if (exitCode !== 0) {
130+
processDiedController.abort(new Error(`process exited with: ${exitCode}`));
131+
}
132+
});
133+
134+
const willClose = once(script, 'close');
135+
136+
// make sure the process ended
137+
const [exitCode] = await willClose;
138+
expect(exitCode, 'process should have exited with zero').to.equal(0);
139+
140+
const messages = on(script, 'message', { signal: processDiedController.signal });
141+
const report = await messages.next();
142+
return report;
143+
}

test/tools/fixtures/close_resource_script.in.js

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,15 @@ const v8 = require('node:v8');
1414
const util = require('node:util');
1515
const timers = require('node:timers');
1616

17-
const sleep = util.promisify(timers.setTimeout);
18-
1917
const run = func;
2018

21-
const MB = (2 ** 10) ** 2;
22-
2319
async function main() {
24-
for (let iteration = 0; iteration < iterations; iteration++) {
25-
await run({ MongoClient, uri, iteration });
26-
global.gc();
27-
}
28-
29-
global.gc();
30-
// Sleep b/c maybe gc will run
31-
await sleep(100);
32-
global.gc();
33-
34-
process.send({ process.report.getReport()});
20+
process.on('beforeExit', (code) => {
21+
process.send({beforeExit: true});
22+
});
23+
await run({ MongoClient, uri, iteration });
24+
const report = process.report.getReport();
25+
process.send({ report });
3526
}
3627

3728
main()

0 commit comments

Comments
 (0)