172 похожих чатов

Добрый день. Есть приложение на джаве, оно, значит, ходит

в постгрес:
Хороводит запросами hibernate, а менеджит соединения spring (включён автокоммит)
Все в шифте
На один под пул из 5 коннектов

В какой-то момент пользователь, что-то удаляет
Летит транзакция (в которой дохрена запросов), приложение видит, что запрос выполнился, а в базе запрос повисает в idle in transaction и ждет коммит
Пользователь если обновляет страничку 1 из 5 раз видит, что данные удалены
Запрос висит, пока его не грохнет балансер или база ну и транзакция откатывается

Это происходит примерно в 30 % случаев

Что попробовали:
- Отключить автокоммит, включить принудительный коммит
- ограничить на под один коннект
- ходить в обход балансера
- ходить в обход пгбаунсера через балансер
- ходить на мастер в обход балансера и пгбаунсера

Куда ещё глянуть можно? Может кто подсказать?

Конфигурация постгреса: балансировщик перенаправляет на пгбаунсер, а самим кластером управляет patroni

17 ответов

25 просмотров

> Летит транзакция (в которой дохрена запросов), приложение видит, что запрос выполнился, а в базе запрос повисает в idle in transaction и ждет коммит Вот это — 100% ошибка в приложении (т.е. её нужно исправить, хороших обходных путей просто нет). > Пользователь если обновляет страничку 1 из 5 раз видит, что данные удалены А вот это уже из области "не может быть". Т.е. PostgreSQL не будет "видеть", что данные удалены, если удалявшая их транзакция не завершилась (или откатилась). Может быть, это какое-то кеширование?

SagitoS- Автор вопроса
Yaroslav Schekin
> Летит транзакция (в которой дохрена запросов), п...

по первому пункту, я не спорю, но разработчики мне не верят, грешат на инфру по второму, не могу подтвердить или опровергнуть, это слова пользователей

Yaroslav Schekin
> Летит транзакция (в которой дохрена запросов), п...

Если приложение выберет из пула тот самый коннект который idle in transaction то может

SagitoS- Автор вопроса
Yaroslav Schekin
> Летит транзакция (в которой дохрена запросов), п...

ну и по второму пункту, локально в рамках коннекта они же обновились, но до коммита не обновились в базе, на сколько я понимаю

Я бы ожидал,что проблема в связке 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 бекендам.

Konstantin Knizhnik
Я бы ожидал,что проблема в связке pgpool - pgbounc...

+1 к тому чтобы проверить что pool_mode != "statement" на pgbouncer-е. :)

SagitoS
ну и по второму пункту, локально в рамках коннекта...

> по первому пункту, я не спорю, но разработчики мне не верят, грешат на инфру Хмм... можно логи записать и посмотреть, но для "инфры" это была бы очень грубая ошибка (это вариант, когда connection pooler отдаёт соединения кому-то ещё посреди незавершённой транзакции). > но до коммита не обновились в базе, на сколько я понимаю Да, но тогда они были бы видны либо всегда (в той же сессии), либо никогда (в других).

SagitoS- Автор вопроса

может с выводом из 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

Yaroslav Schekin
> по первому пункту, я не спорю, но разработчики м...

Большинство pool'ов возвращают connection в pool по connection.close() со стороны приложения. Сложно сказать что при этом неявно делается с транзакцией

Sergey Gr
Большинство pool'ов возвращают connection в pool п...

Ну так должны либо откатывать, либо не возвращать. Иначе это не pooler, а русская рулетка. ;)

Недавно разбирался с "брошенными" транзакциями в wildfly (у него там библиотека с пулером имеет какое-то труднопроизносимое название), весь код, который работает с БД, гарантированно выполняет commit/rollback и выставляет autocommit в true непосредственно перед закрытием. Оказалось, что там внутри смена autocommit передаётся в драйвер не сразу, а после первой значащей команды. Зачем это сделано, не знаю, мб. для поддержки каких-то ee-фишек. Из-за этого даже была бага с фоновой валидацией, её сперва пофиксили нормально (отдачей состояния автокоммита драйверу при возврате подключения в пул), но фикс откатили разработчики из-за возникновения дефекта в каком-то другом проекте. Я им писал, что текущее поведение кривое, они признали проблему и написали, что ищут решение. Воспроизводилось это довольно просто: меняем autocommit на false, затем выполняем rollback, меняем autocommit на true и возвращаем подключение в пул.

SagitoS- Автор вопроса
Radist
Недавно разбирался с "брошенными" транзакциями в w...

спасибо попробуем посмотреть в эту сторону

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

Обсуждают сегодня

а через ESC-код ?
Alexey Kulakov
29
30500 за редактор? )
Владимир
47
Чёт не понял, я ж правильной функцией воспользовался чтобы вывести отладочную информацию? но что-то она не ловится
notme
18
У меня есть функция где происходит это: write_bit(buffer, 1); write_bit(buffer, 0); write_bit(buffer, 1); write_bit(buffer, 1); write_bit(buffer, 1); w...
~
13
Недавно Google Project Zero нашёл багу в SQLite с помощью LLM, о чём достаточно было шумно в определённых интернетах, которые сопровождались рассказами, что скоро всех "ибешни...
Alex Sherbakov
5
Как передать управляющий символ в открытую через CreateProcess консоль? Собсна, есть процедура: procedure TRedirectThread.WriteData(Data: OEMString); var Written: Cardinal;...
Serjone
6
в JclConsole объявлено так: function CtrlHandler(CtrlType: DWORD): BOOL; stdcall; - где ваше объявление с stdcall? у вас на картинке нет stdcall
Karagy
8
Ребят в СИ можно реализовать ООП?
Николай
33
program test; {$mode delphi} procedure proc(v: int32); overload; begin end; procedure proc(v: int64); overload; begin end; var x: uint64; begin proc(x); end. Уж не знаю...
notme
6
https://github.com/erlang/otp/blob/OTP-27.1/lib/kernel/src/logger_h_common.erl#L174 https://github.com/erlang/otp/blob/OTP-27.1/lib/kernel/src/logger_olp.erl#L76 15 лет назад...
Maksim Lapshin
20
Карта сайта