Когда вы переносите приложение на serverless, база данных неожиданно становится узким горлышком. Функции масштабируются с лёгкостью — десятки, а потом и сотни параллельных вызовов отрабатывают за миллисекунды. Но PostgreSQL не разделяет этого энтузиазма: каждое новое подключение требует ресурсов, а когда сотни функций одновременно решат представиться базе, та просто откажет.
Эта коллизия называется connection storm, и обходные пути вроде «поднять лимиты» здесь не работают. Разберём, почему serverless и прямые подключения к PostgreSQL несовместимы без посредника и как выстроить архитектуру, в которой база остаётся доступной даже под шквалом вызовов.
Что такое connection storm и почему serverless его провоцирует
Connection storm (лавина соединений, connection churn) — это ситуация, когда множество клиентов практически одновременно пытаются открыть соединения с базой данных. Сервер PostgreSQL тратит ресурсы на установку каждого нового подключения: форкает процесс, выделяет память под рабочие структуры, проводит аутентификацию. Когда такие попытки приходят сотнями в секунду, база захлёбывается ещё до того, как начнёт обрабатывать запросы.
Короткоживущие клиенты как корень проблемы
Serverless-функция живёт недолго. Она холодно стартует, отрабатывает запрос и умирает. Если в коде функции написано «создать соединение → выполнить SQL → закрыть соединение», то при десяти параллельных вызовах происходит десять полных циклов установки подключения. При тысяче вызовов PostgreSQL получает тысячу рукопожатий за короткий промежуток времени.
Контейнерные приложения тоже страдают от этой проблемы — каждый экземпляр контейнера может держать свой пул, и при автомасштабировании количество соединений растёт линейно с числом реплик. Но serverless драматичнее: здесь нет постоянных контейнеров, есть только всплески функций, каждая из которых может запросить соединение.
Ситуация усугубляется тем, что функции отрабатывают быстро. Соединение открывается, выполняется один-два лёгких запроса, соединение закрывается. База тратит больше времени на обслуживание подключения, чем на полезную работу.
Параметр max_connections и почему его увеличение — ловушка
Параметр max_connections определяет максимальное число одновременных подключений к серверу PostgreSQL. Типовое значение по умолчанию — 100 соединений. Когда лимит исчерпан, база отвечает новым клиентам ошибкой: «FATAL: sorry, too many clients already». На standby-сервере значение этого параметра должно быть не меньше, чем на primary.
Кажется очевидным: раз отказы из-за нехватки слотов, давайте поднимем лимит до 500, до 1000 — и проблема решена. На практике это приводит к деградации производительности всей базы.
Ресурсная модель PostgreSQL и накладные расходы
Каждое соединение в PostgreSQL потребляет память и процессорное время, даже в состоянии покоя. Сервер вынужден отслеживать состояние каждого подключения, планировать запросы среди всех активных клиентов и переключаться между ними. При очень большом числе соединений документация PostgreSQL рекомендует активнее использовать connection pooling — потому что прямая модель «один клиент — одно соединение» не масштабируется.
Когда max_connections завышен, а лавина всё-таки случается, база может принять сотни подключений, но начнёт буксовать под тяжестью их обслуживания. Запросы, которые обычно выполнялись за 2 миллисекунды, растягиваются на секунды. Лавина из 500 соединений может положить сервер эффективнее, чем моментальный отказ при лимите в 100: база не падает, но перестаёт отвечать хоть сколько-нибудь приемлемо.
Архитектурное решение: пулер между приложением и базой
Правильный подход для serverless-среды — не давать функциям подключаться напрямую к PostgreSQL. Между приложением и базой нужно поместить прокси-пулер, который принимает множество короткоживущих клиентов, а к базе держит ограниченный пул долгоживущих соединений и переиспользует их.
Как работает connection pooling
Пул соединений (connection pool) — это управляемый набор заранее установленных подключений к базе данных. Когда функция запрашивает соединение, она на самом деле получает уже готовое из пула. Когда функция «закрывает» соединение, оно не разрывается, а возвращается в пул и становится доступным для следующей функции.
Этот механизм называют мультиплексированием: множество клиентов разделяют небольшое число реальных подключений. База видит не тысячу клиентов, а, скажем, двадцать стабильных соединений, через которые проходят запросы от всех функций.
Пул поглощает лавину на входе, превращая хаотичные всплески подключений в равномерную нагрузку.
RDS Proxy: управляемый прокси для AWS-окружений
Amazon RDS Proxy — это полностью управляемый сервис пулинга соединений для RDS и Aurora. Он спроектирован специально под сценарии, где приложения часто открывают и закрывают соединения или массово создают подключения. Документация AWS Lambda прямо рекомендует RDS Proxy для функций, работающих с базой.
Прокси берёт на себя стоимость установления соединений с базой: он держит тёплый пул, мультиплексирует клиентские запросы и переиспользует соединения. При перегрузе RDS Proxy не выбрасывает ошибку сразу — он может поставить запрос в очередь и дождаться освобождения соединения из пула. Жёсткий отказ превращается в задержку, и приложение получает шанс отработать, а не упасть.
Документация Aurora PostgreSQL подтверждает: connection pooling улучшает время отклика, а большинство версий Aurora PostgreSQL поддерживают RDS Proxy. Для тех версий, где поддержки прокси нет, AWS рекомендует PgBouncer.
PgBouncer: кроссплатформенный инструмент
PgBouncer — это легковесный пулер соединений, который работает практически в любом окружении: на виртуальных машинах, в контейнерах, на managed-сервисах. Он не зависит от конкретного облачного провайдера и даёт тонкий контроль над параметрами пула.
PgBouncer устанавливается рядом с базой или на отдельном узле. Приложение подключается к PgBouncer, а тот — к PostgreSQL, держа фиксированное число соединений. Этот подход работает и в Azure, и в Google Cloud, и в собственной инфраструктуре.
Ключевые отличия и выбор между прокси-пулером и самостоятельным PgBouncer
RDS Proxy — это managed-сервис: вы не думаете об обновлениях, отказоустойчивости и масштабировании самого прокси. Но он доступен только в AWS-экосистеме и оплачивается дополнительно.
PgBouncer требует самостоятельной настройки, мониторинга и поддержки. Зато он бесплатен, не привязан к вендору и позволяет настроить pooling максимально гибко.
Если вы целиком в AWS, используете Lambda и Aurora и готовы платить за управляемый сервис — выбирайте RDS Proxy. Если у вас мультиоблачная архитектура или нужно минимизировать затраты — ставьте PgBouncer. Оба инструмента решают задачу, разница в зоне ответственности и бюджете.
Настройка клиентской стороны: не плодим лишние инстансы
Даже с пулером можно создать проблемы, если клиентская сторона ведёт себя неаккуратно.
Локальные пулы в контейнерах и их суммарный эффект
Распространённая ситуация: разработчик настраивает в каждом контейнере приложения собственный пул соединений (например, через HikariCP, pg-pool или аналоги). При стабильном количестве контейнеров это нормально. Но при автомасштабировании число контейнеров растёт, и каждый поднимает свой пул соединений к базе. Даже локальные пулеры в распределённых контейнерных приложениях могут в сумме превысить лимиты базы.
В serverless-модели это ещё опаснее: количество одновременно работающих функций может меняться от единиц до сотен и тысяч. Если каждая откроет хотя бы два соединения, база получит кратно больше подключений, чем вы ожидали.
Выход — централизованный прокси-пулер перед базой и отказ от локальных пулов внутри функций. Функции должны открывать соединение через прокси, получать одно переиспользуемое подключение и не держать запасных.
Ленивое создание подключений и контроль числа экземпляров
При масштабировании serverless-среды количество соединений на развёртывание растёт. Поэтому инициализируйте подключение только когда оно действительно нужно, и сразу освобождайте после использования. Никаких предварительно открытых соединений вхолостую — это экономит процессорное время и не плодит неиспользуемые подключения.
Мониторинг и симптомы надвигающегося шторма
Connection storm редко приходит без предвестников. У него есть метрики, по которым можно заметить проблему до того, как база перестанет отвечать.
На что смотреть
Первый индикатор — количество активных соединений. Если оно приближается к max_connections, а всплески становятся чаще — пора действовать. Второй сигнал — рост числа ожидающих соединений или ошибок «too many clients» в логах приложения. Третий, более коварный — увеличение времени ответа базы: когда соединений много, планировщик PostgreSQL начинает буксовать, и задержки растут даже при штатном числе запросов.
Мониторинг должен охватывать не только саму базу, но и пулер. Если вы используете RDS Proxy или PgBouncer, следите за длиной очереди на получение соединения и за утилизацией пула. Если пул постоянно полностью занят, возможно, пора немного увеличить его размер или оптимизировать запросы.
Мягкая деградация вместо жёсткого отказа
Хороший пулер не пускает лавину к базе, а буферизует запросы на входе. Вместо исключения «connection refused» клиент получает задержку в несколько сотен миллисекунд или секунд. Приложение может быть готово к этому: добавить повторные попытки с экспоненциальной задержкой, circuit breaker или быстрый отказ с понятным сообщением.
Такой подход называется graceful degradation — вы не избегаете пиковой нагрузки, но проходите её с минимальными потерями: часть запросов отрабатывает медленнее, но ни один не падает с фатальной ошибкой.
FAQ: частые вопросы и короткие ответы
Что такое connection storm при работе с PostgreSQL?
Это лавинообразный всплеск попыток подключения к базе, когда множество клиентов одновременно открывают соединения. База тратит ресурсы на установку подключений и либо резко замедляется, либо отказывает новым клиентам по лимиту max_connections.
Почему serverless-приложения создают так много коротких соединений?
Функции запускаются и завершаются независимо друг от друга. Если каждая функция открывает и закрывает соединение, при масштабировании до сотен параллельных вызовов база получает сотни полных циклов установки подключения за короткое время.
Какой параметр PostgreSQL отвечает за лимит одновременных подключений?
Параметр max_connections задаёт максимальное число конкурентных подключений к серверу. При стандартных настройках PostgreSQL значение по умолчанию — 100.
Нужно ли увеличивать max_connections в serverless-проектах?
Как правило, нет. Высокий лимит не предотвращает лавину, а лишь позволяет базе принять больше соединений перед деградацией. При этом каждое лишнее соединение потребляет память и процессор, замедляя все запросы.
Что лучше использовать: RDS Proxy или самопальный PgBouncer?
RDS Proxy — полностью управляемый сервис AWS, берущий на себя отказоустойчивость и масштабирование пулера. PgBouncer — самостоятельное решение, бесплатное и не привязанное к одному облаку. Выбор зависит от экосистемы и готовности обслуживать инфраструктуру.
Поможет ли встроенный пул соединений внутри Lambda или контейнера?
Локальные пулы решают проблему на уровне одного экземпляра, но при масштабировании суммарное число соединений от всех экземпляров может превысить лимиты базы. Централизованный прокси-пулер перед базой надёжнее.
Как пулер соединений превращает ошибку в задержку?
Когда все соединения пула заняты, пулер не отвергает новый запрос, а ставит его в очередь. Клиент ждёт освобождения соединения вместо того, чтобы сразу получить отказ. Это позволяет пережить пиковую нагрузку без потери запросов.
Три шага к спокойному serverless с PostgreSQL
Connection storm не обязан становиться регулярным ночным кошмаром. Решение укладывается в три шага.
Первый — признайте, что прямые подключения из serverless-функций к PostgreSQL не масштабируются. Как только число параллельных вызовов начинает измеряться десятками, нужен посредник.
Второй — поставьте пулер. RDS Proxy, если вы в AWS и готовы платить за управляемый сервис. PgBouncer, если вам нужен контроль и кроссплатформенность. В обоих случаях база будет видеть фиксированное число долгоживущих соединений, а не толпу короткоживущих клиентов.
Третий — следите за метриками. Количество активных соединений, длина очереди в пулере, время ответа базы, ошибки подключения в логах приложения. Шторм не начинается мгновенно, у вас будет время заметить приближение и расширить пул или оптимизировать запросы.
Serverless и PostgreSQL могут работать вместе без боли. Просто не заставляйте базу здороваться с каждым гостем лично — пусть этим займётся специально обученный посредник.




.svg.webp)


