Bencher Runner Protocol
Das runner-Binary und der API-Server kommunizieren über eine einzige
WebSocket-Verbindung. Diese Referenz beschreibt dieses Protokoll: die ausgetauschten Nachrichten, den
Job-Lebenszyklus, den sie antreiben, und wie Timeouts und Wiederverbindung verhindern, dass Jobs hängen bleiben. Sie ist die ausführliche
Ergänzung zum Leitfaden Self-Hosted Runners.
Sie müssen nichts davon wissen, um einen Runner mit runner up zu betreiben;
es wird zur Transparenz und für alle bereitgestellt, die Tooling rund um die API entwickeln.
Verbindung
Ein Runner unterhält für seinen gesamten Lebenszyklus eine einzige WebSocket-Verbindung zum API-Server. Dieselbe Verbindung bearbeitet sowohl die Job-Zuweisung als auch die Job-Ausführung, und sie bleibt über viele Jobs hinweg offen, wodurch ein erneuter Verbindungsaufbau und Handshake für jeden einzelnen vermieden wird.
- Endpunkt:
/v0/runners/{runner}/channel - Authentifizierung: Der Runner-Schlüssel wird beim Aufbau der Verbindung als
Authorization: Bearer bencher_runner_<key>-Header gesendet. - Nachrichtengröße: Jede Nachricht ist durch das Limit
request_body_max_bytesdes Servers begrenzt (angewendet sowohl auf die maximale Nachrichten- als auch Frame-Größe). Eine Nachricht, die dieses Limit überschreitet, etwa eincompleted-Payload, das großestdout-,stderr- oder Ausgabedateien transportiert, wird auf der Ebene des WebSocket-Protokolls abgelehnt.
Jede Nachricht ist ein JSON-Objekt mit einem event-Feld, das ihren Typ angibt.
Runner-Nachrichten
Nachrichten, die vom Runner an den Server gesendet werden.
| Event | Beschreibung | Payload |
|---|---|---|
ready | Der Runner ist im Leerlauf und fordert einen Job an | Optionales poll_timeout (1-900s) und runner-Metadaten (os, arch, version) |
running | Job-Setup ist abgeschlossen und der Benchmark startet | Keine |
heartbeat | Periodisches Lebenszeichen (etwa einmal pro Sekunde) | Keine |
completed | Der Benchmark wurde erfolgreich abgeschlossen | job (Job-UUID) und results (Ausgabe pro Iteration) |
failed | Der Benchmark ist fehlgeschlagen | job (Job-UUID), results und error |
canceled | Bestätigt eine Abbruchanforderung vom Server | job (Job-UUID) |
Server-Nachrichten
Nachrichten, die vom Server an den Runner gesendet werden.
| Event | Beschreibung | Payload |
|---|---|---|
ack | Bestätigt eine empfangene Nachricht | Optionales job (Job-UUID) |
job | Weist dem Runner einen beanspruchten Job zu | Der beanspruchte Job: seine Spec, Job-Konfiguration und ein kurzlebiges OCI-Pull-Token |
no_job | Das Poll-Timeout ist abgelaufen, ohne dass ein Job verfügbar war | Keine |
cancel | Der Job wurde abgebrochen oder ist abgelaufen; Ausführung stoppen | Keine |
update | Der Runner sollte sich selbst auf eine neue Version aktualisieren | version, url (Download-URL) und checksum (SHA-256) |
Das OCI-Pull-Token in einer job-Nachricht wird beim Beanspruchen des Jobs generiert und niemals gespeichert.
Es ist auf das einzelne Projekt beschränkt, zu dem der Job gehört, ist nur zum Herunterladen (Pull) gedacht und ist kurzlebig,
sodass ein kompromittierter Runner nur Images für das Projekt des von ihm beanspruchten Jobs herunterladen kann.
Verbindungsablauf
Nach dem Verbinden tritt der Runner in eine Polling-Schleife im Leerlauf ein
und sendet ready, bis der Server einen job zuweist
(oder no_job zurückgibt, wenn das Poll-Timeout abläuft, oder update, wenn eine neue Version verfügbar ist).
Sobald er einen Job hat, sendet der Runner running,
streamt heartbeat-Nachrichten, während der Benchmark ausgeführt wird,
und schließt mit einer terminalen completed- oder failed-Nachricht ab.
Der Server bestätigt jede Nachricht mit ack,
und die Verbindung bleibt offen, sodass der Runner für den nächsten Job in die Leerlaufschleife zurückkehrt.
Wird ein Job abgebrochen, antwortet der Server auf ein heartbeat mit cancel.
Der Runner stoppt den Benchmark und antwortet mit canceled, was der Server bestätigt.
Job-Lebenszyklus
Jeder Job durchläuft eine feste Menge von Zuständen, während er beansprucht, ausgeführt und verarbeitet wird.
| Von | Nach | Auslöser |
|---|---|---|
| pending | claimed | Ein Runner beansprucht den Job |
| pending | canceled | Ein Benutzer bricht den Job ab |
| claimed | running | Der Runner sendet running |
| claimed | failed | Der Runner sendet failed, oder der Heartbeat läuft ab |
| claimed | canceled | Ein Benutzer bricht den Job ab |
| running | completed | Der Runner sendet completed |
| running | failed | Der Runner sendet failed, oder der Heartbeat läuft ab |
| running | canceled | Ein Benutzer bricht den Job ab, oder das harte Job-Timeout wird überschritten |
| completed | processed | Der Server verarbeitet die Ergebnisse erfolgreich |
| failed | completed | Der Runner sendet completed erneut und überschreibt einen Fehlschlag durch Heartbeat-Timeout |
processed und canceled sind terminal.
completed und failed sind quasi-terminal:
completed geht in processed über, sobald die Ergebnisse geparst sind,
und failed geht in completed über, wenn der Runner completed erneut sendet.
Jeder Übergang verwendet einen Statusfilter bei seinem Datenbank-Update,
sodass ein Job, der gleichzeitig geändert wurde, neu eingelesen statt überschrieben wird.
Timeouts & Wiederherstellung
Drei sich ergänzende Mechanismen stellen sicher, dass ein Job niemals hängen bleibt, selbst wenn ein Runner abstürzt oder seine Verbindung verliert.
Heartbeat-Timeout
Während die Verbindung offen ist, erkennt ein Read-Timeout einen Runner, der verbunden, aber stumm ist.
Nur gültige Protokollnachrichten setzen den Timer zurück;
ungültiges JSON, Ping/Pong-Frames und Binärnachrichten tun dies nicht.
Bei einem Timeout wird ein Job, der länger als sein Timeout plus eine Karenzzeit gelaufen ist, als canceled markiert,
andernfalls als failed (der Kontakt zum Runner ging verloren).
Hartes Job-Timeout
Der Server erzwingt unabhängig vom Verhalten des Runner eine harte maximale Ausführungsdauer,
sodass ein fehlerhafter oder kompromittierter Runner nicht durch das Senden von Heartbeats unbegrenzt laufen kann.
Wird das Limit (das Job-Timeout plus eine Karenzzeit) überschritten,
wird der Job als canceled markiert und der Runner erhält eine cancel-Nachricht.
Wiederherstellung bei Verbindungsabbruch
Bricht die Verbindung ab, während ein Job noch in Bearbeitung ist,
plant der Server nach dem Heartbeat-Timeout eine Prüfung ein.
Hat sich der Runner erneut verbunden und die Heartbeats wieder aufgenommen, läuft der Job weiter;
andernfalls wird der Job als failed markiert, oder als canceled, falls er das harte Timeout überschritten hatte.
Beim Start stellt der Server außerdem verwaiste claimed-Jobs wieder her,
plant Timeouts für in Bearbeitung befindliche Jobs neu
und verarbeitet completed-Jobs erneut, deren Ergebnisse gespeichert, aber noch nicht geparst wurden.
Wiederverbindung & Ergebniszustellung
Wiederverbindung wird unterstützt und ist idempotent.
Das erneute Senden von running für einen bereits laufenden Job aktualisiert nur dessen Lebenszeichen,
und das erneute Senden einer terminalen completed-, failed- oder canceled-Nachricht ist immer sicher.
Terminale Nachrichten tragen die Job-UUID und erhalten ein ack;
bricht die Verbindung ab, bevor das ack eintrifft,
speichert der Runner das Ergebnis und sendet es bei der nächsten Verbindung erneut, bevor er in den Leerlauf geht.
Das tatsächliche completed-Ergebnis eines Runner kann sogar einen failed-Status durch Heartbeat-Timeout überschreiben.
Kein automatischer erneuter Versuch
Ein failed-Job wird nicht automatisch wiederholt.
Ein fehlgeschlagener Benchmark ist ein Signal, kein zu verbergender Fehler,
sodass das erneute Ausführen Ihnen überlassen bleibt.
Job-Ausgabe
Wenn ein Runner completed oder failed sendet,
wird die vollständige Ausgabe im selben OCI-Storage-Backend gespeichert, das auch für Container-Images verwendet wird,
unter dem Pfad {project}/output/v0/jobs/{job}.
Die gespeicherte Ausgabe enthält ein results-Array pro Iteration und, im Fehlerfall, einen error-String.
Jede Iteration zeichnet ihren exit_code, stdout, stderr
und eine Zuordnung aller erfassten Ausgabedateien zu ihren Inhalten auf.
Nachdem die Ausgabe gespeichert ist, führt der Server den Benchmark-Harness-Adapter auf den Ergebnissen aus,
um Metrics und Alerts in den Report zu parsen, wodurch der Job in processed übergeht.
Die Ausgabe wird zurückgegeben, wenn ein Job mit der GET /v0/projects/{project}/jobs/{job}-API abgefragt wird.
Dasselbe request_body_max_bytes-Limit, das WebSocket-Nachrichten begrenzt,
deckelt die Größe der Ausgabe, die ein Runner zustellen kann.