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_bytes des Servers begrenzt (angewendet sowohl auf die maximale Nachrichten- als auch Frame-Größe). Eine Nachricht, die dieses Limit überschreitet, etwa ein completed-Payload, das große stdout-, 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, optionaler Update-channel und optionale Binary-checksum)
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.

Auf dem stable-Update-Kanal wird ein update gesendet, wenn die Runner-Version von der Server-Version abweicht. Auf dem canary-Update-Kanal ist version gleich canary, und ein update wird gesendet, wenn die vom Runner selbst gemeldete Prüfsumme des Binaries vom veröffentlichten rollierenden Canary-Build abweicht.


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.

API ServerRunnerAPI ServerRunneralt[Job available][Poll timeout][Update available]loop[Idle / polling]loop[Benchmark executes]Connect with runner keyConnectedready (os, arch, version)job (Spec, config, OCI token)no_jobupdate (version, url, checksum)runningackheartbeatack (or cancel)completed (job, results)ack

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
claimed unknown Der Server verliert den Kontakt zum Runner
claimed canceled Ein Benutzer bricht den Job ab
running completed Der Runner sendet completed
running failed Der Runner sendet failed
running unknown Der Server verliert den Kontakt zum Runner
running canceled Ein Benutzer bricht den Job ab, oder das harte Job-Timeout wird überschritten
unknown completed Der Runner sendet completed
unknown failed Der Runner sendet failed
unknown canceled Ein Benutzer bricht den Job ab, oder das harte Job-Timeout wird überschritten
completed processed Der Server verarbeitet die Ergebnisse erfolgreich
completed failed Der Server kann die Ergebnisse nicht verarbeiten
completed unknown Beim Start stellt der Server fest, dass die gespeicherten Ergebnisse verloren gingen

processed, failed und canceled sind terminal: Ein failed- oder canceled-Job ändert seinen Zustand nie wieder. completed ist quasi-terminal: completed geht in processed über, sobald die Ergebnisse geparst sind, in failed, wenn sie nicht verarbeitet werden können, oder in unknown, wenn der Server beim Start feststellt, dass sie verloren gingen. unknown bedeutet, dass der Server den Kontakt zum Runner verloren hat und das Ergebnis noch nicht kennt. Der Zustand löst sich auf, sobald sich der Runner wieder meldet, mit dem Ergebnis, das er sendet, oder als canceled, sobald die Frist des Jobs abläuft. Jeder Übergang verwendet einen Statusfilter bei seinem Datenbank-Update, sodass ein Job, der gleichzeitig geändert wurde, neu eingelesen statt überschrieben wird.

runner claims

user cancels

running

failed

lost contact

user cancels

completed

failed

lost contact

cancel / hard timeout

completed

failed

cancel / hard timeout

results parsed

processing failed

results lost

pending

claimed

canceled

running

failed

unknown

completed

processed


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 unknown (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. Das Limit zählt ab dem Start des Jobs, oder ab seiner Beanspruchung, falls er nie gestartet ist, und gilt auch für einen unknown-Job.

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 unknown markiert, oder als canceled, falls er das harte Timeout überschritten hatte. Einen unknown-Job prüft der Server bei Ablauf seiner Frist erneut. Beim Start markiert der Server außerdem verwaiste claimed-Jobs als unknown, plant Timeouts für in Bearbeitung befindliche und unknown-Jobs neu und verarbeitet completed-Jobs erneut, deren Ergebnisse gespeichert, aber noch nicht geparst wurden; ein completed-Job, dessen Ergebnisse verloren gingen, wird als unknown markiert, damit der Runner sie erneut senden kann.

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 Ergebnis eines Runner löst einen unknown-Job auf. Ein failed- oder canceled-Job ändert seinen Zustand nie wieder: Ein verspätetes Ergebnis wird bestätigt, damit der Runner es nicht erneut sendet, und verworfen.

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.



Published: Fri, June 19, 2026 at 8:00:00 AM UTC | Last Updated: Wed, September 23, 2026 at 12:00:00 AM UTC