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