Что такое database connection leaks и как их находить в продакшене

9 минут чтения
Средний рейтинг статьи — 4.7

Когда 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 detected

HikariCP умеет делать 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, память или сеть. И если его не мониторить, утечки рано или поздно проявятся в самый неподходящий момент.

9 минут чтения
Средний рейтинг статьи — 4.7

Настроить мониторинг за 30 секунд

Надежные оповещения о даунтаймах. Без ложных срабатываний