Przejdź do głównej zawartości

Wzorce odzyskiwania

Twój dostawca płatności ma 90-sekundową awarię o 2 w nocy. Bez wyłącznika każde żądanie checkoutu piętrzy się, czekając na martwą zależność, twoja pula połączeń wysycha, a cała aplikacja się wykłada — nie tylko płatności. Rozwiązaniem nie jest „napisz framework odpornościowy”. To opakowanie sprawdzonej w boju biblioteki (opossum, cockatiel, Resilience4j) wokół tego jednego niestabilnego wywołania, z właściwymi progami i fallbackiem — a to dokładnie ten rodzaj mechanicznej, ale upierdliwej roboty, którą narzędzie AI robi dobrze, jeśli skierujesz je na prawdziwą zależność zamiast na zabawkowy przykład.

Ten przepis pokazuje prompty, które generują działające wyłączniki, ponowienia i fallbacki wokół nazwanych bibliotek, oraz jak udowodnić, że wyłącznik faktycznie się otwiera, za pomocą testu chaosu. Ręcznie sklecona class CircuitBreaker to rzecz, którą AI usuwa, a nie rzecz, którą wdrażasz na produkcję.

  • Prompt, który opakowuje nazwaną niestabilną zależność w wyłącznik opossum (Node) lub Resilience4j (JVM) z logowaniem zmian stanu i fallbackiem
  • Prompt, który dodaje ponowienie z wykładniczym wycofywaniem i jitterem do konkretnego wywołania fetch, ponawiając tylko przy 5xx i ECONNRESET — nigdy przy 400
  • Prompt, który pisze test chaosu wstrzykujący 500 z usługi płatności i sprawdzający, że wyłącznik się otwiera, a fallback odpala
  • Porównanie przed/po podpięciu Sentry MCP, dzięki któremu agent czyta produkcyjny stack trace uzasadniający wyłącznik, zamiast zgadywać
  • Tryby awarii, które sprawiają, że wygenerowany przez AI kod odpornościowy wygląda na poprawny w review, a na produkcji wzmacnia awarię

Błędem, który sprawia, że AI generuje 200 linii autorskich klas Semaphore i Bulkhead, jest proszenie go o „zaimplementuj wyłącznik”. Poproś je zamiast tego o zintegrowanie konkretnej biblioteki wokół konkretnego wywołania. Odporność to rozwiązany problem — opossum i cockatiel na Node, Resilience4j na JVM, tenacity na Pythonie — a agent pisze cienki, poprawny adapter o wiele bardziej niezawodnie, niż wymyśla maszynę stanów.

Krok 1: Opakuj niestabilne wywołanie w prawdziwy wyłącznik

Dział zatytułowany „Krok 1: Opakuj niestabilne wywołanie w prawdziwy wyłącznik”

Wyłącznik to wzorzec o najwyższej dźwigni: powstrzymuje wolną lub martwą zależność przed wyczerpaniem twoich własnych zasobów. Workflow różni się w zależności od narzędzia — Cursor edytuje route w miejscu przy otwartym pliku, Claude Code generuje i uruchamia test jednostkowy w jednym headlessowym przebiegu, a Codex otwiera zmianę na gałęzi — więc oto podział na trzy narzędzia.

Otwórz plik route’a (src/routes/checkout.ts), zaznacz surowe wywołanie paymentService.charge(...) i użyj trybu agenta (Cmd/Ctrl+I). Cursor edytuje wywołanie w miejscu i pokazuje diff, który akceptujesz fragment po fragmencie — właściwy tryb, gdy chcesz patrzeć, jak integracja ląduje w pliku, który znasz.

To, co agent powinien wyprodukować, jest małe. Z opossum wygląda to mniej więcej tak — zauważ, że nie ma tu maszyny stanów, tylko konfiguracja i fallback:

import CircuitBreaker from 'opossum';
const options = { timeout: 3000, errorThresholdPercentage: 50, resetTimeout: 30000 };
const breaker = new CircuitBreaker((order: Order) => paymentService.charge(order), options);
breaker.fallback((order: Order) => {
queue.enqueue('payments', order);
return { status: 'queued' as const };
});
breaker.on('open', () => log.warn('payment breaker OPEN — shedding load to queue'));
breaker.on('halfOpen', () => log.warn('payment breaker HALF_OPEN — probing'));
export const chargeWithBreaker = (order: Order) => breaker.fire(order);

Jeśli jesteś na JVM, równoważny prompt powinien nazwać Resilience4j oraz adnotację @CircuitBreaker ze Spring Boot z fallbackMethod, żeby agent oadnotował twoją istniejącą metodę @Service zamiast ręcznie sklejać slidingWindow. Na Pythonie nazwij tenacity do ponowień i pybreaker do wyłącznika.

Naiwne ponowienia wzmacniają obciążenie podczas częściowej awarii — trzy ponowienia razy każdy klient to samodzielnie zafundowany DDoS. Liczą się dwie zasady: wycofywanie z jitterem (żeby klienci nie ponawiali w jednym rytmie) i ścisły predykat błędu nadającego się do ponowienia (nigdy nie ponawiaj 400 ani niededuplikowalnego, nieidempotentnego POST). Sięgnij po cockatiel albo p-retry na Node zamiast po autorską pętlę. Ten krok jest identyczny we wszystkich trzech narzędziach — to pojedyncza, zlokalizowana edycja — więc wybierz to, które masz otwarte.

Szczegół „opakuj wyłącznik wewnątrz ponowienia” ma znaczenie, a agent go pominie, dopóki mu tego nie powiesz: ponowienia powinny liczyć się do współczynnika awarii wyłącznika, żeby utrzymująca się awaria otwierała wyłącznik zamiast być maskowaną przez nieskończone lokalne ponowienia.

Krok 3: Nałóż łańcuch fallbacków dla graceful degradacji

Dział zatytułowany „Krok 3: Nałóż łańcuch fallbacków dla graceful degradacji”

Dla odczytów łańcuch fallbacków utrzymuje funkcję przy życiu, gdy główne źródło jest niedostępne: główna baza, potem replika do odczytu, potem cache (oznaczony jako przestarzały), potem bezpieczna wartość domyślna. Opiniotwórczy punkt: każdy poziom musi być sensownie zdegradowany i oznaczony, albo wdrożysz ciche zwracanie nieaktualnych danych. Trzymaj to jako małą uporządkowaną listę asynchronicznych thunków — a nie framework FallbackChain<T>.

Wentyl bezpieczeństwa z tego promptu — ponowne rzucenie NotFound zamiast przelecenia dalej — to linia, która oddziela odporność od fabryki bugów. Fallback, który połyka prawowite 404 i serwuje zcache’owany profil, jest gorszy niż sama awaria.

Krok 4: Użyj Sentry MCP, żeby agent rozumował na bazie prawdziwej awarii

Dział zatytułowany „Krok 4: Użyj Sentry MCP, żeby agent rozumował na bazie prawdziwej awarii”

Do tego momentu agent zgaduje, która zależność jest niestabilna. Podepnij Sentry MCP, a odczyta on faktyczny produkcyjny problem — stack trace, częstotliwość, padający span — zanim zaproponuje wyłącznik. To różnica między „dodaj gdzieś odporność” a „przełam obwód tego wywołania, bo rzuciło ETIMEDOUT 1182 razy w ciągu ostatniej godziny”.

Konfiguracja MCP jest niemal identyczna we wszystkich trzech narzędziach — wszystkie trzy mówią w MCP, a Sentry to hostowany serwer HTTP, więc nie ma żadnej paczki do zainstalowania:

Dodaj do .cursor/mcp.json (lub globalnego ~/.cursor/mcp.json), a następnie zatwierdź go w Settings → MCP:

{
"mcpServers": {
"sentry": { "url": "https://mcp.sentry.dev/mcp" }
}
}

Z podłączonym serwerem porównanie przed/po jest wyraziste. Przed: „Dodaj wyłącznik do przepływu checkoutu” produkuje wyłącznik na dowolnym wywołaniu, które agent uzna za ryzykowne. Po:

Jeśli trwałe połączenie wydaje się przesadą do jednorazowego triażu, jednozadaniowy Agent Skill może być lżejszy niż pełny serwer MCP. Przejrzyj skills.sh i zainstaluj uniwersalnym CLI — npx skills add <owner/repo> (z vercel-labs/skills) — które działa w Cursorze, Claude Code i Codeksie. Użyj skilla, gdy chcesz skupionego, powtarzalnego wsparcia; użyj MCP, gdy chcesz, by agent utrzymywał żywe, odpytywalne połączenie z danymi o twoich incydentach.

Wyłącznik, którego nigdy nie wyzwoliłeś w teście, to wyłącznik, który debugujesz w trakcie incydentu. Wstrzyknij awarię i sprawdź odzyskiwanie — to krok, który ogólne poradniki pomijają.

  1. Postaw stub dla zależności, którą opakowałeś (np. nock dla HTTP albo mock paymentService), który możesz na żądanie przełączyć w tryb awarii.

  2. Wywołaj wystarczająco wiele padających wywołań, by przekroczyć errorThresholdPercentage, a potem sprawdź, że wyłącznik jest otwarty i fallback się wykonał — nie tylko, że został rzucony błąd.

  3. Przesuń czas poza resetTimeout, wyślij jeden sukces i sprawdź, że wyłącznik się zamyka (sonda half-open). Użyj fałszywych timerów, żeby test wykonywał się w milisekundach.

Dla pełnego chaosu na poziomie całego systemu, wykraczającego poza test jednostkowy — ubicia poda, dodania opóźnień na warstwie sieci — wygeneruj tym samym narzędziem skrypt obciążeniowy k6 albo konfigurację Toxiproxy, ale samo zachowanie wyłącznika trzymaj pokryte szybkim testem jednostkowym powyżej, żeby uruchamiał się przy każdym PR-ze.

Wygenerowany przez AI kod odpornościowy jest niebezpieczny właśnie dlatego, że wygląda na poprawny. Oto tryby awarii, które warto sprawdzić, zanim wdrożysz:

  • Wzorce debugowania — znajdź pierwotną przyczynę, która w ogóle uzasadnia wyłącznik
  • Wzorce logowania — emituj ustrukturyzowane, skorelowane logi, które czynią zmiany stanu wyłącznika odpytywalnymi
  • Wzorce monitorowania — zamień wyzwolony wyłącznik w alert, który faktycznie kogoś wybudzi