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

fix(model): avoid adding timeout on Model.init() buffering to avoid unintentional dangling open handles #15251

Merged
merged 2 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
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
41 changes: 25 additions & 16 deletions lib/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -818,32 +818,41 @@ Connection.prototype.dropCollection = async function dropCollection(collection)
/**
* Waits for connection to be established, so the connection has a `client`
*
* @param {Boolean} [noTimeout=false] if set, don't put a timeout on the operation. Used internally so `mongoose.model()` doesn't leave open handles.
* @return Promise
* @api private
*/

Connection.prototype._waitForConnect = async function _waitForConnect() {
Connection.prototype._waitForConnect = async function _waitForConnect(noTimeout) {
if ((this.readyState === STATES.connecting || this.readyState === STATES.disconnected) && this._shouldBufferCommands()) {
const bufferTimeoutMS = this._getBufferTimeoutMS();
let timeout = null;
let timedOut = false;
// The element that this function pushes onto `_queue`, stored to make it easy to remove later
const queueElement = {};
await Promise.race([
new Promise(resolve => {
queueElement.fn = resolve;
this._queue.push(queueElement);
}),
new Promise(resolve => {
timeout = setTimeout(
() => {
timedOut = true;
resolve();
},
bufferTimeoutMS
);
})
]);

// Mongoose executes all elements in `_queue` when initial connection succeeds in `onOpen()`.
const waitForConnectPromise = new Promise(resolve => {
queueElement.fn = resolve;
this._queue.push(queueElement);
});

if (noTimeout) {
await waitForConnectPromise;
} else {
await Promise.race([
waitForConnectPromise,
new Promise(resolve => {
timeout = setTimeout(
() => {
timedOut = true;
resolve();
},
bufferTimeoutMS
);
})
]);
}

if (timedOut) {
const index = this._queue.indexOf(queueElement);
Expand Down
2 changes: 1 addition & 1 deletion lib/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -1112,7 +1112,7 @@ Model.init = function init() {
);
if (autoCreate == null) {
// `autoCreate` may later be set when the connection is opened, so wait for connect before checking
await conn._waitForConnect();
await conn._waitForConnect(true);
autoCreate = utils.getOption(
'autoCreate',
this.schema.options,
Expand Down
Loading