Cofanie wydania: kod to najłatwiejsza część
- data
- kategoria
- Reliability Engineering
- czytanie
- 2 min / 421 słów
Rollback wydania brzmi prosto.
wróć do poprzedniej wersji
W przypadku samego kodu często naprawdę jest prosto.
Można wskazać poprzedni obraz kontenera.
Można cofnąć release w platformie.
Można przełączyć ruch z green na blue.
Można wyłączyć flagę.
Problem zaczyna się wtedy, gdy nowe wydanie zdążyło zmienić świat poza własnym procesem.
Kod jest odwracalny
Artefakt aplikacji jest zwykle niemutowalny.
api:1.41.0
api:1.42.0
Jeżeli 1.42.0 ma błąd, można uruchomić 1.41.0.
To jest czysty przypadek rollbacku.
Warunek jest jeden:
stara wersja musi nadal umieć działać w aktualnym środowisku
To środowisko obejmuje konfigurację, schemat bazy, formaty wiadomości, zewnętrzne API i dane, które użytkownicy zdążyli zapisać.
Jeżeli nowa wersja zmieniła tylko logikę liczenia rabatu, rollback kodu może wystarczyć.
Jeżeli nowa wersja zmieniła strukturę danych, rollback kodu może uruchomić stary proces na nowym świecie.
I wtedy zaczyna się prawdziwy problem.
Migracje nie cofają się same
Najniebezpieczniejsze rollbacki dotyczą migracji.
Przykład:
ALTER TABLE orders DROP COLUMN legacy_status;
Nowa wersja aplikacji już nie używa legacy_status.
Stara wersja nadal używa.
Jeżeli po takiej migracji cofniemy tylko kod, stara aplikacja może przestać działać natychmiast.
Dlatego bezpieczne zmiany schematu zwykle robi się w kilku krokach.
dodaj nowe pole
pisz do starego i nowego pola
przełącz odczyt na nowe pole
przestań pisać do starego pola
usuń stare pole dopiero po czasie
To jest nudniejsze niż jedna migracja.
Ale pozwala cofnąć kod w środku procesu.
Rollback nie jest wtedy "cofnij migrację".
Rollback brzmi:
wróć do poprzedniej wersji aplikacji, bo schemat nadal ją wspiera
Dane są jednokierunkowe częściej niż kod
Czasem migracja nie usuwa kolumny.
Czasem zmienia znaczenie danych.
Przykład:
status = paid
zamienia się na:
payment_state = captured
fulfillment_state = ready
Można napisać migrację w przód.
Nie zawsze da się napisać migrację w tył bez utraty informacji.
Jeszcze gorzej, jeśli po deployu użytkownicy zaczęli tworzyć nowe rekordy już w nowym modelu.
Stara wersja aplikacji może nie wiedzieć, co z nimi zrobić.
Dlatego pytanie przed wydaniem nie brzmi tylko:
czy mamy rollback?
Lepsze pytanie brzmi:
czy poprzednia wersja rozumie stan, który może powstać po deployu?
Skutki uboczne wychodzą poza bazę
Wydanie może też wysłać eventy, maile, webhooki albo zadania do kolejki.
Rollback kodu nie cofnie:
wysłanej wiadomości
opublikowanego eventu
wykonanej płatności
zadania, które czeka w kolejce
cache z nowym formatem
indeksu wyszukiwarki
Jeżeli nowa wersja opublikowała event w nowym formacie, stara wersja konsumenta może go nie zrozumieć.
Jeżeli nowa wersja zapisała cache w nowym kształcie, stara wersja może czytać błędne dane.
Jeżeli nowa wersja wysłała webhook do partnera, nie da się udawać, że to się nie stało.
Rollback ogranicza przyszłe szkody.
Nie kasuje automatycznie skutków, które już zaszły.
Feature flag to czasem lepszy rollback
Jeżeli problem dotyczy jednej ścieżki, najlepszym rollbackiem może być wyłączenie flagi.
nowy kod zostaje
nowe zachowanie znika
To jest szybsze niż przebudowanie obrazu albo kolejny rollout.
Ale działa tylko wtedy, gdy flaga została zaprojektowana jako prawdziwy przełącznik bezpieczeństwa.
Flaga, która ukrywa przycisk w UI, nie zatrzyma zadania cron.
Flaga, która wyłącza endpoint, nie cofnie migracji.
Flaga, która działa tylko przy odczycie, nie ochroni zapisu.
Minimalny runbook rollbacku
Dobry runbook cofania wydania powinien mieć kilka punktów.
jaki objaw uznajemy za powód rollbacku
kto podejmuje decyzję
który artefakt jest poprzednią dobrą wersją
czy migracje są kompatybilne w tył
czy trzeba wyłączyć flagi
czy trzeba zatrzymać kolejki albo joby
jak sprawdzamy, że rollback pomógł
Najważniejszy punkt jest prosty:
rollback kodu nie jest rollbackiem systemu
Jest tylko jednym z narzędzi.
Jeżeli błąd zdążył uszkodzić dane, trzeba przejść do recovery danych.