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ę.
Co z tego wyniesiesz
Dział zatytułowany „Co z tego wyniesiesz”- 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 przy5xxiECONNRESET— nigdy przy400 - 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ę
Workflow
Dział zatytułowany „Workflow”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.
Claude Code to właściwe narzędzie, gdy chcesz mieć wyłącznik oraz przechodzący test w jednym nieinteraktywnym przebiegu — na przykład wewnątrz zadania naprawczego w CI. Uruchom go headlessowo i pozwól mu edytować, uruchomić test i iterować aż do zielonego:
claude -p "Wrap paymentService.charge in src/routes/checkout.ts with an opossum \circuit breaker (timeout 3000ms, errorThresholdPercentage 50, resetTimeout 30000), \with a fallback that enqueues the order and returns { status: 'queued' }. Then add a \vitest test in tests/checkout.breaker.test.ts that forces 5 consecutive rejections \and asserts breaker.opened === true and the fallback ran. Run 'npm test' and fix \failures until it passes." \ --allowedTools Read Edit BashFlaga -p uruchamia raz i kończy działanie (idealna do skryptów); --allowedTools ogranicza zakres tego, czego może dotknąć, więc przebieg w CI nie pobłądzi.
Codex błyszczy, gdy chcesz, by zmiana dotarła jako gałąź do przejrzenia, a nie jako edycje w twoim drzewie roboczym. Z poziomu CLI pozwól mu działać w izolowanym workspace i otworzyć PR:
codex --ask-for-approval on-request \ "Add an opossum circuit breaker around paymentService.charge in \ src/routes/checkout.ts: timeout 3000ms, errorThresholdPercentage 50, \ resetTimeout 30000, fallback enqueues the order and returns { status: 'queued' }. \ Add a vitest spec proving the breaker opens after repeated failures. \ Open a PR titled 'resilience: circuit-break payment charge'."W Codex Cloud lub rozszerzeniu do IDE ten sam prompt działa na worktree, więc wyłącznik i jego test lądują na gałęzi, którą przeglądasz — przydatne, gdy niestabilne wywołanie jest w usłudze, której nie chcesz edytować na żywo.
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.
Krok 2: Dodaj ponowienie, które nie pogarsza awarii
Dział zatytułowany „Krok 2: Dodaj ponowienie, które nie pogarsza awarii”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" } }}claude mcp add --transport http sentry https://mcp.sentry.dev/mcpNastępnie uruchom /mcp w REPL-u, aby dokończyć logowanie OAuth.
Dodaj do ~/.codex/config.toml:
[mcp_servers.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.
Krok 5: Udowodnij to testem chaosu
Dział zatytułowany „Krok 5: Udowodnij to testem chaosu”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ą.
-
Postaw stub dla zależności, którą opakowałeś (np.
nockdla HTTP albo mockpaymentService), który możesz na żądanie przełączyć w tryb awarii. -
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. -
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.
Kiedy to się psuje
Dział zatytułowany „Kiedy to się psuje”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:
Co dalej
Dział zatytułowany „Co dalej”- 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