в постгрес:
Хороводит запросами hibernate, а менеджит соединения spring (включён автокоммит)
Все в шифте
На один под пул из 5 коннектов
В какой-то момент пользователь, что-то удаляет
Летит транзакция (в которой дохрена запросов), приложение видит, что запрос выполнился, а в базе запрос повисает в idle in transaction и ждет коммит
Пользователь если обновляет страничку 1 из 5 раз видит, что данные удалены
Запрос висит, пока его не грохнет балансер или база ну и транзакция откатывается
Это происходит примерно в 30 % случаев
Что попробовали:
- Отключить автокоммит, включить принудительный коммит
- ограничить на под один коннект
- ходить в обход балансера
- ходить в обход пгбаунсера через балансер
- ходить на мастер в обход балансера и пгбаунсера
Куда ещё глянуть можно? Может кто подсказать?
Конфигурация постгреса: балансировщик перенаправляет на пгбаунсер, а самим кластером управляет patroni
> Летит транзакция (в которой дохрена запросов), приложение видит, что запрос выполнился, а в базе запрос повисает в idle in transaction и ждет коммит Вот это — 100% ошибка в приложении (т.е. её нужно исправить, хороших обходных путей просто нет). > Пользователь если обновляет страничку 1 из 5 раз видит, что данные удалены А вот это уже из области "не может быть". Т.е. PostgreSQL не будет "видеть", что данные удалены, если удалявшая их транзакция не завершилась (или откатилась). Может быть, это какое-то кеширование?
по первому пункту, я не спорю, но разработчики мне не верят, грешат на инфру по второму, не могу подтвердить или опровергнуть, это слова пользователей
Если приложение выберет из пула тот самый коннект который idle in transaction то может
ну и по второму пункту, локально в рамках коннекта они же обновились, но до коммита не обновились в базе, на сколько я понимаю
Пардон, это что за пулер такой?
Я бы ожидал,что проблема в связке pgpool - pgbouncer. Кто-то из них похоже "рвёт" транзакцию.Какой уровень стоит в pgbouncer: statement или session? Но если пробовали работать с постгресом напрямую и проблема всё равно воспроизводится,то тогда скорее всего она на стороне приложения. Возможно hybernate "помог". Попробоуйте включит на сервере логгирование всех statement-ов. Тогда увидите - что дошло,а что нет.
Поскольку упомянута java и spring-pool, то стоит наверно предположить что connection берётся из пула на каждый запрос, а не хранится в user session. В связи с этим после выполнения запроса, вызывается connection.close() который и возвращает коннект обратно в пул. Но при этом не был вызван commit. В таком случаи конечно это ошибка приложения ибо connection.close() был вызван без предварительного вызова commit-а, раз транзакция осталась idle in transaction. Если же connection берётся из пула и хранится в user session, то это вообще грубо говоря бардак ибо lifecycle user session-а может прерваться в любой момент, что приведёт к подвисшим транзакциям условно навсегда и idle бекендам.
+1 к тому чтобы проверить что pool_mode != "statement" на pgbouncer-е. :)
> по первому пункту, я не спорю, но разработчики мне не верят, грешат на инфру Хмм... можно логи записать и посмотреть, но для "инфры" это была бы очень грубая ошибка (это вариант, когда connection pooler отдаёт соединения кому-то ещё посреди незавершённой транзакции). > но до коммита не обновились в базе, на сколько я понимаю Да, но тогда они были бы видны либо всегда (в той же сессии), либо никогда (в других).
может с выводом из pg_stat_activity еще идеи появятся у кого-то SELECT pid, query_start, age(clock_timestamp(), query_start), usename, state, query FROM pg_stat_activity WHERE query NOT ILIKE '%pg_stat_activity%' and state = 'idle in transaction' ORDER BY query_start desc; 24896,2021-08-16 08:44:53.704133,0 years 0 mons 0 days 0 hours 0 mins 12.553178 secs,имя юзера,idle in transaction,COMMIT насколько я понимаю, коммит прилетает в базу, но транзакция его не исполняет получается последний запрос COMMIT
Большинство pool'ов возвращают connection в pool по connection.close() со стороны приложения. Сложно сказать что при этом неявно делается с транзакцией
Ну так должны либо откатывать, либо не возвращать. Иначе это не pooler, а русская рулетка. ;)
; у commit забыта.
Не, вру, имеет право.
Недавно разбирался с "брошенными" транзакциями в wildfly (у него там библиотека с пулером имеет какое-то труднопроизносимое название), весь код, который работает с БД, гарантированно выполняет commit/rollback и выставляет autocommit в true непосредственно перед закрытием. Оказалось, что там внутри смена autocommit передаётся в драйвер не сразу, а после первой значащей команды. Зачем это сделано, не знаю, мб. для поддержки каких-то ee-фишек. Из-за этого даже была бага с фоновой валидацией, её сперва пофиксили нормально (отдачей состояния автокоммита драйверу при возврате подключения в пул), но фикс откатили разработчики из-за возникновения дефекта в каком-то другом проекте. Я им писал, что текущее поведение кривое, они признали проблему и написали, что ищут решение. Воспроизводилось это довольно просто: меняем autocommit на false, затем выполняем rollback, меняем autocommit на true и возвращаем подключение в пул.
спасибо попробуем посмотреть в эту сторону
Обсуждают сегодня