Skip to content

Commit 62dd17c

Browse files
authored
feat(driver-bench): add driver benchmarks to dbx-tools (#11)
1 parent a0efea2 commit 62dd17c

File tree

174 files changed

+501325
-2
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

174 files changed

+501325
-2
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"private": true,
44
"workspaces": [
55
"packages/bson-bench",
6-
"packages/eslint-config"
6+
"packages/driver-bench"
77
],
88
"devDependencies": {
99
"@tsconfig/node16": "^16.1.3",
@@ -27,7 +27,7 @@
2727
"typescript-cached-transpile": "^0.0.6"
2828
},
2929
"scripts": {
30-
"check:eslint": "eslint -v && eslint --max-warnings=0 --ext '.js,.ts' packages/**/src packages/**/test",
30+
"check:eslint": "eslint -v && eslint --max-warnings=0 --ext '.js,.ts,.mts' packages/**/src packages/**/test",
3131
"fix:eslint": "npm run check:eslint -- --fix"
3232
}
3333
}

packages/driver-bench/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules/
2+
lib/

packages/driver-bench/LICENSE

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Copyright © 2023 MongoDB, Inc
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.

packages/driver-bench/package.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "driver-bench",
3+
"version": "1.0.0",
4+
"description": "",
5+
"module": "./lib/mod.mjs",
6+
"bin": {
7+
"mndb": "./lib/cli.mjs"
8+
},
9+
"scripts": {
10+
"test": "echo \"Warning: no test specified\" && exit 0",
11+
"prepare": "tsc"
12+
},
13+
"keywords": [],
14+
"author": "The MongoDB NodeJS Team <[email protected]>",
15+
"license": "Apache-2.0",
16+
"dependencies": {
17+
"bson": "^6.7.0"
18+
}
19+
}

packages/driver-bench/readme.md

Lines changed: 21 additions & 0 deletions

packages/driver-bench/src/cli.mts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#!/usr/bin/env node
2+
3+
import child_process from 'node:child_process';
4+
import events from 'node:events';
5+
import module from 'node:module';
6+
import path from 'node:path';
7+
import process from 'node:process';
8+
9+
let require;
10+
11+
let { MONGODB_DRIVER_PATH = '' } = process.env;
12+
const { DEBUG_BENCH = 'no' } = process.env;
13+
14+
if (MONGODB_DRIVER_PATH === '') {
15+
require ??= module.createRequire(import.meta.dirname);
16+
MONGODB_DRIVER_PATH = require.resolve('mongodb');
17+
}
18+
19+
const benchmark = child_process.fork(path.join(import.meta.dirname, './driverBench/index.mjs'), {
20+
execArgv: DEBUG_BENCH === 'yes' ? ['--enable-source-maps'] : [],
21+
stdio: 'inherit',
22+
env: { MONGODB_DRIVER_PATH }
23+
});
24+
await events.once(benchmark, 'exit');
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import fs from 'node:fs';
2+
import path from 'node:path';
3+
import process from 'node:process';
4+
import { Readable } from 'node:stream';
5+
import { pipeline } from 'node:stream/promises';
6+
7+
const { MONGODB_DRIVER_PATH = '' } = process.env;
8+
const { MongoClient, GridFSBucket } = await import(
9+
MONGODB_DRIVER_PATH.length !== 0 ? MONGODB_DRIVER_PATH : 'mongodb'
10+
);
11+
12+
const DB_NAME = 'perftest';
13+
const COLLECTION_NAME = 'corpus';
14+
15+
const SPEC_DIRECTORY = path.resolve(import.meta.dirname, '..', '..', 'src', 'driverBench', 'spec');
16+
17+
export function loadSpecFile(filePath): Buffer;
18+
export function loadSpecFile(filePath, encoding): Buffer;
19+
export function loadSpecFile(filePath, encoding: 'utf8'): string;
20+
export function loadSpecFile(filePath, encoding?: BufferEncoding): string | Buffer {
21+
const fp = [SPEC_DIRECTORY].concat(filePath);
22+
return fs.readFileSync(path.join(...fp), encoding);
23+
}
24+
25+
export function loadSpecString(filePath) {
26+
return loadSpecFile(filePath, 'utf8');
27+
}
28+
29+
export function makeClient() {
30+
this.client = new MongoClient(process.env.MONGODB_URI || 'mongodb://127.0.0.1:27017', {
31+
serverSelectionTimeoutMS: 2000
32+
});
33+
}
34+
35+
export function connectClient() {
36+
return this.client.connect();
37+
}
38+
39+
export function disconnectClient() {
40+
this.client.close();
41+
}
42+
43+
export function initDb() {
44+
this.db = this.client.db(DB_NAME);
45+
}
46+
47+
export function dropDb() {
48+
return this.db.dropDatabase();
49+
}
50+
51+
export function createCollection() {
52+
return this.db.createCollection(COLLECTION_NAME);
53+
}
54+
55+
export function initCollection() {
56+
this.collection = this.db.collection(COLLECTION_NAME);
57+
}
58+
59+
export function dropCollection() {
60+
return this.collection.drop().catch(e => {
61+
if (e.code !== 26 /* NamespaceNotFound */) {
62+
throw e;
63+
}
64+
});
65+
}
66+
67+
export function initBucket() {
68+
this.bucket = new GridFSBucket(this.db);
69+
}
70+
71+
export function dropBucket() {
72+
return this.bucket && this.bucket.drop();
73+
}
74+
75+
export function makeLoadJSON(name) {
76+
return function () {
77+
this.doc = JSON.parse(loadSpecString(['single_and_multi_document', name]));
78+
};
79+
}
80+
81+
export function makeLoadTweets(makeId) {
82+
return function () {
83+
const doc = this.doc;
84+
const tweets = [];
85+
for (let _id = 1; _id <= 10000; _id += 1) {
86+
tweets.push(Object.assign({}, doc, makeId ? { _id } : {}));
87+
}
88+
89+
return this.collection.insertMany(tweets);
90+
};
91+
}
92+
93+
export function makeLoadInsertDocs(numberOfOperations) {
94+
return function () {
95+
this.docs = [];
96+
for (let i = 0; i < numberOfOperations; i += 1) {
97+
this.docs.push(Object.assign({}, this.doc));
98+
}
99+
};
100+
}
101+
102+
export async function writeSingleByteFileToBucket() {
103+
const stream = this.bucket.openUploadStream('setup-file.txt');
104+
const oneByteFile = Readable.from('a');
105+
return pipeline(oneByteFile, stream);
106+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { writeFile } from 'node:fs/promises';
2+
import os from 'node:os';
3+
import { inspect } from 'node:util';
4+
5+
import { Runner } from '../mongoBench/index.mjs';
6+
import {
7+
makeMultiBench,
8+
makeParallelBenchmarks,
9+
makeSingleBench
10+
} from '../mongoBench/suites/index.mjs';
11+
12+
const bsonType = 'js-bson';
13+
// TODO(NODE-4606): test against different driver configurations in CI
14+
const hw = os.cpus();
15+
const ram = os.totalmem() / 1024 ** 3;
16+
const platform = { name: hw[0].model, cores: hw.length, ram: `${ram}GB` };
17+
18+
const systemInfo = () =>
19+
[
20+
`\n- cpu: ${platform.name}`,
21+
`- cores: ${platform.cores}`,
22+
`- arch: ${os.arch()}`,
23+
`- os: ${process.platform} (${os.release()})`,
24+
`- ram: ${platform.ram}\n`
25+
].join('\n');
26+
console.log(systemInfo());
27+
28+
function average(arr) {
29+
return arr.reduce((x, y) => x + y, 0) / arr.length;
30+
}
31+
32+
const benchmarkRunner = new Runner()
33+
.suite('singleBench', suite => makeSingleBench(suite))
34+
.suite('multiBench', suite => makeMultiBench(suite))
35+
.suite('parallel', suite => makeParallelBenchmarks(suite));
36+
37+
benchmarkRunner
38+
.run()
39+
.then(microBench => {
40+
const singleBench = average([
41+
microBench.singleBench.findOne,
42+
microBench.singleBench.smallDocInsertOne,
43+
microBench.singleBench.largeDocInsertOne
44+
]);
45+
const multiBench = average(Object.values(microBench.multiBench));
46+
47+
const parallelBench = average([
48+
microBench.parallel.ldjsonMultiFileUpload,
49+
microBench.parallel.ldjsonMultiFileExport,
50+
microBench.parallel.gridfsMultiFileUpload,
51+
microBench.parallel.gridfsMultiFileDownload
52+
]);
53+
54+
const readBench = average([
55+
microBench.singleBench.findOne,
56+
microBench.multiBench.findManyAndEmptyCursor,
57+
microBench.multiBench.gridFsDownload,
58+
microBench.parallel.gridfsMultiFileDownload,
59+
microBench.parallel.ldjsonMultiFileExport
60+
]);
61+
const writeBench = average([
62+
microBench.singleBench.smallDocInsertOne,
63+
microBench.singleBench.largeDocInsertOne,
64+
microBench.multiBench.smallDocBulkInsert,
65+
microBench.multiBench.largeDocBulkInsert,
66+
microBench.multiBench.gridFsUpload,
67+
microBench.parallel.ldjsonMultiFileUpload,
68+
microBench.parallel.gridfsMultiFileUpload
69+
]);
70+
71+
const driverBench = average([readBench, writeBench]);
72+
73+
const benchmarkResults = {
74+
singleBench,
75+
multiBench,
76+
parallelBench,
77+
readBench,
78+
writeBench,
79+
driverBench,
80+
...microBench.parallel,
81+
...microBench.bsonBench,
82+
...microBench.singleBench,
83+
...microBench.multiBench
84+
};
85+
86+
return Object.entries(benchmarkResults).map(([benchmarkName, result]) => {
87+
return {
88+
info: {
89+
test_name: benchmarkName,
90+
tags: [bsonType]
91+
},
92+
metrics: [{ name: 'megabytes_per_second', value: result }]
93+
};
94+
});
95+
})
96+
.then(data => {
97+
const results = JSON.stringify(data, undefined, 2);
98+
console.log(inspect(data, { depth: Infinity, colors: true }));
99+
return writeFile('results.json', results);
100+
})
101+
.catch(err => console.error(err));
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)