Los trabajadores persistentes pueden hacer que tu compilación sea más rápida. Si tiene acciones repetidas en su compilación que tienen un costo de inicio alto o que puedes aprovechar el almacenamiento en caché de acciones cruzadas, implementar tu propio trabajador para realizar estas acciones.
El servidor de Bazel se comunica con el trabajador mediante stdin
/stdout
. Integra
admite el uso de búferes de protocolo o cadenas JSON.
La implementación del trabajador tiene dos partes:
Convertir al trabajador
Un trabajador persistente cumple con algunos requisitos:
- Dice
WorkRequests
desde su
stdin
. - Escribe WorkResponses (y solo
WorkResponse
) en sustdout
. - Acepta la marca
--persistent_worker
. El wrapper debe reconocer el de línea de comandos--persistent_worker
y solo es persistente si se pasa esa marca; de lo contrario, debe realizar una compilación en un solo intento y salir.
Si tu programa cumple con estos requisitos, se puede usar como trabajador persistente.
Solicitudes de trabajo
Un WorkRequest
contiene una lista de argumentos para el trabajador, una lista de
de resumen que representan las entradas a las que puede acceder el trabajador (esto no es
se aplica de manera forzosa, pero puedes usar esta información para el almacenamiento en caché) y un ID de solicitud, que es 0
para trabajadores de singleplex.
NOTA: Si bien la especificación del búfer de protocolo usa "snake case" (request_id
),
el protocolo JSON usa “capitalización medial” (requestId
) Este documento usa mayúsculas mediales
en los ejemplos de JSON, pero usar snake case cuando se habla del campo, independientemente de
protocolo.
{
"arguments" : ["--some_argument"],
"inputs" : [
{ "path": "/path/to/my/file/1", "digest": "fdk3e2ml23d"},
{ "path": "/path/to/my/file/2", "digest": "1fwqd4qdd" }
],
"requestId" : 12
}
El campo opcional verbosity
se puede usar para solicitar resultados de depuración adicionales del trabajador. Depende completamente del trabajador qué generar y cómo hacerlo. Los valores más altos indican una salida más detallada. Pasar la marca --worker_verbose
a Bazel establece el campo verbosity
en 10, pero se pueden usar valores más pequeños o más grandes de forma manual para diferentes cantidades de salida.
Solo los trabajadores que admiten sandbox múltiple usan el campo opcional sandbox_dir
.
Respuestas de trabajo
Una WorkResponse
contiene un ID de solicitud, un código de salida de cero o distinto de cero y un
string de salida que describe los errores encontrados en el procesamiento o la ejecución
la solicitud. El campo output
contiene una descripción breve. Los registros completos se pueden escribir en el stderr
del trabajador. Debido a que los trabajadores solo pueden escribir WorkResponses
en stdout
, es común que redireccionen el stdout
de cualquier herramienta que usen a stderr
.
{
"exitCode" : 1,
"output" : "Action failed with the following message:\nCould not find input
file \"/path/to/my/file/1\"",
"requestId" : 12
}
Según la norma de protobufs, todos los campos son opcionales. Sin embargo, Bazel requiere
el WorkRequest
y el WorkResponse
correspondiente para tener la misma solicitud
id, por lo que se debe especificar el ID de la solicitud si no es cero. Este es un WorkResponse
válido.
{
"requestId" : 12,
}
Un request_id
de 0 indica una solicitud de “monoplex”, que se usa cuando esta solicitud no se puede procesar en paralelo con otras solicitudes. El servidor garantiza que
un trabajador determinado recibe solicitudes con solo request_id
0 o solo
request_id
es mayor que cero. Las solicitudes de singleplex se envían en serie por
Por ejemplo, si el servidor no envía otra solicitud hasta haber recibido un
(excepto para las solicitudes de cancelación, que se indican a continuación).
Notas
- Cada búfer de protocolo está precedido por su longitud en formato
varint
(consultaMessageLite.writeDelimitedTo()
). - Las solicitudes y respuestas JSON no están precedidas por un indicador de tamaño.
- Las solicitudes JSON mantienen la misma estructura que el protobuf, pero usan JSON estándar y usan mayúsculas y minúsculas para todos los nombres de campos.
- Para mantener las mismas propiedades de retrocompatibilidad y compatibilidad con versiones posteriores que protobuf, los trabajadores de JSON deben tolerar campos desconocidos en estos mensajes y usar los valores predeterminados de protobuf para los valores faltantes.
- Bazel almacena las solicitudes como protobufs y las convierte a JSON con el formato JSON de protobuf.
Cancelación
De manera opcional, los trabajadores pueden permitir que se cancelen las solicitudes de trabajo antes de que finalicen.
Esto es particularmente útil en relación con la ejecución dinámica, donde
suele interrumpirse a causa de una ejecución remota más rápida. Para permitir
cancelación, agrega supports-worker-cancellation: 1
al
execution-requirements
(consulta a continuación) y configura la
--experimental_worker_cancellation
.
Una solicitud de cancelación es un WorkRequest
con el campo cancel
configurado (y, de manera similar, una respuesta de cancelación es un WorkResponse
con el campo was_cancelled
configurado). El único otro campo que debe estar en una solicitud de cancelación o respuesta de cancelación es request_id
, que indica qué solicitud se debe cancelar. El request_id
será igual a 0 para los trabajadores de un soloplex o request_id
que no sea 0 de un
envió WorkRequest
para trabajadores multiplex. El servidor puede enviar solicitudes de cancelación
para las solicitudes que el trabajador ya respondió, en cuyo caso, la cancelación
se debe ignorar la solicitud.
Cada mensaje de WorkRequest
no cancelado debe responderse exactamente una vez, ya sea
pero no, fue cancelada. Una vez que el servidor haya enviado una solicitud de cancelación, el trabajador puede responder con un WorkResponse
con el request_id
establecido y el campo was_cancelled
establecido como verdadero. También se acepta el envío de un WorkResponse
normal, pero el
Se ignorarán los campos output
y exit_code
.
Una vez que se envía una respuesta para un WorkRequest
, el trabajador no debe tocar el
en su directorio de trabajo. El servidor puede limpiar los archivos, incluidos los temporales.
Cómo crear la regla que usa el trabajador
También necesitarás crear una regla que genere acciones que realizará el trabajador. Crear una regla de Starlark que use un trabajador es igual que crear cualquier otra regla.
Además, la regla debe contener una referencia al trabajador en sí, y hay algunos requisitos para las acciones que produce.
Cómo hacer referencia al trabajador
La regla que usa el trabajador debe incluir un campo que haga referencia al trabajador
por lo que deberás crear una instancia de una regla \*\_binary
para definir
tu trabajador. Si tu trabajador se llama MyWorker.Java
, esta podría ser la
regla asociada:
java_binary(
name = "worker",
srcs = ["MyWorker.Java"],
)
Esto crea el "trabajador" etiqueta, que hace referencia al objeto binario del trabajador. Luego, definirás una regla que use el trabajador. Esta regla debe definir un atributo que se refiere al objeto binario del trabajador.
Si el objeto binario del trabajador que compilaste está en un paquete llamado "work", que se encuentra en el nivel superior de la compilación, esta podría ser la definición del atributo:
"worker": attr.label(
default = Label("//work:worker"),
executable = True,
cfg = "exec",
)
cfg = "exec"
indica que el trabajador se debe compilar para ejecutarse en tu plataforma de ejecución en lugar de en la plataforma de destino (es decir, el trabajador se usa como herramienta durante la compilación).
Requisitos de la acción de trabajo
La regla que usa el trabajador crea acciones para que este realice. Estas acciones tienen algunos requisitos.
El campo "arguments" Toma una lista de cadenas, todas excepto la última que son argumentos que se pasan al trabajador al inicio. El último elemento de los “argumentos” list es un argumento
flag-file
(@preceded). Los trabajadores leen los argumentos del archivo de marca especificado por WorkRequest. Tu puede escribir argumentos que no sean de inicio para el trabajador en este archivo marcador.El campo "execution-requirements", que toma un diccionario que contiene
"supports-workers" : "1"
,"supports-multiplex-workers" : "1"
o ambos.Los campos "arguments" y "execution-requirements" son obligatorios para todas las acciones que se envían a los trabajadores. Además, las acciones que debe ejecutar Los trabajadores JSON deben incluir
"requires-worker-protocol" : "json"
en el requisitos de ejecución."requires-worker-protocol" : "proto"
también es un requisito de ejecución válido, aunque no es obligatorio para los trabajadores de proto, ya que son los predeterminados.También puedes establecer un
worker-key-mnemonic
en los requisitos de ejecución. Esta puede ser útil si vuelves a usar el ejecutable para varios tipos de acciones y deseas distinguir acciones de este trabajador.Los archivos temporales generados durante la acción deben guardarse en el directorio del trabajador. Esto habilita la zona de pruebas.
Supón una definición de regla con “trabajador” descrito anteriormente, además de
a “srcs” que representa las entradas, una “salida” atributo
que representa las salidas, y un argumento “args” atributo que representa al trabajador
argumentos de inicio, la llamada a ctx.actions.run
podría ser la siguiente:
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 ver otro ejemplo, consulta Cómo implementar trabajadores persistentes.
Ejemplos
La base de código de Bazel usa Trabajadores del compilador de Java, además de un ejemplo de trabajador JSON que se usa en nuestras pruebas de integración.
Puedes usar su andamiaje para convertir cualquier herramienta basada en Java en un trabajador pasando la devolución de llamada correcta.
Para ver un ejemplo de una regla que usa un trabajador, consulta la prueba de integración de trabajadores de Bazel.
Los colaboradores externos implementaron trabajadores en varios idiomas. Consulta las implementaciones de Polyglot de trabajadores persistentes de Bazel. Puedes Encuentra muchos más ejemplos en GitHub.