Skip to content

Commit

Permalink
fix(ReplicaSet): detect primary from stdout instead of admin call
Browse files Browse the repository at this point in the history
* detect primary from stdout

* typescript tweak, to avoid any type

* Add timeout for _waitForPrimary as failure scenario
  • Loading branch information
jardakotesovec authored and nodkz committed Aug 2, 2019
1 parent cffa710 commit c081ece
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 21 deletions.
39 changes: 18 additions & 21 deletions packages/mongodb-memory-server-core/src/MongoMemoryReplSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,28 +261,25 @@ export default class MongoMemoryReplSet extends EventEmitter {
return server;
}

async _waitForPrimary(timeout: number = 30000): Promise<boolean> {
if (!this.admin) {
return false;
}
const replStatus: ReplStatusResultT = await this.admin.command({
serverStatus: 1,
metrics: 0,
locks: 0,
async _waitForPrimary(timeout: number = 30000): Promise<void> {
const timeoutPromise = new Promise((resolve, reject) => {
let id = setTimeout(() => {
clearTimeout(id);
reject('Timed out in ' + timeout + 'ms. When waiting for primary.');
}, timeout);
});
this.debug(' replStatus:', replStatus);
const hasPrimary = replStatus.repl.ismaster;
if (!hasPrimary) {
const restTimeout = timeout - 500;
if (restTimeout <= 0) {
throw new Error(`No PRIMARY elected yet. Timeout expired. Exiting...`);
}
this.debug(`No PRIMARY yet. Waiting... ${restTimeout}ms`);
return new Promise((resolve) =>
setTimeout(() => resolve(this._waitForPrimary(restTimeout)), 500)
);
}

return true;
await Promise.race([
...this.servers.map((server) => {
const instanceInfo = server.getInstanceInfo();
if (!instanceInfo) {
throw new Error('_waitForPrimary - instanceInfo not present ');
}
return instanceInfo.instance.waitPrimaryReady();
}),
timeoutPromise,
]);

this.debug('_waitForPrimary detected one primary instance ');
}
}
21 changes: 21 additions & 0 deletions packages/mongodb-memory-server-core/src/util/MongoInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ export default class MongoInstance {
// @ts-ignore: Need to initialize this function
instanceReady: Function;
// @ts-ignore: Need to initialize this function
waitForPrimaryResolveFns: Function[];
// @ts-ignore: Need to initialize this function
isInstancePrimary: boolean;
// @ts-ignore: Need to initialize this function
instanceFailed: Function;
isInstanceReady: boolean;

Expand All @@ -44,6 +48,8 @@ export default class MongoInstance {
this.isInstanceReady = false;
this.childProcess = null;
this.killerProcess = null;
this.waitForPrimaryResolveFns = [];
this.isInstancePrimary = false;

if (this.opts.debug) {
if (!this.opts.instance) this.opts.instance = {};
Expand Down Expand Up @@ -153,6 +159,15 @@ export default class MongoInstance {
return this.childProcess ? this.childProcess.pid : undefined;
}

async waitPrimaryReady(): Promise<boolean> {
if (this.isInstancePrimary) {
return true;
}
return new Promise((resolve) => {
this.waitForPrimaryResolveFns.push(resolve);
});
}

_launchMongod(mongoBin: string): ChildProcess {
const spawnOpts = this.opts.spawn || {};
if (!spawnOpts.stdio) spawnOpts.stdio = 'pipe';
Expand Down Expand Up @@ -228,6 +243,12 @@ export default class MongoInstance {
}
} else if (/\*\*\*aborting after/i.test(log)) {
this.instanceFailed('Mongod internal error');
} else if (/transition to primary complete; database writes are now permitted/.test(log)) {
this.isInstancePrimary = true;
this.waitForPrimaryResolveFns.forEach((resolveFn) => {
this.debug('Calling waitForPrimary resolve function');
resolveFn(true);
});
}
}
}

0 comments on commit c081ece

Please sign in to comment.