Bencher Protocolo Runner


El binario runner y el servidor API se comunican a través de una única conexión WebSocket. Esta referencia describe ese protocolo: los mensajes intercambiados, el ciclo de vida del Job que impulsan, y cómo los tiempos de espera y la reconexión evitan que los Jobs se queden bloqueados. Es el complemento detallado de la guía Runners Autoalojados.

No necesitas conocer nada de esto para operar un Runner con runner up; se proporciona por transparencia y para cualquiera que construya herramientas en torno a la API.


Conexión

Un Runner mantiene una única conexión WebSocket al servidor API durante todo su ciclo de vida. La misma conexión maneja tanto la asignación de Jobs como la ejecución de Jobs, y permanece abierta a lo largo de muchos Jobs, evitando una reconexión y un handshake para cada uno.

  • Endpoint: /v0/runners/{runner}/channel
  • Autenticación: la clave del Runner se envía como cabecera Authorization: Bearer bencher_runner_<key> cuando se establece la conexión.
  • Tamaño de mensaje: cada mensaje está limitado por el límite request_body_max_bytes del servidor (aplicado tanto al tamaño máximo del mensaje como del frame). Un mensaje que excede este límite, como un payload completed que transporta stdout, stderr o archivos de salida grandes, se rechaza a nivel del protocolo WebSocket.

Cada mensaje es un objeto JSON con un campo event que identifica su tipo.


Mensajes del Runner

Mensajes enviados desde el Runner al servidor.

EventDescripciónPayload
readyEl Runner está inactivo y solicitando un Jobpoll_timeout opcional (1-900s) y metadatos del runner (os, arch, version)
runningLa configuración del Job está completa y el benchmark está comenzandoNinguno
heartbeatSeñal periódica de vida (aproximadamente una vez por segundo)Ninguno
completedEl benchmark se completó correctamentejob (UUID del Job) y results (salida por iteración)
failedEl benchmark fallójob (UUID del Job), results, y error
canceledConfirma una cancelación desde el servidorjob (UUID del Job)

Mensajes del Servidor

Mensajes enviados desde el servidor al Runner.

EventDescripciónPayload
ackConfirma un mensaje recibidojob opcional (UUID del Job)
jobAsigna un Job reclamado al RunnerEl Job reclamado: su Spec, la configuración del Job, y un token de descarga OCI de corta duración
no_jobEl tiempo de espera del poll expiró sin Job disponibleNinguno
cancelEl Job fue cancelado o agotó su tiempo; detén la ejecuciónNinguno
updateEl Runner debe auto-actualizarse a una nueva versiónversion, url (URL de descarga), y checksum (SHA-256)

El token de descarga OCI en un mensaje job se genera cuando se reclama el Job y nunca se almacena. Está limitado al único proyecto al que pertenece el Job, es solo de descarga, y es de corta duración, de modo que un Runner comprometido solo puede descargar Images del proyecto del Job que reclamó.


Flujo de Conexión

Después de conectarse, el Runner entra en un bucle de sondeo inactivo, enviando ready hasta que el servidor asigna un job (o devuelve no_job cuando el poll agota su tiempo, o update cuando hay una nueva versión disponible). Una vez que tiene un Job, el Runner envía running, transmite mensajes heartbeat mientras el benchmark se ejecuta, y termina con un mensaje terminal completed o failed. El servidor confirma cada mensaje con ack, y la conexión permanece abierta para que el Runner vuelva al bucle inactivo para el siguiente Job.

Si un Job es cancelado, el servidor responde a un heartbeat con cancel. El Runner detiene el benchmark y responde con canceled, lo cual el servidor confirma.

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

Ciclo de Vida del Job

Cada Job recorre un conjunto fijo de estados a medida que es reclamado, ejecutado y procesado.

DesdeHaciaDisparador
pendingclaimedUn Runner reclama el Job
pendingcanceledUn usuario cancela el Job
claimedrunningEl Runner envía running
claimedfailedEl Runner envía failed, o el heartbeat agota su tiempo
claimedcanceledUn usuario cancela el Job
runningcompletedEl Runner envía completed
runningfailedEl Runner envía failed, o el heartbeat agota su tiempo
runningcanceledUn usuario cancela el Job, o se excede el tiempo de espera duro del Job
completedprocessedEl servidor procesa correctamente los resultados
failedcompletedEl Runner reenvía completed, anulando un fallo por timeout de heartbeat

processed y canceled son terminales. completed y failed son cuasi-terminales: completed pasa a processed una vez que se analizan los resultados, y failed pasa a completed si el Runner reenvía completed. Cada transición usa un filtro de estado en su actualización de base de datos, de modo que un Job que fue modificado de forma concurrente se vuelve a leer en lugar de sobrescribirse.

runner claims

user cancels

running

failed / timeout

user cancels

completed

failed / timeout

cancel / hard timeout

results recovered

results parsed

pending

claimed

canceled

running

failed

completed

processed


Tiempos de Espera y Recuperación

Tres mecanismos complementarios aseguran que un Job nunca se quede bloqueado, incluso si un Runner se cae o pierde su conexión.

Timeout de Heartbeat

Mientras la conexión está abierta, un timeout de lectura detecta un Runner que está conectado pero en silencio. Solo los mensajes válidos del protocolo reinician el temporizador; el JSON inválido, los frames ping/pong, y los mensajes binarios no lo hacen. Al agotarse el tiempo, un Job que ha corrido más tiempo que su timeout más un periodo de gracia se marca como canceled, y de lo contrario se marca como failed (se perdió el contacto con el Runner).

Timeout Duro del Job

El servidor impone una duración máxima de ejecución dura independiente del comportamiento del Runner, de modo que un Runner defectuoso o comprometido no pueda correr indefinidamente enviando heartbeats. Cuando se excede el límite (el timeout del Job más un periodo de gracia), el Job se marca como canceled y el Runner recibe un mensaje cancel.

Recuperación de Desconexión

Si la conexión se cae mientras un Job sigue en curso, el servidor programa una comprobación tras el timeout de heartbeat. Si el Runner se ha reconectado y reanudado los heartbeats, el Job continúa; de lo contrario, el Job se marca como failed, o canceled si había excedido el timeout duro. Al arrancar, el servidor también recupera Jobs claimed huérfanos, reprograma los timeouts de los Jobs en curso, y vuelve a procesar los Jobs completed cuyos resultados se almacenaron pero aún no se analizaron.

Reconexión y Entrega de Resultados

La reconexión es compatible e idempotente. Reenviar running para un Job que ya está en ejecución solo refresca su señal de vida, y reenviar un mensaje terminal completed, failed o canceled siempre es seguro. Los mensajes terminales transportan el UUID del Job y reciben un ack; si la conexión se cae antes de que llegue el ack, el Runner almacena el resultado y lo reenvía en la siguiente conexión antes de pasar a inactivo. El resultado completed real de un Runner puede incluso anular un estado failed por timeout de heartbeat.

Sin Reintento Automático

Un Job failed no se reintenta automáticamente. Un benchmark fallido es una señal, no un error que ocultar, así que volver a ejecutarlo queda en tus manos.


Salida del Job

Cuando un Runner envía completed o failed, la salida completa se almacena en el mismo backend de almacenamiento OCI usado para las Images de contenedor, en la ruta {project}/output/v0/jobs/{job}.

La salida almacenada contiene un array results por iteración y, en caso de fallo, una cadena error. Cada iteración registra su exit_code, stdout, stderr, y un mapa de cualquier archivo de salida recolectado con su contenido. Después de que la salida se almacena, el servidor ejecuta el adaptador del harness de benchmark sobre los resultados para analizar Metrics y Alerts en el Report, transicionando el Job a processed.

La salida se devuelve cuando se consulta un Job con la API GET /v0/projects/{project}/jobs/{job}. El mismo límite request_body_max_bytes que acota los mensajes WebSocket limita el tamaño de la salida que un Runner puede entregar.



Published: Fri, June 19, 2026 at 8:00:00 AM UTC