Skip to content

Commit a1c83de

Browse files
authored
test(NODE-6723): isolate and warmup benchmarks (#4398)
1 parent 1d0b2b4 commit a1c83de

File tree

12 files changed

+604
-8
lines changed

12 files changed

+604
-8
lines changed

.evergreen/config.in.yml

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ functions:
142142
working_dir: "src"
143143
timeout_secs: 300
144144
binary: bash
145-
args:
145+
args:
146146
- .evergreen/run-tests.sh
147147

148148
"run serverless tests":
@@ -158,11 +158,11 @@ functions:
158158
timeout_secs: 300
159159
working_dir: src
160160
binary: bash
161-
env:
161+
env:
162162
AUTH: 'auth'
163163
SSL: 'ssl'
164164
add_expansions_to_env: true
165-
args:
165+
args:
166166
- .evergreen/run-serverless-tests.sh
167167

168168
"start-load-balancer":
@@ -882,6 +882,23 @@ functions:
882882
binary: bash
883883
args:
884884
- ${PROJECT_DIRECTORY}/.evergreen/run-benchmarks.sh
885+
886+
# TODO(NODE-6729): Remove this task when the original tasks are using the new runner
887+
"run new spec driver benchmarks":
888+
- command: subprocess.exec
889+
type: test
890+
params:
891+
working_dir: "src"
892+
env:
893+
PROJECT_DIRECTORY: ${PROJECT_DIRECTORY}
894+
MONGODB_URI: ${MONGODB_URI}
895+
DRIVERS_TOOLS: ${DRIVERS_TOOLS}
896+
MONGODB_CLIENT_OPTIONS: ${MONGODB_CLIENT_OPTIONS}
897+
NEW_BENCH: "true"
898+
binary: bash
899+
args:
900+
- ${PROJECT_DIRECTORY}/.evergreen/run-benchmarks.sh
901+
885902
"run x509 auth tests":
886903
- command: subprocess.exec
887904
type: test
@@ -1191,7 +1208,7 @@ task_groups:
11911208
binary: bash
11921209
args:
11931210
- ${DRIVERS_TOOLS}/.evergreen/serverless/delete-instance.sh
1194-
1211+
11951212
tasks:
11961213
- ".serverless"
11971214

.evergreen/config.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,20 @@ functions:
825825
binary: bash
826826
args:
827827
- ${PROJECT_DIRECTORY}/.evergreen/run-benchmarks.sh
828+
run new spec driver benchmarks:
829+
- command: subprocess.exec
830+
type: test
831+
params:
832+
working_dir: src
833+
env:
834+
PROJECT_DIRECTORY: ${PROJECT_DIRECTORY}
835+
MONGODB_URI: ${MONGODB_URI}
836+
DRIVERS_TOOLS: ${DRIVERS_TOOLS}
837+
MONGODB_CLIENT_OPTIONS: ${MONGODB_CLIENT_OPTIONS}
838+
NEW_BENCH: 'true'
839+
binary: bash
840+
args:
841+
- ${PROJECT_DIRECTORY}/.evergreen/run-benchmarks.sh
828842
run x509 auth tests:
829843
- command: subprocess.exec
830844
type: test
@@ -3305,6 +3319,24 @@ tasks:
33053319
- command: perf.send
33063320
params:
33073321
file: src/results.json
3322+
- name: run-spec-benchmark-tests-node-server-new
3323+
tags:
3324+
- run-spec-benchmark-tests
3325+
- performance
3326+
exec_timeout_secs: 3600
3327+
commands:
3328+
- command: expansions.update
3329+
type: setup
3330+
params:
3331+
updates:
3332+
- {key: NODE_LTS_VERSION, value: v22.11.0}
3333+
- {key: VERSION, value: v6.0-perf}
3334+
- {key: TOPOLOGY, value: server}
3335+
- {key: AUTH, value: noauth}
3336+
- {key: MONGODB_CLIENT_OPTIONS, value: '{}'}
3337+
- func: install dependencies
3338+
- func: bootstrap mongo-orchestration
3339+
- func: run new spec driver benchmarks
33083340
- name: run-unit-tests-node-16
33093341
tags:
33103342
- unit-tests
@@ -5122,6 +5154,7 @@ buildvariants:
51225154
- run-spec-benchmark-tests-node-server-timeoutMS-0
51235155
- run-spec-benchmark-tests-node-server-monitorCommands-true
51245156
- run-spec-benchmark-tests-node-server-logging
5157+
- run-spec-benchmark-tests-node-server-new
51255158
- name: rhel8-custom-dependency-tests
51265159
display_name: Custom Dependency Version Test
51275160
run_on: rhel80-large

.evergreen/generate_evergreen_tasks.js

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ TASKS.push(
212212
{ func: 'run socks5 tests' }
213213
]
214214
}
215-
]
215+
]
216216
);
217217

218218
TASKS.push({
@@ -432,8 +432,8 @@ for (const {
432432
const expansions = { NODE_LTS_VERSION, NPM_VERSION };
433433
const taskNames = tasks.map(({ name }) => name);
434434

435-
expansions.CLIENT_ENCRYPTION = String(!!clientEncryption)
436-
expansions.TEST_CSFLE = expansions.CLIENT_ENCRYPTION
435+
expansions.CLIENT_ENCRYPTION = String(!!clientEncryption);
436+
expansions.TEST_CSFLE = expansions.CLIENT_ENCRYPTION;
437437

438438
BUILD_VARIANTS.push({ name, display_name, run_on, expansions, tasks: taskNames });
439439
}
@@ -752,6 +752,28 @@ function addPerformanceTasks() {
752752
]
753753
});
754754

755+
// TODO(NODE-6729): Remove this task when the original tasks are using the new runner
756+
const makePerfTaskNEW = (name, MONGODB_CLIENT_OPTIONS) => ({
757+
name,
758+
tags: ['run-spec-benchmark-tests', 'performance'],
759+
exec_timeout_secs: 3600,
760+
commands: [
761+
updateExpansions({
762+
NODE_LTS_VERSION: 'v22.11.0',
763+
VERSION: 'v6.0-perf',
764+
TOPOLOGY: 'server',
765+
AUTH: 'noauth',
766+
MONGODB_CLIENT_OPTIONS: JSON.stringify(MONGODB_CLIENT_OPTIONS)
767+
}),
768+
...[
769+
'install dependencies',
770+
'bootstrap mongo-orchestration',
771+
'run new spec driver benchmarks'
772+
].map(func => ({ func }))
773+
// No perf send! just testing
774+
]
775+
});
776+
755777
const tasks = [
756778
makePerfTask('run-spec-benchmark-tests-node-server', {}),
757779
makePerfTask('run-spec-benchmark-tests-node-server-timeoutMS-120000', { timeoutMS: 120000 }),
@@ -762,7 +784,8 @@ function addPerformanceTasks() {
762784
makePerfTask('run-spec-benchmark-tests-node-server-logging', {
763785
mongodbLogPath: 'stderr',
764786
mongodbLogComponentSeverities: { default: 'trace' }
765-
})
787+
}),
788+
makePerfTaskNEW('run-spec-benchmark-tests-node-server-new', {})
766789
];
767790

768791
TASKS.push(...tasks);

.evergreen/run-benchmarks.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ export MONGODB_CLIENT_OPTIONS=$MONGODB_CLIENT_OPTIONS
1111

1212
npm run build:ts
1313

14+
if [[ "${NEW_BENCH:-}" == "true" ]]; then
15+
pushd test/benchmarks/driver_bench
16+
npm start
17+
popd
18+
exit 0
19+
fi
1420

1521
# If MONGODB_CLIENT_OPTIONS contains mongodbLogComponentSeverities redirect stderr to a file
1622
if [[ $MONGODB_CLIENT_OPTIONS == *"mongodbLogComponentSeverities"* ]]; then
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
results.json
2+
results_*.json
3+
package-lock.json
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "driver_bench",
3+
"version": "0.0.0",
4+
"private": true,
5+
"scripts": {
6+
"prestart": "tsc",
7+
"start": "node lib/main.mjs"
8+
}
9+
}
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
import child_process from 'node:child_process';
2+
import fs from 'node:fs/promises';
3+
import module from 'node:module';
4+
import path from 'node:path';
5+
import process from 'node:process';
6+
7+
const __dirname = import.meta.dirname;
8+
const require = module.createRequire(__dirname);
9+
10+
/**
11+
* The path to the MongoDB Node.js driver.
12+
* This MUST be set to the directory the driver is installed in
13+
* NOT the file "lib/index.js" that is the driver's export.
14+
*/
15+
export const MONGODB_DRIVER_PATH = (() => {
16+
let driverPath = process.env.MONGODB_DRIVER_PATH;
17+
if (!driverPath?.length) {
18+
driverPath = path.resolve(__dirname, '../../../..');
19+
}
20+
return driverPath;
21+
})();
22+
23+
/** Grab the version from the package.json */
24+
export const { version: MONGODB_DRIVER_VERSION } = require(
25+
path.join(MONGODB_DRIVER_PATH, 'package.json')
26+
);
27+
28+
/**
29+
* Use git to optionally determine the git revision,
30+
* but the benchmarks could be run against an npm installed version so this should be allowed to fail
31+
*/
32+
export const MONGODB_DRIVER_REVISION = (() => {
33+
try {
34+
return child_process
35+
.execSync('git rev-parse --short HEAD', {
36+
cwd: MONGODB_DRIVER_PATH,
37+
encoding: 'utf8'
38+
})
39+
.trim();
40+
} catch {
41+
return 'unknown revision';
42+
}
43+
})();
44+
45+
/**
46+
* Find the BSON dependency inside the driver PATH given and grab the version from the package.json.
47+
*/
48+
export const MONGODB_BSON_PATH = path.join(MONGODB_DRIVER_PATH, 'node_modules', 'bson');
49+
export const { version: MONGODB_BSON_VERSION } = require(
50+
path.join(MONGODB_BSON_PATH, 'package.json')
51+
);
52+
53+
/**
54+
* If you need to test BSON changes, you should clone, checkout and build BSON.
55+
* run: `npm link` with no arguments to register the link.
56+
* Then in the driver you are testing run `npm link bson` to use your local build.
57+
*
58+
* This will symlink the BSON into the driver's node_modules directory. So here
59+
* we can find the revision of the BSON we are testing against if .git exists.
60+
*/
61+
export const MONGODB_BSON_REVISION = await (async () => {
62+
const bsonGitExists = await fs.access(path.join(MONGODB_BSON_PATH, '.git')).then(
63+
() => true,
64+
() => false
65+
);
66+
if (!bsonGitExists) {
67+
return 'installed from npm';
68+
}
69+
try {
70+
return child_process
71+
.execSync('git rev-parse --short HEAD', {
72+
cwd: path.join(MONGODB_BSON_PATH),
73+
encoding: 'utf8'
74+
})
75+
.trim();
76+
} catch {
77+
return 'unknown revision';
78+
}
79+
})();
80+
81+
export const MONGODB_CLIENT_OPTIONS = (() => {
82+
const optionsString = process.env.MONGODB_CLIENT_OPTIONS;
83+
let options = undefined;
84+
if (optionsString?.length) {
85+
options = JSON.parse(optionsString);
86+
}
87+
return { ...options };
88+
})();
89+
90+
export const MONGODB_URI = (() => {
91+
if (process.env.MONGODB_URI?.length) return process.env.MONGODB_URI;
92+
return 'mongodb://127.0.0.1:27017';
93+
})();
94+
95+
export function snakeToCamel(name: string) {
96+
return name
97+
.split('_')
98+
.map((s, i) => (i !== 0 ? s[0].toUpperCase() + s.slice(1) : s))
99+
.join('');
100+
}
101+
102+
import type mongodb from '../../../../mongodb.js';
103+
export type { mongodb };
104+
105+
const { MongoClient, GridFSBucket } = require(path.join(MONGODB_DRIVER_PATH));
106+
107+
const DB_NAME = 'perftest';
108+
const COLLECTION_NAME = 'corpus';
109+
110+
const SPEC_DIRECTORY = path.resolve(__dirname, '..', '..', 'driverBench', 'spec');
111+
112+
export function metrics(test_name: string, result: number, count: number) {
113+
return {
114+
info: {
115+
test_name,
116+
// Args can only be a map of string -> int32. So if its a number leave it be,
117+
// if it is anything else test for truthiness and set to 1 or 0.
118+
args: Object.fromEntries(
119+
Object.entries(MONGODB_CLIENT_OPTIONS).map(([key, value]) => [
120+
key,
121+
typeof value === 'number' ? value : value ? 1 : 0
122+
])
123+
)
124+
},
125+
metrics: [
126+
{ name: 'megabytes_per_second', value: result },
127+
// Reporting the count so we can verify programmatically or in UI how many iterations we reached
128+
{ name: 'count', value: count }
129+
]
130+
} as const;
131+
}
132+
133+
/**
134+
* This class exists to abstract some of the driver API so we can gloss over version differences.
135+
* For use in setup/teardown mostly.
136+
*/
137+
export class DriverTester {
138+
public client: mongodb.MongoClient;
139+
constructor() {
140+
this.client = new MongoClient(MONGODB_URI, MONGODB_CLIENT_OPTIONS);
141+
}
142+
143+
public get db() {
144+
return this.client.db(DB_NAME);
145+
}
146+
147+
public get collection() {
148+
return this.client.db(DB_NAME).collection(COLLECTION_NAME);
149+
}
150+
151+
public get bucket(): mongodb.GridFSBucket {
152+
return new GridFSBucket(this.db);
153+
}
154+
155+
async drop() {
156+
const utilClient = new MongoClient(MONGODB_URI, MONGODB_CLIENT_OPTIONS);
157+
const db = utilClient.db(DB_NAME);
158+
const collection = db.collection(COLLECTION_NAME);
159+
await collection.drop().catch(() => null);
160+
await db.dropDatabase().catch(() => null);
161+
await utilClient.close();
162+
}
163+
164+
async create() {
165+
const utilClient = new MongoClient(MONGODB_URI, MONGODB_CLIENT_OPTIONS);
166+
try {
167+
await utilClient.db(DB_NAME).createCollection(COLLECTION_NAME);
168+
} finally {
169+
await utilClient.close();
170+
}
171+
}
172+
173+
async load(filePath: string, type: 'json' | 'string' | 'buffer'): Promise<any> {
174+
const content = await fs.readFile(path.join(SPEC_DIRECTORY, filePath));
175+
if (type === 'buffer') return content;
176+
const string = content.toString('utf8');
177+
if (type === 'string') return string;
178+
if (type === 'json') return JSON.parse(string);
179+
throw new Error('unknown type: ' + type);
180+
}
181+
182+
async insertManyOf(document: Record<string, any>, length: number, addId = false) {
183+
const utilClient = new MongoClient(MONGODB_URI, MONGODB_CLIENT_OPTIONS);
184+
const db = utilClient.db(DB_NAME);
185+
const collection = db.collection(COLLECTION_NAME);
186+
try {
187+
await collection.insertMany(
188+
Array.from({ length }, (_, _id) => ({ ...(addId ? { _id } : {}), ...document })) as any[]
189+
);
190+
} finally {
191+
await utilClient.close();
192+
}
193+
}
194+
195+
async close() {
196+
await this.client.close();
197+
}
198+
}
199+
200+
export const driver = new DriverTester();

0 commit comments

Comments
 (0)