Skip to content

Commit

Permalink
perf(document): only call undoReset() 1x/document
Browse files Browse the repository at this point in the history
Fix #15255
  • Loading branch information
vkarpov15 committed Feb 13, 2025
1 parent 61d16b0 commit 29a02a4
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 0 deletions.
3 changes: 3 additions & 0 deletions lib/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -3540,6 +3540,9 @@ Document.prototype.$__reset = function reset() {
*/

Document.prototype.$__undoReset = function $__undoReset() {
if (this.$isSubdocument) {
return;
}
if (this.$__.backup == null || this.$__.backup.activePaths == null) {
return;
}
Expand Down
42 changes: 42 additions & 0 deletions test/document.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const ArraySubdocument = require('../lib/types/arraySubdocument');
const Query = require('../lib/query');
const assert = require('assert');
const idGetter = require('../lib/helpers/schema/idGetter');
const sinon = require('sinon');
const util = require('./util');
const utils = require('../lib/utils');

Expand Down Expand Up @@ -14296,6 +14297,47 @@ describe('document', function() {

delete mongoose.Schema.Types.CustomType;
});

it('handles undoReset() on deep recursive subdocuments (gh-15255)', async function() {
const RecursiveSchema = new mongoose.Schema({});

const s = [RecursiveSchema];
RecursiveSchema.path('nested', s);

const generateRecursiveDocument = (depth, curr = 0) => {
return {
name: `Document of depth ${curr}`,
nested: depth > 0 ? new Array(2).fill().map(() => generateRecursiveDocument(depth - 1, curr + 1)) : [],
__v: 5
};
};
const TestModel = db.model('Test', RecursiveSchema);
const data = generateRecursiveDocument(10);
const doc = new TestModel(data);
await doc.save();

sinon.spy(Document.prototype, '$__undoReset');

try {
const d = await TestModel.findById(doc._id);
d.increment();
d.data = 'asd';
// Force a version error by updating the document directly
await TestModel.collection.updateOne({ _id: doc._id }, { $inc: { __v: 1 } });
const err = await d.save().then(() => null, err => err);
assert.ok(err);
assert.equal(err.name, 'VersionError');
// `$__undoReset()` should be called 1x per subdoc, plus 1x for top-level doc. Without fix for gh-15255,
// this would fail because `$__undoReset()` is called nearly 700k times for only 2046 subdocs
assert.strictEqual(Document.prototype.$__undoReset.getCalls().length, d.$getAllSubdocs().length + 1);
assert.ok(Document.prototype.$__undoReset.getCalls().find(call => call.thisValue === d), 'top level doc was not reset');
for (const subdoc of d.$getAllSubdocs()) {
assert.ok(Document.prototype.$__undoReset.getCalls().find(call => call.thisValue === subdoc), `${subdoc.name} was not reset`);
}
} finally {
sinon.restore();
}
});
});

describe('Check if instance function that is supplied in schema option is available', function() {
Expand Down

0 comments on commit 29a02a4

Please sign in to comment.