Konrad Kowalski (rootsher)Principal Platform & Reliability Architect011101000011111101100100110111011100010000110000

PITR jednego tenanta: odtworzenie jednej bazy

data
kategoria
Reliability Engineering
także w
Databases · Incident Management
czytanie
2 min / 340 słów

Najprostszy przykład PITR pojedynczej bazy to system multi-tenant, w którym każdy tenant ma własną bazę.

text
tenant_acme
tenant_globex
tenant_initech

O 10:43 błędny job zepsuł dane tylko w tenant_acme.

Pozostali tenantci działają poprawnie.

Nie ma sensu cofać całego serwera.

Chcemy cofnąć tylko jedną bazę:

text
tenant_acme -> stan z 10:42

Cel operacji

Operacja ma być mała i konkretna.

text
odtworzyć kopię serwera do 10:42
wziąć z niej bazę tenant_acme
podmienić produkcyjną bazę tego tenanta
wznowić ruch tenanta

To nie jest pełny disaster recovery.

To jest recovery jednego klienta.

1. Zamrożenie tenanta

Najpierw zatrzymujemy zapisy tylko dla tenant_acme.

text
ustaw tenant_acme w maintenance mode
zatrzymaj workery tenant_acme
zostaw pozostałych tenantów online

Chodzi o to, żeby uszkodzona baza nie przyjmowała nowych zapisów podczas recovery.

Jeżeli tego nie zrobimy, będziemy odtwarzać stan z przeszłości, a równolegle produkcja będzie dopisywać nowe dane do starej bazy.

2. Restore obok

Nie odtwarzamy od razu na produkcji.

Stawiamy instancję pomocniczą z backupu i WAL do wybranego czasu.

text
target time: 10:42

Po restore mamy pomocniczy serwer ze stanem sprzed błędu.

Na nim sprawdzamy, czy baza tenant_acme wygląda dobrze.

text
czy błędne dane zniknęły
czy aplikacja umie czytać bazę
czy podstawowe zapytania działają

3. Dump jednej bazy

Z pomocniczego serwera bierzemy tylko bazę uszkodzonego tenanta.

bash
pg_dump -Fc tenant_acme > tenant_acme_1042.dump

Nie interesują nas bazy innych tenantów.

Nie przenosimy całego serwera.

Wyciągamy tylko ten fragment, który chcemy przywrócić.

4. Import jako nowa baza

W produkcji tworzymy nową bazę, na przykład:

text
tenant_acme_restored

I importujemy dump:

bash
createdb tenant_acme_restored
pg_restore -d tenant_acme_restored tenant_acme_1042.dump

Stara baza tenant_acme nadal istnieje.

To ważne, bo można jeszcze porównać dane albo wrócić do niej, jeśli restore okaże się zły.

5. Przełączenie tenanta

Aplikacja musi wiedzieć, że tenant_acme ma teraz używać nowej bazy.

W najprostszym modelu mamy tabelę albo konfigurację:

text
tenant: acme
database: tenant_acme

Zmieniamy ją na:

text
tenant: acme
database: tenant_acme_restored

Potem restartujemy albo odświeżamy procesy, które trzymają stare połączenia.

text
API
workery
joby cykliczne

6. Sprawdzenie

Po przełączeniu robimy krótki sanity check.

text
tenant_acme loguje się poprawnie
podstawowy odczyt działa
podstawowy zapis działa
workery piszą do nowej bazy
metryki nie pokazują błędów

Jeżeli wszystko wygląda dobrze, wyłączamy maintenance mode dla tenant_acme.

Starej bazy nie kasujemy od razu.

Zostaje jako materiał do porównania i postmortem.

Co z danymi po 10:42

Jeżeli tenant był zamrożony dopiero o 11:15, wszystko zapisane między 10:42 a 11:15 nie pojawi się w odtworzonej bazie.

To jest koszt tej operacji.

Trzeba go nazwać wprost:

text
co tracimy
co da się przepisać ręcznie
co da się odtworzyć z logów
co trzeba zakomunikować klientowi

PITR nie jest cofaniem czasu bez konsekwencji.

Jest wyborem punktu, do którego wracamy.

Minimalna procedura

Całość można sprowadzić do krótkiej checklisty.

text
potwierdź, że problem dotyczy tylko tenant_acme
zamroź tenant_acme
odtwórz serwer pomocniczy do 10:42
zrób dump bazy tenant_acme
zaimportuj dump jako tenant_acme_restored
przełącz tenant_acme na nową bazę
sprawdź odczyt, zapis i workery
odmroź tenant_acme
zostaw starą bazę do analizy

To jest sens PITR pojedynczej bazy w tym przykładzie.

Nie ratujemy całej platformy.

Nie dotykamy innych tenantów.

Odtwarzamy jedną bazę, bo granica problemu jest jasna.