Automattic / mongoose

MongoDB object modeling designed to work in an asynchronous environment.
https://mongoosejs.com
MIT License
26.89k stars 3.83k forks source link

await mongoose.connect() not fully connected #14665

Closed mrteddi closed 2 months ago

mrteddi commented 3 months ago

Prerequisites

Mongoose version

8.4.1

Node.js version

18.19.1

MongoDB server version

7.0.4

Typescript version (if applicable)

No response

Description

I am migrating from Mongoose 6.12.3 to 8.4.1 and am getting undefined when trying to reference mongoose.connection.db immediately after connection is awaited

Steps to Reproduce

async function connect() {
  await mongoose.connect(url_connection_string)
  console.log(mongoose.connection.db) // is undefined in mongoose 7.x+, was valid and populated in mongoose 6.12.3
}

I noticed that mongoose isn't fully connecting before the promise for mongoose.connect() is being resolved. In the mongoose.connection object, I noticed a $initialConnection field that looked like '$initialConnection': Promise { <pending> } showing a promise still pending.

If I then did

await mongoose.connection.$initialConnection
console.log(mongoose.connection.db) // no longer undefined

Also

await new Promise((resolve, reject) => {
  mongoose.connection.on("connected").then(() => resolve()).catch((err) => reject(err))
})
console.log(mongoose.connection.db) // no longer undefined

Expected Behavior

I would expect the database to be fully connected and accessible with mongo.connect

async function connect() {
  await mongoose.connect(url_connection_string)
  console.log(mongoose.connection.db) // is immediately accessible in mongoose 7.x+
}
Imanghvs commented 3 months ago

@mrteddi I tested it with both mongoose@8.4.1 and mongoose@7.6.13 and it works. Mongodb version was 7.0.11 (I don't know if the patch difference with your version is affecting it or not). Here's my sample code:

const {default: mongoose} = require('mongoose');
const uri = 'mongodb://127.0.0.1:27017';
const initializeDB = async () => {
  const connection = await mongoose.connect(uri);
  console.log('connection: ', mongoose.connection.db); // It works. It's not undefined.
};

initializeDB();
vkarpov15 commented 3 months ago

I tried the following script on v8.4.1 and v7.6.13, I got a Db instance printed to the console in both cases, so I'm unable to repro this. Can you please provide a complete script that demonstrates the issue you're seeing?

const mongoose = require('mongoose');

connect();

async function connect() {
  await mongoose.connect('mongodb://127.0.0.1:27017/mongoose_test');
  console.log(mongoose.connection.db) // is undefined in mongoose 7.x+, was valid and populated in mongoose 6.12.3
}
CreativeWarlock commented 3 months ago

@mrteddi Are you running MongoDB instances in Kubernetes pods?

Maybe this is related (if not ignore me): I had the issue of Node.js instances running too quickly in my K8s pods while for some (to me unknown) reason the connections to the MongoDB were not properly established in time. This lead to the occasional problem that the service could not load data from the MongoDB.

I then came up with a simple solution to retry connecting to the MongoDB during the initialization process and only load data after the connection was actually established:

import mongoose from 'mongoose'

async function connectToMongoDb(
    uri: string,
    maxRetryAttempts: number,
    waitSecondsBetweenAttempts: number,
    options?: mongoose.ConnectOptions | undefined
) {
    let currentRetryAttempt = 0

    /* const options = {
        useNewUrlParser: true,
        useUnifiedTopology: true,
        serverSelectionTimeoutMS: 5000,
        autoIndex: false, // Don't build indexes
        maxPoolSize: 10, // Maintain up to 10 socket connections
        socketTimeoutMS: 45000, // Close sockets after 45 seconds of inactivity
        family: 4 // Use IPv4, skip trying IPv6
    } */ // adjust the above options in case you need to configure your MongoDB connection

    while (currentRetryAttempt < maxRetryAttempts) {
        try {
            console.log('Connecting to MongoDb ...')
            mongoose.set('strictQuery', true)
            await mongoose.connect(uri, options)
            const conn = await mongoose.createConnection(uri, options).asPromise()
            console.log(`...done! (conn.readyState: ${conn.readyState})`)
            return
        } catch (error) {
            console.log(
                `MongoDB connection unsuccessful. Retrying in ${waitSecondsBetweenAttempts} seconds. (${
                    currentRetryAttempt + 1
                }/${maxRetryAttempts})`
            )
            currentRetryAttempt++
            await new Promise((resolve) => setTimeout(resolve, waitSecondsBetweenAttempts * 1000))
        }
    }

    console.log(`MongoDB connection failed after ${maxRetryAttempts} retries. Exiting function.`)
    process.exit(1)
}

export { connectToMongoDb }

Then in your initialization of your Node.js app you can then call

await connectToMongoDb(process.env.MONGO_URI, 5, 5) // e.g. MONGO_URI ='mongodb://my-mongo-srv:27017/my-service'
// ... load data MongoDB you need
github-actions[bot] commented 2 months ago

This issue is stale because it has been open 14 days with no activity. Remove stale label or comment or this will be closed in 5 days

github-actions[bot] commented 2 months ago

This issue was closed because it has been inactive for 19 days and has been marked as stale.