Workers permanentes podem tornar seu build mais rápido. Se você tem ações repetidas no build com alto custo de inicialização ou que se beneficiar do armazenamento em cache de várias ações, implemente seu próprio mecanismo worker para executar essas ações.
O servidor do Bazel se comunica com o worker usando stdin
/stdout
. Ela
oferece suporte ao uso de buffers de protocolo ou strings JSON.
A implementação do worker tem duas partes:
- O worker.
- A regra que usa o worker.
Tornar o worker
Os workers permanentes cumprem alguns requisitos:
- Leitura
WorkRequests
do
stdin
. - Ele escreve
WorkResponses
(e apenas
WorkResponse
s) aostdout
. - Ela aceita a sinalização
--persistent_worker
. O wrapper precisa reconhecer Sinalização de linha de comando--persistent_worker
e só se torna persistente se a sinalização é transmitida. Caso contrário, ela precisa fazer uma compilação única e sair.
Se o seu programa cumprir esses requisitos, ele poderá ser usado como um serviço trabalhador!
Solicitações de trabalho
Um WorkRequest
contém uma lista de argumentos para o worker, uma lista de
pares de caminho e resumo que representam as entradas que o worker pode acessar (isso não é
aplicada, mas é possível usar essas informações para armazenamento em cache) e um ID de solicitação, que é 0
para workers Singleplex.
OBSERVAÇÃO: embora a especificação do buffer de protocolo use "snake case" (request_id
),
o protocolo JSON usa letras concatenadas (requestId
). Este documento usa letras concatenadas
nos exemplos JSON, mas snake-case ao falar sobre o campo, independentemente
protocolo.
{
"arguments" : ["--some_argument"],
"inputs" : [
{ "path": "/path/to/my/file/1", "digest": "fdk3e2ml23d"},
{ "path": "/path/to/my/file/2", "digest": "1fwqd4qdd" }
],
"requestId" : 12
}
O campo verbosity
opcional pode ser usado para solicitar outras saídas de depuração
do worker. Cabe inteiramente ao worker o que e como produzir. Alta
valores indicam uma saída mais detalhada. Transmitir a sinalização --worker_verbose
para
O Bazel define o campo verbosity
como 10, mas é possível usar valores menores ou maiores.
manualmente para diferentes quantidades de saída.
O campo sandbox_dir
opcional é usado apenas por workers compatíveis
sandbox de multiplex.
Respostas de trabalho
Uma WorkResponse
contém um ID de solicitação, um código de saída zero ou diferente de zero e uma
mensagem de saída que descreve todos os erros encontrados no processamento ou na execução
da solicitação. Um worker precisa capturar a stdout
e a stderr
de qualquer ferramenta
chamadas e relatá-las pela WorkResponse
. Gravando-o em stdout
de
o processo do worker não é seguro porque interfere no protocolo do worker.
Gravá-lo no stderr
do processo do worker é seguro, mas o resultado é
coletados em um arquivo de registro por worker, em vez de atribuídos a ações individuais.
{
"exitCode" : 1,
"output" : "Action failed with the following message:\nCould not find input
file \"/path/to/my/file/1\"",
"requestId" : 12
}
De acordo com a norma para protobufs, todos os campos são opcionais. No entanto, o Bazel requer
o WorkRequest
e o WorkResponse
correspondente, para ter a mesma solicitação
id , portanto, o ID da solicitação deverá ser especificado se for diferente de zero. Este é um endereço de e-mail
WorkResponse
:
{
"requestId" : 12,
}
Um request_id
de 0 indica um "singleplex". , usada quando esta solicitação
não podem ser processados em paralelo com outras solicitações. O servidor garante que
um determinado worker recebe solicitações com apenas request_id
0 ou apenas
request_id
maior que zero. As solicitações Singleplex são enviadas em série para
exemplo se o servidor não enviar outra solicitação até que tenha recebido uma
(exceto para solicitações de cancelamento, veja abaixo).
Observações
- Cada buffer de protocolo é precedido pelo tamanho no formato
varint
(consulteMessageLite.writeDelimitedTo()
. - As solicitações e respostas JSON não são precedidas por um indicador de tamanho.
- As solicitações JSON mantêm a mesma estrutura que o protobuf, mas usam as JSON e use letras concatenadas para todos os nomes de campo.
- Para manter as mesmas propriedades de compatibilidade com versões anteriores e posteriores como protobuf, os workers JSON precisam tolerar campos desconhecidos nessas mensagens, e usar os padrões protobuf para valores ausentes.
- O Bazel armazena solicitações como protobufs e as converte em JSON usando: Formato JSON do protobuf
Cancelamento
Os workers podem permitir que as solicitações de trabalho sejam canceladas antes de serem concluídas.
Isso é útil principalmente na execução dinâmica, em que os
pode ser interrompida regularmente por uma execução remota mais rápida. Para permitir
cancelamento, adicione supports-worker-cancellation: 1
ao
execution-requirements
(veja abaixo) e defina o
--experimental_worker_cancellation
.
Uma solicitação de cancelamento é um WorkRequest
com o campo cancel
definido (e
Da mesma forma, uma resposta de cancelamento é uma WorkResponse
com o was_cancelled
ou campo definido). O único outro campo que precisa estar em uma solicitação ou cancelamento
resposta é request_id
, indicando qual solicitação deve ser cancelada. O request_id
o campo será 0 para workers singleplex ou o request_id
diferente de 0 de um valor
WorkRequest
enviado para workers multiplex. O servidor pode enviar solicitações de cancelamento
solicitações que o worker já respondeu. Nesse caso, o botão de
solicitação deve ser ignorada.
Cada mensagem WorkRequest
que não é de cancelamento precisa ser respondida exatamente uma vez, seja ela
mas não foi cancelado. Depois que o servidor enviar uma solicitação de cancelamento, o worker poderá
responda com uma WorkResponse
com a request_id
definida e a was_cancelled
.
definido como verdadeiro. O envio de um WorkResponse
normal também é aceito, mas a
Os campos output
e exit_code
serão ignorados.
Depois que uma resposta é enviada para uma WorkRequest
, o worker não pode tocar na
no diretório de trabalho. O servidor é sem custo financeiro para limpar os arquivos,
incluindo arquivos temporários.
Criar a regra que usa o worker
Você também precisará criar uma regra que gere ações a serem realizadas pelo worker. Criar uma regra Starlark que usa um worker é semelhante criar qualquer outra regra.
Além disso, a regra precisa conter uma referência ao próprio worker. há alguns requisitos para as ações que ela produz.
Como se referir ao worker
A regra que usa o worker precisa conter um campo que se refira ao worker
portanto, você precisa criar uma instância de uma regra \*\_binary
para definir
seu worker. Se o nome do worker for MyWorker.Java
, essa pode ser a
regra associada:
java_binary(
name = "worker",
srcs = ["MyWorker.Java"],
)
Isso cria o "worker" rótulo, que se refere ao binário do worker. Em seguida, você defina uma regra que use o worker. Essa regra deve definir um atributo que refere-se ao binário do worker.
Se o binário do worker que você criou estiver em um pacote chamado "work", que está na parte de cima do build, esta pode ser a definição do atributo:
"worker": attr.label(
default = Label("//work:worker"),
executable = True,
cfg = "exec",
)
cfg = "exec"
indica que o worker precisa ser criado para execução no seu
plataforma de execução em vez de na plataforma de destino (ou seja, o worker é usado
como ferramenta durante o build).
Requisitos de ação de trabalho
A regra que usa o worker cria ações para ele executar. Esses têm alguns requisitos.
O campo "arguments". Isso pega uma lista de strings, todas, exceto a última que são argumentos passados para o worker na inicialização. O último elemento em os "argumentos" list é um argumento
flag-file
(precedido por @). Leitura de workers os argumentos do arquivo de sinalização especificado por WorkRequest. Seu regra pode gravar argumentos que não sejam de inicialização para o worker nesse arquivo de sinalização.O campo "execution-requirements", que pega um dicionário contendo
"supports-workers" : "1"
,"supports-multiplex-workers" : "1"
ou ambos.Os "argumentos" e "execution-requirements" campos são obrigatórios para todas de entrada e saída enviadas aos workers. Além disso, as ações que devem ser executadas Os workers JSON precisam incluir
"requires-worker-protocol" : "json"
no requisitos de execução."requires-worker-protocol" : "proto"
também está um requisito de execução válido, embora não seja necessário para workers proto, já que são o padrão.Também é possível definir um
worker-key-mnemonic
nos requisitos de execução. Isso pode ser útil se você estiver reutilizando o executável para vários tipos de ação e para distinguir as ações deste worker.Os arquivos temporários gerados durante a ação devem ser salvos do worker. Isso ativa o sandbox.
Supondo uma definição de regra com "worker" o atributo descrito acima, além
em "srcs", que representa as entradas, uma "saída" atributo
que representa as saídas, e um "args" que representa o worker
startup, a chamada para ctx.actions.run
pode ser:
ctx.actions.run(
inputs=ctx.files.srcs,
outputs=[ctx.outputs.output],
executable=ctx.executable.worker,
mnemonic="someMnemonic",
execution_requirements={
"supports-workers" : "1",
"requires-worker-protocol" : "json"},
arguments=ctx.attr.args + ["@flagfile"]
)
Para outro exemplo, consulte Como implementar workers permanentes.
Exemplos
A base de código do Bazel usa Workers do compilador Java, além de um exemplo de worker JSON usada em nossos testes de integração.
É possível usar scaffolding para transformar qualquer ferramenta baseada em Java em um worker transmitindo o callback correto.
Para conferir um exemplo de regra que usa um worker, consulte a teste de integração do worker.
Colaboradores externos implementaram workers em várias linguagens. faça uma olhe para Implementações Polyglot de workers permanentes do Bazel. Você pode encontre muito mais exemplos no GitHub.