Skip to content

Commit 668b42a

Browse files
fix(MongoInstance::stop): should not wait too long to open a connection to ReplSet (#813)
Co-authored-by: hasezoey <[email protected]>
1 parent 73691c8 commit 668b42a

File tree

2 files changed

+50
-3
lines changed

2 files changed

+50
-3
lines changed

packages/mongodb-memory-server-core/src/util/MongoInstance.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,8 @@ export class MongoInstance extends EventEmitter implements ManagerBase {
443443
);
444444

445445
con = await MongoClient.connect(uriTemplate(ip, port, 'admin'), {
446+
// stopping a instance should not take long to connect to, default would be 30 seconds
447+
serverSelectionTimeoutMS: 5000, // 5 seconds
446448
...this.extraConnectionOptions,
447449
directConnection: true,
448450
});

packages/mongodb-memory-server-core/src/util/__tests__/MongoInstance.test.ts

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
UnexpectedCloseError,
1111
} from '../errors';
1212
import { assertIsError } from '../../__tests__/testUtils/test_utils';
13-
import { MongoClient } from 'mongodb';
13+
import { MongoClient, MongoServerSelectionError } from 'mongodb';
1414

1515
jest.setTimeout(100000); // 10s
1616

@@ -279,7 +279,7 @@ describe('MongodbInstance', () => {
279279
expect(dbUtil.killProcess).not.toBeCalled();
280280
});
281281

282-
it('"kill" should not try to open a connection to a not running ReplSet', async () => {
282+
it('"kill" should not try to open a connection to a not running ReplSet instance', async () => {
283283
const gotPort = await getFreePort();
284284
const mongod = new MongodbInstance({
285285
instance: {
@@ -292,12 +292,57 @@ describe('MongodbInstance', () => {
292292
await mongod.start();
293293
jest.spyOn(MongoClient, 'connect');
294294
process.kill(mongod.mongodProcess!.pid, 'SIGKILL');
295-
await new Promise<void>((resolve) => setTimeout(resolve, 100));
295+
// loop until the mongod process actually exits
296+
while (dbUtil.isAlive(mongod.mongodProcess!.pid)) {
297+
await new Promise<void>((resolve) => setTimeout(resolve, 5));
298+
}
296299
await mongod.stop();
297300

298301
expect(MongoClient.connect).not.toBeCalled();
299302
});
300303

304+
test('"kill" should not wait too much to open a connection to a ReplSet instance', async () => {
305+
const gotPort = await getFreePort();
306+
307+
// mock indicating some kind of server selection problem (undetected shutdown / unresponsive)
308+
// so that the ".stop" function does not exit too early
309+
jest.spyOn(MongodbInstance.prototype, 'closeHandler').mockImplementationOnce(() => void 0);
310+
311+
const mongod = new MongodbInstance({
312+
instance: {
313+
replSet: 'testset',
314+
ip: '127.0.0.1',
315+
port: gotPort,
316+
dbPath: tmpDir,
317+
},
318+
});
319+
await mongod.start();
320+
mongod.extraConnectionOptions = {
321+
// the following is set to 1s to speed-up the test, instead of the set default of 5s (or the 30s of mongodb default)
322+
serverSelectionTimeoutMS: 1000, // 1 second
323+
};
324+
jest.spyOn(MongoClient, 'connect');
325+
jest.spyOn(MongoClient.prototype, 'db');
326+
jest.spyOn(console, 'warn').mockImplementationOnce(() => void 0);
327+
process.kill(mongod.mongodProcess!.pid, 'SIGKILL');
328+
// loop until the mongod process actually exits
329+
while (dbUtil.isAlive(mongod.mongodProcess!.pid)) {
330+
await new Promise<void>((resolve) => setTimeout(resolve, 5));
331+
}
332+
333+
// mock indicating some kind of server selection problem (undetected shutdown / unresponsive)
334+
// so that the ".stop" function does not exit too early
335+
jest.spyOn(dbUtil, 'isAlive').mockImplementationOnce(() => true);
336+
337+
await mongod.stop();
338+
// connect should be called, but never beyond that (con.db is called next)
339+
expect(MongoClient.connect).toHaveBeenCalledTimes(1);
340+
expect(MongoClient.prototype.db).not.toHaveBeenCalled();
341+
// should print a warning about "ECONREFUSED" or "Server selection timed-out"
342+
expect(console.warn).toHaveBeenCalledTimes(1);
343+
expect(console.warn).toHaveBeenCalledWith(expect.any(MongoServerSelectionError));
344+
}, 8000); // the default serverSelectionTimeoutMS is 30s, the overwritten config is 5s, so waiting 8s should be fine
345+
301346
it('"_launchMongod" should throw an error if "mongodProcess.pid" is undefined', () => {
302347
const mongod = new MongodbInstance({ instance: { port: 0, dbPath: '' } }); // dummy values - they shouldnt matter
303348
const mockBinary = '/tmp/thisShouldNotExist';

0 commit comments

Comments
 (0)