Соединения с базой данных пикают при запуске Heroku Dyno с помощью django-db-geventpool - MAX_CONNS Not Enforced
Я использую Django, Gunicorn с Gevent и django-db-geventpool на Heroku (Performance L dynos, WEB_CONCURRENCY=17). Количество соединений с базой данных значительно увеличивается при запуске дино, превышая ожидаемое количество соединений.
Ожидаемое поведение
Учитывая мою установку:
MAX_CONNS=4
(на одного рабочего)REUSE_CONNS=2
WEB_CONCURRENCY=17
(рабочих на одну диномашину)- 6 динамо-машин в производстве
Я ожидаю, что каждый dyno будет держать не более 68 соединений (17 рабочих * 4 MAX_CONNS). Однако при запуске я вижу, что отдельные дино временно удерживают 150+ незадействованных соединений, что способствует превышению лимита Heroku в 500 соединений.
Ключевые вопросы
- Что делает django-db-geventpool, если запрашивается больше
MAX_CONNS
соединений?- Ставятся ли запросы в очередь и вынуждены ли они ждать, пока освободится соединение?
- Или же django-db-geventpool игнорирует
MAX_CONNS
и позволяет соединениям превышать лимит?
- Почему я могу наблюдать скачки соединений во время запуска dyno?
Шаги, предпринятые до сих пор
- Убедились, что всплеск происходит только при запуске, а не при нормальном трафике.
- Проверил
pg_stat_activity
и увидел много незадействованных соединений с того же дино. - Убедился, что у меня нет утечки соединений от Celery, заданий cron или фоновых задач.
Кто-нибудь сталкивался с этой проблемой при использовании django-db-geventpool на Heroku? Есть ли какие-нибудь соображения по поводу того, относится ли это к MAX_CONNS
или соединения могут превысить лимит при высоком параллелизме?
Да, это должно абсолютно соответствовать MAX_CONNS
, равному на одного работника. Я полагаю, что происходит то, что у вас на короткое время вдвое больше работников. Я столкнулся с чем-то подобным в AWS Elastic Beanstalk. Виновником были Все сразу Развертывание, которое отвечало за наличие 2-х экземпляров и, таким образом, удвоило количество подключений и превысило лимит подключений к базе данных. Все, что было нужно, - это изменить его на пакетный запуск. К сожалению, в Heroku, похоже, нет подобной опции. Что вы можете сделать, так это временно уменьшить масштаб вашего приложения heroku ps:scale worker=5
, поработать с динамометром и снова увеличить его, как только закончите heroku ps:scale worker=17
Я предполагаю, что ваш динамометр использует множество процессов (я думаю, что выше 8 для каждого динамометра)
итак, MAX_CONNS
x WEB_CONCURRENCY
x No. of processes
= По всем соединениям
, что в данном случае равно 4 x 17 x 8 = 544
или больше.
из django-db-geventpool GITHUB:
Ваш веб-сервер использует более одного процесса? В этом случае каждый процесс независим и соединения с базой данных не могут быть общими, таким образом, 2 процесса по 15 подключений в каждом могут открывать 30 подключений
Из-за того, как работают python и gevent, пул разделяется между потоками (или гринлетами) внутри одного и того же процесса python, соединение не может быть разделено с другим процессом. Помните, что с gevent один процесс может использовать сотни или тысячи подключений одновременно, этот пул позволяет установить ограничение для каждого процесса, а также избежать запросов django DB для блокировки процесса, что сделает gevent бесполезным. Если вам нужен глобальный пул, следует использовать pgbouncer или что-то подобное.
Таким образом, значение MAX_CONNS должно быть равно (разрешенные подключения postgres / количество процессов) или меньше.