Postgres - Как отлаживать/трассировать соединение "Idle in transaction"

Я использую Postgres для одного из моих приложений, а иногда (не очень часто) одно из соединений переходит в состояние <IDLE> in transaction, и оно сохраняет приобретенную блокировку, которая заставляет другие подключения ждать на этих блокировках, в конечном итоге заставляя мое приложение зависать.

Ниже приведен результат таблицы pg_stat_activity для этого процесса:

select * from pg_stat_activity

24081 | db     |     798 |    16384 | db     |                  | 10.112.61.218 |                 |       59034 | 2013-09-12 23:46:05.132267+00 | 2013-09-12 23:47:31.763084+00 | 2013-09-12 23:47:31.763534+00 | f       | <IDLE> in transaction

Это означает, что PID=798 находится в состоянии <IDLE> in transaction. Клиентский процесс на веб-сервере найден следующим образом, используя client_port (59034) из вышеприведенного вывода.

sudo netstat -apl | grep 59034

tcp        0      0 ip-10-112-61-218.:59034 db-server:postgresql    ESTABLISHED 23843/pgbouncer

Я знаю, что что-то не так в моем коде приложения (я убил одно из запущенного приложения cron и освободил блокировки), из-за чего соединение зависает, но я не могу его проследить.

Это не очень часто, и я не могу найти каких-либо определенных шагов воспроизведения, поскольку это происходит только на рабочем сервере.

Я хотел бы получить информацию о том, как отслеживать такое простаивающее соединение, например. получение последнего выполненного запроса или какого-либо обратного следа, чтобы определить, какая часть кода вызывает эту проблему.

Ответ 1

Если вы обновляетесь до 9.2 или выше, представление pg_stat_activity покажет вам, что последний запрос был выполнен для соединений idle in transaction.

select * from pg_stat_activity  \x\g\x

...
waiting          | f
state            | idle in transaction
query            | select count(*) from pg_class ;

Вы также можете (даже в 9.1) посмотреть pg_locks, чтобы узнать, какие блокировки хранятся в процессе idle in transaction. Если у него есть только блокировки на очень часто используемых объектах, это может не сильно сузить дело, но если это был особый замок, который мог бы точно сказать, где именно в вашем коде выглядеть.

Если вы застряли с 9.1, вы можете использовать отладчик, чтобы получить все, кроме первых 22 символов запроса (первые 22 будут перезаписаны сообщением <IDLE> in transaction\0). Например:

(gdb) printf "%s\n", ((MyBEEntry->st_activity)+22)