From 249c279ee21c0635ab22be10788ffcae52ad3bb5 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Wed, 4 Dec 2024 19:25:05 -0500 Subject: [PATCH] fix(NODE-6583): upgrade to BSON v6.10.1 to remove internal unbounded type cache (#4338) --- package-lock.json | 8 +- package.json | 2 +- test/benchmarks/driverBench/common.js | 78 +++++++++++++++++-- test/benchmarks/driverBench/index.js | 24 ++++-- .../mongoBench/suites/parallelBench.js | 78 +++++++++---------- test/readme.md | 54 +++++++++++++ 6 files changed, 189 insertions(+), 55 deletions(-) diff --git a/package-lock.json b/package-lock.json index b4d5e41460..cc1128f070 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "dependencies": { "@mongodb-js/saslprep": "^1.1.9", - "bson": "^6.10.0", + "bson": "^6.10.1", "mongodb-connection-string-url": "^3.0.0" }, "devDependencies": { @@ -3661,9 +3661,9 @@ } }, "node_modules/bson": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.0.tgz", - "integrity": "sha512-ROchNosXMJD2cbQGm84KoP7vOGPO6/bOAW0veMMbzhXLqoZptcaYRVLitwvuhwhjjpU1qP4YZRWLhgETdgqUQw==", + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.1.tgz", + "integrity": "sha512-P92xmHDQjSKPLHqFxefqMxASNq/aWJMEZugpCjf+AF/pgcUpMMQCg7t7+ewko0/u8AapvF3luf/FoehddEK+sA==", "license": "Apache-2.0", "engines": { "node": ">=16.20.1" diff --git a/package.json b/package.json index dbfa1afc6f..b15eb562ea 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ }, "dependencies": { "@mongodb-js/saslprep": "^1.1.9", - "bson": "^6.10.0", + "bson": "^6.10.1", "mongodb-connection-string-url": "^3.0.0" }, "peerDependencies": { diff --git a/test/benchmarks/driverBench/common.js b/test/benchmarks/driverBench/common.js index d4abaac5e6..8c2b03ea19 100644 --- a/test/benchmarks/driverBench/common.js +++ b/test/benchmarks/driverBench/common.js @@ -4,10 +4,72 @@ const fs = require('fs'); const path = require('path'); const { Readable } = require('stream'); const { pipeline } = require('stream/promises'); -const { MongoClient } = require('../../..'); -const { GridFSBucket } = require('../../..'); -// eslint-disable-next-line no-restricted-modules -const { MONGODB_ERROR_CODES } = require('../../../lib/error'); +const child_process = require('child_process'); + +/** + * The path to the MongoDB Node.js driver. + * This MUST be set to the directory the driver is installed in + * NOT the file "lib/index.js" that is the driver's export. + */ +const MONGODB_DRIVER_PATH = (() => { + let driverPath = process.env.MONGODB_DRIVER_PATH; + if (!driverPath?.length) { + driverPath = path.resolve(__dirname, '../../..'); + } + return driverPath; +})(); + +const { MongoClient, GridFSBucket } = require(MONGODB_DRIVER_PATH); + +/** Grab the version from the package.json */ +const { version: MONGODB_DRIVER_VERSION } = require(path.join(MONGODB_DRIVER_PATH, 'package.json')); + +/** + * Use git to optionally determine the git revision, + * but the benchmarks could be run against an npm installed version so this should be allowed to fail + */ +const MONGODB_DRIVER_REVISION = (() => { + try { + return child_process + .execSync('git rev-parse --short HEAD', { + cwd: MONGODB_DRIVER_PATH, + encoding: 'utf8' + }) + .trim(); + } catch { + return 'unknown revision'; + } +})(); + +/** + * Find the BSON dependency inside the driver PATH given and grab the version from the package.json. + */ +const MONGODB_BSON_PATH = path.join(MONGODB_DRIVER_PATH, 'node_modules', 'bson'); +const { version: MONGODB_BSON_VERSION } = require(path.join(MONGODB_BSON_PATH, 'package.json')); + +/** + * If you need to test BSON changes, you should clone, checkout and build BSON. + * run: `npm link` with no arguments to register the link. + * Then in the driver you are testing run `npm link bson` to use your local build. + * + * This will symlink the BSON into the driver's node_modules directory. So here + * we can find the revision of the BSON we are testing against if .git exists. + */ +const MONGODB_BSON_REVISION = (() => { + if (!fs.existsSync(path.join(MONGODB_BSON_PATH, '.git'))) { + return 'installed from npm'; + } + try { + return child_process + .execSync('git rev-parse --short HEAD', { + cwd: path.join(MONGODB_BSON_PATH), + encoding: 'utf8' + }) + .trim(); + } catch { + return 'unknown revision'; + } +})(); const DB_NAME = 'perftest'; const COLLECTION_NAME = 'corpus'; @@ -67,7 +129,7 @@ function initCollection() { function dropCollection() { return this.collection.drop().catch(e => { - if (e.code !== MONGODB_ERROR_CODES.NamespaceNotFound) { + if (e.code !== 26 /* NamespaceNotFound */) { throw e; } }); @@ -117,6 +179,12 @@ async function writeSingleByteFileToBucket() { module.exports = { MONGODB_URI, MONGODB_CLIENT_OPTIONS, + MONGODB_DRIVER_PATH, + MONGODB_DRIVER_VERSION, + MONGODB_DRIVER_REVISION, + MONGODB_BSON_PATH, + MONGODB_BSON_VERSION, + MONGODB_BSON_REVISION, makeClient, connectClient, disconnectClient, diff --git a/test/benchmarks/driverBench/index.js b/test/benchmarks/driverBench/index.js index 3e2ecf30ae..8bdc35c7f8 100644 --- a/test/benchmarks/driverBench/index.js +++ b/test/benchmarks/driverBench/index.js @@ -2,6 +2,7 @@ const MongoBench = require('../mongoBench'); const os = require('node:os'); +const util = require('node:util'); const process = require('node:process'); const Runner = MongoBench.Runner; @@ -11,7 +12,15 @@ let bsonType = 'js-bson'; const { writeFile } = require('fs/promises'); const { makeParallelBenchmarks, makeSingleBench, makeMultiBench } = require('../mongoBench/suites'); -const { MONGODB_CLIENT_OPTIONS } = require('./common'); +const { + MONGODB_CLIENT_OPTIONS, + MONGODB_DRIVER_PATH, + MONGODB_DRIVER_VERSION, + MONGODB_DRIVER_REVISION, + MONGODB_BSON_PATH, + MONGODB_BSON_VERSION, + MONGODB_BSON_REVISION +} = require('./common'); const hw = os.cpus(); const ram = os.totalmem() / 1024 ** 3; @@ -24,7 +33,10 @@ const systemInfo = () => `- arch: ${os.arch()}`, `- os: ${process.platform} (${os.release()})`, `- ram: ${platform.ram}`, - `- node: ${process.version}\n` + `- node: ${process.version}`, + `- driver: ${MONGODB_DRIVER_VERSION} (${MONGODB_DRIVER_REVISION}): ${MONGODB_DRIVER_PATH}`, + ` - options ${util.inspect(MONGODB_CLIENT_OPTIONS)}`, + `- bson: ${MONGODB_BSON_VERSION} (${MONGODB_BSON_REVISION}): (${MONGODB_BSON_PATH})\n` ].join('\n'); console.log(systemInfo()); @@ -52,8 +64,8 @@ benchmarkRunner // https://spruce.mongodb.com/task/mongo_node_driver_next_performance_tests_run_spec_benchmark_tests_node_server_4bc3e500b6f0e8ab01f052c4a1bfb782d6a29b4e_f168e1328f821bbda265e024cc91ae54_24_11_18_15_37_24/logs?execution=0 const parallelBench = average([ - microBench.parallel.ldjsonMultiFileUpload ?? 44.02343490518617, - microBench.parallel.ldjsonMultiFileExport ?? 31.83182984813926, + microBench.parallel.ldjsonMultiFileUpload, + microBench.parallel.ldjsonMultiFileExport, microBench.parallel.gridfsMultiFileUpload, microBench.parallel.gridfsMultiFileDownload ]); @@ -63,7 +75,7 @@ benchmarkRunner microBench.multiBench.findManyAndEmptyCursor, microBench.multiBench.gridFsDownload, microBench.parallel.gridfsMultiFileDownload, - microBench.parallel.ldjsonMultiFileExport ?? 31.83182984813926 + microBench.parallel.ldjsonMultiFileExport ]); const writeBench = average([ microBench.singleBench.smallDocInsertOne, @@ -71,7 +83,7 @@ benchmarkRunner microBench.multiBench.smallDocBulkInsert, microBench.multiBench.largeDocBulkInsert, microBench.multiBench.gridFsUpload, - microBench.parallel.ldjsonMultiFileUpload ?? 44.02343490518617, + microBench.parallel.ldjsonMultiFileUpload, microBench.parallel.gridfsMultiFileUpload ]); diff --git a/test/benchmarks/mongoBench/suites/parallelBench.js b/test/benchmarks/mongoBench/suites/parallelBench.js index 197646e395..b011603b75 100644 --- a/test/benchmarks/mongoBench/suites/parallelBench.js +++ b/test/benchmarks/mongoBench/suites/parallelBench.js @@ -13,7 +13,9 @@ const { dropBucket, initCollection, initDb, - connectClient + connectClient, + dropCollection, + createCollection } = require('../../driverBench/common'); const { pipeline } = require('stream/promises'); const { EJSON } = require('bson'); @@ -34,7 +36,6 @@ async function clearTemporaryDirectory() { await Promise.all(files.map(file => rm(file))); } -// eslint-disable-next-line no-unused-vars async function ldjsonMultiUpload() { const directory = resolve(benchmarkFileDirectory, 'ldjson_multi'); const files = await readdir(directory); @@ -63,7 +64,6 @@ async function ldjsonMultiUpload() { await Promise.all(uploads); } -// eslint-disable-next-line no-unused-vars async function ldjsonMultiExport() { const skips = Array.from({ length: 100 }, (_, index) => index * 5000); @@ -113,43 +113,43 @@ async function gridfsMultiFileDownload() { * @returns Benchmark */ function makeParallelBenchmarks(suite) { - // .benchmark('ldjsonMultiFileUpload', benchmark => - // // https://github.com/mongodb/specifications/blob/master/source/benchmarking/benchmarking.rst#ldjson-multi-file-import - // benchmark - // .taskSize(565) - // .setup(makeClient) - // .setup(connectClient) - // .setup(initDb) - // .setup(dropDb) - // .beforeTask(initCollection) - // .beforeTask(dropCollection) - // .beforeTask(createCollection) - // .task(ldjsonMultiUpload) - // .teardown(dropDb) - // .teardown(disconnectClient) - // ) - // .benchmark('ldjsonMultiFileExport', benchmark => - // // https://github.com/mongodb/specifications/blob/master/source/benchmarking/benchmarking.rst#ldjson-multi-file-export - // benchmark - // .taskSize(565) - // .setup(makeClient) - // .setup(connectClient) - // .setup(initDb) - // .setup(dropDb) - // .beforeTask(initCollection) - // .beforeTask(dropCollection) - // .beforeTask(createCollection) - // .beforeTask(ldjsonMultiUpload) - // .beforeTask(initTemporaryDirectory) - // .task(ldjsonMultiExport) - // .afterTask(clearTemporaryDirectory) - // .teardown(dropDb) - // .teardown(async function () { - // await rm(this.temporaryDirectory, { recursive: true, force: true }); - // }) - // .teardown(disconnectClient) - // ) return suite + .benchmark('ldjsonMultiFileUpload', benchmark => + // https://github.com/mongodb/specifications/blob/master/source/benchmarking/benchmarking.rst#ldjson-multi-file-import + benchmark + .taskSize(565) + .setup(makeClient) + .setup(connectClient) + .setup(initDb) + .setup(dropDb) + .beforeTask(initCollection) + .beforeTask(dropCollection) + .beforeTask(createCollection) + .task(ldjsonMultiUpload) + .teardown(dropDb) + .teardown(disconnectClient) + ) + .benchmark('ldjsonMultiFileExport', benchmark => + // https://github.com/mongodb/specifications/blob/master/source/benchmarking/benchmarking.rst#ldjson-multi-file-export + benchmark + .taskSize(565) + .setup(makeClient) + .setup(connectClient) + .setup(initDb) + .setup(dropDb) + .beforeTask(initCollection) + .beforeTask(dropCollection) + .beforeTask(createCollection) + .beforeTask(ldjsonMultiUpload) + .beforeTask(initTemporaryDirectory) + .task(ldjsonMultiExport) + .afterTask(clearTemporaryDirectory) + .teardown(dropDb) + .teardown(async function () { + await rm(this.temporaryDirectory, { recursive: true, force: true }); + }) + .teardown(disconnectClient) + ) .benchmark('gridfsMultiFileUpload', benchmark => // https://github.com/mongodb/specifications/blob/master/source/benchmarking/benchmarking.rst#gridfs-multi-file-upload benchmark diff --git a/test/readme.md b/test/readme.md index deae82a2de..bce66e7c07 100644 --- a/test/readme.md +++ b/test/readme.md @@ -241,6 +241,60 @@ beforeEach(() => { }); ``` +## Running Benchmarks + +```sh +npm run check:bench +``` + +Refer to the `run-spec-benchmark-tests-node-server` task for Node.js version, MongoDB server version, and platform that we run benchmarks against in CI. + +The server is run in standalone mode and the server versions are aliased by this script: https://github.com/mongodb-labs/drivers-evergreen-tools/blob/5048cca80e9ca62642409de2d401058bbd7057fa/.evergreen/mongodl.py#L58 check the latest version to see what alias the driver is running against. + +The host used is described in detail here: https://spruce.mongodb.com/distro/rhel90-dbx-perf-large/settings/general (Auth required to view) + +Here is a rough list of the key configurations: + +- cpu: Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz +- cores: 16 +- arch: x64 +- os: RHEL 9.0 linux (5.14.0-70.75.1.el9_0.x86_64) +- ram: 64 GB + +It is best to try reproductions against as similar a deployment as possible to isolate regressions. + +### Configuration + +The benchmarks can be directed to test different settings and driver versions. + +The following are environment variables and how the benchmark runner uses them: + +- `MONGODB_DRIVER_PATH` - (default: current working driver) if set MUST be set to the directory a driver version is in, usually another clone of the driver checked out to a different revision. +- `MONGODB_CLIENT_OPTIONS` - (default: empty object) if set MUST be a JSON string that will be parsed and passed as the second argument to the MongoClient constructor. +- `MONGODB_URI` - (default: `mongodb://127.0.0.1:27017`) if set MUST be a valid MongoDB connection string and it will be used as the host the benchmarks will run against. + +It may be desirable to test how changes to `BSON` impact the driver's performance. + +To do this: +- clone the changed version of BSON + - run the build script for that repo (usually done by `npm install` for you) +- run `npm link` +- over in the driver repo run `npm link bson` + +When you run the benchmarks verify that the BSON version has been picked by the version references that are printed out: + +```md +- cpu: Apple M1 Max +- cores: 10 +- arch: arm64 +- os: darwin (23.6.0) +- ram: 32GB +- node: v22.6.0 +- driver: 6.11.0 (df3ea32a9): .../mongodb + - options {} +- bson: 6.10.1 (installed from npm): (.../mongodb/node_modules/bson) +``` + ## Testing with Special Environments In order to test some features, you will need to generate and set a specialized group of environment variables. The subsections below will walk you through how to generate and set the environment variables for these features.