Blog JSystems - uwalniamy wiedzę!

Szukaj

PostgreSQL

idle in transaction blokuje bazę - jak temu zaradzić

W skrócie

  • W bazie piętrzą się sesje w stanie idle in transaction: nic nie robią, ale trzymają otwarte transakcje, blokady i blokują sprzątanie martwych wierszy.
  • Aplikacja otworzyła transakcję (BEGIN albo autocommit wyłączony), wykonała zapytanie i zapomniała zrobić COMMIT lub ROLLBACK. Transakcja wisi, trzymając zasoby.
  • Znajdź takie sesje w pg_stat_activity, rozłącz najgroźniejsze, a na stałe ustaw idle_in_transaction_session_timeout i popraw obsługę transakcji w aplikacji.

Stan idle in transaction to jeden z najbardziej podstępnych problemów PostgreSQL. Sesja nic nie robi, więc łatwo ją przeoczyć, a jednocześnie blokuje VACUUM, trzyma stary horyzont XID i może zatrzymywać inne zapytania. Pokazujemy, jak je namierzyć i jak zabezpieczyć się na przyszłość.

Jak to wygląda w praktyce

W pg_stat_activity widzisz backendy ze stanem idle in transaction, których xact_start jest sprzed wielu minut albo godzin. Równocześnie autovacuum nie nadąża, tabele puchną, a wiek najstarszego XID rośnie. Czasem inne zapytania wiszą, bo taka sesja trzyma blokadę na wierszu, którego dotknęła przed zawieszeniem. Liczba połączeń zbliża się do limitu, bo martwe transakcje nie zwalniają slotów.

Dlaczego tak się dzieje

Sesja wchodzi w idle in transaction, gdy wykona BEGIN (lub pracuje z wyłączonym autocommit), zrobi jakieś zapytanie, po czym nie wysyła ani COMMIT, ani ROLLBACK. Z punktu widzenia bazy transakcja trwa i musi być traktowana poważnie: jej blokady są aktywne, a horyzont widoczności, który ustala, blokuje VACUUM przed usunięciem martwych wierszy nowszych niż ta transakcja.

Przyczyna niemal zawsze leży po stronie aplikacji: pula połączeń oddaje połączenie z otwartą transakcją, kod obsługuje wyjątek i nie wywołuje rollbacku, albo długie operacje po stronie aplikacji (wolne API, czekanie na użytkownika) odbywają się w środku otwartej transakcji. Skutki są niewspółmierne do bezczynności sesji - to dlatego ten stan jest tak groźny.

Jak to rozwiązać krok po kroku

  1. Namierz winowajców, od najstarszych: SELECT pid, usename, application_name, state, xact_start, now() - xact_start AS czas, left(query,60) AS ostatnie FROM pg_stat_activity WHERE state = 'idle in transaction' ORDER BY xact_start;.
  2. Oceń wpływ każdej sesji: sprawdź, czy trzyma blokady, które blokują innych - SELECT pid, cardinality(pg_blocking_pids(pid)) FROM pg_stat_activity; oraz czy nie trzyma najstarszego horyzontu XID. Kolumna application_name podpowie, która aplikacja zostawia otwarte transakcje.
  3. Rozłącz sesje, które wiszą długo i szkodzą: SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle in transaction' AND now() - xact_start > interval '10 minutes';. Transakcje zostaną wycofane, blokady zwolnione.
  4. Ustaw automatyczne zamykanie takich sesji na przyszłość: w postgresql.conf ustaw idle_in_transaction_session_timeout = '5min' i wykonaj SELECT pg_reload_conf();. Baza sama zakończy transakcję, która zbyt długo wisi bezczynnie.
  5. Popraw aplikację: upewnij się, że każdy blok transakcyjny kończy się COMMIT lub ROLLBACK także w scenariuszu błędu, i że pula połączeń wykonuje rollback przy oddawaniu połączenia. Nie wykonuj powolnych operacji zewnętrznych wewnątrz otwartej transakcji.
  6. Dla puli połączeń (np. PgBouncer albo pula frameworka) włącz mechanizm, który czyści stan połączenia między użyciami, aby transakcja nie przechodziła do kolejnego zadania.

Jak sprawdzić, że zadziałało

Ponownie odpytaj pg_stat_activity WHERE state = 'idle in transaction' - lista powinna być pusta lub zawierać tylko świeże, krótkotrwałe wpisy. Po ustawieniu timeoutu przetestuj go: otwórz sesję, wykonaj BEGIN; SELECT 1; i zostaw ją - po upływie limitu połączenie powinno zostać zamknięte przez bazę z komunikatem o przekroczeniu idle_in_transaction_session_timeout. Sprawdź też, że autovacuum znów sprząta (spadający n_dead_tup) i że liczba aktywnych połączeń wróciła do normy.

Wróć do listy: 100 najczęstszych pytań i problemów z PostgreSQL

Szkolenie Administracja, replikacja i tuning baz danych PostgreSQL

Sprawdź szkolenie: Administracja, replikacja i tuning baz danych PostgreSQL

To szkolenie może być dofinansowane z KFS lub BUR.

★★★★★Średnia ocena naszych szkoleń w Google: 5/5

Szkolenie Zaawansowana administracja PostgreSQL - HA, DR, monitoring, skalowanie

Sprawdź szkolenie: Zaawansowana administracja PostgreSQL (HA, DR, monitoring, skalowanie)

To szkolenie może być dofinansowane z KFS lub BUR.

★★★★★Średnia ocena naszych szkoleń w Google: 5/5

Najczęściej zadawane pytania

Dlaczego idle in transaction jest groźniejsze niż zwykłe idle?
Sesja idle nie trzyma niczego, a idle in transaction wciąż utrzymuje otwartą transakcję: jej blokady są aktywne i blokuje VACUUM przed usunięciem martwych wierszy. Bezczynność jest więc pozorna, bo szkody realne.
Jak automatycznie zamykać wiszące transakcje w PostgreSQL?
Ustaw parametr idle_in_transaction_session_timeout, na przykład na 5 minut, i przeładuj konfigurację. Baza sama zakończy i wycofa każdą transakcję, która pozostaje bezczynna dłużej niż ten limit, zwalniając jej zasoby.
Skąd biorą się sesje idle in transaction?
Niemal zawsze z aplikacji: kod otwiera transakcję i nie robi COMMIT ani ROLLBACK, pula połączeń oddaje połączenie z otwartą transakcją, albo powolne operacje zewnętrzne dzieją się w środku otwartej transakcji.
Czy rozłączenie sesji idle in transaction jest bezpieczne?
Tak, ponieważ taka sesja nie wykonała jeszcze COMMIT, jej transakcja zostanie po prostu wycofana. Żaden zatwierdzony zapis nie zniknie. Użyj pg_terminate_backend, aby zwolnić trzymane przez nią blokady i horyzont.

Komentarze (0)

Musisz być zalogowany by móc dodać komentarz. Zaloguj się przez Google

Brak komentarzy...