Ugrás a tartalomra
Rapcsány Krisztián Rapcsány Krisztián
← Összes cikk
2 perc olvasás

Webhook küldés újrapróbálással, kétszeri küldés nélkül

Laravel Sorok API

A média szolgáltatásom a videófeldolgozás végén visszahívást (webhookot) küld az üzemeltető rendszernek. Ez két ok miatt nehezebb, mint elsőre tűnik: a fogadó oldal lehet éppen elérhetetlen, és a feldolgozás lépései egymástól függetlenül fejeződnek be, így ugyanaz a kész állapot többször is előállhat.

Újrapróbálás növekvő várakozással

A küldés sorban fut, legfeljebb öt próbálkozással, köztük 10 másodperc, 30 másodperc, 1 perc, majd 5 perc várakozással. Ha a válasz nem sikeres, kivételt dobok, így a sor tudja, hogy újra kell próbálnia.

Ne menjen ki kétszer

A miniatűr, a stream és az MP4 külön készül el, és mindegyik elindíthatja a küldést. Ezért a küldés előtt zárolt sort olvasok egy tranzakcióban, és beírom, hogy a küldés megtörtént. Ha már be van írva, a második job csendben kilép.

SendVideoWebhookJob.php
class SendVideoWebhookJob implements ShouldQueue
{
    use Queueable;

    public $tries = 5;
    public $backoff = [10, 30, 60, 300];   // seconds to wait between attempts
    public $timeout = 30;

    public function handle(): void
    {
        // Several jobs can reach the "finished" state independently:
        // only the first may send, so claim the flag under a row lock.
        $shouldSend = DB::transaction(function () use ($video) {
            $locked = Video::whereKey($video->id)->lockForUpdate()->first();

            if (! $locked || $locked->webhook_sent_at !== null) {
                return false;
            }

            $locked->update(['webhook_sent_at' => now()]);

            return true;
        });

        if (! $shouldSend) {
            return;
        }

        try {
            $response = Http::timeout(10)->post($callbackUrl, $payload);

            if ($response->failed()) {
                throw new \RuntimeException("Webhook endpoint responded with HTTP {$response->status()}");
            }
        } catch (\Throwable $e) {
            // Release the flag, otherwise the retry would think it was already sent.
            $video->update(['webhook_sent_at' => null]);

            throw $e;
        }
    }
}

Kivonat valódi kódból, anonimizálva.

Ha a küldés hibázik

A jelzőt a sikertelen küldés után visszaállítom, és újra dobom a kivételt. Ha ezt kihagyom, az újrapróbálás azt hinné, hogy a küldés már megvolt, és nem küldene semmit. Ezt könnyű elfelejteni, mert a hibátlan úton minden jól működik.

Hasonló problémán dolgozol?

Ha elakadtál, vagy átnéztetnéd a megoldásodat, írj, szívesen megnézem.

Írok neked →
Ajánlatot kérek →