Blog JSystems - uwalniamy wiedzę!

Szukaj

PostgreSQL

Konflikt w replikacji logicznej - jak go rozwiązać

W skrócie

  • Replikacja logiczna stanęła: subskrypcja przestała nakładać zmiany, a w logu subskrybenta powtarza się ten sam błąd.
  • Zwykle to konflikt danych - na odbiorcy istnieje już wiersz o tym samym kluczu albo brakuje wiersza do zmiany, więc PostgreSQL nie może zastosować transakcji i zapętla próby.
  • Znajdź transakcję powodującą konflikt, usuń przyczynę po stronie subskrybenta lub pomiń problematyczną transakcję, a potem zabezpiecz się przed powtórką.

Replikacja logiczna przesyła zmiany na poziomie wierszy między niezależnymi bazami, więc dane po stronie odbiorcy mogą się rozjechać ze strumieniem od wydawcy. Gdy tak się dzieje, subskrypcja zatrzymuje się na błędnej transakcji i nie rusza dalej. Pokażemy, jak rozpoznać konflikt i bezpiecznie go rozwiązać.

Jak to wygląda w praktyce

Dane na subskrybencie przestają się aktualizować, a opóźnienie replikacji rośnie. W logu subskrybenta w kółko powtarza się komunikat o naruszeniu unikalności (duplicate key) albo o tym, że nie znaleziono wiersza do aktualizacji lub usunięcia. Proces worker replikacji logicznej restartuje się co kilka sekund, próbując nałożyć tę samą transakcję i za każdym razem wykłada się na tym samym błędzie. W widoku pg_stat_subscription widać, że subskrypcja tkwi w miejscu. Na wydawcy odpowiadający jej slot logiczny zaczyna narastać, bo strumień nie jest konsumowany, co dodatkowo blokuje czyszczenie martwych wierszy i puchnie WAL. Efekt: replika logiczna zamarza, dopóki ktoś ręcznie nie usunie przyczyny.

Dlaczego tak się dzieje

W replikacji logicznej subskrybent to normalna, zapisywalna baza. Jeśli coś zapisze do replikowanej tabeli lokalnie (albo dane były niespójne od początku), łatwo o kolizję ze zmianą przychodzącą od wydawcy. Klasyka to konflikt unikalności: wydawca wstawia wiersz z kluczem, który na subskrybencie już istnieje, więc INSERT łamie ograniczenie. Drugi typ to brakujący wiersz: wydawca wysyła UPDATE lub DELETE wiersza, którego na subskrybencie nie ma, bo ktoś go wcześniej lokalnie usunął. Do tego dochodzą problemy z identyfikacją wierszy - jeśli tabela nie ma klucza głównego ani ustawionej tożsamości repliki (REPLICA IDENTITY), PostgreSQL nie potrafi jednoznacznie wskazać wiersza do zmiany. Ponieważ replikacja stosuje transakcje w kolejności i nie może pominąć błędnej samoczynnie, cały strumień staje na pierwszej transakcji, która się nie udaje.

Jak to rozwiązać krok po kroku

  1. Odczytaj z logu subskrybenta dokładny błąd i identyfikator transakcji, która blokuje strumień (log podaje LSN oraz nazwę subskrypcji). To punkt wyjścia dla każdej dalszej decyzji.
  2. Ustal typ konfliktu: naruszenie unikalności (duplikat), brak wiersza do zmiany, czy problem z tożsamością repliki. Sposób naprawy zależy od typu.
  3. Jeśli to duplikat, a dane od wydawcy są prawdziwsze, usuń lub popraw kolidujący wiersz po stronie subskrybenta, żeby przychodzący INSERT albo UPDATE mógł się nałożyć.
  4. Jeśli brakuje wiersza do aktualizacji lub usunięcia, a zmiana jest bezpieczna do odrzucenia, możesz pominąć problematyczną transakcję: ustaw na subskrypcji parametr pominięcia po wskazanym LSN (ALTER SUBSCRIPTION ... SKIP). Rób to świadomie, bo tracisz tę jedną zmianę.
  5. Gdy przyczyną jest brak jednoznacznej identyfikacji wierszy, dodaj klucz główny albo ustaw REPLICA IDENTITY FULL na tabeli, żeby UPDATE i DELETE miały po czym trafiać w wiersz.
  6. Po usunięciu przyczyny wznów nakładanie: w razie potrzeby przeładuj lub włącz subskrypcję (ALTER SUBSCRIPTION ... ENABLE) i obserwuj, czy strumień rusza.
  7. Zabezpiecz się na przyszłość: nie pisz lokalnie do tabel objętych subskrypcją, dopilnuj kluczy głównych i rozważ rozdzielenie zakresów danych, żeby wydawca i subskrybent nie kolidowały.

Jak sprawdzić, że zadziałało

Po naprawie zajrzyj do pg_stat_subscription na subskrybencie - pozycja odebranego i zastosowanego LSN powinna znów rosnąć, a nie stać w miejscu. Log subskrybenta powinien przestać powtarzać ten sam błąd. Na wydawcy sprawdź w pg_replication_slots, że slot logiczny znów jest konsumowany i przestaje narastać. Na koniec test end-to-end: wstaw kontrolny wiersz do replikowanej tabeli na wydawcy i potwierdź, że w krótkim czasie pojawia się on na subskrybencie. Jeśli zmiana dociera, a opóźnienie spada, konflikt został rozwiązany.

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

Co powoduje konflikt w replikacji logicznej?
Konflikt bierze się stąd, że subskrybent to normalna, zapisywalna baza, więc dane po jego stronie mogą się rozjechać ze strumieniem od wydawcy. Najczęstsze przypadki to naruszenie unikalności (wydawca wstawia wiersz z kluczem, który już istnieje na subskrybencie) oraz brak wiersza do zmiany (wydawca wysyła UPDATE lub DELETE wiersza, którego na subskrybencie nie ma). Dochodzi też brak jednoznacznej tożsamości wierszy, gdy tabela nie ma klucza głównego ani ustawionego REPLICA IDENTITY.
Jak pominąć transakcję, która blokuje subskrypcję?
Odczytaj z logu subskrybenta LSN transakcji powodującej błąd, a następnie ustaw na subskrypcji parametr pominięcia po tym LSN poleceniem ALTER SUBSCRIPTION ze wskazaniem SKIP. Po tym subskrypcja przeskoczy problematyczną transakcję i ruszy dalej. Rób to świadomie, bo tracisz tę jedną zmianę na stałe. Pomijanie ma sens, gdy zmiana jest bezpieczna do odrzucenia, na przykład dotyczy wiersza już usuniętego lokalnie. Jeśli dane od wydawcy są ważne, lepiej usunąć przyczynę konfliktu po stronie subskrybenta.
Dlaczego UPDATE i DELETE w replikacji logicznej wymagają klucza?
Bo subskrybent musi jednoznacznie wskazać wiersz do zmiany. Replikacja logiczna identyfikuje wiersze po tożsamości repliki (REPLICA IDENTITY), która domyślnie opiera się na kluczu głównym. Jeśli tabela nie ma klucza głównego ani ustawionego REPLICA IDENTITY, operacje UPDATE i DELETE nie mają po czym trafić w wiersz i replikacja się wykłada. Rozwiązanie to dodanie klucza głównego albo ustawienie REPLICA IDENTITY FULL, które używa całego wiersza jako identyfikatora, kosztem większego narzutu.
Jak zapobiec konfliktom w replikacji logicznej na przyszłość?
Przede wszystkim nie zapisuj lokalnie do tabel objętych subskrypcją. Konflikt najłatwiej powstaje, gdy aplikacja pisze do tej samej tabeli, którą wypełnia replikacja. Dopilnuj, by wszystkie replikowane tabele miały klucze główne. Jeśli po obu stronach muszą zachodzić zapisy, rozdziel zakresy danych, na przykład przez rozłączne zakresy kluczy albo osobne tabele, żeby wydawca i subskrybent nie kolidowały. Warto też monitorować pg_stat_subscription, żeby wychwycić zatrzymanie strumienia wcześnie.

Komentarze (0)

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

Brak komentarzy...