PostgreSQL — Transaction ID Exhaustion (Wraparound)

Tomasz Gintowt
2 min readApr 29, 2021

--

TL;TR O co chodzi i co trzeba zrobić jeśli zobaczysz

autovacuum: VACUUM public.x (to prevent wraparound)

Historia jak wiele innych, zaplanowana przez deweloperów poranna wrzuta przeciąga się w nieskończoność. Większość operacji na bazie trwa niemiłosiernie długo. Ktoś wpadł na pomysł, żeby ubić proces autovacuum i wyłączyć je całkowicie. Niestety, proces “VACUUM (to prevent wraparound)” pojawia się ponownie, co z tym zrobić ?

Kilka słów wstępu, PostgreSQL używa mechanizmu zwanego MVCC ( MultiVersion Concurrency Control ), dzięki któremu kontroluje które transakcje są widoczne dla którego użytkownika. Wyobraźmy sobie sytuacje, w której USER 1, wykonuje polecenie “BEGIN, update ….. “, jego transakcja jeszcze się nie zakończyła, a już USER2 wywołuje to samo polecenie tylko z innymi parametrami. Skad PostgreSQL wie, które dane są poprawne dla którego użytkownika ? Właśnie dzięki MVCC. O MVCC można by napisać całą książkę, dlatego jeden przykład. Na dowolnej tabeli w twojej bazie wywołaj

SELECT x,y, xmin, xmax from tabela;
id | name | xmin | xmax
----+------------------+------------+------
15 |Jakies dane | 1880828026 | 0

xmin i xmax to są właśnie wartości wykorzystywane przez MVCC. Ilość ID transakcji jest ograniczona, wynosi 32 bity. Jeśli dalej jesteś ciekaw, polecam zapoznać się z tqual.c w kodzie PostgreSQL.

Wracając do naszego problemu, co możemy zrobić ? Za to, kiedy uruchomi się autovacuum(to prevent wraparound) odpowiada zmienna autovacuum_freeze_max_age, zmiana tego parametru wymaga restartu instancji. W opisanej sytuacji, zmieniłem wartość z 200.000.000 na 300.000.000. Co pozwoliło dokończyć rozpoczętą już wrzutę. Następnego dnia, uruchomiłem vacuum i przywróciłem poprzednie wartości.

Jako rozwiązanie docelowe polecam regularne uruchamianie vacuumdb z opcją freeze.

vacuumdb --all --freeze --jobs=2 --echo --analyze

Poniżej znajdziecie kilka przydatnych zapytań, które można dodać do monitoringu i pomogą wam wcześniej reagować.

SELECT datname
, age(datfrozenxid)
, current_setting('autovacuum_freeze_max_age')
FROM pg_database
ORDER BY 2 DESC;

Tutaj trochę więcej szczegółów, wyniki są ograniczone do bazy w której obecnie jesteśmy.

SELECT c.oid::regclass
, age(c.relfrozenxid)
, pg_size_pretty(pg_total_relation_size(c.oid))
FROM pg_class c
JOIN pg_namespace n on c.relnamespace = n.oid
WHERE relkind IN ('r', 't', 'm')
AND n.nspname NOT IN ('pg_toast')
ORDER BY 2 DESC LIMIT 100;

Do swojego monitoringu dorzuciłem zapytanie, które pokazuje procentową najwyższa wartość , do tego alert przy 80% i można spać spokojnie.

SELECT round((age(datfrozenxid)::float *100.0 ) / current_setting('autovacuum_freeze_max_age')::float) FROM pg_database ORDER BY 1 DESC limit 1;

Tomasz Gintowt jest Architektem/DevOps/DBA, głównie skupiony na dostarczaniu rozwiązań składowania i przetwarzania danych. Nie są mu obce wszelkiej maści bazy danych, systemy real-time data i streamingu. Obecnie pracuje z Apache Kafka, RabbitMQ, Elastic Stack i PostgreSQL. Prywatnie fan ciasta marchewkowego.

https://www.linkedin.com/in/tomasz-gintowt/

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Tomasz Gintowt
Tomasz Gintowt

Written by Tomasz Gintowt

Architect, DevOps, SysOps, and DBA. Currently, I’m an IT Systems Engineer working with Apache Kafka, RabbitMQ, PostgreSQL, Elastic Stack in Real-Time Data Team.

No responses yet

Write a response