Skip to content

Mongoose findOneAndUpdate hangs under heavy load #14877

Closed
@andreialecu

Description

@andreialecu

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    underlying library issueThis issue is a bug with an underlying library, like the MongoDB driver or mongodb-core

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions