Что такое database connection leaks и как их находить в продакшене
Когда backend внезапно начинает отвечать медленно, база данных показывает сотни открытых соединений, а приложение периодически получает ошибки вроде:
Too many connectionsили:
remaining connection slots are reservedочень часто проблема оказывается не в самой СУБД, а в connection leak.
Утечки соединений — одна из самых неприятных production-проблем. Они могут неделями не проявляться под обычной нагрузкой, а потом внезапно положить сервис в пиковый момент.
В этой статье разберёмся, что такое database connection leak, почему он возникает и как искать такие проблемы в production.
Что такое connection leak
Connection leak — это ситуация, когда приложение открыло соединение к базе данных, но не закрыло его корректно.
Например:
const conn = await pool.getConnection();
const rows = await conn.query("SELECT * FROM users");Если после выполнения запроса соединение не вернуть обратно в pool:
conn.release();оно останется занятым.
С точки зрения приложения всё может выглядеть нормально.
Но постепенно:
- количество свободных соединений уменьшается
- pool начинает ждать освобождения
- запросы подвисают
- latency растёт
- backend начинает упираться в timeout
- база достигает лимита connections
В итоге приложение перестаёт нормально работать.
Почему leaks особенно опасны в production
В dev-среде утечки часто незаметны.
Причины:
- маленькая нагрузка
- короткое время жизни процесса
- мало параллельных запросов
- частые рестарты
Но в production ситуация другая.
Допустим:
- сервис держит pool на 50 connections
- один из запросов иногда забывает release
- leak происходит в 1% запросов
При высоком RPS pool постепенно исчерпается.
Самое неприятное — это может происходить медленно.
Например:
- утром всё нормально
- к вечеру latency начинает расти
- ночью начинаются timeout
- под нагрузкой сервис падает
После рестарта всё снова «магически чинится».
Как обычно возникают connection leaks
Самый распространённый сценарий — exception before release.
Например:
const conn = await pool.getConnection();
const user = await conn.query(query);
throw new Error("something failed");
conn.release();Из-за exception код release никогда не выполнится.
Правильный вариант:
const conn = await pool.getConnection();
try {
const user = await conn.query(query);
} finally {
conn.release();
}Другие частые причины:
- забытый rollback в transaction
- зависшие background jobs
- долгие streaming-запросы
- ORM баги
- unhandled promise rejection
- ранний return из функции
- panic/crash до cleanup
Особенно опасны сложные async-цепочки, где соединение передаётся между несколькими слоями приложения.
Как выглядят симптомы connection leak
На production leak обычно проявляется постепенно.
Типичные симптомы:
- рост latency
- timeout запросов
- pool exhaustion
- рост количества sleeping connections
- скачки CPU у backend
- деградация API под нагрузкой
- ошибки подключения к БД
В PostgreSQL можно увидеть:
SELECT count(*) FROM pg_stat_activity;Или:
SELECT pid, state, query
FROM pg_stat_activity;В MySQL:
SHOW PROCESSLIST;Если количество соединений постоянно растёт и не уменьшается — это уже подозрительно.
Особенно если много соединений находятся:
- idle
- idle in transaction
- Sleep
Почему idle in transaction особенно опасен
В PostgreSQL состояние:
idle in transactionсчитается очень плохим сигналом.
Это означает:
- transaction открыта
- запросы уже не выполняются
- соединение простаивает
- locks могут удерживаться
- vacuum может блокироваться
Такие leaks опаснее обычных idle connections.
Они могут:
- удерживать row locks
- мешать autovacuum
- раздувать таблицы
- ухудшать производительность всей БД
Иногда один забытый transaction способен деградировать весь PostgreSQL-кластер.
Как искать connection leaks в production
Первое — мониторинг количества соединений.
Полезные метрики:
- active connections
- idle connections
- idle in transaction
- pool utilization
- DB wait time
- query latency
- connection errors
Если connections растут со временем и не возвращаются обратно — вероятен leak.
Второе — correlation с нагрузкой.
Например:
- после определённого API количество connections резко увеличивается
- после cron job pool заполняется
- один endpoint вызывает рост idle sessions
Третье — tracing.
OpenTelemetry и distributed tracing помогают увидеть:
-
какие запросы открывают соединения
-
сколько времени они удерживаются
-
где transaction не закрывается
Это особенно полезно в микросервисах.
Полезные техники диагностики
Некоторые pool implementation умеют логировать долгоживущие connections.
Например:
Connection leak detectedHikariCP умеет делать leakDetectionThreshold.
Если connection удерживается слишком долго — появляется warning.
Также полезно:
- логировать begin/commit transaction
- считать open transactions
- отслеживать connection acquire time
- мониторить pool exhaustion
Для PostgreSQL очень полезен:
SELECT * FROM pg_stat_activity;А также:
SELECT * FROM pg_locks;Можно увидеть зависшие transaction и удерживаемые locks.
Почему рестарт приложения временно «лечит» leak
Это одна из самых коварных особенностей.
После рестарта:
- процесс завершается
- все TCP connections закрываются
- pool очищается
- база освобождает sessions
Сервис снова работает нормально.
Из-за этого многие долго не могут найти проблему.
Кажется, что:
- «PostgreSQL иногда тупит»
- «сеть флапает»
- «сервер перегружен»
Хотя проблема воспроизводится снова после накопления leaks.
Если приложение «чинится» после рестарта — connection leak всегда стоит проверить одним из первых.
Как избежать connection leaks
Главное правило:
Любое открытое соединение должно гарантированно закрываться.
Практически всегда нужен:
try/finallyили:
deferили context manager.
Также полезно:
- ограничивать время transaction
- использовать query timeout
- настраивать pool timeout
- избегать long-running transaction
- использовать auto cleanup
- мониторить pool metrics
Для production желательно иметь алерты на:
- рост connections
- pool exhaustion
- idle in transaction
- DB wait time
- connection acquisition latency
Потому что connection leak редко возникает мгновенно.
Обычно инфраструктура деградирует постепенно, и мониторинг позволяет поймать проблему до полного отказа.
Итоги
Database connection leak — это утечка соединений к базе данных, при которой приложение перестаёт возвращать connections обратно в pool.
Под небольшой нагрузкой проблема может быть незаметной, но в production leaks постепенно приводят к:
- exhaustion pool
- росту latency
- timeout
- ошибкам подключения
- деградации PostgreSQL/MySQL
Чаще всего leaks возникают из-за:
- exception before release
- незакрытых transaction
- async bugs
- ошибок cleanup
Самый эффективный способ борьбы — сочетание:
- правильного lifecycle management
- pool timeout
- observability
- мониторинга connections
- tracing
В production-системах connection pool — такой же критичный ресурс, как CPU, память или сеть. И если его не мониторить, утечки рано или поздно проявятся в самый неподходящий момент.
Настроить мониторинг за 30 секунд
Надежные оповещения о даунтаймах. Без ложных срабатываний