Skip to content

feat: add skipOriginalStackTraces option to avoid stack trace performance overhead #15345

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 6, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions lib/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -2638,12 +2638,9 @@ Document.prototype.validate = async function validate(pathsToValidate, options)
if (this.$isSubdocument != null) {
// Skip parallel validate check for subdocuments
} else if (this.$__.validating && !_skipParallelValidateCheck) {
parallelValidate = new ParallelValidateError(this, {
parentStack: options && options.parentStack,
conflictStack: this.$__.validating.stack
});
throw new ParallelValidateError(this);
} else if (!_skipParallelValidateCheck) {
this.$__.validating = new ParallelValidateError(this, { parentStack: options && options.parentStack });
this.$__.validating = true;
}

if (parallelValidate != null) {
Expand Down
6 changes: 4 additions & 2 deletions lib/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -4431,10 +4431,12 @@ Query.prototype.exec = async function exec(op) {
str = str.slice(0, 60) + '...';
}
const err = new MongooseError('Query was already executed: ' + str);
err.originalStack = this._executionStack;
if (!this.model.base.options.skipOriginalStackTraces) {
err.originalStack = this._executionStack;
}
throw err;
} else {
this._executionStack = new Error().stack;
this._executionStack = this.model.base.options.skipOriginalStackTraces ? true : new Error().stack;
}

let skipWrappedFunction = null;
Expand Down
1 change: 1 addition & 0 deletions lib/validOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const VALID_OPTIONS = Object.freeze([
'sanitizeProjection',
'selectPopulatedPaths',
'setDefaultsOnInsert',
'skipOriginalStackTraces',
'strict',
'strictPopulate',
'strictQuery',
Expand Down
15 changes: 15 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1228,4 +1228,19 @@ describe('mongoose module:', function() {
assert.equal(m.connection.readyState, 1);
});
});

it('supports skipOriginalStackTraces option (gh-15194)', async function() {
const schema = new Schema({ name: { type: String, required: true } });
const m = new mongoose.Mongoose();
m.set('skipOriginalStackTraces', true);
await m.connect(start.uri);

const TestModel = m.model('Test', schema);
const q = TestModel.find({});
await q.exec();
assert.strictEqual(q._executionStack, true);

const err = await q.exec().then(() => null, err => err);
assert.strictEqual(err.originalStack, undefined);
});
});
8 changes: 8 additions & 0 deletions types/mongooseoptions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,5 +215,13 @@ declare module 'mongoose' {
* to their database property names. Defaults to false.
*/
translateAliases?: boolean;

/**
* Mongoose queries currently store an `_executionStack` property that stores the stack trace
* of where the query was originally executed for debugging `Query was already executed` errors.
* This behavior can cause performance issues with bundlers and source maps. Set this option to
* `true` to disable Mongoose query stack trace collection.
*/
skipOriginalStackTraces?: boolean;
}
}