Cómo crear trabajadores persistentes

Informar un problema Ver fuente Por la noche · 7.2 · 7.1 · 7.0 · 6.5 · 6.4

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 trabajadora tiene dos partes:

Convertir al trabajador

Un trabajador persistente cumple algunos requisitos:

  • Dice WorkRequests desde su stdin.
  • Escribe WorkResponses (y solo WorkResponse) a su stdout.
  • 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.

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 del protocolo.

{
  "arguments" : ["--some_argument"],
  "inputs" : [
    { "path": "/path/to/my/file/1", "digest": "fdk3e2ml23d"},
    { "path": "/path/to/my/file/2", "digest": "1fwqd4qdd" }
 ],
  "requestId" : 12
}

Se puede usar el campo opcional verbosity para solicitar un resultado de depuración adicional del trabajador. Depende completamente del trabajador qué y cómo generar. Más alta los valores indican un resultado más detallado. Pasa 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. manualmente para diferentes cantidades de resultados.

Solo los trabajadores compatibles usan el campo opcional sandbox_dir Zona de pruebas multiplex

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. puede que los registros completos se escribirá en el stderr del trabajador. Debido a que los trabajadores solo pueden escribir WorkResponses a stdout, es común que el trabajador redireccione el stdout de las herramientas que usa para 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 los 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. Esta es una cuenta válida WorkResponse

{
  "requestId" : 12,
}

Un request_id de 0 indica un "singleplex". que se usa cuando esta solicitud no se pueden 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 (consulta MessageLite.writeDelimitedTo()
  • Las solicitudes y respuestas de JSON no están precedidas por un indicador de tamaño.
  • Las solicitudes JSON mantienen la misma estructura que el protobuf, pero usan JSON y usa mayúsculas mediales para todos los nombres de campo.
  • Para mantener las mismas propiedades de retrocompatibilidad como protobuf, los trabajadores JSON deben tolerar campos desconocidos en estos mensajes. y usar los valores predeterminados de protobuf para valores faltantes.
  • Bazel almacena solicitudes como protobufs y las convierte a JSON usando 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 una WorkResponse con el parámetro was_cancelled de campo). El único otro campo que debe estar en una solicitud de cancelación o es request_id, que indica qué solicitud cancelar. El request_id será 0 para 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 ha enviado una solicitud de cancelación, el trabajador responde con un WorkResponse con el request_id configurado y el was_cancelled configurado 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 archivos temporales.

Crea 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 de recurso, que hace referencia al objeto binario del trabajador. A continuación, definir una regla que use el trabajador. Esta regla debe definir un atributo que se refiere al objeto binario del trabajador.

Si el objeto binario de trabajador que compilaste está en un paquete llamado "work", que se encuentra en la parte superior nivel 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 debe compilarse para ejecutarse en tu de ejecución, en lugar de la plataforma de destino (es decir, el trabajador se usa como herramienta durante la compilación).

Requisitos para las acciones laborales

La regla que usa el trabajador crea las acciones que debe realizar el trabajador. Estos 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). Lectura de los trabajadores los argumentos del archivo de marcas 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 “argumentos” y "execution-requirements" campos son obligatorios para todos acciones enviadas 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 proto ya que son las predeterminadas.

    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 en el transcurso de la acción deben guardarse en la 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 API de Bazel prueba de integración de trabajadores.

Los colaboradores externos implementaron trabajadores en una variedad de idiomas. toma un mirar Implementaciones de Polyglot en trabajadores persistentes de Bazel. Puedes Encuentra muchos más ejemplos en GitHub.