Blog JSystems - uwalniamy wiedzę!

Szukaj

PostgreSQL

Jak awaryjnie rozłączyć sesję (pg_terminate_backend)

W skrócie

  • Jedna zawieszona albo długo trzymająca blokady sesja potrafi zablokować całą aplikację i nie oddaje połączenia sama z siebie.
  • Dzieje się tak, bo backend czeka na klienta, na blokadę albo na transakcję, która nigdy się nie kończy - a serwer nie zabija takiej sesji z automatu.
  • Znajdujemy PID w pg_stat_activity i awaryjnie kończymy sesję funkcją pg_terminate_backend(pid), która wysyła sygnał SIGTERM.

Prędzej czy później trafisz na sytuację, w której jedna sesja "wisi" i psuje pracę wszystkim pozostałym. Pokażemy Ci, jak ją namierzyć i bezpiecznie odłączyć, oraz kiedy sięgnąć po pg_terminate_backend, a kiedy wystarczy łagodniejszy sposób.

Jak to wygląda w praktyce

Aplikacja przestaje odpowiadać na operacje na jednej tabeli, choć serwer nie jest obciążony. W monitoringu widzisz połączenie, które od kilkudziesięciu minut jest w stanie idle in transaction - ktoś otworzył transakcję i jej nie zamknął. Innym razem próba DROP TABLE albo ALTER TABLE stoi w nieskończoność, bo czeka na blokadę trzymaną przez zawieszoną sesję. Bywa też, że proces po stronie klienta został ubity, a mimo to backend w PostgreSQL wciąż żyje i blokuje zasoby. W każdym z tych przypadków sesja sama się nie posprząta i trzeba ją zakończyć ręcznie.

Dlaczego tak się dzieje

PostgreSQL uruchamia dla każdego połączenia osobny proces (backend). Ten proces żyje tak długo, jak długo trwa połączenie - serwer nie ma powodu, by kończyć go tylko dlatego, że "nic nie robi". Jeśli klient otworzył transakcję i porzucił ją bez COMMIT lub ROLLBACK, transakcja trzyma blokady i blokuje odzyskiwanie martwych krotek przez VACUUM. Gdy sieć padnie, a serwer nie ma jeszcze włączonego wykrywania zerwanych połączeń (tcp_keepalives_idle), backend potrafi czekać na klienta w nieskończoność. Domyślnie nie ma też limitu czasu na porzuconą transakcję - dopóki nie ustawisz idle_in_transaction_session_timeout, takie sesje będą wisieć aż ktoś je rozłączy.

Jak to rozwiązać krok po kroku

  1. Znajdź winowajcę. Wykonaj zapytanie do widoku aktywności i wypatrz sesje, które wiszą najdłużej: SELECT pid, usename, state, now() - state_change AS od_kiedy, left(query, 60) AS zapytanie FROM pg_stat_activity WHERE state <> 'idle' ORDER BY state_change;
  2. Oceń, co to za sesja. Zwróć uwagę na kolumny state (szczególnie idle in transaction), wait_event_type, application_name i czas w state_change. Upewnij się, że to faktycznie martwa albo blokująca sesja, a nie ważny, długi raport w toku.
  3. Sprawdź, czy ta sesja kogoś blokuje. Funkcja pg_blocking_pids(pid) zwróci listę procesów, które trzymają blokadę oczekiwaną przez dany PID - to pomaga potwierdzić, kto jest źródłem, a kto ofiarą.
  4. Jeśli sesja tylko wykonuje długie zapytanie, a chcesz zachować połączenie, spróbuj najpierw łagodniej - anuluj samo zapytanie: SELECT pg_cancel_backend(<pid>);. To wysyła SIGINT i przerywa polecenie bez rozłączania klienta.
  5. Gdy anulowanie nie pomaga albo sesja jest zawieszona w transakcji, zakończ ją awaryjnie: SELECT pg_terminate_backend(<pid>);. Funkcja wysyła SIGTERM, backend robi ROLLBACK otwartej transakcji i się zamyka.
  6. Musisz mieć odpowiednie uprawnienia. Rozłączyć cudzą sesję może superużytkownik albo rola z członkostwem w pg_signal_backend - tę drugą warto nadać administratorowi aplikacyjnemu, żeby nie logować się jako superuser.
  7. Zapobiegaj nawrotom. Ustaw idle_in_transaction_session_timeout (np. '5min'), rozważ statement_timeout dla ról aplikacyjnych i włącz tcp_keepalives_idle, żeby serwer sam wykrywał zerwane połączenia.

Jak sprawdzić, że zadziałało

Funkcja zwraca true, jeśli sygnał został wysłany. Zaraz potem wykonaj ponownie zapytanie do pg_stat_activity i upewnij się, że danego pid już nie ma na liście - to znaczy, że backend faktycznie się zakończył (zwrócenie true oznacza tylko, że sygnał doszedł, nie że proces zniknął w tej samej milisekundzie). Sprawdź też, że zablokowane wcześniej polecenie ruszyło z miejsca, a pg_blocking_pids dla ofiary zwraca już pustą tablicę. Jeśli PID uparcie zostaje, poczekaj chwilę i sprawdź ponownie - sesja utknięta na nieprzerywalnej operacji systemowej może potrzebować kilku sekund.

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

Czym różni się pg_terminate_backend od pg_cancel_backend?
pg_cancel_backend wysyła sygnał SIGINT i przerywa tylko bieżące zapytanie, zostawiając sesję i połączenie przy życiu. pg_terminate_backend wysyła SIGTERM i kończy cały proces backendu, czyli rozłącza całą sesję, wycofując przy tym jej otwartą transakcję. Zasada jest prosta: najpierw próbuj anulować zapytanie, a dopiero gdy to nie pomaga albo sesja wisi w otwartej transakcji, terminuj ją.
Jakie uprawnienia są potrzebne, żeby rozłączyć cudzą sesję?
Rozłączyć cudzą sesję może superużytkownik albo rola, która ma nadane członkostwo w roli pg_signal_backend. Warto nadać administratorowi aplikacyjnemu właśnie pg_signal_backend, żeby nie musiał logować się jako superuser. Własną sesję możesz zakończyć zawsze, bez dodatkowych uprawnień.
Czy pg_terminate_backend zwracające true oznacza, że proces już zniknął?
Nie do końca. Wartość true oznacza tylko, że sygnał SIGTERM został poprawnie wysłany do procesu, a nie że backend zakończył się w tej samej chwili. Zwykle sesja znika w ułamku sekundy, ale by mieć pewność, wykonaj ponownie zapytanie do pg_stat_activity i sprawdź, że danego PID nie ma już na liście.
Jak w ogóle unikać zawieszonych sesji zamiast ciągle je ubijać?
Ustaw idle_in_transaction_session_timeout, żeby serwer sam kończył transakcje porzucone na zbyt długo, dodaj statement_timeout dla ról aplikacyjnych oraz włącz tcp_keepalives_idle, dzięki czemu PostgreSQL wykryje zerwane połączenia sieciowe. Te trzy ustawienia razem eliminują większość przypadków, w których musiałbyś ręcznie rozłączać sesje.

Komentarze (0)

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

Brak komentarzy...