Refaktoryzacja bez przestojów

Refaktoryzacja bez przestojów: Jak refaktoryzować systemy bez wyłączania ich z sieci

Systemy produkcyjne nie mogą się zatrzymywać. Platforma finansowa przetwarzająca transakcje o 2 w nocy, system dokumentacji medycznej obsługujący lekarzy w różnych strefach czasowych, aplikacja logistyczna śledząca przesyłki na różnych kontynentach: żaden z nich nie ma okna konserwacyjnego, które pozwoliłoby na refaktoryzację. Jednak wszystkie one gromadzą dług techniczny, opierają się na decyzjach architektonicznych podjętych w ramach wcześniejszych ograniczeń i ostatecznie wymagają zmian strukturalnych, aby zachować łatwość utrzymania, skalowalność i bezpieczeństwo. Refaktoryzacja bez przestojów to dyscyplina, która rozwiązuje to napięcie: rozwija działający system bez przerywania świadczonych przez niego usług.

Modernizuj bez przestojów

Przebudowuj swoje aplikacje na żywo w środowisku produkcyjnym, korzystając z kontroli i precyzji na poziomie korporacyjnym

Eksploruj SMART TS XL

Wyzwanie nie jest czysto techniczne. Jest to wyzwanie organizacyjne i architektoniczne. Refaktoryzacja systemu, który nie może przejść w tryb offline, wymaga innego modelu mentalnego niż refaktoryzacja systemu w fazie rozwoju: każda zmiana musi być wstecznie kompatybilna, dopóki nie przestanie być, każda zmiana strukturalna musi być odwracalna, każda walidacja musi być przeprowadzana na rzeczywistym ruchu, a nie na testach syntetycznych. Techniki, które to umożliwiają, w tym wdrożenia blue-green, przełączanie funkcji, wzorzec „strangler fig”, migracje baz danych typu „rozszerz i skonsoliduj” oraz idempotentne architektury sterowane zdarzeniami, są dobrze udokumentowane. Rzadziej poruszany jest temat ich współdziałania jako spójna strategia trwałej, bezpiecznej zmiany strukturalnej w systemach, które muszą służyć użytkownikom przez cały proces.

Jak powinna wyglądać Twoja architektura, aby zmiany nie powodowały przestojów

Najczęstszym pytaniem, jakie zadają zespoły, decydując się na refaktoryzację bez przestojów, jest kwestia architektury: co należy zmienić w sposobie budowy systemu, zanim będzie można rozpocząć refaktoryzację? Odpowiedzią nie jest pojedynczy wzorzec, ale zestaw właściwości strukturalnych, które system musi posiadać, aby refaktoryzacja na żywo była bezpieczna. Zrozumienie tych właściwości jest warunkiem wstępnym do wszystkich pozostałych elementów tego przewodnika.

Pierwszą cechą jest niezależna wdrażalność. Każdy komponent, który zostanie poddany refaktoryzacji, musi być wdrażalny bez konieczności jednoczesnego wdrażania jego zależności. Jeśli zmiana usługi A wymaga jednoczesnej zmiany usługi B i usługi C, aby zapobiec awarii, wdrożenie usługi A bez przestojów jest strukturalnie niemożliwe: te trzy usługi stanowią w zasadzie pojedynczą jednostkę wdrożeniową, niezależnie od liczby repozytoriów, w których się znajdują. Niezależna wdrażalność wymaga wstecznie kompatybilnych interfejsów, wersjonowanych kontraktów oraz wyeliminowania skoordynowanych wymagań wdrożeniowych między usługami.

Drugą cechą jest odwracalność. Każde wdrożenie, które zmienia zachowanie systemu na żywo, musi być odwracalne w ciągu kilku minut, a nie godzin. Odwracalność to nie tylko kwestia utrzymania dostępności starego pliku binarnego. Wymaga ona, aby stan bazy danych, stan pamięci podręcznej, stan sesji i każdy stan systemu zewnętrznego zmodyfikowany przez nową wersję były zgodne ze starą wersją. Jeśli nowa wersja zapisuje dane w formacie, którego stara wersja nie może odczytać, wdrożenie jest z definicji nieodwracalne, a brak przestoju jest niemożliwy, ponieważ każde wycofanie zmian spowoduje błędy.

Trzecią właściwością są obserwowalne przejścia stanu. Refaktoryzacja, która przenosi zachowanie z jednej ścieżki kodu do drugiej bez obserwowalnych metryk na obu ścieżkach, jest działaniem w ciemno. Zespół nie wie, czy przejście kończy się sukcesem, czy niepowodzeniem, nie może wcześnie wykryć regresji i nie może podejmować decyzji opartych na danych o tym, kiedy przyspieszyć lub zatrzymać migrację. Obserwowalność musi zostać zaimplementowana przed rozpoczęciem refaktoryzacji, a nie dodana po pojawieniu się problemu. Jak zbadano w kontekście refaktoryzacja przyrostowa i dług techniczny, strukturalna widoczność tego, co kod robi i co od niego zależy, stanowi podstawę planowania wszelkich zmian, które nie mogą sobie pozwolić na niepowodzenie w środowisku produkcyjnym.

Wdrożenie niebiesko-zielone: ​​wzorzec bazowy

Wdrożenie niebiesko-zielone to podstawowy wzorzec dla wydań bez przestojów. Istnieją dwa identyczne środowiska produkcyjne: niebieskie obsługujące ruch na żywo oraz zielone, otrzymujące nową wersję. Nowa wersja jest wdrażana, testowana i walidowana w zielonym środowisku, podczas gdy niebieskie środowisko kontynuuje obsługę użytkowników bez zakłóceń. Po walidacji zielonego środowiska ruch jest przełączany atomowo. Wycofywanie jest odwrotne: ruch jest przełączany z powrotem do niebieskiego, który pozostaje dostępny przez cały czas.

Wzorzec wydaje się prosty. Jego trudność leży w warstwie bazy danych. Gdy oba środowiska muszą odczytywać i zapisywać dane w tej samej bazie danych, schemat bazy danych musi być jednocześnie kompatybilny z obiema wersjami. Migracja, która usuwa kolumnę, zmienia nazwę pola lub typ danych, powoduje awarię starego środowiska w momencie jej wykonania. Właśnie dlatego wdrożenie blue-green jest nierozerwalnie związane ze wzorcem migracji schematu „rozszerz i skonsoliduj” opisanym w sekcji dotyczącej bazy danych tego przewodnika.

Wersje kanaryjskie i techniki wdrażania etapowego

Wersje Canary rozszerzają model niebiesko-zielony, kierując określony odsetek ruchu do nowej wersji, zamiast przełączać cały ruch naraz. Wdrożenie Canary może rozpocząć się od jednego procenta użytkowników, obserwować wskaźniki błędów, opóźnienia i wskaźniki biznesowe dla tej grupy, a następnie stopniowo zwiększać ten odsetek: pięć, dwadzieścia, pięćdziesiąt, sto. Na każdym etapie automatyczne bramki sprawdzają, czy kluczowe wskaźniki nie uległy pogorszeniu poza zdefiniowane progi. W przypadku awarii bramki, wdrażanie zostaje wstrzymane, a odsetek Canary zostaje zredukowany do zera.

Techniki wdrażania etapowego dodają logikę kierowania do tego procesu. Zamiast routingu wyłącznie procentowego, ruch można segmentować według kohorty użytkowników, regionu geograficznego, poziomu subskrypcji lub charakterystyki sesji. Pozwala to na walidację nowej wersji pod kątem konkretnej populacji użytkowników, która jest dla niej najbardziej obciążająca, zanim ta populacja zostanie w pełni zmigrowana. Kluczowym wymogiem jest, aby infrastruktura routingu, niezależnie od tego, czy jest to moduł równoważenia obciążenia, brama API, czy sieć usług, obsługiwała granularność kierowania wymaganą podczas wdrażania.

Metryki regulujące bramki typu „canary” muszą zostać zdefiniowane przed rozpoczęciem wdrażania. Współczynnik błędów, opóźnienie p99, czas zapytania do bazy danych oraz metryki specyficzne dla firmy, takie jak współczynnik konwersji lub wskaźnik powodzenia płatności, są ważnymi kryteriami bramek. Progi bramek powinny być kalibrowane względem wartości bazowej zmierzonej dla istniejącej wersji przy porównywalnym obciążeniu, a nie względem teoretycznych założeń. Wdrożenie, które przechodzi przez bramkę przy dwóch procentach ruchu, ale kończy się niepowodzeniem przy dwudziestu procentach, nie zostało zweryfikowane: bramka typu „canary” była zbyt mała, aby być reprezentatywna. Prawidłowe wdrożenie etapowe wymaga wystarczającej ekspozycji ruchu na każdym etapie, aby uzyskać statystycznie istotne porównanie.

Przełączniki funkcji i wyłączniki awaryjne

Przełączniki funkcji oddzielają wdrażanie kodu od aktywacji zachowania. Zrefaktoryzowana ścieżka kodu jest wdrażana w stanie nieaktywnym, kontrolowanym przez przełącznik, który określa, którzy użytkownicy lub żądania wykonują nową logikę. Przełącznik można włączać stopniowo, ukierunkować na określone grupy lub natychmiast cofnąć bez ponownego wdrażania. Dzięki temu przełączniki funkcji stanowią główny mechanizm migracji logiki biznesowej bez przestojów, w przeciwieństwie do zmian w infrastrukturze, gdzie bardziej odpowiednie są wzorce niebiesko-zielone lub kanarkowe.

Wyłącznik awaryjny (kill switch) to defensywny odpowiednik przełączników funkcji: przełączników, których celem nie jest włączenie nowego zachowania, lecz jego natychmiastowe wyłączenie w przypadku nieprawidłowego działania. Wyłącznik awaryjny (kill switch) w przypadku przebudowanego systemu rozliczeniowego, nowego przepływu uwierzytelniania lub nowej warstwy dostępu do danych zapewnia dyżurnemu inżynierowi ścieżkę odzyskiwania danych w jednym działaniu, która nie wymaga wdrożenia, wycofania bazy danych ani koordynacji międzyzespołowej. Wyłącznik awaryjny powinien być skonfigurowany w systemie, który umożliwia jego aktywację poprzez wywołanie API, konsolę zarządzania flagami funkcji lub integrację z automatycznymi alertami, tak aby opóźnienie aktywacji wynosiło sekundy, a nie minuty.

Higiena przełączników to poważny problem operacyjny. Przełączniki, które nigdy nie są czyszczone, gromadzą się w bazie kodu, co utrudnia wnioskowanie o przepływie sterowania i tworzy niejawne zależności między stanem przełącznika a stanem danych. Każdy przełącznik powinien mieć udokumentowanego właściciela, planowaną datę wygaśnięcia i zgłoszenie do czyszczenia. Dług przełączników jest tak samo realny, jak każda inna forma długu technicznego i narasta szybciej, ponieważ przełączniki zazwyczaj chronią najbardziej aktywnie zmieniające się części systemu.

Refaktoryzacja bazy danych bez przestoju

Zmiany w bazie danych stanowią najtrudniejszy element refaktoryzacji bez przestojów, ponieważ bazy danych są stanowe, współdzielone i wolno się je modyfikuje na dużą skalę. Aplikację można wdrożyć i wycofać w ciągu kilku minut. Migracja bazy danych, która modyfikuje tabelę zawierającą setki milionów wierszy, może zająć godziny, nie można jej łatwo cofnąć po zatwierdzeniu i utrzymuje blokady blokujące odczyty i zapisy na czas jej trwania. Prawidłowa refaktoryzacja bazy danych wymaga innego podejścia niż refaktoryzacja kodu aplikacji, a większość zespołów odkrywa to już podczas pierwszej próby zmiany schematu w tabeli o dużym natężeniu ruchu.

Główną zasadą jest, że każda zmiana w bazie danych musi być wstecznie kompatybilna z poprzednią wersją aplikacji, dopóki poprzednia wersja nie zostanie już wdrożona. Brzmi to oczywisto, ale ma nieoczywiste implikacje. Zmiana nazwy kolumny wymaga dodania nowej nazwy jako aliasu lub duplikatu, zanim stara nazwa zostanie usunięta. Zmiana typu kolumny wymaga równoległego wypełnienia kolumny-cienia nowego typu, zanim stara kolumna zostanie wycofana. Usunięcie tabeli wymaga potwierdzenia, że ​​żadna wdrożona wersja aplikacji z niej nie odczytuje. Każda z tych operacji to proces wieloetapowy rozłożony na wiele wdrożeń, a nie pojedyncza migracja, która jest uruchamiana jednorazowo. Jak omówiono w szerszym kontekście Refaktoryzacja COBOL-a w obrębie starszych struktur danychwyzwaniem jest ewolucja struktur danych współdzielonych przez wiele programów i systemów bez skoordynowanego przejścia na nowe rozwiązanie, co stanowi jedną z głównych trudności refaktoryzacji na skalę przedsiębiorstwa.

Wzorzec rozszerzania-kurczenia

Wzorzec „rozszerz-skontraktuj” formalizuje wieloetapowe podejście do zmian schematu. W fazie rozszerzania nowe elementy schematu są dodawane addytywnie: nowa kolumna obok starej, nowa tabela obok starej, nowy indeks obok starego. Aplikacja jest aktualizowana, aby zapisywać zarówno do starej, jak i nowej struktury, ale nadal odczytuje dane ze starej struktury. Żadne dane nie są tracone, żadne istniejące zapytanie nie ulega awarii, a stara wersja aplikacji nadal działa, ponieważ stare elementy schematu są nadal obecne.

W fazie kontrakcji, która ma miejsce w oddzielnym wdrożeniu po pełnym wdrożeniu i walidacji nowej wersji, stare elementy schematu są usuwane. Na tym etapie żadna działająca wersja aplikacji nie jest od nich zależna. Usunięcie jest bezpieczne, ponieważ zostało zweryfikowane poprzez obserwację, a nie przyjęte w procesie planowania.

Wzorzec „rozszerz-skróć” wymaga dyscypliny w zakresie kolejności wdrażania. Migracja bazy danych, która dodaje nową kolumnę, musi zostać wdrożona przed wersją aplikacji, która do niej zapisuje. Migracja bazy danych, która usuwa starą kolumnę, musi zostać wdrożona po wycofaniu wszystkich wersji aplikacji, które ją odczytują. Te wymagania dotyczące kolejności powinny być zakodowane w potoku wdrażania, aby migracje nie mogły być przeprowadzane w niewłaściwej kolejności.

Narzędzia do refaktoryzacji starszych kanałów danych bez konieczności przepisywania kodu

Tradycyjne potoki danych, zwłaszcza te oparte na platformach przetwarzania wsadowego, narzędziach ETL lub przenoszeniu danych na komputerach mainframe, stanowią szczególne wyzwanie: transformują i przenoszą dane w sposób ciągły, nie można ich zatrzymać na czas migracji i często są słabo udokumentowane do tego stopnia, że ​​pełny zakres ich działania pozostaje nieznany, dopóki coś nie ulegnie awarii. Refaktoryzacja tych potoków bez całkowitego przepisania wymaga narzędzi, które potrafią obserwować, co potok aktualnie robi, weryfikować, czy zrefaktoryzowana wersja generuje równoważne dane wyjściowe, oraz umożliwiać stopniowe, a nie nagłe przejście.

Przechwytywanie danych zmian to najszersze narzędzie do refaktoryzacji potoku na żywo. CDC przechwytuje każdą operację zapisu w tabeli źródłowej jako strumień zdarzeń, umożliwiając zasilanie zarówno starego potoku, jak i nowego potoku zastępczego z tego samego źródła bez konieczności modyfikacji żadnego z nich. Stary potok kontynuuje działanie, nowy potok jest uruchamiany równolegle z tym samym strumieniem zdarzeń, a wyniki są porównywane. Rozbieżności identyfikują logikę transformacji, która nie została poprawnie zaimplementowana. Po potwierdzeniu parzystości stary potok jest wyłączany z eksploatacji.

Narzędzia do migracji schematów, takie jak Liquibase i Flyway, zapewniają wersjonowane, sekwencyjne migracje, które można stosować przyrostowo i wycofywać w połączeniu z dyscypliną rozszerzania i ograniczania. Śledzą one, które migracje zostały zastosowane w każdym środowisku i zapobiegają aplikacjom w nieuporządkowanej kolejności. W przypadku starszych potoków działających na komputerach mainframe lub bazach danych VSAM, odpowiednik jest zarządzany za pośrednictwem Rozszerzenie JCL i zarządzanie zbiorami danych kontroluje sposób, w jaki programy uzyskują dostęp do danych podczas przejścia, zapewniając, że ani stary, ani nowy program nie zostanie uruchomiony w oparciu o niekompatybilny układ zbioru danych.

Jak zmodernizować starsze bazy danych bez przestojów

Konkretne wyzwanie modernizacji starszej bazy danych, przejścia ze schematu DB2 na komputerze mainframe do relacyjnej bazy danych w środowisku hostowanym w chmurze, migracji ze struktury VSAM opartej na plikach do schematu relacyjnego lub konsolidacji wielu starszych baz danych w nowym, ujednoliconym magazynie, wymaga stosowania wszystkich powyższych technik po kolei przez dłuższy okres czasu.

Podejście, które zawsze działa, to: zacznij od parzystości odczytu, następnie osiągnij parzystość zapisu, następnie migruj odczyty, potem migruj zapisy, a na końcu wyłącz dotychczasowy magazyn. Parzystość odczytu oznacza, że ​​nowy magazyn zawiera wszystkie dane zawarte w starym magazynie i może obsługiwać wszystkie zapytania aplikacji. Parzystość zapisu oznacza, że ​​każdy zapis dokonywany przez aplikację do starego magazynu jest również stosowany do nowego magazynu, albo poprzez podwójne zapisy w aplikacji, albo poprzez replikację CDC. Po potwierdzeniu obu warunków parzystości w warunkach obciążenia produkcyjnego, odczyty można migrować do nowego magazynu (weryfikacja danych wyjściowych), następnie zapisy można migrować, a następnie dotychczasowy magazyn można wyłączyć z eksploatacji.

Na żadnym etapie tej sekwencji usługa nie zostaje przerwana. Na każdym etapie poprzedni stan można przywrócić, przenosząc odczyty lub zapisy z powrotem do poprzedniej pamięci. Czas trwania każdego etapu jest określany na podstawie pewności wygenerowanej przez walidację, a nie na podstawie ustalonej daty kalendarzowej.

Narzędzia do refaktoryzacji starszych systemów bez przepisywania kodu

Przepisanie starego systemu od podstaw jest prawie zawsze bardziej kosztowne i ryzykowne niż stopniowa refaktoryzacja. Pełne przepisanie wymaga jednoczesnego utrzymania starego systemu w środowisku produkcyjnym i tworzenia zamiennika o porównywalnej funkcjonalności, zarządzania luką w parzystości funkcji między nimi oraz przeprowadzenia przejścia na nowy system, który w istocie jest wdrożeniem zupełnie innego systemu bez przestojów. Większość organizacji podejmujących się pełnego przepisywania odkrywa w trakcie procesu, że stary system zawierał zachowania, których nie udokumentowali, których zamiennik jeszcze nie replikuje, a od których użytkownicy są uzależnieni.

Przyrostowa refaktoryzacja z odpowiednimi narzędziami pozwala uniknąć tej pułapki, czyniąc stary system czytelnym przed wprowadzeniem zmian. Punktem wyjścia jest analiza strukturalna: zrozumienie, co robi każdy komponent istniejącego systemu, co od niego zależy i od czego on zależy. Tej analizy nie da się przeprowadzić poprzez czytanie dokumentacji (która zazwyczaj jest nieobecna lub niedokładna w przypadku starszych systemów) ani poprzez ręczne czytanie kodu na dużą skalę. Wymaga ona zautomatyzowanych narzędzi, które analizują istniejący kod, konstruują graf zależności i umożliwiają tworzenie zapytań do tego grafu. Jak opisano w kontekście zarządzanie wyzwaniami związanymi z integracją starszych systemówPierwszym krokiem w każdym programie refaktoryzacji starszych rozwiązań jest zapewnienie widoczności strukturalnej, która nie występuje w żadnym artefakcie utrzymywanym przez człowieka.

Wzór Dusiciela dla Monolitów

Wzorzec „dusiciel fig” to dominująca strategia architektoniczna polegająca na stopniowej wymianie monolitu bez konieczności całkowitego przepisania lub przełączenia. Nowa funkcjonalność jest budowana jako niezależne usługi równolegle z monolitem. Warstwa routingu, zazwyczaj brama API lub odwrotny serwer proxy, przechwytuje żądania przychodzące i kieruje je do monolitu lub do nowej usługi w oparciu o reguły routingu. Monolit nadal obsługuje cały ruch, który nie został jeszcze zmigrowany. Nowa usługa obsługuje tylko ruch jawnie do niej kierowany.

Z czasem dodawane są kolejne reguły routingu. Więcej ścieżek jest kierowanych do nowych usług. Monolit obsługuje coraz mniejszą część całkowitego ruchu. Ostatecznie monolit nie obsługuje niczego i może zostać wycofany z eksploatacji. Żadne pojedyncze wdrożenie w tym procesie nie jest na tyle duże, aby stanowiło znaczące ryzyko. Każda zmiana reguły routingu jest indywidualnie testowalna i indywidualnie odwracalna. Figura dusiciela nie jest techniką szybkiej transformacji: jest techniką bezpiecznej transformacji trwającej tygodnie, miesiące lub lata, w zależności od złożoności systemu, który ma zostać uduszony.

Krytycznym wymogiem implementacji wzorca „strangler fig” jest oddzielenie warstwy routingu zarówno od monolitu, jak i od nowych usług. Warstwa routingu osadzona w monolicie nie może kierować ruchu poza monolit. Serwer proxy musi znajdować się przed obydwoma, umożliwiając kierowanie ruchu do obu usług na podstawie konfiguracji, którą można zmienić bez modyfikowania monolitu ani nowej usługi.

Refaktoryzacja starszych interfejsów API do usług natywnych w chmurze bez przestojów

Migracja starszego interfejsu API do natywnego dla chmury zamiennika to konkretny przykład zastosowania wzorca „strangler fig” z dodatkowymi ograniczeniami: starszy interfejs API może mieć konsumentów, których nie można aktualizować jednocześnie, umowa interfejsu API musi zostać utrzymana w trakcie przejścia na chmurę zamiennik może mieć inne cechy wydajności, które w nieoczekiwany sposób wpływają na konsumentów.

Standardowe podejście polega na wdrożeniu natywnego dla chmury zamiennika opartego na tym samym kontrakcie API, co starsze API, skierowaniu określonego procentu ruchu do zamiennika za pomocą technik kanarkowych, sprawdzeniu parzystości wyjściowej dla tego procentu ruchu i stopniowym zwiększaniu procentu trasowania. Użytkownicy nie muszą niczego zmieniać podczas tego przejścia, ponieważ kontrakt API zostaje zachowany. Warstwa routingu obsługuje przejście transparentnie.

Przejście z integracji podstawowych na interfejsy API oprogramowania pośredniczącego bez przestojów, które w danych Search Console w tym artykule pojawia się jako zapytanie o wysokiej intencji, to dokładnie ten scenariusz: moment, w którym warstwa routingu zostaje zaktualizowana, aby kierować sto procent ruchu do nowego systemu, a starsze API zostaje wycofane z eksploatacji. To przejście nigdy nie powinno być pojedynczym zdarzeniem atomowym. Powinno być ostatnim etapem stopniowego wdrażania, które już zweryfikowało nowy system przy coraz wyższym procencie ruchu. Do momentu ostatecznego przejścia nowy system obsłużył już cały wolumen ruchu; przejście jedynie usuwa ścieżkę zapasową, która nie jest już potrzebna.

Idempotencja, ponowne próby i przełączanie awaryjne w systemach po refaktoryzacji

Refaktoryzacja systemu wykorzystującego architekturę sterowaną zdarzeniami, kolejki komunikatów lub rozproszone wywołania usług wprowadza klasę problemów, których nie rozwiązują wzorce skoncentrowane wyłącznie na wdrażaniu: co dzieje się z operacjami w trakcie realizacji, gdy usługa przechodzi ze starej wersji do nowej? Zdarzenia opublikowane w starej wersji mogą docierać do modułu obsługi zdarzeń obsługującego nową wersję. Żądania zainicjowane w starym API mogą docierać do modułu obsługi zdarzeń, który został już zrefaktoryzowany do nowej struktury wewnętrznej. Transakcje, które zostały częściowo zakończone w ramach starej logiki, mogą wymagać dokończenia lub skompensowania w ramach nowej logiki.

Odpowiedzią na wszystkie te problemy jest idempotentność: projektowanie każdej operacji tak, aby dawała taki sam wynik, niezależnie od tego, czy jest wykonywana jednokrotnie, czy wielokrotnie. Idempotentny moduł obsługi zdarzeń, który odbiera zduplikowane zdarzenie podczas przejścia między wdrożeniami, generuje takie same dane wyjściowe, jak ten, który odbiera zdarzenie dokładnie raz. Idempotentna operacja zapisu, która jest odtwarzana w ramach wycofywania zmian, generuje ten sam stan bazy danych, co pierwotny zapis. Idempotentność nie jest jedynie problemem refaktoryzacji: jest ogólną właściwością odpornych systemów rozproszonych. Jednak to właśnie podczas przejść między refaktoryzacjami jej brak powoduje najbardziej widoczne awarie.

Dodawanie ponownych prób i przełączania awaryjnego dostawcy bez dużej refaktoryzacji

Jednym z najczęściej zadawanych pytań w danych Search Console na potrzeby tego artykułu jest to, jak dodać funkcje ponawiania prób i przełączania awaryjnego do istniejącej aplikacji, w szczególności aplikacji opartej na platformie Rails lub podobnym frameworku, bez konieczności przeprowadzania kompleksowej refaktoryzacji. Odpowiedź brzmi: ponawianie prób i przełączanie awaryjne można dodać jako problem przekrojowy na poziomie infrastruktury, bez konieczności modyfikowania implementacji poszczególnych usług.

Na poziomie infrastruktury sieć usług, taka jak Istio lub Linkerd, może zostać skonfigurowana tak, aby automatycznie ponawiała nieudane żądania, do określonej liczby ponownych prób, z wykładniczym odliczaniem i wahaniami czasu reakcji (jitter), aby uniknąć efektu „thundering herd behavior”. Nie wymaga to żadnych zmian w kodzie aplikacji, ponieważ mechanizm ponawiania jest zaimplementowany w serwerze proxy sidecar, który przechwytuje wszystkie żądania przychodzące i wychodzące. Przełączanie awaryjne dostawcy można zaimplementować w podobny sposób: jeśli dostawca główny zwróci błąd przekraczający próg, sieć kieruje kolejne żądania do dostawcy pomocniczego, dopóki dostawca główny nie odzyska sprawności.

W warstwie aplikacji, gdy liczba ponownych prób na poziomie infrastruktury jest niewystarczająca, ponieważ logika ponownych prób musi uwzględniać stan biznesowy, można wprowadzić lekką bibliotekę ponownych prób lub kolejkę zadań na granicy między aplikacją a zależnościami zewnętrznymi, bez konieczności wewnętrznej restrukturyzacji aplikacji. Kluczem jest odizolowanie logiki ponownych prób i przełączania awaryjnego na granicy integracji, a nie rozproszenie jej w całej warstwie logiki biznesowej. Dzięki temu zachowanie ponownych prób jest widoczne, testowalne i konfigurowalne bez ingerencji w podstawową strukturę aplikacji. Jak omówiono w kontekście zwinne praktyki refaktoryzacjiWprowadzenie wzorców niezawodności na poziomie infrastruktury przed refaktoryzacją logiki biznesowej zmniejsza obszar tego, co należy zweryfikować po każdej zmianie.

Idempotentność w architekturach sterowanych zdarzeniami z wykorzystaniem strumieni Redis

Architektury o niskim opóźnieniu i sterowane zdarzeniami, wykorzystujące Redis Streams lub podobne technologie, stają przed szczególnym problemem idempotentności podczas refaktoryzacji: grupy konsumentów mogą przetwarzać zdarzenia w różnym tempie, konsument odczytujący zdarzenia w nowej wersji mógł już przetworzyć zdarzenia, których nie przetworzyła stara wersja, a operacje odtwarzania lub odzyskiwania mogą wielokrotnie dostarczać to samo zdarzenie do modułów obsługi, które nie zostały zaprojektowane do obsługi duplikatów.

Standardowym podejściem jest przypisanie unikalnego identyfikatora każdemu zdarzeniu w momencie publikacji i śledzenie przetworzonych identyfikatorów zdarzeń w trwałej bazie danych. Przed przetworzeniem zdarzenia, moduł obsługi sprawdza, czy identyfikator został już przetworzony. Jeśli tak, zdarzenie jest potwierdzane i odrzucane bez ponownego przetwarzania. Jeśli nie, zdarzenie jest przetwarzane, a identyfikator rejestrowany. Ta logika deduplikacji musi być atomowa: jeśli moduł obsługi przetworzy zdarzenie, ale wystąpi błąd przed zarejestrowaniem identyfikatora, zdarzenie zostanie ponownie przetworzone przy następnym dostarczeniu. Użycie atomowych operacji Redis lub transakcyjnych zapisów do zarejestrowania identyfikatora w ramach operacji przetwarzania zapobiega temu wyścigowi.

Podczas przejścia refaktoryzacji, w którym zmienia się logika konsumenta, identyfikatory idempotentności zapewniają dodatkową korzyść: umożliwiają odtworzenie strumienia zdarzeń względem nowej logiki konsumenta i porównanie wyników z zarejestrowanymi wynikami starej logiki konsumenta, umożliwiając testowanie porównawcze bez narażania użytkowników na działanie nowej logiki.

Automatyzacja refaktoryzacji w procesach CI/CD

Dyscyplina refaktoryzacji bez przestojów nie może być utrzymana za pomocą procesów ręcznych. Każde wdrożenie w programie bez przestojów wymaga sekwencji walidacji: weryfikacji przed wdrożeniem, czy nowa wersja jest zgodna z aktualnym stanem bazy danych, oceny typu canary gate przy każdym wzroście procentowego ruchu, automatycznego porównania wyników między starymi i nowymi ścieżkami kodu oraz weryfikacji po wdrożeniu, czy kluczowe wskaźniki nie uległy pogorszeniu. Ręczne wykonywanie tych kroków dla każdej zmiany nie jest stabilne operacyjnie i prowadzi do błędów ludzkich w najbardziej krytycznych momentach procesu.

Proces CI/CD do refaktoryzacji bez przestojów to nie tylko proces budowania i wdrażania. To proces walidacji: sekwencja zautomatyzowanych bramek, które muszą przejść wszystkie etapy, zanim zmiana przejdzie do następnego etapu wdrożenia. Każda bramka stanowi konkretne, mierzalne kryterium. Niespełnienie którejś z bramek zatrzymuje proces i generuje alert. Spełnienie wszystkich bramek automatycznie przechodzi do następnego etapu wdrożenia. Jak opisano w szerszym omówieniu Praktyki CI/CD dla środowisk mainframe i korporacyjnychPodstawowym wymogiem jest to, aby proces wdrażania egzekwował tę samą dyscyplinę dla każdej zmiany, niezależnie od jej rozmiaru, oraz aby egzekwowanie było zautomatyzowane, a nie uzależnione od uwagi poszczególnych inżynierów.

Bramki etapów potoku do refaktoryzacji na żywo

Bramki etapowe to punkty kontrolne walidacji, które wdrożenie musi przejść przed przejściem dalej. W przypadku potoku refaktoryzacji bez przestojów minimalny zestaw bramek przedstawia się następująco.

Przed wdrożeniem: sprawdzenie zgodności schematu potwierdza, że ​​migracja bazy danych jest wstecznie kompatybilna z bieżącą wersją aplikacji, automatyczne testy kontraktu weryfikują, czy odpowiedzi API nowej wersji są kompatybilne z kontraktem poprzedniej wersji, a statyczna analiza zależności potwierdza, że ​​żadna zależność wprowadzona w nowej wersji nie będzie kolidowała z zależnością wymaganą przez istniejące środowisko.

Po wdrożeniu w wersji Canary: porównanie współczynnika błędów między ruchem w wersji Canary a ruchem bazowym, porównanie opóźnień na p50, p95 i p99, porównanie metryk biznesowych dla każdej metryki, na którą wpływa zmieniona ścieżka kodu, oraz minimalne okno obserwacji, w którym wersja Canary musi pozostać stabilna, zanim wzrośnie procent ruchu.

Wdrożenie po pełnym wdrożeniu: zestaw testów regresyjnych w punktach końcowych produkcyjnych, kontrole spójności bazy danych potwierdzające, że wszelkie migracje z podwójnym zapisem lub rozszerzeniem i kontraktem zachowały spójność, oraz potwierdzenie, że poprzedni artefakt wdrożenia nadal jest dostępny do wycofania.

Refaktoryzacja i egzekwowanie przepisów zgodnie z wymogami

Refaktoryzacja oparta na zgodności wprowadza dodatkowe ograniczenie, które muszą egzekwować bramki potoku: każda zmiana musi być w sposób oczywisty zgodna z obowiązującymi przepisami lub polityką organizacji. W branżach regulowanych oznacza to, że potok wdrożenia musi generować ślad audytu pokazujący, co zostało zmienione, kiedy zostało wdrożone, jakie walidacje zostały przeprowadzone i kto je zatwierdził. Zautomatyzowane bramki potoku, które rejestrują swoje własne wykonanie, w tym stan wejściowy, kryteria bramki oraz wynik pozytywny/negatywny, zapewniają ten ślad audytu bez konieczności ręcznej dokumentacji.

Inteligentne platformy refaktoryzacji z funkcjami egzekwowania w całym zespole, które pojawiają się jako zapytanie w danych Search Console dla tego artykułu, to narzędzia integrujące walidację zgodności z procesem refaktoryzacji: wymuszając spójne stosowanie wzorców refaktoryzacji w zespołach, brak ponownego wprowadzania przestarzałych interfejsów oraz zgodność zmian strukturalnych ze standardami architektonicznymi zdefiniowanymi na poziomie organizacji. Możliwości te wykraczają poza możliwości samego procesu CI/CD, ponieważ wymagają zrozumienia semantyki modyfikowanego kodu, a nie tylko tego, czy się kompiluje i przechodzi testy.

Refaktoryzacja komputerów mainframe i CICS bez przestojów

Środowiska komputerów mainframe stanowią najbardziej wymagającą wersję refaktoryzacji bez przestojów, ponieważ ograniczenia mają charakter strukturalny, a nie konfigurowalny. Programu transakcyjnego CICS nie można zastąpić poprzez wdrożenie nowego obrazu kontenera i przełączenie modułu równoważenia obciążenia. Zastąpienie programu w CICS wymaga polecenia NEWCOPY lub PHASEIN, które ładuje nową wersję programu do pamięci. Polecenie NEWCOPY natychmiast zastępuje starą wersję, wpływając na wszystkie transakcje rozpoczynające się po wykonaniu polecenia. Polecenie PHASEIN czeka na zakończenie wszystkich aktualnie aktywnych transakcji korzystających ze starej wersji przed jej zastąpieniem, zapewniając płynniejsze przejście dla długotrwałych transakcji.

Żaden z mechanizmów nie zapewnia natychmiastowego wycofania. Jeśli nowa wersja programu ma wadę, powrót do starej wersji wymaga ponownego wydania polecenia NEWCOPY lub PHASEIN z poprzednim modułem ładowania. Wymaga to zachowania poprzedniego modułu ładowania w bibliotece ładowania oraz udokumentowania, przećwiczenia i umożliwienia wykonania procedury wycofania przez zespół dyżurny, bez konieczności angażowania pierwotnego programisty.

Współdzielone pliki VSAM nakładają dodatkowe ograniczenia. Wiele transakcji CICS i programów wsadowych może jednocześnie uzyskiwać dostęp do tego samego pliku VSAM. Strukturalna zmiana układu pliku, taka jak dodanie lub rozszerzenie segmentu rekordu, wymaga, aby wszystkie programy uzyskujące dostęp do pliku zostały zaktualizowane przed zmianą układu lub równocześnie z nią, albo aby plik obsługiwał wiele formatów rekordów w okresie przejściowym. Jest to odpowiednik wzorca rozszerzania i skracania w komputerach mainframe: nowy układ musi być zgodny ze starymi programami w okresie przejściowym, a stare programy muszą zostać zaktualizowane przed wycofaniem starego układu. Kontrolowane rozszerzanie układów zbiorów danych i parametrów dostępu do programów to mechanizm, który umożliwia to zgodne współistnienie bez konieczności wymiany plików.

Strategie eliminacji okna wsadowego

Tradycyjne przetwarzanie wsadowe na komputerach mainframe zakłada istnienie okna wsadowego: okresu, w którym przetwarzanie transakcji online jest zawieszone, zadania wsadowe są wykonywane bez zakłóceń, a dane wynikowe są gotowe do następnego okresu przetwarzania online. Wyeliminowanie okna wsadowego, niezbędnego do zapewnienia rzeczywistego braku przestojów, oznacza konieczność przeprojektowania modelu przetwarzania wsadowego, tak aby zadania wsadowe mogły być wykonywane równolegle z transakcjami online bez uszkadzania współdzielonych danych.

Standardowe podejścia obejmują blokowanie zasobów na poziomie rekordu, a nie pliku, mini przetwarzanie wsadowe sterowane zdarzeniami, które przetwarza małe obciążenia w sposób ciągły, a nie okresowo duże obciążenia, oraz bazy danych z replikacją odczytu, które obsługują obciążenia raportowania wsadowego, nie konkurując z przetwarzaniem transakcji online o dostęp do zapisu. Każde z tych podejść wymaga zmian zarówno w programach, jak i wzorcach dostępu do danych, ale żadne nie wymaga, aby okno przetwarzania wsadowego pozostawało aktywne podczas przejścia: samo przejście można przeprowadzić etapowo, stosując tę ​​samą metodę walidacji z podwójnym przebiegiem, co w przypadku każdej innej refaktoryzacji systemu na żywo.

Refaktoryzacja programów COBOL z wykorzystaniem analizy wpływu

Bezpieczna refaktoryzacja programu COBOL wymaga dokładnej wiedzy, przed wprowadzeniem jakichkolwiek zmian, które inne programy go wywołują, które kopie współdzieli z innymi programami, które zbiory danych odczytuje i zapisuje oraz które systemy niższego rzędu zależą od generowanych przez niego danych. Bez tej wiedzy strukturalnej każda zmiana w programie niesie ze sobą nieznane ryzyko: refaktoryzowany program może uszkodzić niezidentyfikowany obiekt wywołujący, wygenerować dane wyjściowe w formacie, którego system niższego rzędu nie może przeanalizować, lub zmodyfikować współdzieloną strukturę danych w sposób wpływający na inne programy korzystające z tej samej kopii.

Zautomatyzowana analiza wpływu rozwiązuje ten problem poprzez skonstruowanie kompletnego grafu zależności programu COBOL przed rozpoczęciem refaktoryzacji. Graf ten przedstawia każdego wywołującego, każdy współdzielony copybook, każdy dostęp do zbioru danych oraz każdego odbiorcę w dół strumienia, uporządkowane według typu relacji i konkretnej lokalizacji odniesienia. Plan refaktoryzacji jest następnie generowany z grafu wpływu: programy wywołujące zmieniony program muszą zostać przetestowane pod kątem nowej wersji, zmodyfikowane copybooki muszą zostać zweryfikowane pod kątem wszystkich programów, które je zawierają, a zmienione układy zbiorów danych muszą zostać zweryfikowane pod kątem wszystkich programów uzyskujących dostęp do tych samych zbiorów danych. Jak opisano w rozwiązania analizy wpływu Możliwości, jakie zapewnia IN-COM, stanowią różnicę między programem refaktoryzującym, który odkrywa konsekwencje wdrożenia po jego wdrożeniu, a programem, który kwantyfikuje je wcześniej.

Weryfikacja, wycofywanie i obserwowalność

Refaktoryzacja bez przestojów generuje ciągły wynik, który musi być stale monitorowany. Monitorowanie nie polega na sprawdzaniu post hoc, czy wszystko działa: jest to aktywna brama na każdym etapie procesu wdrażania i główny mechanizm, dzięki któremu problemy są wykrywane wystarczająco wcześnie, aby zapobiec wpływowi na użytkowników.

Model weryfikacji refaktoryzacji bez przestojów składa się z trzech warstw. Pierwszą z nich jest monitorowanie syntetyczne: transakcje skryptowe, które symulują zachowanie użytkownika i są stale uruchamiane w środowisku produkcyjnym, weryfikując, czy kluczowe przepływy danych zostały ukończone pomyślnie. Monitory syntetyczne wychwytują błędy występujące w określonych ścieżkach kodu, których prawdziwi użytkownicy mogą nie wykrywać w okresach niskiego ruchu, i stanowią punkt odniesienia dla zachowań, z którym można porównywać wyniki testów kanarkowych.

Drugą warstwą jest monitorowanie różnicowe: porównywanie w czasie rzeczywistym metryk między wdrożeniem kanarkowym a wdrożeniem bazowym, w tym wskaźników błędów, rozkładów opóźnień, wskaźników biznesowych i zużycia zasobów. Monitorowanie różnicowe nie wymaga progów bezwzględnych: wymaga porównania względnego. Wdrożenie kanarkowe, które wykazuje wskaźniki błędów o dwa procent wyższe niż w wariancie bazowym, stanowi problem niezależnie od tego, czy bezwzględny wskaźnik błędów przekracza którykolwiek indywidualnie zdefiniowany próg.

Trzecią warstwą jest weryfikacja spójności danych. W każdym refaktoryzowaniu, które obejmuje podwójne zapisy, migracje schematów lub równoległe uruchomienia systemów, spójność danych między starą a nową reprezentacją musi być stale weryfikowana. Porównania sum kontrolnych, porównania liczby rekordów i zapytania doraźne, które weryfikują określone wartości pól pod kątem oczekiwanych transformacji, przyczyniają się do pewności, że warstwa danych zachowuje się poprawnie podczas przejścia. Jak zbadano w kontekście Czym jest analiza wpływu i dlaczego jest ważna, możliwość sprawdzenia konsekwencji zmiany w odniesieniu do zdefiniowanego zbioru oczekiwań jest tym, co odróżnia strukturalne refaktoryzowanie od spekulatywnej zmiany.

Mechanizmy natychmiastowego wycofywania zmian

Plan przywracania, którego wykonanie zajmuje trzydzieści minut, nie jest planem przywracania systemu bez przestojów. Do momentu jego zakończenia użytkownicy mają już zapewnione trzydzieści minut niesprawnej usługi. Natychmiastowe przywracanie wymaga, aby każde wdrożenie było od początku projektowane z myślą o możliwości jego odwrócenia, a nie modernizowane po wystąpieniu problemu.

W przypadku wdrożeń aplikacji natychmiastowe wycofanie oznacza utrzymanie poprzedniego artefaktu wdrożenia w stanie dostępnym, wstępnie rozgrzanym i ukierunkowanym na ten sam stan bazy danych. Przełączanie ruchu za pośrednictwem modułu równoważenia obciążenia lub zmiana reguły bramy API powinna być jedyną czynnością wymaganą do powrotu do poprzedniej wersji. Jest to możliwe, gdy stan bazy danych jest wstecznie kompatybilny z poprzednią wersją, co gwarantuje zasada rozszerzania i skracania w warstwie migracji bazy danych.

W przypadku migracji baz danych, natychmiastowe wycofanie wymaga, aby każda migracja przeprowadzona w fazie rozszerzania była odwracalna bez utraty danych. Kolumnę dodaną w fazie rozszerzania można usunąć w procesie wycofania. Kolumny zmodyfikowanej w sposób destrukcyjny nie można przywrócić bez kopii zapasowej. Dlatego destrukcyjne zmiany schematu, takie jak usuwanie kolumn, modyfikacja typów w sposób niezgodny z oczekiwaniami lub zmniejszenie precyzji, nie powinny być wprowadzane, dopóki nowa wersja nie zostanie w pełni wdrożona i zweryfikowana, a stara wersja nie zostanie całkowicie wycofana.

W jaki sposób SMART TS XL Obsługuje programy refaktoryzacji bez przestojów

SMART TS XL rozwiązuje problem widoczności strukturalnej, który leży u podstaw każdej awarii refaktoryzacji bez przestojów: zespoły próbują refaktoryzować działające systemy bez pełnego obrazu tego, co zawierają te systemy, co od czego zależy i jakie będą konsekwencje każdej planowanej zmiany. Platforma pobiera kod źródłowy z każdego języka i platformy w środowisku, w tym COBOL, JCL, Java, .NET, Python, JavaScript i SQL, i konstruuje ujednolicony model odniesień, który reprezentuje strukturalne relacje całego systemu.

Przed dokonaniem zmiany refaktoryzacyjnej, SMART TS XLFunkcja analizy wpływu śledzi graf zależności od modyfikowanego komponentu na zewnątrz, poprzez każdego użytkownika wywołującego, każdą współdzieloną strukturę danych, każdego odbiorcę podrzędnego i każdy program, na który zmiana będzie miała wpływ. Rezultatem jest konkretna, wyliczona lista konsekwencji uporządkowana według ważności i komponentu, a nie ogólna ocena ryzyka. Lista ta umożliwia prawidłowe zaplanowanie sekwencji refaktoryzacji bez przestojów: wiedząc, którzy odbiorcy muszą zostać zaktualizowani przed wdrożeniem zmienionego komponentu, które migracje baz danych muszą zostać wykonane przed wdrożeniem poszczególnych aplikacji oraz które systemy podrzędne muszą zostać zweryfikowane przed wycofaniem starej wersji.

SMART TS XLMożliwość wizualizacji kodu sprawia, że ​​graf zależności jest łatwy w nawigacji dla zespołów, które nie znają dogłębnie każdej warstwy refaktoryzowanego systemu. Architekci mogą zobaczyć, jak komponenty się łączą, zanim przeprojektują strukturę połączeń. Programiści mogą zobaczyć, co wywołuje funkcję, zanim zmienią jej sygnaturę. Zespoły operacyjne mogą zobaczyć, do czego wykorzystywany jest zbiór danych, zanim zmodyfikują jego układ. Ta widoczność jest warunkiem wstępnym dla ustrukturyzowanego, odwracalnego i etapowego programu refaktoryzacji, którego wymaga działanie bez przestojów.

Refaktoryzacja bez przestojów jako praktyka ciągła

Techniki opisane w tym przewodniku nie są jednorazowymi interwencjami. Stanowią one operacyjny słownik organizacji programistycznej, która zdecydowała się traktować systemy produkcyjne jako stale ewoluujące, a nie okresowo wymieniane. Wdrożenia blue-green, wydania canary, przełączanie funkcji, migracje typu expand-contract, ekstrakcja fig dusicieli, idempotentne przetwarzanie zdarzeń i bramki wdrażania wymuszane przez potok nie są procedurami awaryjnymi: są to standardowe procedury operacyjne zespołu, który bezpiecznie i często wprowadza zmiany strukturalne.

Osiągnięcie tego stanu wymaga inwestycji w narzędzia, infrastrukturę i praktykę organizacyjną wykraczającą poza jakąkolwiek indywidualną inicjatywę refaktoryzacji. Narzędzia muszą obsługiwać niezależne wdrażanie, obserwowalne przejścia między stanami i natychmiastowe wycofywanie zmian. Infrastruktura musi obsługiwać podział ruchu, środowiska blue-green oraz synchronizację danych opartą na CDC. Praktyka organizacyjna musi obejmować analizę wpływu przed wdrożeniem, monitorowanie różnic po wdrożeniu oraz regularne próby wycofywania zmian, które potwierdzają, że ścieżka wycofywania zmian działa w realistycznych warunkach.

Organizacje, które inwestują w tę metodę, odkrywają, że koszt każdej zmiany maleje w miarę dojrzewania praktyki: każda kolejna refaktoryzacja jest mniej ryzykowna niż poprzednia, ponieważ infrastruktura pomocnicza jest już wdrożona, zespół wypracował sobie osąd na temat tego, które progi bramkowe są odpowiednie dla poszczególnych zmian, a zgromadzona wiedza strukturalna w narzędziach takich jak SMART TS XL Dzięki temu każda planowana zmiana ma dokładniejszy zakres niż poprzednia. Celem refaktoryzacji bez przestojów nie jest bezpieczne wprowadzenie pojedynczej zmiany. Chodzi o to, aby każda zmiana była wprowadzana bezpiecznie, w sposób ciągły, bez konieczności proszenia użytkowników o akceptację okna konserwacyjnego.