PostgreSQL: Czy grozi Ci problem transaction wraparound?

20-sie-2022

Pozwól, że nie będę tłumaczył skąd bierze się problem wraparound w PostgreSQL. Faktem jest jednak, że śpi się lepiej, kiedy masz świadomość, że taki problem Ci nie zagraża. Jak więc sprawdzić, czy jeszcze daleko do zderzenia z wraparound?

Oto query, które można uruchomić na bazie, żeby sprawdzić, co się dzieje z identyfikatorami transakcji:

  SELECT 
  datname, 
  age(datfrozenxid) AS frozen_xid_age, 
  ROUND(
    100 *(
      age(datfrozenxid)/ 2146483647.0 :: float
    )
  ) consumed_txid_pct, 
  current_setting('autovacuum_freeze_max_age'):: int - age(datfrozenxid) AS remaining_aggressive_vacuum 
FROM 
  pg_database 
WHERE 
  datname NOT IN (
    'cloudsqladmin', 'template0', 'template1'
  );

Query pochodzi z https://cloud.google.com/blog/products/databases/how-to-accelerate-transaction-id-freezing-in-cloud-sql-for-postgresql

Oj ciekawe obliczenia się tu dzieją:

  • Każda baza przechowuje w swoich metadanych datfrozenxid – jest to identyfikator ostatniej transakcji, jaka została zamrożona. Jeśli ten numer jest stosunkowo blisko numeru bieżącej transakcji txid_current(), to dobrze – do przepełnienia mamy daleką drogę 🙂
  • Ponieważ maksymalna liczba transakcji to 2^31, to można łatwo policzyć procent drogi do przepełnienia licznika transakcji, co dzieje się w drugiej kolumnie tego zapytania
  • Można wreszcie wyznaczyć, ile jeszcze transakcji zostało do uruchomienia agresywnego autovacuum. Wystarczy do parametru autovacuum_freeze_max_age odjąć wiek datfrozenxid. Co istotne – odejmujemy wiek (age), a nie sam numer transakcji.

Zdarza sie, że adminom myli się wiek z numerem transakcji. Pomocne może być pamiętanie o następującej równości:

age(datfrozenxid) = txid_current() - datforzenxid

Ogólnie rzecz ujmując powinniśmy unikać operacji obliczeniowych na identyfikatorach transakcji, bo po wraparound ta matematyka może już nie działać, ale przynajmniej w początkowym etapie działania bazy (przed pierwszym przekręceniem licznika transakcji, wyniki powinny być intuicyjne. Można nawet pokusić się o sprawdzenie, czy age() zwraca rzeczywiście wynik odejmowania wspomnianych wartości:

dvdrental=# SELECT
   datname,
   txid_current(),
   datfrozenxid, age(datfrozenxid),
   txid_current() - datfrozenxid::text::bigint AS age_calc
FROM pg_database;
  datname  | txid_current | datfrozenxid | age  | age_calc
-----------+--------------+--------------+------+----------
 postgres  |         1847 |         1596 |  251 |      251
 dvdrental |         1847 |         1826 |   21 |       21
 template1 |         1847 |          726 | 1121 |     1121
 template0 |         1847 |          726 | 1121 |     1121
(4 rows)

Widać, że age() zwraca wynik odejmowania txid_current() – datfrozenxid

Zapobiegliwy admin może więc sobie śledzić wyniki pierwszego z zaprezentowanych tu zapytań i w miarę możliwości… spać spokojnie

Komentarze są wyłączone

Autor: Rafał Kraik