Auf der Produktionsmaschine traten vereinzelt Timeouts nach codeberg.org auf
("Operation timed out after 15003 milliseconds with 0 bytes received"), waehrend
die Verbindung sonst in ~0,1s antwortet.
Bisher fiel der Dauerlaeufer nach so einem Fehlschlag in den regulaeren Takt
zurueck. Weil SyncTrigger::consume() die Webhook-Anforderung schon vor dem Lauf
quittiert, war eine per Webhook angeforderte Aktualisierung damit verloren und
kam erst bis zu MONITOR_SYNC_INTERVAL Sekunden spaeter - genau die Verzoegerung,
die der Webhook abschaffen sollte.
Neu wird der naechste Versuch nach einem Fehlschlag vorgezogen: 5s, 10s, 20s, …
gedeckelt auf das regulaere Intervall (Sync\SyncSchedule). Ein kurzer Aussetzer
ist damit binnen Sekunden ueberstanden, eine laengere Codeberg-Stoerung laeuft
trotzdem nicht in einen Dauerbeschuss. Nach einem erfolgreichen Lauf gilt sofort
wieder das regulaere Intervall.
Die Wartezeit-Berechnung liegt in einer eigenen Klasse, weil die Schleife in
bin/sync-codeberg.php selbst nicht testbar ist.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
43 lines
1.5 KiB
PHP
43 lines
1.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Monitor\Api\Sync;
|
|
|
|
/**
|
|
* Bestimmt, wie lange der Sync-Dauerlaeufer (bin/sync-codeberg.php --watch) bis zum
|
|
* naechsten Lauf wartet.
|
|
*
|
|
* Nach einem erfolgreichen Lauf ist das schlicht MONITOR_SYNC_INTERVAL. Nach einem
|
|
* Fehlschlag wird der naechste Versuch dagegen deutlich vorgezogen: Ein per Webhook
|
|
* angeforderter Sync ist zu diesem Zeitpunkt bereits quittiert (SyncTrigger::consume()
|
|
* loescht die Anforderung vor dem Lauf), er wuerde sonst also erst nach vollen 600s
|
|
* nachgeholt - genau die Verzoegerung, die der Webhook abschaffen soll.
|
|
*
|
|
* Der Abstand waechst exponentiell (5s, 10s, 20s, …), gedeckelt auf das regulaere
|
|
* Intervall. Damit erholt sich ein kurzer Aussetzer (beobachtet: vereinzelte
|
|
* curl-Timeouts nach 15s ohne Antwort) binnen Sekunden, waehrend eine laengere
|
|
* Codeberg-Stoerung nicht in einen Dauerbeschuss laeuft.
|
|
*/
|
|
final class SyncSchedule
|
|
{
|
|
/** Ab hier waere 2^$failures groesser als jedes sinnvolle Intervall - schuetzt vor Overflow. */
|
|
private const MAX_EXPONENT = 20;
|
|
|
|
public function __construct(
|
|
private readonly int $intervalSeconds,
|
|
private readonly int $retryBaseSeconds = 5,
|
|
) {
|
|
}
|
|
|
|
public function secondsUntilNextRun(int $consecutiveFailures): int
|
|
{
|
|
if ($consecutiveFailures < 1) {
|
|
return $this->intervalSeconds;
|
|
}
|
|
|
|
$exponent = min($consecutiveFailures - 1, self::MAX_EXPONENT);
|
|
|
|
return min($this->intervalSeconds, $this->retryBaseSeconds * (2 ** $exponent));
|
|
}
|
|
}
|