Spolehlivá práce na pozadí
Jak implementovat retry a exponential backoff
Opakování pomůže jen přechodné chybě. Když ho nastavíš špatně, z malé chyby udělá výpadek.
Nejdřív stručně
Co je retry a backoff?
Retry je další pokus o stejnou úlohu po dočasné chybě. Exponential backoff prodlužuje čekání mezi pokusy, takže výpadek dáš druhé straně šanci přežít.
Typický příklad je odeslání dat přes externí API. Když síť na chvíli selže, úloha se vrátí do fronty zpráv a worker ji zkusí později.
Připrav si
Co budeš potřebovat
Než úlohu začneš opakovat, rozhodni, kdy je to bezpečné.
- Asynchronní úlohu nebo jiný jasně vymezený kus práce.
- Frontu zpráv a worker. V Symfony ji obslouží Symfony Messenger.
- Seznam chyb, které jsou přechodné: síť, timeout, 429 nebo 5xx.
- Idempotentní handler. Stejná zpráva nesmí vytvořit druhou platbu, e-mail nebo objednávku. Pomůže idempotence.
Krok 1
Nastav opakování po malých krocích
Začni konzervativně. Tři pokusy bývají lepší start než nekonečné doručování.
1. Rozliš chyby, které opakovat smíš
- Síťová chyba, timeout, 429 a většina 5xx často znamenají dočasný problém.
- 400, 401, 403 a chybějící data většinou nejsou dočasná. Zprávu pošli do failed transportu a oprav příčinu.
- U zápisu do cizího API používej idempotency key. Jinak odpověď mohla selhat až po úspěšném zápisu.
if (in_array($status, [429, 500, 502, 503, 504], true)) { /* retry */ } RFC 9110: HTTP status codes 2. Nastav exponential backoff
- Začni třeba na 1 sekundě. Další čekání násob dvěma: 1 s, 2 s, 4 s.
- Nastav maximální zpoždění. Po dlouhém výpadku nechceš, aby se úloha vracela za několik dní bez kontroly.
- Přidej malou náhodu, takzvaný jitter. Více workerů pak nevystřelí ve stejnou vteřinu.
delay = min(1000 * 2 ** attempt, 60000) + random(0, 250) Symfony Messenger: retry strategy 3. Uchovej neúspěšnou úlohu
- Po posledním pokusu ji pošli do failed transportu. Musí tam být čitelný důvod i ID původního záznamu.
- Nastav upozornění podle počtu selhaných úloh. Nečekej, až si jich všimne zákazník.
- Po opravě příčiny úlohu ručně vrať do zpracování. Neopakuj bezhlavě celou frontu.
php bin/console messenger:failed:retry --force Symfony Messenger: failed messages Krok 2
Ověř plán pokusů
Před produkcí simuluj chybu, která po chvíli zmizí.
-
Nech první dva pokusy selhat
V logu musíš vidět zvyšující se intervaly a nakonec úspěch, ne okamžitou smyčku.
-
Pošli neopakovatelnou chybu
Pro 400 nebo neplatná data se úloha nemá vracet stále dokola. Očekávej failed transport a čitelný záznam.
php bin/console messenger:failed:show -
Pošli stejnou zprávu dvakrát
Po úspěchu nesmí vzniknout druhý vedlejší efekt. To ověřuje idempotenci handleru.
Když to zlobí
Nejčastější chyby
Worker útočí na API stále dokola
Chybí backoff nebo limit pokusů. Přidej rostoucí zpoždění a konečný počet pokusů.
Důležitá úloha se po chybě ztratí
Nakonfiguruj failed transport a upozornění. Zpráva musí být dohledatelná, ne jen zalogovaná.
Retry vytvoří duplicitní data
Handler není bezpečný při opakování. Ulož externí ID nebo idempotency key a kontroluj idempotenci.
Všechny chyby se opakují
Rozděl dočasné síťové chyby a chyby dat či oprávnění. Druhé oprav, nevracej je do fronty.
Hotovo
Retry má jasné hranice.
Opakování je omezené, čekání roste a neúspěšná práce zůstane dohledatelná.