Экспресс-API — узел-запросы postgres зависают, время ожидания запросов истекает

#node.js #postgresql #express #node-postgres

Вопрос:

У меня есть node.js экспресс-API (узел v14.17.5, экспресс «~4.16.0»), с логикой, в значительной степени ориентированной на выполнение SQL-запросов. Я использую библиотеку node-postgres (версия pg «^8.6.0») для подключения к базе данных postgres. После интенсивного трафика ( 100 запросов в секунду) API начал отвечать 504 (время ожидания запроса истекло) на каждый запрос. Когда я перезапускаю процесс узла, API отвечает, как и ожидалось, на запросы в течение примерно 1 минуты, и после этого каждый запрос в каждом экспресс-маршруте приходит на первый SQL-запрос и просто зависает, в результате чего получается 504.

Мне удалось определить, что (вероятно) является причиной этой проблемы, но я не знаю, нет ли у меня какой-либо логической ошибки в моем db.js файл. db.js Файл-это мой очень простой пользовательский «модуль» БД, в котором собрана логика БД. Я следовал документации по объединению узлов postgres и создаю и использую только 1 пул во всем API, потому что в каждом случае использования мне требуется db.js с: const db = require("./db.js");

Это содержимое db.js файла:

 const { Pool } = require('pg');

let pool = null;

function newPool() {
    /* 
        I have managed to (temporarily?) fix my issue with changing the maximum number of clients
        in Pool to 1000 (the default value according to node-postgres docs was only 10!
    */
    // return new Pool({ connectionString: "private_db_connection_string" });
    return new Pool({ connectionString: "private_db_connection_string", max: 1000 });
}

module.exports.getPool = function () {
    if (pool) {
        return pool;
    }
    pool = newPool();
    return pool;
}

module.exports.getClient = async function () {
    const pool = this.getPool();
    const client = await pool.connect();
    return client;
}

module.exports.query = async function (sql) {
    const pool = this.getPool();
    const { rows } = await pool.query(sql);
    return { rows };
}
 

Просто примечание: я обрабатываю возможные ошибки (если таковые возникают) внутри блоков try-catch в разделе «Логика бизнеса». Затем, в каждом маршруте, где мне требуется этот модуль, я просто выполняю запросы в пуле с вызовом:
await db.query("some SQL query");

Большую часть времени запросы выполняются таким образом — с использованием пула. Это рекомендуется в соответствии с документами, поскольку это устраняет риск утечки информации о клиенте. Есть всего несколько случаев, когда необходимы транзакции, и когда они мне нужны, я получаю клиента из пула client = db.getClient(); и всегда освобождаю клиента client.release(); .

До того, как я увеличил максимальное количество клиентов в пуле pg (когда истекало время ожидания каждого запроса), в базе данных postgres также была зарегистрирована какая-то странная ошибка (и она продолжала возникать).:

 request to flush past end of generated WAL;
writing block 0 of relation base/...
xlog flush request 8/... is not satisfied --- flushed only to 8/...
could not write block 0 of base/...
 

Как я уже упоминал, после изменения максимального числа клиентов пула на 1000 (ранее я не указывал максимальное значение в коде, а значение по умолчанию равно 10 в соответствии с документами) API теперь работает должным образом, и SQL-запросы больше не зависают (максимальное количество подключений в базе данных postgres также было увеличено). Я знаю, что проблема была (есть?) Только в моем коде, потому что база данных была легко доступна из любого другого процесса. Также обратите внимание, что базовая инфраструктура достаточно хороша для управления такой нагрузкой, поэтому эта проблема не была связана с перегрузкой оборудования. Я подозревал, что у меня есть какая-то логическая ошибка в db.js и есть утечка клиентского соединения, но я не могу найти никакой ошибки, даже прочитав документы уже несколько раз.

Теперь это работает после того, как я увеличил максимальное количество клиентов в объекте параметров пула до 1000 (API отвечает, как и ожидалось), я знаю, что по умолчанию (10) клиентов в пуле было недостаточно для такого количества одновременных запросов. Однако я не понимаю, почему каждый запрос продолжался до тех пор, пока не был выполнен первый SQL-запрос, а затем он просто зависал, в конечном итоге истекло время ожидания и не продолжался. Я думал, что даже при наличии 10 доступных клиентов в пуле должно быть несколько запросов, которые продолжают выполнять второй SQL — запрос (те, которым удается получить доступного клиента из пула), но, просмотрев журналы, я знаю, что при запуске процесса узла было небольшое окно-первая 1 минута, когда запросы продолжались для выполнения всех (или большинства) SQL-запросов. После этого каждый запрос начинал истекать после выполнения первого SQL-запроса по заданному маршруту.

Я думаю, что именно это и произошло: когда запрос X выполнил свой первый запрос, клиент был немедленно передан запросу X 1, ожидающему доступного клиента, и так далее. В конце концов, время ожидания запроса X истекло, и это то, что продолжало происходить. Я бы это понял, но первая минута была другой, и в каждом запросе выполнялось больше запросов. Я этого не понимаю, почему первая 1 минута выполнения процесса должна отличаться от остальных? (учитывая большое количество запросов с самого начала) Дополнительный вопрос: узел просто отбрасывает запрос, когда клиент получает 504? Я думал, что он продолжит выполнение запроса, если в коде нет никакого неявного ответа/возврата

Комментарии:

1. Код, который вы разместили в db.js не похоже, что он отвечает за ваши зависшие запросы. Вероятно, это что-то другое. Попробуйте войти в журнал, когда начинаются/заканчиваются запросы, когда клиент получен/выпущен (исправьте ошибку, если нужно!), А также запрашивайте Postgres для активных в данный момент запросов.