From 965e27a9db42941580d588f671c9b1093dff7abc Mon Sep 17 00:00:00 2001 From: Mike Noseworthy Date: Thu, 11 Jan 2024 12:28:38 -0330 Subject: [PATCH 1/2] fix(getport): Randomize first port using crypto The current implementation of `getPort()` relies on using `Date.now()` to get the first port to try to launch mongo on, even when the `EXP_NET0LISTEN` flag is set. This causes a couple issues: - If the `Date` module is mocked, it can result in `getFreePort()` returning the same first port every time. - If multiple mongos are being started at once in parallel, it's possible for the same first port to be picked leading to port conflicts In order to better randomize the initial port selection so that port conflicts can be avoided, instead of using `Date.now()` for an initial seed, use `crypto.randomInt()` which should provide more randomness and hopefully avoid some of these race conditions. Fixes #827 --- .../mongodb-memory-server-core/src/util/getport/index.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/mongodb-memory-server-core/src/util/getport/index.ts b/packages/mongodb-memory-server-core/src/util/getport/index.ts index 09652f830..9f8671583 100644 --- a/packages/mongodb-memory-server-core/src/util/getport/index.ts +++ b/packages/mongodb-memory-server-core/src/util/getport/index.ts @@ -1,4 +1,5 @@ import resolveConfig, { ResolveConfigVariables, envToBool } from '../resolveConfig'; +import * as crypto from 'node:crypto'; import * as net from 'node:net'; import debug from 'debug'; @@ -48,10 +49,10 @@ export async function getFreePort( firstPort?: number, max_tries: number = MAX_DEFAULT_TRIES ): Promise { - // use "Date" as a semi-random value to lessen conflicts between simultaneous tests - firstPort = firstPort || validPort(Date.now()); + // Get a random value from crypto to use as first port if none is given + firstPort = firstPort || validPort(crypto.randomInt(MIN_PORT, MAX_PORT)); - // clear ports cache after some time, but not on a interval + // clear ports cache after some time, but not on an interval if (PORTS_CACHE.timeSet && Date.now() - PORTS_CACHE.timeSet > PORTS_CACHE_CLEAN_TIME) { PORTS_CACHE.ports.clear(); PORTS_CACHE.timeSet = Date.now(); From b84c82a7b15629a91151f18dd70faa34accf6766 Mon Sep 17 00:00:00 2001 From: hasezoey Date: Sat, 13 Jan 2024 15:16:42 +0100 Subject: [PATCH 2/2] fix(getport): change crypto.randomInt "max" to be inclusive --- packages/mongodb-memory-server-core/src/util/getport/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mongodb-memory-server-core/src/util/getport/index.ts b/packages/mongodb-memory-server-core/src/util/getport/index.ts index 9f8671583..8f6c59583 100644 --- a/packages/mongodb-memory-server-core/src/util/getport/index.ts +++ b/packages/mongodb-memory-server-core/src/util/getport/index.ts @@ -50,7 +50,7 @@ export async function getFreePort( max_tries: number = MAX_DEFAULT_TRIES ): Promise { // Get a random value from crypto to use as first port if none is given - firstPort = firstPort || validPort(crypto.randomInt(MIN_PORT, MAX_PORT)); + firstPort = firstPort || validPort(crypto.randomInt(MIN_PORT, MAX_PORT + 1)); // clear ports cache after some time, but not on an interval if (PORTS_CACHE.timeSet && Date.now() - PORTS_CACHE.timeSet > PORTS_CACHE_CLEAN_TIME) {