Bencher Runner Protocol


O binário runner e o servidor de API se comunicam por uma única conexão WebSocket. Esta referência descreve esse protocolo: as mensagens trocadas, o ciclo de vida do Job que elas conduzem e como os timeouts e a reconexão evitam que os Jobs fiquem travados. É a companhia detalhada do guia Self-Hosted Runners.

Você não precisa saber nada disso para operar um Runner com runner up; isso é fornecido por transparência e para qualquer pessoa que esteja construindo ferramentas em torno da API.


Conexão

Um Runner mantém uma única conexão WebSocket ao servidor de API durante todo o seu ciclo de vida. A mesma conexão trata tanto a atribuição quanto a execução do Job, e permanece aberta entre muitos Jobs, evitando uma reconexão e handshake para cada um.

  • Endpoint: /v0/runners/{runner}/channel
  • Autenticação: a chave do Runner é enviada como um cabeçalho Authorization: Bearer bencher_runner_<key> quando a conexão é estabelecida.
  • Tamanho da mensagem: cada mensagem é limitada pelo limite request_body_max_bytes do servidor (aplicado tanto ao tamanho máximo da mensagem quanto ao do frame). Uma mensagem que excede esse limite, como um payload completed carregando grandes stdout, stderr ou arquivos de saída, é rejeitada no nível do protocolo WebSocket.

Toda mensagem é um objeto JSON com um campo event que identifica seu tipo.


Mensagens do Runner

Mensagens enviadas do Runner para o servidor.

EventDescriçãoPayload
readyO Runner está ocioso e solicitando um Jobpoll_timeout opcional (1-900s) e metadados do runner (os, arch, version)
runningA configuração do Job está concluída e o benchmark está iniciandoNenhum
heartbeatSinal periódico de atividade (cerca de uma vez por segundo)Nenhum
completedO benchmark foi concluído com sucessojob (UUID do Job) e results (saída por iteração)
failedO benchmark falhoujob (UUID do Job), results e error
canceledConfirma um cancelamento vindo do servidorjob (UUID do Job)

Mensagens do Servidor

Mensagens enviadas do servidor para o Runner.

EventDescriçãoPayload
ackConfirma uma mensagem recebidajob opcional (UUID do Job)
jobAtribui um Job reivindicado ao RunnerO Job reivindicado: sua Spec, a configuração do Job e um token de pull OCI de curta duração
no_jobO timeout de polling expirou sem nenhum Job disponívelNenhum
cancelO Job foi cancelado ou expirou; pare a execuçãoNenhum
updateO Runner deve se auto-atualizar para uma nova versãoversion, url (URL de download) e checksum (SHA-256)

O token de pull OCI em uma mensagem job é gerado quando o Job é reivindicado e nunca é armazenado. Ele é restrito ao único projeto ao qual o Job pertence, é somente para pull e é de curta duração, de modo que um Runner comprometido só pode baixar Images do projeto do Job que reivindicou.


Fluxo de Conexão

Após conectar, o Runner entra em um loop de polling ocioso, enviando ready até que o servidor atribua um job (ou retorne no_job quando o polling expira, ou update quando uma nova versão está disponível). Assim que tem um Job, o Runner envia running, transmite mensagens heartbeat enquanto o benchmark executa e finaliza com uma mensagem terminal completed ou failed. O servidor confirma cada mensagem com ack, e a conexão permanece aberta para que o Runner retorne ao loop ocioso para o próximo Job.

Se um Job é cancelado, o servidor responde a um heartbeat com cancel. O Runner para o benchmark e responde com canceled, que o 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 do Job

Cada Job passa por um conjunto fixo de estados conforme é reivindicado, executado e processado.

DeParaGatilho
pendingclaimedUm Runner reivindica o Job
pendingcanceledUm usuário cancela o Job
claimedrunningO Runner envia running
claimedfailedO Runner envia failed, ou o heartbeat expira
claimedcanceledUm usuário cancela o Job
runningcompletedO Runner envia completed
runningfailedO Runner envia failed, ou o heartbeat expira
runningcanceledUm usuário cancela o Job, ou o timeout rígido do Job é excedido
completedprocessedO servidor processa os resultados com sucesso
failedcompletedO Runner reenvia completed, sobrepondo uma falha por timeout de heartbeat

processed e canceled são terminais. completed e failed são quase-terminais: completed transiciona para processed assim que os resultados são analisados, e failed transiciona para completed se o Runner reenviar completed. Toda transição usa um filtro de status em sua atualização no banco de dados, de modo que um Job que foi modificado concorrentemente é relido em vez de sobrescrito.

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


Timeouts & Recuperação

Três mecanismos complementares garantem que um Job nunca fique travado, mesmo que um Runner falhe ou perca sua conexão.

Timeout de Heartbeat

Enquanto a conexão está aberta, um timeout de leitura detecta um Runner que está conectado mas silencioso. Apenas mensagens de protocolo válidas reiniciam o temporizador; JSON inválido, frames de ping/pong e mensagens binárias não. Em caso de timeout, um Job que rodou por mais tempo do que seu timeout mais um período de carência é marcado como canceled, e caso contrário marcado como failed (o contato com o Runner foi perdido).

Timeout Rígido do Job

O servidor impõe uma duração máxima de execução rígida, independente do comportamento do Runner, de modo que um Runner com bug ou comprometido não possa rodar indefinidamente enviando heartbeats. Quando o limite (o timeout do Job mais um período de carência) é excedido, o Job é marcado como canceled e o Runner recebe uma mensagem cancel.

Recuperação de Desconexão

Se a conexão cai enquanto um Job ainda está em andamento, o servidor agenda uma verificação após o timeout de heartbeat. Se o Runner reconectou e retomou os heartbeats, o Job continua; caso contrário, o Job é marcado como failed, ou canceled se tiver excedido o timeout rígido. Na inicialização, o servidor também recupera Jobs claimed órfãos, reagenda timeouts para Jobs em andamento e reprocessa Jobs completed cujos resultados foram armazenados mas ainda não analisados.

Reconexão & Entrega de Resultados

A reconexão é suportada e idempotente. Reenviar running para um Job já em execução apenas atualiza sua atividade, e reenviar uma mensagem terminal completed, failed ou canceled é sempre seguro. Mensagens terminais carregam o UUID do Job e recebem um ack; se a conexão cair antes de o ack chegar, o Runner armazena o resultado e o reenvia na próxima conexão antes de ficar ocioso. O resultado completed real de um Runner pode até sobrepor um status failed por timeout de heartbeat.

Sem Retentativa Automática

Um Job failed não é retentado automaticamente. Um benchmark que falhou é um sinal, não um erro a ser escondido, então reexecutá-lo fica a seu critério.


Saída do Job

Quando um Runner envia completed ou failed, a saída completa é armazenada no mesmo backend de armazenamento OCI usado para as Images de contêiner, no caminho {project}/output/v0/jobs/{job}.

A saída armazenada contém um array results por iteração e, em caso de falha, uma string error. Cada iteração registra seu exit_code, stdout, stderr e um mapa de quaisquer arquivos de saída coletados para seus conteúdos. Após a saída ser armazenada, o servidor executa o adaptador de harness de benchmark sobre os resultados para analisar Metrics e Alerts no Report, transicionando o Job para processed.

A saída é retornada quando um Job é consultado com a API GET /v0/projects/{project}/jobs/{job}. O mesmo limite request_body_max_bytes que limita as mensagens WebSocket restringe o tamanho da saída que um Runner pode entregar.



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