Membuat Pekerja Persisten

Worker persisten dapat mempercepat build Anda. Jika Anda memiliki tindakan berulang dalam build yang memiliki biaya startup tinggi atau akan mendapatkan manfaat dari caching lintas tindakan, Anda mungkin ingin menerapkan worker persisten Anda sendiri untuk melakukan tindakan ini.

Server Bazel berkomunikasi dengan worker menggunakan stdin/stdout. Server ini mendukung penggunaan buffer protokol atau string JSON.

Implementasi worker memiliki dua bagian:

Membuat worker

Worker persisten memenuhi beberapa persyaratan:

  • Membaca WorkRequests dari stdin.
  • Menulis WorkResponses (dan hanya WorkResponses) ke stdout.
  • Menerima flag --persistent_worker. Wrapper harus mengenali flag command line --persistent_worker dan hanya membuat dirinya persisten jika flag tersebut diteruskan, jika tidak, wrapper harus melakukan kompilasi satu kali dan keluar.

Jika program Anda memenuhi persyaratan ini, program tersebut dapat digunakan sebagai worker persisten.

Permintaan pekerjaan

WorkRequest berisi daftar argumen ke worker, daftar pasangan path-digest yang mewakili input yang dapat diakses oleh worker (hal ini tidak diterapkan, tetapi Anda dapat menggunakan info ini untuk caching), dan ID permintaan, yang bernilai 0 untuk worker singleplex.

CATATAN: Meskipun spesifikasi buffer protokol menggunakan "snake case" (request_id), protokol JSON menggunakan "camel case" (requestId). Dokumen ini menggunakan camel case dalam contoh JSON, tetapi snake case saat membahas kolom, terlepas dari protokol.

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

Kolom verbosity opsional dapat digunakan untuk meminta output proses debug tambahan dari worker. Worker sepenuhnya berhak menentukan apa dan bagaimana outputnya. Nilai yang lebih tinggi menunjukkan output yang lebih verbose. Meneruskan flag --worker_verbose ke Bazel akan menetapkan kolom verbosity ke 10, tetapi nilai yang lebih kecil atau lebih besar dapat digunakan secara manual untuk jumlah output yang berbeda.

Kolom sandbox_dir opsional hanya digunakan oleh worker yang mendukung sandboxing multiplex.

Respons pekerjaan

WorkResponse berisi ID permintaan, kode keluar nol atau bukan nol, dan pesan output yang menjelaskan error yang terjadi dalam memproses atau menjalankan permintaan. Worker harus mengambil stdout dan stderr dari alat apa pun yang dipanggilnya dan melaporkannya melalui WorkResponse. Menulisnya ke stdout proses worker tidak aman, karena akan mengganggu protokol worker. Menulisnya ke stderr proses worker aman, tetapi hasilnya dikumpulkan dalam file log per worker, bukan dikaitkan dengan tindakan individual.

{
  "exitCode" : 1,
  "output" : "Action failed with the following message:\nCould not find input
    file \"/path/to/my/file/1\"",
  "requestId" : 12
}

Sesuai dengan norma untuk protobuf, semua kolom bersifat opsional. Namun, Bazel mengharuskan WorkRequest dan WorkResponse yang sesuai memiliki ID permintaan yang sama, sehingga ID permintaan harus ditentukan jika bukan nol. Ini adalah WorkResponse yang valid.

{
  "requestId" : 12,
}

request_id bernilai 0 menunjukkan permintaan "singleplex", yang digunakan saat permintaan ini tidak dapat diproses secara paralel dengan permintaan lain. Server menjamin bahwa worker tertentu menerima permintaan dengan hanya request_id 0 atau hanya request_id yang lebih besar dari nol. Permintaan singleplex dikirim secara serial, misalnya jika server tidak mengirim permintaan lain hingga menerima respons (kecuali untuk permintaan pembatalan, lihat di bawah).

Catatan

  • Setiap buffer protokol didahului oleh panjangnya dalam varint format (lihat MessageLite.writeDelimitedTo().
  • Permintaan dan respons JSON tidak didahului oleh indikator ukuran.
  • Permintaan JSON mempertahankan struktur yang sama dengan protobuf, tetapi menggunakan JSON standar dan menggunakan camel case untuk semua nama kolom.
  • Untuk mempertahankan properti kompatibilitas mundur dan maju yang sama dengan protobuf, worker JSON harus mentoleransi kolom yang tidak diketahui dalam pesan ini, dan menggunakan default protobuf untuk nilai yang tidak ada.
  • Bazel menyimpan permintaan sebagai protobuf dan mengonversinya ke JSON menggunakan format JSON protobuf

Pembatalan

Worker dapat secara opsional mengizinkan permintaan pekerjaan dibatalkan sebelum selesai. Hal ini sangat berguna sehubungan dengan eksekusi dinamis, yang eksekusi lokalnya dapat secara teratur terganggu oleh eksekusi jarak jauh yang lebih cepat. Untuk mengizinkan pembatalan, tambahkan supports-worker-cancellation: 1 ke kolom execution-requirements (lihat di bawah) dan tetapkan flag --experimental_worker_cancellation.

Permintaan pembatalan adalah WorkRequest dengan kolom cancel yang ditetapkan (dan demikian pula respons pembatalan adalah WorkResponse dengan kolom was_cancelled yang ditetapkan). Satu-satunya kolom lain yang harus ada dalam permintaan pembatalan atau respons pembatalan adalah request_id, yang menunjukkan permintaan mana yang akan dibatalkan. Kolom request_id akan bernilai 0 untuk worker singleplex atau request_id bukan 0 dari WorkRequest yang sebelumnya dikirim untuk worker multiplex. Server dapat mengirim permintaan pembatalan untuk permintaan yang telah direspons oleh worker, yang dalam hal ini permintaan pembatalan harus diabaikan.

Setiap pesan WorkRequest non-pembatalan harus dijawab tepat satu kali, baik dibatalkan atau tidak. Setelah server mengirim permintaan pembatalan, worker dapat merespons dengan WorkResponse dengan request_id yang ditetapkan dan kolom was_cancelled ditetapkan ke benar (true). Pengiriman WorkResponse reguler juga diterima, tetapi kolom output dan exit_code akan diabaikan.

Setelah respons dikirim untuk WorkRequest, worker tidak boleh menyentuh file di direktori kerjanya. Server bebas membersihkan file, termasuk file sementara.

Membuat aturan yang menggunakan worker

Anda juga harus membuat aturan yang menghasilkan tindakan yang akan dilakukan oleh worker. Membuat aturan Starlark yang menggunakan worker sama seperti membuat aturan lainnya.

Selain itu, aturan harus berisi referensi ke worker itu sendiri, dan ada beberapa persyaratan untuk tindakan yang dihasilkannya.

Mereferensikan worker

Aturan yang menggunakan worker harus berisi kolom yang merujuk ke worker itu sendiri, sehingga Anda harus membuat instance aturan \*\_binary untuk menentukan worker. Jika worker Anda bernama MyWorker.Java, ini mungkin aturan terkait:

java_binary(
    name = "worker",
    srcs = ["MyWorker.Java"],
)

Tindakan ini akan membuat label "worker", yang merujuk ke biner worker. Kemudian, Anda akan menentukan aturan yang menggunakan worker. Aturan ini harus menentukan atribut yang merujuk ke biner worker.

Jika biner worker yang Anda buat berada dalam paket bernama "work", yang berada di tingkat atas build, ini mungkin definisi atributnya:

"worker": attr.label(
    default = Label("//work:worker"),
    executable = True,
    cfg = "exec",
)

cfg = "exec" menunjukkan bahwa worker harus dibuat untuk berjalan di platform eksekusi Anda , bukan di platform target (yaitu, worker digunakan sebagai alat selama build).

Persyaratan tindakan pekerjaan

Aturan yang menggunakan worker membuat tindakan yang akan dilakukan oleh worker. Tindakan ini memiliki beberapa persyaratan.

  • Kolom "arguments". Kolom ini mengambil daftar string, yang semuanya kecuali yang terakhir adalah argumen yang diteruskan ke worker saat startup. Elemen terakhir dalam daftar "arguments" adalah argumen flag-file yang diawali dengan @. Worker membaca argumen dari flagfile yang ditentukan berdasarkan per WorkRequest. Aturan Anda dapat menulis argumen non-startup untuk worker ke flagfile ini.

  • Kolom "execution-requirements", yang mengambil kamus yang berisi "supports-workers" : "1", "supports-multiplex-workers" : "1", atau keduanya.

    Kolom "arguments" dan "execution-requirements" diperlukan untuk semua tindakan yang dikirim ke worker. Selain itu, tindakan yang harus dijalankan oleh worker JSON harus menyertakan "requires-worker-protocol" : "json" di kolom persyaratan eksekusi. "requires-worker-protocol" : "proto" juga merupakan persyaratan eksekusi yang valid, meskipun tidak diperlukan untuk worker proto, karena merupakan default.

    Anda juga dapat menetapkan worker-key-mnemonic dalam persyaratan eksekusi. Hal ini mungkin berguna jika Anda menggunakan kembali executable untuk beberapa jenis tindakan dan ingin membedakan tindakan berdasarkan worker ini.

  • File sementara yang dibuat selama tindakan harus disimpan ke direktori worker. Tindakan ini akan mengaktifkan sandboxing.

Dengan asumsi definisi aturan dengan atribut "worker" yang dijelaskan di atas, selain atribut "srcs" yang mewakili input, atribut "output" yang mewakili output, dan atribut "args" yang mewakili argumen startup worker, panggilan ke ctx.actions.run mungkin adalah:

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"]
 )

Untuk contoh lain, lihat Menerapkan worker persisten.

Contoh

Basis kode Bazel menggunakan worker compiler Java, selain contoh worker JSON yang digunakan dalam pengujian integrasi kami.

Anda dapat menggunakan scaffolding mereka untuk membuat alat berbasis Java menjadi worker dengan meneruskan callback yang benar.

Untuk contoh aturan yang menggunakan worker, lihat pengujian integrasi worker Bazel .

Kontributor eksternal telah menerapkan worker dalam berbagai bahasa; lihat Implementasi Polyglot dari worker persisten Bazel. Anda dapat menemukan banyak contoh lainnya di GitHub.