Blog JSystems - uwalniamy wiedzę!
Blog JSystems - uwalniamy wiedzę!
PostgreSQL
Po failoverze replikacja logiczna przestaje działać - slot logiczny został na starym primary
Ten problem dotyczy układu, w którym źródłem replikacji logicznej jest klaster wysokiej dostępności: primary z repliką fizyczną, a do tego subskrybent, na przykład baza raportowa. Failover fizyczny, czyli awaryjne przejęcie roli primary przez replikę, przechodzi gładko, aplikacja pisze już do nowego primary, a baza raportowa po cichu przestaje dostawać zmiany. Odtworzyliśmy to na PostgreSQL 16. Pokażemy, dlaczego sam nowy slot nie wystarcza i jak wznowić replikację bez luki w danych, osobno dla wersji 16 i 17.
Po przełączeniu adresu subskrypcji na nowy primary (ALTER SUBSCRIPTION sub_orders CONNECTION '...') proces apply startuje co kilka sekund i od razu kończy pracę. Log subskrybenta:
LOG: logical replication apply worker for subscription "sub_orders" has started
ERROR: could not start WAL streaming: ERROR: replication slot "sub_orders" does not exist
LOG: background worker "logical replication worker" (PID 1029446) exited with exit code 1W pg_stat_subscription kolumna pid jest pusta, a SELECT slot_name, slot_type FROM pg_replication_slots; na nowym primary nie zwraca żadnego wiersza, choć na starym primary slot sub_orders typu logical był aktywny.
Subskrypcja odbiera zmiany przez slot replikacji logicznej, czyli znacznik na serwerze źródłowym, który pamięta, do którego miejsca w WAL (dzienniku zmian) subskrybent potwierdził odbiór, i pilnuje, żeby potrzebne pliki WAL nie zniknęły. Slot jest zapisany w katalogu pg_replslot primary. Nie trafia do WAL, więc replika fizyczna go nie dostaje, a pg_basebackup kopiuje pg_replslot jako pusty katalog. Po awansie replika nie ma slotu. PostgreSQL 16 pozwala już utworzyć slot logiczny na replice (lekcja, przygotowana na starszej wersji, pokazuje jeszcze błąd logical decoding cannot be used while in recovery), ale taki slot nie śledzi pozycji slotu z primary.
Zobacz też: Szkolenie Zaawansowana administracja PostgreSQL: High Availability, Disaster Recovery
Utworzenie slotu na nowym primary wygląda na naprawę, lecz nowy slot zaczyna od chwili utworzenia. W naszym teście wiersz wstawiony na nowym primary między failoverem a utworzeniem slotu (id = 1002) nigdy nie dotarł do subskrybenta, a późniejszy (id = 1003) dotarł. Replikacja działała dalej, z luką, której nic nie zgłosiło.
SELECT subname, subenabled, subslotname FROM pg_subscription; i SELECT subname, pid, received_lsn FROM pg_stat_subscription;. Pusty pid i błąd o brakującym slocie w logu to ten scenariusz.DROP SUBSCRIPTION się nie uda (could not drop replication slot "sub_orders" on publisher), więc najpierw odłącz slot: ALTER SUBSCRIPTION sub_orders DISABLE; i ALTER SUBSCRIPTION sub_orders SET (slot_name = NONE);, potem DROP SUBSCRIPTION sub_orders; i TRUNCATE orders; dla tabel z publikacji.CREATE SUBSCRIPTION sub_orders CONNECTION 'host=NOWY_PRIMARY dbname=shop user=...' PUBLICATION pub_orders;. Domyślne copy_data = true skopiuje tabele od nowa, a slot powstanie na nowym primary.wal_level = logical (kontekst postmaster, restart), a replika także hot_standby_feedback = on i fizyczny slot w primary_slot_name. Bez nich primary może usunąć wiersze katalogu systemowego potrzebne skopiowanemu slotowi: w naszym teście VACUUM katalogu na primary unieważnił slot na replice, a jej log pokazał invalidating obsolete replication slot "sub_orders". Tuż przed przełączeniem skopiuj katalog $PGDATA/pg_replslot/sub_orders z primary na replikę i zrestartuj replikę, bo sloty są wczytywane przy starcie. Na replice SELECT slot_name, wal_status, conflicting FROM pg_replication_slots; musi pokazać w kolumnach wal_status i conflicting wartości reserved i f. Potem zatrzymaj primary, awansuj replikę i zmień CONNECTION subskrypcji. U nas wiersze sprzed i po przełączeniu dotarły bez strat. Skopiowany slot od razu zatrzymuje WAL na replice, więc nie trzymaj go na zapas.WITH (failover = true). Na replice dokumentacja wymaga sync_replication_slots = on, hot_standby_feedback = on, fizycznego slotu w primary_slot_name i dbname w primary_conninfo, a na primary zdecydowanie zaleca wpisanie tego slotu do synchronized_standby_slots, żeby subskrybent nie wyprzedził repliki. Przed przełączeniem sprawdź na replice SELECT slot_name, (synced AND NOT temporary AND invalidation_reason IS NULL) AS failover_ready FROM pg_replication_slots;, w kolumnie failover_ready ma być t. Po awansie nadal zmieniasz CONNECTION subskrypcji na nowy primary. W wersji 16 tego mechanizmu nie ma.slots w konfiguracji dynamicznej z type: logical, database i plugin. Patroni kopiuje go na repliki (z ich restartem), przesuwa jego pozycję co loop_wait sekund i sam włącza hot_standby_feedback. Działa to od PostgreSQL 11. Dokumentacja Patroni uprzedza, że po failoverze część zmian może dotrzeć do subskrybenta drugi raz.Po odbudowie subskrypcji porównaj dane na subskrybencie z nowym primary i sprawdź stan synchronizacji tabel:
reporting=# SELECT count(*), max(id) FROM orders;
count | max
-------+------
1003 | 1003
(1 row)
reporting=# SELECT srrelid::regclass, srsubstate FROM pg_subscription_rel;
srrelid | srsubstate
---------+------------
orders | r
(1 row)Stan r (ready) oznacza zakończoną kopię początkową i zwykłą replikację. Na nowym primary SELECT slot_name, active, confirmed_flush_lsn FROM pg_replication_slots; pokazuje slot z active = t, a confirmed_flush_lsn rośnie po każdej nowej zmianie. Błędy o brakującym slocie przestają trafiać do logu subskrybenta.
Wróć do listy: 112 najczęstszych pytań i problemów z PostgreSQL
Komentarze (0)
Brak komentarzy...