Mongoose startSession() hangs

Viewed 4072

I am using mongoose to connect to my Mongodb Atlas cluster through my nodejs server.

There is a certain operation which is done as a transaction. Mongoose needs mongoose.startSession() to be called to start a transaction. Very infrequently, this mongoose.startSession() call hangs indefinitely. There is no certain way to reproduce this.

log.info('starting lock session');
const mongoSession = await mongoose.startSession();
log.info('lock session started');

In above code, starting lock session. gets logged, but lock session started doesn't get logged when issue occurs.

I connect to the db like below:

const dburl = 'mongodb+srv://myuser:mypassword@myapp.mongodb.net/mydb?retryWrites=true&w=majority';
mongoose.connect(dburl, {useNewUrlParser: true}, err => {
    if (err) {
        log.warn('Error occurred when connecting to database. ' + err);
    }
});

What could be the reason for this? Could this be due to something wrong with the database? Is there any way I can further troubleshoot this?

2 Answers

This looks like a bug in mongoose, and I reported it to mongoose but still didn't get a response.

https://github.com/Automattic/mongoose/issues/8325

I wrote following function which I can use to wait until the mongoose connection is ready before calling startSession(), and it fixes my problem.

function waitForMongooseConnection(mongoose) {
    return new Promise((resolve) => {
        const connection = mongoose.connection;
        if (connection.readyState === 1) {
            resolve();
            return;
        }
        console.log('Mongoose connection is not ready. Waiting for open or reconnect event.');
        let resolved = false;
        const setResolved = () => {
            console.log('Mongoose connection became ready. promise already resolved: ' + resolved);
            if (!resolved) {
                console.log('Resolving waitForMongooseConnection');
                resolved = true;
                resolve();
            }
        };
        connection.once('open', setResolved);
        connection.once('reconnect', setResolved);
    });
}

With above function, I can start session like below:

log.info('starting session');
await waitForMongooseConnection(mongoose);
const mongoSession = await mongoose.startSession();
log.info('session started');

Note that I had to turn off useUnifiedTopology. Otherwise, 'reconnect' didn't get called.

mongoose.connect(config.db, {useNewUrlParser: true, useUnifiedTopology: false}, err => {
    if (err) {
        log.warn('Error occurred when connecting to database. ' + err);
    }
});

I resolved this issue by using connection object coming from mongoose.createConnection(uri, options)

const connection = mongoose.createConnection(uri, options);
const session = await connection.startSession();
session.startTransaction();
await MySchema.create({
    value: "Created?",
    session: session, // giving session here
});
await session.commitTransaction();
session.endSession();

mongoose.connection is different than this connection object.

Related