@alenov
Программист

Как найти причину «Exception: idle transaction timeout» в приложении Flask+psycopg2+postgresql+pgbouncer?

Была такая проблема. С периодичностью раз в месяц web-приложение останавливалось с ошибкой 502. Причина - postgresql переставал обрабатывать запросы, потому что был превышен лимит количества соединений. Список процессов показывал кучу открытых ожидающих соединений, которые блокировались каким-то запросом. Причём запрос был вполне рядовой, который обычно выполнялся мгновенно. Причину, которая приводила к такой блокировке, выяснить не хватило ума. Но было несколько процессов "idle in transaction", и после убивания одного из них всё восстанавливалось.

В postgresql 9.5 ещё нет опции, устанавливающей таймаут на висячие транзакции. Поэтому было решено установить Pgbouncer.
В итоге есть postgresql, перед ним pgbouncer в pool_mode=transaction.

Пару недель всё работало замечательно. Но в течение последних двух дней вдруг без всякой видимой причины образовалась проблема. Web-приложение возвращает пользователю 500, пользователь удивляется, пробует ещё раз, всё опять работает нормально. Через несколько минут, уже в другом месте приложения, ситуация повторяется. Пользователь нервничает.

В логе приложения (Python, Flask, psycopg2):

Exception on /any-url-of-the-application [POST]
...
...
Exception: idle transaction timeout
server closed the connection unexpectedly
	This probably means the server terminated abnormally
	before or while processing the request.


Смотрю, на каких запросах падает - на любых, которые в обычной ситуации отрабатывают мгновенно.

В pgbouncer.ini:
idle_transaction_timeout = 600

Нагрузка на сервер - 10-20 соединений в секунду. В системных логах, в логах Postgresql и Pgbouncer - нет ничего подозрительного. Список процессов не показывает наличие процессов postgres с висящими транзакциями. Какие системные всплески, приводящие к кратковременному исчерпанию ресурсов, которые могут приводить к такой специфической ошибке - ума не приложу.

Странно вот что. Если бы такая ошибка возникала в обслуживающих скриптах, выполняющих длинные регулярные задачи в cron, было бы понятно, где копать. А тут - простой юзер, работает себе в браузере, и вдруг после очередного клика на кнопке - ошибка 500! idle_transaction_timeout = 600 - это 10 минут. Юзер же получает 500 мгновенно. Если учесть, что он при каждом обновлении страницы создаёт в контексте Flask-приложения своё собственное соединение, как оно может вдруг упасть по причине idle transaction timeout?

Что это может быть?
  • Вопрос задан
  • 1542 просмотра
Пригласить эксперта
Ответы на вопрос 2
Melkij
@Melkij
PostgreSQL DBA
и после убивания одного из них всё восстанавливалось.

остаётся надеяться что не kill -9

Начните с проверки, что ваше приложение действительно корректно переоткрывает соединение отстреленное по инициативе базы или баунсера. А не оставляет его в пуле навечно, выдавая сохранённый текст ошибки.

Кстати говоря, вы не назвали версию pgbouncer. Возможно и баунсер вы поставили столь же старый как и базу. Припоминаю возможно релевантный фикс в 1.10
Ответ написан
dimonchik2013
@dimonchik2013
non progredi est regredi
тут все изучили?

или к вашей версии Постгре неприменимо?

очевидно, что дело в настойках базы / пула
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы