Closed
Description
Prerequisites
- I have written a descriptive issue title
- I have searched existing issues to ensure the bug has not already been reported
Mongoose version
8.5.1
Node.js version
20.17.0
MongoDB server version
7.0.12
Typescript version (if applicable)
No response
Description
We have a batch job that does a bunch of updates that used to work a while ago, but recently started hanging on calls to findOneAndUpdate
. The promise stops resolving after a while.
I have been able to create a repro for this, but it seems to only viably reproduce on MongoDB Atlas (or probably other remote servers). On localhost it doesn't seem to reproduce.
Also if I bypass mongoose entirely and use the mongodb driver directly, it seems to work.
Steps to Reproduce
const mongoose = require('mongoose');
const Parallel = require('async-parallel');
const { setTimeout } = require('timers/promises');
const ObjectId = mongoose.Types.ObjectId;
const Schema = mongoose.Schema;
const testSchema = new Schema({
fieldA: {
type: Schema.Types.ObjectId,
required: true,
},
fieldB: {
type: String,
required: true,
},
fieldC: {
type: Number,
default: 0,
},
fieldD: {
subFieldA: [String],
subFieldB: [String],
subFieldC: [String],
},
fieldK: {
type: Date,
default: Date.now,
},
});
const Test = mongoose.model('Test', testSchema);
async function createObject() {
await setTimeout(100 + Math.random() * 100);
return {
fieldA: new ObjectId(),
// random hex
fieldB: new ObjectId().toHexString(),
fieldC: 0,
fieldD: {
// make this big:
subFieldA: Array.from({ length: 2000 }, () =>
new ObjectId().toHexString(),
),
subFieldB: Array.from({ length: 2000 }, () =>
new ObjectId().toHexString(),
),
subFieldC: Array.from({ length: 2000 }, () =>
new ObjectId().toHexString(),
),
},
};
}
async function run() {
await mongoose.connect(
// 'mongodb://localhost:27017/testmongoose', // could not reproduce on localhost
'mongodb+srv://....mongodb.net/testmongoose?ssl=true&authSource=admin', // replace with a mongodb atlas connection string
);
let tidx = 0;
let total = 10000;
await Parallel.each(
Array.from({ length: total }),
async (_, i) => {
const obj = await createObject();
const result = await Promise.race([
Test.findOneAndUpdate(
{ fieldB: obj.fieldB, fieldK: { $lt: new Date() } },
obj,
{
upsert: true,
},
),
// following is faster and doesn't have the issue
// Test.db.collection('test123').findOneAndUpdate(
// { fieldB: obj.fieldB, fieldK: { $lt: new Date() } },
// { $set: obj },
// {
// upsert: true,
// },
// ),
setTimeout(30000, new Error('timeout')),
]);
if (result instanceof Error) {
console.log(`Timeout after ${tidx}/${total}`);
process.exit(1);
}
tidx++;
console.log(`${tidx}/${total}`);
},
{ concurrency: 20 },
);
console.log('done');
mongoose.disconnect();
}
run();
Expected Behavior
This must've started happening after some recent update to either mongoose, mongodb, or a mongo version. Unclear which, we haven't ran this job in a while and just noticed it occuring now.