Ir para o conteúdo
mcpd
sem daemon
Código

Workers

Um worker é um processo seu, servindo corpos de etapa ao daemon. Esta página é o protocolo que ele fala, e por que cada chamada existe.

O que é um worker

Um worker guarda os corpos das etapas reivindicáveis e pede trabalho ao daemon. Ele roda onde o seu código roda, com os seus imports, o seu pool de conexões e as suas credenciais, e o daemon nunca vê nada disso. O daemon entrega a ele um lease com a entrada já renderizada e as etapas anteriores; o worker roda o corpo e relata como foi. É essa a relação inteira.

Todo SDK traz um. Ele registra os workflows que guarda, faz long polling por leases, manda heartbeat enquanto um corpo roda, e responde a cada recusa pelo motivo dela. O exemplo abaixo é o mesmo programa da página Workflows; esta é a parte que o serve.

servir os corpos

// Serve step bodies for as long as this process lives. The worker registers
// the definition, long-polls for leases, heartbeats while a body runs, and
// answers every 409 by its reason.
const worker = new Worker(mcpd, { concurrency: 8 }).register(support)
void worker.serve()

O protocolo

Cinco chamadas, e um worker que as fala pode ser escrito contra a API HTTP sem SDK nenhum. O worker de cada SDK é um cliente delas e de mais nada.

One lease

The daemon learns that a body is running from started, that the worker is alive from the heartbeats, and how the step ended from complete or fail. Miss the heartbeats and the lease is reclaimed; a later call about it is refused with a reason, never lost.

Worker Daemon the body runs POST /steps/claim long poll, up to 60 s a lease input, prior steps, ids POST /steps/{lease}/started before the body, always heartbeat every 10 s while it runs heartbeat heartbeat POST /steps/{lease}/complete output, state, usage run_status, next or a 409, one of four
ChamadaO que ela faz
POST /v1/api/workers/register Anuncia o worker e grava as definições que ele guarda. A resposta traz lease_ttl_ms e heartbeat_ms, os dois números sobre os quais o resto do protocolo roda.
POST /v1/api/steps/claim Oferece até max etapas prontas para rodar, segurando a requisição aberta por até wait_ms quando não há nenhuma. O daemon segura uma reivindicação por no máximo quatro quintos do tempo limite de requisição dele, 24 segundos no padrão, e responde na hora quando para, então uma reivindicação pode voltar vazia antes de wait_ms: reivindique de novo. Uma reivindicação pode se limitar a uma execução, que é como um script leva a própria execução até o fim.
POST /v1/api/steps/{lease}/started Declara que o corpo está rodando. Antes do corpo, sempre: é o que diz ao daemon a diferença entre oferecido e rodando.
POST /v1/api/steps/{lease}/heartbeat Estende o lease. A única prova que o daemon tem de que um worker está vivo.
POST /v1/api/steps/{lease}/complete, /fail Termina a etapa: a saída, um patch no estado da execução e o uso que o daemon precifica; ou um erro com a classe que decide se ele é repetido.

Leases

Um lease é uma tentativa, segurada por um worker, por um tempo limitado. Ele carrega a entrada renderizada, as etapas anteriores indexadas por id, o estado e a correlação da execução, uma chave que é estável entre as repetições desta etapa, e os ids de trace e de span do span de etapa que o daemon abriu. Um corpo que registra os próprios spans pelo contexto da etapa os coloca sob aquele span, então a chamada de modelo dentro de uma etapa cai no trace da execução na profundidade certa.

Um lease para o qual o worker deixa de mandar heartbeat vence. O daemon marca a tentativa como órfã e, passada uma janela de recuperação, a devolve à fila sob at_least_once ou a faz falhar sob at_most_once. Um worker que volta de uma pausa e manda algo contra um lease já recuperado não é ignorado: ele é avisado.

As quatro recusas

Uma chamada de worker que o daemon não pode aceitar é recusada com um 409 que carrega um de quatro motivos. Eles são fechados, e o worker de cada SDK trata cada um pelo nome, porque um worker que junta todos em um ramo só abandona etapas que já terminou ou relata falha onde houve sucesso.

MotivoO que quer dizerO que o worker faz
already_applied O complete ou o fail anterior deste mesmo lease chegou e a resposta dele se perdeu. Trata como sucesso e não manda mais nada.
fenced Existe uma tentativa mais nova, ou outro lease encerrou este. Abandona o trabalho e não manda mais nada por este lease. Um heartbeat recusado assim também cancela o corpo em execução pelo contexto dele.
run_terminal A execução acabou, por terminar, falhar, estourar o tempo ou ser cancelada. Para, e deixa de reivindicar para aquela execução.
not_started Um complete ou um fail chegou para uma tentativa at_most_once cujo started nunca chegou. Relata isso em alto e bom som como um bug no worker. Os workers dos SDKs não conseguem produzir esse caso; um escrito à mão consegue.

O corpo

Um corpo recebe um contexto, a entrada já renderizada e as etapas anteriores. O contexto é onde ele relata o que o daemon deve guardar: um patch no estado da execução, mesclado no complete com as chaves de primeiro nível substituindo as antigas; os tokens que ele gastou, que o daemon precifica com a mesma tabela com que precifica spans; e um sinal de cancelamento que dispara quando o lease se perde ou a execução acaba, para que um corpo que passe esse sinal à própria E/S pare na hora certa. Os tokens se somam sozinhos: um span que o corpo registra pelo contexto, e todo span abaixo dele, soma o uso ao da etapa quando termina, então um corpo que registra as chamadas de modelo como spans nunca soma à mão, e set_usage é a substituição explícita para um corpo que não registra nenhuma.

Um corpo termina devolvendo a saída dele ou levantando um erro. Levante o erro repetível do SDK para ser repetido dentro do orçamento da etapa; levante uma falha de etapa tipada para nomear qualquer outra classe; qualquer outra coisa é fatal. A mensagem vai para o daemon e aparece no diário. Um stack trace não vai.

Um lease de uma definição, de uma versão ou de uma etapa que o worker não guarda falha como unservable e o corpo nunca roda. A tentativa volta para a fila, para um worker que a guarde, sem gastar o orçamento de repetições, que é o que deixa duas versões de um worker rodarem lado a lado durante uma implantação.

Escalar horizontalmente

Rode quantos workers quiser contra um daemon. Cada um reivindica até a própria concorrência, o daemon entrega cada tentativa a exatamente um deles, e o fence faz com que um lease que dois workers de algum modo seguram termine com um vencedor só. O id de um worker fica gravado em toda tentativa que ele pegou, então o diário diz qual processo rodou uma etapa. Dê um nome a cada processo, ou deixe o SDK derivar um a partir do host.

Uma reivindicação é um long poll, então uma frota ociosa custa ao daemon uma requisição aberta por worker e nenhum tráfego. Enquanto espera, uma reivindicação procura trabalho quando uma execução começa, uma etapa termina, um prazo dispara ou um evento retoma uma espera, e no máximo duas vezes por segundo fora isso. Uma frota ocupada é limitada pela concorrência dos próprios workers, e um corpo que leva uma hora custa um heartbeat a cada dez segundos e mais nada.

Chaves

As chamadas de worker pedem o escopo worker; iniciar, sinalizar e cancelar execuções pedem execute. Os dois recaem sobre as chaves de escrita quando não estão definidos, e uma chave de administração cobre ambos. Um worker que só serve etapas não precisa de uma chave capaz de iniciar uma execução, e um webhook que só publica eventos não precisa de uma chave capaz de reivindicar uma etapa. Veja Configuração para as opções.

As chamadas de worker não são registradas no log de auditoria, embora um lease carregue a entrada da etapa e as saídas das etapas anteriores. Elas são um laço de máquina que reivindica muitas vezes por minuto, e uma linha por reivindicação enterraria as leituras que uma pessoa fez. Ler uma execução de volta pelos endpoints de execução é registrado; veja Segurança.

A seguir

  • Iniciar e acompanhar uma execução, e responder a uma espera: Execuções.
  • A definição que o worker serve: Workflows.
  • Todos os endpoints do protocolo de worker, gerados a partir da especificação: API HTTP, workers.