Workers multiplex (recurso experimental)

Esta página descreve os workers multiplex, como escrever regras compatíveis com multiplex e soluções alternativas para algumas limitações.

Os workers multiplex permitem que o Bazel processe várias solicitações com um único processo de worker. Para workers com vários threads, o Bazel pode usar menos recursos para alcançar o mesmo desempenho ou um desempenho melhor. Por exemplo, em vez de ter um processo de worker por worker, o Bazel pode ter quatro workers multiplexados conversando com o mesmo processo de worker, que pode processar solicitações em paralelo. Para linguagens como Java e Scala, isso economiza o tempo de aquecimento da JVM e o tempo de compilação JIT. Em geral, permite o uso de um cache compartilhado entre todos os workers do mesmo tipo.

Visão geral

Há duas camadas entre o servidor do Bazel e o processo de worker. Para determinados mnemônicos que podem executar processos em paralelo, o Bazel recebe um WorkerProxy do pool de workers. O WorkerProxy encaminha as solicitações para o processo de worker sequencialmente, juntamente com um request_id. O processo de worker processa a solicitação e envia respostas para o WorkerMultiplexer. Quando o WorkerMultiplexer recebe uma resposta, ele analisa o request_id e encaminha as respostas de volta para o WorkerProxy correto. Assim como com workers não multiplexados, toda a comunicação é feita por entrada/saída padrão, mas a ferramenta não pode usar apenas stderr para saída visível ao usuário (confira abaixo).

Cada worker tem uma chave. O Bazel usa o código de hash da chave (composto por variáveis de ambiente, a raiz de execução e o mnemônico) para determinar qual WorkerMultiplexer usar. Os WorkerProxys se comunicam com o mesmo WorkerMultiplexer se tiverem o mesmo código de hash. Portanto, supondo que as variáveis de ambiente e a raiz de execução sejam as mesmas em uma única invocação do Bazel, cada mnemônico exclusivo só pode ter um WorkerMultiplexer e um processo de worker. O número total de workers, incluindo workers regulares e WorkerProxys, ainda é limitado por --worker_max_instances.

Como escrever regras compatíveis com multiplex

O processo de worker da regra precisa ser multithread para aproveitar os workers multiplex. O Protobuf permite que um conjunto de regras analise uma única solicitação, mesmo que haja várias solicitações acumuladas no fluxo. Sempre que o processo de worker analisar uma solicitação do fluxo, ele precisará processar a solicitação em um novo thread. Como threads diferentes podem ser concluídos e gravados no fluxo ao mesmo tempo, o processo de worker precisa garantir que as respostas sejam gravadas de forma atômica (as mensagens não se sobrepõem). As respostas precisam conter o request_id da solicitação que estão processando.

Como processar a saída multiplex

Os workers multiplex precisam ter mais cuidado ao processar a saída do que os workers singleplex. Tudo o que for enviado para stderr será incluído em um único arquivo de registro compartilhado entre todos os WorkerProxys do mesmo tipo, intercalados aleatoriamente entre solicitações simultâneas. Embora seja uma boa ideia redirecionar stdout para stderr, não colete essa saída no campo output de WorkResponse, porque isso pode mostrar ao usuário partes de saída corrompidas. Se a ferramenta enviar apenas a saída orientada ao usuário para stdout ou stderr, será necessário mudar esse comportamento antes de ativar os workers multiplex.

Como ativar os workers multiplex

Os workers multiplex não são ativados por padrão. Um conjunto de regras pode ativar os workers multiplex usando a tag supports-multiplex-workers nos execution_requirements de uma ação, assim como a tag supports-workers ativa os workers regulares. Como acontece ao usar workers regulares, uma estratégia de worker precisa ser especificada no nível do conjunto de regras (por exemplo, --strategy=[some_mnemonic]=worker) ou geralmente no nível da estratégia (por exemplo, --dynamic_local_strategy=worker,standalone). Nenhuma flag adicional é necessária, e supports-multiplex-workers tem precedência sobre supports-workers, se ambos estiverem definidos. É possível desativar os workers multiplex globalmente transmitindo --noworker_multiplex.

Um conjunto de regras é incentivado a usar workers multiplex, se possível, para reduzir a pressão da memória e melhorar o desempenho. No entanto, os workers multiplex não são compatíveis com a execução dinâmica, a menos que implementem o sandbox multiplex. A tentativa de executar workers multiplex não em sandbox com execução dinâmica usará silenciosamente workers singleplex em sandbox.

Sandbox multiplex

Os workers multiplex podem ser colocados em sandbox adicionando suporte explícito a eles nas implementações de worker. Embora o sandbox de worker singleplex possa ser feito executando cada processo de worker no próprio sandbox, os workers multiplex compartilham o diretório de trabalho do processo entre várias solicitações paralelas. Para permitir o sandbox de workers multiplex, o worker precisa oferecer suporte à leitura e gravação em um subdiretório especificado em cada solicitação, em vez de diretamente no diretório de trabalho.

Para oferecer suporte ao sandbox multiplex, o worker precisa usar o campo sandbox_dir do WorkRequest e usá-lo como um prefixo para todas as leituras e gravações de arquivos. Embora os campos arguments e inputs permaneçam inalterados em uma solicitação não em sandbox, as entradas reais são relativas ao sandbox_dir. O worker precisa traduzir os caminhos de arquivo encontrados em arguments e inputs para ler desse caminho modificado e também precisa gravar todas as saídas relativas ao sandbox_dir. Isso inclui caminhos como '.', bem como caminhos encontrados em arquivos especificados nos argumentos (como "argfile" argumentos).

Depois que um worker oferece suporte ao sandbox multiplex, o conjunto de regras pode declarar esse suporte adicionando supports-multiplex-sandboxing aos execution_requirements de uma ação. O Bazel vai usar o sandbox multiplex se a flag --experimental_worker_multiplex_sandboxing for transmitida ou se o worker for usado com execução dinâmica.

Os arquivos de worker de um worker multiplex em sandbox ainda são relativos ao diretório de trabalho do processo de worker. Assim, se um arquivo for usado para executar o worker e como entrada, ele precisará ser especificado como uma entrada no argumento do arquivo de flag e em tools, executable ou runfiles.