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.

15 minut · Symfony Messenger

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íš

  1. Síťová chyba, timeout, 429 a většina 5xx často znamenají dočasný problém.
  2. 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.
  3. 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

  1. Začni třeba na 1 sekundě. Další čekání násob dvěma: 1 s, 2 s, 4 s.
  2. Nastav maximální zpoždění. Po dlouhém výpadku nechceš, aby se úloha vracela za několik dní bez kontroly.
  3. 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

  1. Po posledním pokusu ji pošli do failed transportu. Musí tam být čitelný důvod i ID původního záznamu.
  2. Nastav upozornění podle počtu selhaných úloh. Nečekej, až si jich všimne zákazník.
  3. 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í.

  1. Nech první dva pokusy selhat

    V logu musíš vidět zvyšující se intervaly a nakonec úspěch, ne okamžitou smyčku.

  2. 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
  3. 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á.

Zavolejte mi

Zavolám vám následující pracovní den mezi 9:00 a 17:00.

Můžete mi také zavolat rovnou.

+420 605 181 728

Nechte mi telefonní číslo a pošlete žádost o zpětné zavolání.

Odesláním souhlasíte se zpracováním údajů pro vyřízení žádosti.