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_bytesdel servidor (aplicado tanto al tamaño máximo del mensaje como del frame). Un mensaje que excede este límite, como un payloadcompletedque transportastdout,stderro 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.
| Event | Descripción | Payload |
|---|---|---|
ready | El Runner está inactivo y solicitando un Job | poll_timeout opcional (1-900s) y metadatos del runner (os, arch, version) |
running | La configuración del Job está completa y el benchmark está comenzando | Ninguno |
heartbeat | Señal periódica de vida (aproximadamente una vez por segundo) | Ninguno |
completed | El benchmark se completó correctamente | job (UUID del Job) y results (salida por iteración) |
failed | El benchmark falló | job (UUID del Job), results, y error |
canceled | Confirma una cancelación desde el servidor | job (UUID del Job) |
Mensajes del Servidor
Mensajes enviados desde el servidor al Runner.
| Event | Descripción | Payload |
|---|---|---|
ack | Confirma un mensaje recibido | job opcional (UUID del Job) |
job | Asigna un Job reclamado al Runner | El Job reclamado: su Spec, la configuración del Job, y un token de descarga OCI de corta duración |
no_job | El tiempo de espera del poll expiró sin Job disponible | Ninguno |
cancel | El Job fue cancelado o agotó su tiempo; detén la ejecución | Ninguno |
update | El Runner debe auto-actualizarse a una nueva versión | version, 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.
Ciclo de Vida del Job
Cada Job recorre un conjunto fijo de estados a medida que es reclamado, ejecutado y procesado.
| Desde | Hacia | Disparador |
|---|---|---|
| pending | claimed | Un Runner reclama el Job |
| pending | canceled | Un usuario cancela el Job |
| claimed | running | El Runner envía running |
| claimed | failed | El Runner envía failed, o el heartbeat agota su tiempo |
| claimed | canceled | Un usuario cancela el Job |
| running | completed | El Runner envía completed |
| running | failed | El Runner envía failed, o el heartbeat agota su tiempo |
| running | canceled | Un usuario cancela el Job, o se excede el tiempo de espera duro del Job |
| completed | processed | El servidor procesa correctamente los resultados |
| failed | completed | El 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.
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.