【问题标题】:MongoDB Atlas connections to cluster(s) exceeded- NextJS + NextAuthMongoDB Atlas 与集群的连接超出 - NextJS + NextAuth
【发布时间】:2022-06-13 14:03:36
【问题描述】:

我正在使用 NextJS 创建一个网站,并使用 NextAuth 进行身份验证。该数据库在 MongoDB Atlas 中处于免费层。

我有两个版本的数据库连接代码。一个是这样的:

/**
 *      MongoDB Connection
 * 
 *  */
import mongoose from 'mongoose'

const MONGODB_URI = process.env.MONGODB_URL

if (! process.env.MONGODB_URL) {
  throw new Error(
    'Please define the MONGODB_URI environment variable inside .env.local'
  )
}

/**
 * Global is used here to maintain a cached connection across hot reloads
 * in development. This prevents connections growing exponentially
 * during API Route usage.
 */
let cached = global.mongoose

if (!cached) {
  cached = global.mongoose = { conn: null, promise: null }
}

async function dbConnect() {
  if (cached.conn) {
    return cached.conn
  }

  if (!cached.promise) {
    const opts = {
      useNewUrlParser: true,
      useUnifiedTopology: true,
      bufferCommands: false,
    //   bufferMaxEntries: 0,
    //   useFindAndModify: false,
    //   useCreateIndex: true,
    }

    cached.promise = mongoose.connect(process.env.MONGODB_URL, opts).then((mongoose) => {
      return mongoose
    })
  }
  cached.conn = await cached.promise
  return cached.conn
}

export default dbConnect

因此,在通过代码进行任何与数据库相关的查询之前,我会调用await dbConnect()。它工作正常。

但是为了在数据库中存储会话,在 NextAuth 中,我无法使用上述功能。所以为此,我正在使用这个自定义代码(/lib/mongodb.js):

/**
 * 
 *      Used only for Next-Auth
 * 
 */

import { MongoClient } from "mongodb"

const uri = process.env.MONGODB_URL
const options = {
  useUnifiedTopology: true,
  useNewUrlParser: true,
}

let client
let clientPromise

if (!process.env.MONGODB_URL) {
  throw new Error("Please add your Mongo URI to .env.local")
}

if (process.env.NODE_ENV === "development") {
  // In development mode, use a global variable so that the value
  // is preserved across module reloads caused by HMR (Hot Module Replacement).
  if (!global._mongoClientPromise) {
    client = new MongoClient(uri, options)
    global._mongoClientPromise = client.connect()
  }
  clientPromise = global._mongoClientPromise
} else {
  // In production mode, it's best to not use a global variable.
  client = new MongoClient(uri, options)
  clientPromise = client.connect()
}

// Export a module-scoped MongoClient promise. By doing this in a
// separate module, the client can be shared across functions.
export default clientPromise

而我的 /pages/api/auth/[...nextauth].js 中的代码是这样的:

import NextAuth from 'next-auth'
import { MongoDBAdapter } from "@next-auth/mongodb-adapter"
import mongodb from '../../../lib/mongodb'
//...

export default async function auth(req, res) {
    return await NextAuth(req, res, {    
        //....

        adapter: MongoDBAdapter({
            db: (await mongodb).db("my_db_name_here")            
        }),

        //....
    })
}

这是正在使用的包:

"mongodb": "^4.1.2",
"mongoose": "^6.0.1",    
"next": "11.0.1",
"next-auth": "^4.0.0-beta.6",
"react": "17.0.2",
"react-dom": "17.0.2",

问题是,我有时会收到如下电子邮件通知:

我的网站仍处于测试阶段(仅由两个人测试)并托管在 Vercel 服务器中。我相信这可能是因为 NextAuth 每次都在创建新的数据库连接?对出了什么问题有任何想法吗?

【问题讨论】:

    标签: mongodb next.js mongodb-atlas vercel next-auth


    【解决方案1】:

    next-auth 中的clientPromise 是本地的,每次创建新客户端和5 个连接。只需使用global.mongoose.conn

    MongoDBAdapter 的文档说它需要一个可以解析为客户端的承诺,所以它必须是这样的:

    export default NextAuth({
      adapter: MongoDBAdapter(dbConnect().then(mon => mon.connection.getClient())),
      ...
    })
    

    在您的情况下,您似乎使用 db.我找不到任何 MongoDBAdapter 的引用来接受类似 {db: ...} 的东西,但您可以从 mongoose 获取数据库,如下所示:

    await (dbConnect().then(mon => mon.connection.getClient().db("my_db_name_here")))
    

    或不带参数使用与猫鼬连接中配置的相同的数据库。

    更新

    Vercel creates new DB connection for every request 中介绍了来自 Vercel 的连接数问题

    【讨论】:

    • 我尝试使用您提供的第二种解决方案(通过 db),但在控制台中出现错误提示 conn.getClient is not a function
    • 当我使用第一个解决方案时,它抛出了这个错误:TypeError: Cannot read property 'collection' of undefined. UnhandledPromiseRejectionWarning: TypeError: conn.getClient is not a function
    • @AkhileshBChandran,抱歉,错过了“连接”位。您的 sn-p 中的global.mongoose.conn 不是顾名思义的连接,而是整个猫鼬,所以它应该是conn.connection.getClient() 而不是conn.getClient()。我更新了答案
    • 谢谢。第一个解决方案是抛出TypeError: Cannot read property 'collection' of undefined。但是第二个选项不会引发任何错误,并且似乎工作正常。但是我该如何仔细检查呢?我的意思是有没有办法通过控制台日志或关于有多少连接的东西来检查?
    • 在第二个 sn-p 中,仅当 process.env.NODE_ENV === "development" 连接图表看起来不太好时,您才重新使用全局承诺。你确定这是你连接atlas的唯一地方,只有一个nodejs服务器,连接池是每个客户端默认5个连接?恐怕解决方法需要先进行一些调试才能确定根本原因。
    猜你喜欢
    • 2021-03-09
    • 2018-03-05
    • 2019-09-13
    • 1970-01-01
    • 2019-06-10
    • 2020-01-15
    • 2017-12-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多