Skip to content
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

mongose docReducer function inside __getAllSubdocs taking server hgh cpu even if my use-case doesn't involve embedded docs at all? #15029

Open
2 tasks done
jayantmf opened this issue Nov 11, 2024 · 1 comment · May be fixed by #15055
Milestone

Comments

@jayantmf
Copy link

jayantmf commented Nov 11, 2024

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the performance issue has not already been reported

Last performant version

4.9.9

Slowed down in version

4.9.9

Node.js version

v16.16.0

🦥 Performance issue

In the Mongoose lib CPU profiling, I noticed that the docReducer function present in document.js is consuming 30% of Application CPU resources. Upon further investigation, I found multiple calls to __getAllSubdocs during the document save call, which calls docReducer. This function recursively checks each key in the document to see if there are embedded documents. However, my collection does not contain embedded documents(embedded is not needed for my usecase), and I would like to avoid this overhead. Is there a flag to skip the docReducer call, or do you guys have any suggestions on how to handle this? Or what can be done here?

Shall i comment the code var subDocs = Object.keys(this._doc).reduce(docReducer.bind(this), []); and simply return []; ?

In the latest Mongo version docReducer function is optimized but its still there with big overhead.

`

Document.prototype.$__getAllSubdocs = function() {
DocumentArray || (DocumentArray = require('./types/documentarray'));
Embedded = Embedded || require('./types/embedded');

  function docReducer(seed, path) {
    var val = this[path];

if (val instanceof Embedded) {
  seed.push(val);
}

if (val && val.$isSingleNested) {
  seed = Object.keys(val._doc).reduce(docReducer.bind(val._doc), seed);
  seed.push(val);
}

if (val && val.isMongooseDocumentArray) {
  val.forEach(function _docReduce(doc) {
    if (!doc || !doc._doc) {
      return;
    }
    if (doc instanceof Embedded) {
      seed.push(doc);
    }
    seed = Object.keys(doc._doc).reduce(docReducer.bind(doc._doc), seed);
  });
} else if (val instanceof Document && val.$__isNested) {
  if (val) {
    seed = Object.keys(val).reduce(docReducer.bind(val), seed);
  }
}

return seed;
  }

  var subDocs = Object.keys(this._doc).reduce(docReducer.bind(this), []); 
  return subDocs; 
// shall i comment var subDocs = Object.keys(this._doc).reduce(docReducer.bind(this), []);  ? and return []
};

`

Steps to Reproduce

calling mongoose save docReducer of __getAllSubdocs gets called

Expected Behavior

No response

@vkarpov15
Copy link
Collaborator

I checked and it looks like $getAllSubdocs() loops over every path in the document - we can optimize that because String paths will never have a subdocument for instance.

Also, another issue is that a successful save() calls $getAllSubdocs() 5 times:

  1. getPathsToValidate()
  2. saveSubdocsPreSave()
  3. $__reset()
  4. _setIsNew()
  5. saveSubdocsPostSave()

That's definitely something we can optimize, there's no need to call $getAllSubdocs() 5 times.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants