Как надежно подключиться к 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;