Как надежно подключиться к Mongodb в режиме без сервера?

8 из десяти раз все хорошо соединяется. Тем не менее, я иногда получаю MongoClient must be connected before calling MongoClient.prototype.db ошибка. Как я должен изменить свой код, чтобы он работал надежно (100%)?

Я попробовал фрагмент кода от одного из создателей платформы Now Zeit.

Мой обработчик

const { send } = require('micro');
const { handleErrors } = require('../../../lib/errors');
const cors = require('../../../lib/cors')();
const qs = require('micro-query');
const mongo = require('../../../lib/mongo');
const { ObjectId } = require('mongodb');

const handler = async (req, res) => {
  let { limit = 5 } = qs(req);

  limit = parseInt(limit);
  limit = limit > 10 ? 10 : limit;

  const db = await mongo();

  const games = await db
    .collection('games_v3')
    .aggregate([
      {
        $match: {
          removed: { $ne: true }
        }
      },
      { $sample: { size: limit } }
    ])
    .toArray();

  send(res, 200, games);
};

module.exports = handleErrors(cors(handler));

Мой скрипт монго, который повторно использует соединение в случае, если лямбда еще теплая:

// Based on: https://spectrum.chat/zeit/now/now-2-0-connect-to-database-on-every-function-invocation~e25b9e64-6271-4e15-822a-ddde047fa43d?m=MTU0NDkxODA3NDExMg==
const MongoClient = require('mongodb').MongoClient;

if (!process.env.MONGODB_URI) {
  throw new Error('Missing env MONGODB_URI');
}

let client = null;

module.exports = function getDb(fn) {
  if (client && !client.isConnected) {
    client = null;
    console.log('[mongo] client discard');
  }

  if (client === null) {
    client = new MongoClient(process.env.MONGODB_URI, {
      useNewUrlParser: true
    });
    console.log('[mongo] client init');
  } else if (client.isConnected) {
    console.log('[mongo] client connected, quick return');
    return client.db(process.env.MONGO_DB_NAME);
  }

  return new Promise((resolve, reject) => {
    client.connect(err => {
      if (err) {
        client = null;
        console.error('[mongo] client err', err);
        return reject(err);
      }

      console.log('[mongo] connected');
      resolve(client.db(process.env.MONGO_DB_NAME));
    });
  });
};

Мне нужен мой обработчик, чтобы быть на 100% надежным.

2 ответа

  if (client && !client.isConnected) {
    client = null;
    console.log('[mongo] client discard');
  }

Этот код может вызвать проблемы! Даже если вы устанавливаете client в null, этот клиент все еще существует, будет продолжать подключаться к Монго, не будет собирать мусор, и его код подключения обратного вызова будет по-прежнему выполняться, но в обратном вызове client будет ссылаться на следующий созданный клиент, который не обязательно подключен.

Распространенным шаблоном для такого рода кода является возвращение только одного обещания от getDB вызов:

let clientP = null;
function getDb(fn) {
  if (clientP) return clientP;
  clientP = new Promise((resolve, reject) => {
    client = new MongoClient(process.env.MONGODB_URI, {
      useNewUrlParser: true
    });
    client.connect(err => {
      if (err) {
        console.error('[mongo] client err', err);
        return reject(err);
      }

      console.log('[mongo] connected');
      resolve(client.db(process.env.MONGO_DB_NAME));
    });
  });
  return clientP;
};

Я была такая же проблема. В моем случае это было вызвано вызовом getDb() до того, как был возвращен предыдущий вызов getDb(). В этом случае я считаю, что client.isConnected возвращает true, хотя он все еще подключается.

Это было вызвано тем, что вы забыли поместить "await" перед вызовом getDb() в одном месте. Я разыскал, который путем вывода callstack из getDb, используя:

console.log(new Error().stack);

Я не вижу той же проблемы в примере кода в вопросе, хотя она может быть вызвана другим фрагментом кода, который не показан.

Я написал эту статью, в которой рассказывается о бессерверных соединениях лямбда и БД. Есть несколько хороших концепций, которые могут помочь вам найти первопричину вашей проблемы. Также есть примеры и варианты использования того, как уменьшить проблемы с пулом соединений.

Просто взглянув на ваш код, я могу сказать, что этого не хватает:

context.callbackWaitsForEmptyEventLoop = false;

Бессерверные: Dynamodb x Mongodb x Aurora бессерверные

Другие вопросы по тегам