From 328ddaacced5ac0ecf4fb10e054a8609d9d6c4e9 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Tue, 24 Sep 2024 15:49:14 -0400 Subject: [PATCH] fix(document): avoid massive perf degradation when saving new doc with 10 level deep subdocs Fix #14897 --- lib/document.js | 4 +++- test/document.test.js | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/document.js b/lib/document.js index 64e65df8494..c12e0f03fb6 100644 --- a/lib/document.js +++ b/lib/document.js @@ -2724,7 +2724,9 @@ function _getPathsToValidate(doc, pathsToValidate, pathsToSkip) { } if (doc.$isModified(fullPathToSubdoc, null, modifiedPaths) && - !doc.isDirectModified(fullPathToSubdoc) && + // Avoid using isDirectModified() here because that does additional checks on whether the parent path + // is direct modified, which can cause performance issues re: gh-14897 + !doc.$__.activePaths.getStatePaths('modify').hasOwnProperty(fullPathToSubdoc) && !doc.$isDefault(fullPathToSubdoc)) { paths.add(fullPathToSubdoc); diff --git a/test/document.test.js b/test/document.test.js index 6a5765fe116..7150ffe64b4 100644 --- a/test/document.test.js +++ b/test/document.test.js @@ -13905,6 +13905,27 @@ describe('document', function() { const objectWithGetters = result.toObject({ getters: true, virtuals: false }); assert.strictEqual(objectWithGetters.level1.level2.level3.property, 'TESTVALUE'); }); + + it('handles inserting and saving large document with 10-level deep subdocs (gh-14897)', async function() { + const levels = 10; + + let schema = new Schema({ test: { type: String, required: true } }); + let doc = { test: 'gh-14897' }; + for (let i = 0; i < levels; ++i) { + schema = new Schema({ level: Number, subdocs: [schema] }); + doc = { level: (levels - i), subdocs: [{ ...doc }, { ...doc }] }; + } + + const Test = db.model('Test', schema); + const savedDoc = await Test.create(doc); + + let cur = savedDoc; + for (let i = 0; i < levels - 1; ++i) { + cur = cur.subdocs[0]; + } + cur.subdocs[0] = { test: 'updated' }; + await savedDoc.save(); + }); }); describe('Check if instance function that is supplied in schema option is available', function() {