Skip to content

Commit fb19029

Browse files
authored
chore(cli-repl): add startup timing measurement functionality (#1741)
1 parent 4fa95bb commit fb19029

File tree

7 files changed

+112
-11
lines changed

7 files changed

+112
-11
lines changed

package-lock.json

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/build/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
"@mongodb-js/dl-center": "^1.1.1",
6666
"@octokit/rest": "^17.9.0",
6767
"aws-sdk": "^2.674.0",
68-
"boxednode": "^2.1.2",
68+
"boxednode": "^2.2.0",
6969
"command-exists": "^1.2.9",
7070
"download": "^8.0.0",
7171
"es-aggregate-error": "^1.0.9",

packages/cli-repl/src/cli-repl.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import path from 'path';
4242
import { promisify } from 'util';
4343
import { getOsInfo } from './get-os-info';
4444
import { UpdateNotificationManager } from './update-notification-manager';
45+
import { markTime } from './startup-timing';
4546

4647
/**
4748
* Connecting text key.
@@ -226,9 +227,11 @@ export class CliRepl implements MongoshIOProvider {
226227
): Promise<void> {
227228
const { version } = require('../package.json');
228229
await this.verifyNodeVersion();
230+
markTime('verified node version');
229231

230232
this.isContainerizedEnvironment =
231233
await this.getIsContainerizedEnvironment();
234+
markTime('checked containerized environment');
232235

233236
if (!this.cliOptions.nodb) {
234237
const cs = new ConnectionString(driverUri);
@@ -249,19 +252,23 @@ export class CliRepl implements MongoshIOProvider {
249252
} catch (err: any) {
250253
this.warnAboutInaccessibleFile(err);
251254
}
255+
markTime('ensured shell homedir');
252256

253257
await this.logManager.cleanupOldLogfiles();
258+
markTime('cleaned up log files');
254259
const logger = await this.logManager.createLogWriter();
255260
if (!this.cliOptions.quiet) {
256261
this.output.write(`Current Mongosh Log ID:\t${logger.logId}\n`);
257262
}
258263
this.logWriter = logger;
264+
markTime('instantiated log writer');
259265

260266
logger.info('MONGOSH', mongoLogId(1_000_000_000), 'log', 'Starting log', {
261267
execPath: process.execPath,
262268
envInfo: redactSensitiveData(this.getLoggedEnvironmentVariables()),
263269
...(await buildInfo()),
264270
});
271+
markTime('logged initial message');
265272

266273
let analyticsSetupError: Error | null = null;
267274
try {
@@ -272,6 +279,7 @@ export class CliRepl implements MongoshIOProvider {
272279
analyticsSetupError = err;
273280
}
274281

282+
markTime('created analytics instance');
275283
setupLoggerAndTelemetry(
276284
this.bus,
277285
logger,
@@ -284,6 +292,7 @@ export class CliRepl implements MongoshIOProvider {
284292
},
285293
version
286294
);
295+
markTime('completed telemetry setup');
287296

288297
if (analyticsSetupError) {
289298
this.bus.emit('mongosh:error', analyticsSetupError, 'analytics');
@@ -298,6 +307,7 @@ export class CliRepl implements MongoshIOProvider {
298307
}
299308

300309
this.globalConfig = await this.loadGlobalConfigFile();
310+
markTime('read global config files');
301311

302312
// Needs to happen after loading the mongosh config file(s)
303313
void this.fetchMongoshUpdateUrl();
@@ -331,6 +341,7 @@ export class CliRepl implements MongoshIOProvider {
331341
}
332342

333343
driverOptions = await this.prepareOIDCOptions(driverOptions);
344+
markTime('prepared OIDC options');
334345

335346
let initialServiceProvider;
336347
try {
@@ -346,10 +357,12 @@ export class CliRepl implements MongoshIOProvider {
346357
}
347358
throw err;
348359
}
360+
markTime('completed SP setup');
349361
const initialized = await this.mongoshRepl.initialize(
350362
initialServiceProvider,
351363
await this.getMoreRecentMongoshVersion()
352364
);
365+
markTime('initialized mongosh repl');
353366
this.injectReplFunctions();
354367

355368
const commandLineLoadFiles = this.cliOptions.fileNames ?? [];
@@ -388,6 +401,7 @@ export class CliRepl implements MongoshIOProvider {
388401
this.mongoshRepl
389402
),
390403
});
404+
markTime('set up editor');
391405

392406
if (willExecuteCommandLineScripts) {
393407
this.mongoshRepl.setIsInteractive(willEnterInteractiveMode);
@@ -425,16 +439,20 @@ export class CliRepl implements MongoshIOProvider {
425439
* - Programmatic users who really want a dependency on a snippet can use
426440
* snippet('load-all') to explicitly load snippets
427441
*/
442+
markTime('start load snippets');
428443
await snippetManager?.loadAllSnippets();
444+
markTime('done load snippets');
429445
}
430446
await this.loadRcFiles();
447+
markTime('loaded rc files');
431448

432449
this.verifyPlatformSupport();
433450

434451
// We only enable/disable here, since the rc file/command line scripts
435452
// can disable the telemetry setting.
436453
this.setTelemetryEnabled(await this.getConfig('enableTelemetry'));
437454
this.bus.emit('mongosh:start-mongosh-repl', { version });
455+
markTime('starting repl');
438456
await this.mongoshRepl.startRepl(initialized);
439457
}
440458

@@ -514,13 +532,15 @@ export class CliRepl implements MongoshIOProvider {
514532
let lastEvalResult: any;
515533
let exitCode = 0;
516534
try {
535+
markTime('start eval scripts');
517536
for (const script of evalScripts) {
518537
this.bus.emit('mongosh:eval-cli-script');
519538
lastEvalResult = await this.mongoshRepl.loadExternalCode(
520539
script,
521540
'@(shell eval)'
522541
);
523542
}
543+
markTime('finished eval scripts');
524544
} catch (err) {
525545
// We have two distinct flows of control in the exception case;
526546
// if we are running in --json mode, we treat the error as a
@@ -559,6 +579,7 @@ export class CliRepl implements MongoshIOProvider {
559579
this.output.write(formattedResult + '\n');
560580
}
561581

582+
markTime('wrote eval output, start loading external files');
562583
for (const file of files) {
563584
if (!this.cliOptions.quiet) {
564585
this.output.write(
@@ -567,6 +588,7 @@ export class CliRepl implements MongoshIOProvider {
567588
}
568589
await this.mongoshRepl.loadExternalFile(file);
569590
}
591+
markTime('finished external files');
570592
return exitCode;
571593
}
572594

@@ -951,6 +973,7 @@ export class CliRepl implements MongoshIOProvider {
951973
* Close all open resources held by this REPL instance.
952974
*/
953975
async close(): Promise<void> {
976+
markTime('start closing');
954977
if (this.closing) {
955978
return;
956979
}
@@ -970,6 +993,7 @@ export class CliRepl implements MongoshIOProvider {
970993
await new Promise((resolve) => this.output.write('', resolve));
971994
}
972995
}
996+
markTime('output flushed');
973997
this.closing = true;
974998
const analytics = this.toggleableAnalytics;
975999
let flushError: string | null = null;
@@ -978,6 +1002,7 @@ export class CliRepl implements MongoshIOProvider {
9781002
const flushStart = Date.now();
9791003
try {
9801004
await promisify(analytics.flush.bind(analytics))();
1005+
markTime('flushed analytics');
9811006
} catch (err: any) {
9821007
flushError = err.message;
9831008
} finally {
@@ -995,6 +1020,7 @@ export class CliRepl implements MongoshIOProvider {
9951020
}
9961021
);
9971022
await this.logWriter?.flush();
1023+
markTime('flushed log writer');
9981024
this.bus.emit('mongosh:closed');
9991025
}
10001026

packages/cli-repl/src/mongosh-repl.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import formatOutput, { formatError } from './format-output';
3535
import { makeMultilineJSIntoSingleLine } from '@mongosh/js-multiline-to-singleline';
3636
import { LineByLineInput } from './line-by-line-input';
3737
import type { FormatOptions } from './format-output';
38+
import { markTime } from './startup-timing';
3839

3940
/**
4041
* All CLI flags that are useful for {@link MongoshNodeRepl}.
@@ -168,10 +169,12 @@ class MongoshNodeRepl implements EvaluationListener {
168169
);
169170
const shellEvaluator = new ShellEvaluator(
170171
instanceState,
171-
(value: any) => value
172+
(value: any) => value,
173+
markTime
172174
);
173175
instanceState.setEvaluationListener(this);
174176
await instanceState.fetchConnectionInfo();
177+
markTime('fetched connection info');
175178

176179
const { buildInfo, extraInfo } = instanceState.connectionInfo;
177180
let mongodVersion = extraInfo?.is_stream
@@ -185,6 +188,7 @@ class MongoshNodeRepl implements EvaluationListener {
185188
}
186189
await this.greet(mongodVersion, moreRecentMongoshVersion);
187190
await this.printBasicConnectivityWarning(instanceState);
191+
markTime('greeted');
188192

189193
this.inspectCompact =
190194
(await this.getConfig('inspectCompact')) ?? this.inspectCompact;
@@ -194,6 +198,7 @@ class MongoshNodeRepl implements EvaluationListener {
194198
(await this.getConfig('showStackTraces')) ?? this.showStackTraces;
195199
this.redactHistory =
196200
(await this.getConfig('redactHistory')) ?? this.redactHistory;
201+
markTime('fetched config vars');
197202

198203
const repl = asyncRepl.start({
199204
// 'repl' is not supported in startup snapshots yet.
@@ -342,6 +347,7 @@ class MongoshNodeRepl implements EvaluationListener {
342347
// https://github.com/nodejs/node/issues/36773
343348
(repl as Mutable<typeof repl>).line = '';
344349

350+
markTime('created repl object');
345351
const historyFile = this.ioProvider.getHistoryFilePath();
346352
try {
347353
await promisify(repl.setupHistory).call(repl, historyFile);
@@ -404,6 +410,7 @@ class MongoshNodeRepl implements EvaluationListener {
404410
this.output.write(this.writer(warn) + '\n');
405411
}
406412

413+
markTime('set up history file');
407414
(repl as any).on(asyncRepl.evalStart, () => {
408415
this.bus.emit('mongosh:evaluate-started');
409416
});
@@ -435,6 +442,7 @@ class MongoshNodeRepl implements EvaluationListener {
435442
}
436443
}
437444

445+
markTime('finished initialization');
438446
return { __initialized: 'yes' };
439447
}
440448

@@ -537,6 +545,7 @@ class MongoshNodeRepl implements EvaluationListener {
537545
context: any,
538546
filename: string
539547
): Promise<any> {
548+
markTime('start repl eval');
540549
if (!this.insideAutoCompleteOrGetPrompt) {
541550
this.lineByLineInput.enableBlockOnNewLine();
542551
}
@@ -590,6 +599,7 @@ class MongoshNodeRepl implements EvaluationListener {
590599
}
591600
throw err;
592601
} finally {
602+
markTime('done repl eval');
593603
if (!this.insideAutoCompleteOrGetPrompt && !interrupted) {
594604
// In case of an interrupt, onAsyncSigint will print the prompt when completed
595605
repl.setPrompt(await this.getShellPrompt());
@@ -598,6 +608,7 @@ class MongoshNodeRepl implements EvaluationListener {
598608
if (this.loadNestingLevel <= 1) {
599609
this.bus.emit('mongosh:eval-complete'); // For testing purposes.
600610
}
611+
markTime('re-set prompt');
601612
}
602613
}
603614

packages/cli-repl/src/run.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ function enableFipsIfRequested() {
1313
}
1414
enableFipsIfRequested();
1515

16+
import { markTime } from './startup-timing';
1617
import { CliRepl } from './cli-repl';
1718
import { parseCliArgs } from './arg-parser';
1819
import { runSmokeTests } from './smoke-tests';
@@ -47,6 +48,8 @@ if ((v8 as any)?.startupSnapshot?.isBuildingSnapshot?.()) {
4748

4849
(v8 as any).startupSnapshot.setDeserializeMainFunction(() => {
4950
enableFipsIfRequested();
51+
markTime('loaded pre-snapshot deps');
52+
5053
void main();
5154
});
5255
} else {
@@ -55,6 +58,7 @@ if ((v8 as any)?.startupSnapshot?.isBuildingSnapshot?.()) {
5558

5659
// eslint-disable-next-line complexity
5760
async function main() {
61+
markTime('entered main');
5862
if (process.env.MONGOSH_RUN_NODE_SCRIPT) {
5963
// For uncompiled mongosh: node /path/to/this/file script ... -> node script ...
6064
// FOr compiled mongosh: mongosh mongosh script ... -> mongosh script ...
@@ -213,6 +217,7 @@ async function main() {
213217
shellHomePaths: shellHomePaths,
214218
globalConfigPaths: globalConfigPaths,
215219
});
220+
markTime('entering repl.start()');
216221
await repl.start(connectionInfo.connectionString, {
217222
productName: 'MongoDB Shell',
218223
productDocsLink: 'https://www.mongodb.com/docs/mongodb-shell/',

0 commit comments

Comments
 (0)