Worker แบบถาวร ช่วยให้บิลด์เร็วขึ้นได้ หากคุณมีการดำเนินการซ้ำๆ ในบิลด์ที่มีค่าใช้จ่ายในการเริ่มต้นสูงหรือได้รับประโยชน์จากการแคชข้ามการดำเนินการ คุณอาจต้องการติดตั้งใช้งาน Worker แบบถาวรของคุณเองเพื่อดำเนินการเหล่านี้
เซิร์ฟเวอร์ Bazel สื่อสารกับ Worker โดยใช้ stdin/stdout และรองรับการใช้บัฟเฟอร์โปรโตคอลหรือสตริง JSON
การติดตั้งใช้งาน Worker มี 2 ส่วน ได้แก่
- The Worker.
- กฎที่ใช้ Worker
การสร้าง Worker
Worker แบบถาวรต้องเป็นไปตามข้อกำหนด 2-3 ข้อดังนี้
- อ่าน
WorkRequests
จาก
stdin - เขียน
WorkResponses
(และเฉพาะ
WorkResponseเท่านั้น) ลงในstdout - ยอมรับแฟล็ก
--persistent_workerWrapper ต้องจดจำแฟล็กบรรทัดคำสั่ง--persistent_workerและทำให้ตัวเองเป็นแบบถาวรก็ต่อเมื่อมีการส่งแฟล็กดังกล่าว ไม่เช่นนั้นจะต้องทำการคอมไพล์แบบครั้งเดียวและออก
หากโปรแกรมของคุณเป็นไปตามข้อกำหนดเหล่านี้ ก็สามารถใช้เป็น Worker แบบถาวรได้
คำขอทำงาน
WorkRequest มีรายการอาร์กิวเมนต์สำหรับ Worker, รายการคู่เส้นทาง-ไดเจสต์ที่แสดงอินพุตที่ Worker เข้าถึงได้ (ระบบไม่ได้บังคับ แต่คุณใช้ข้อมูลนี้สำหรับการแคชได้) และรหัสคำขอ ซึ่งเป็น 0 สำหรับ Worker แบบซิงเกิลเพล็กซ์
หมายเหตุ: แม้ว่าข้อกำหนดบัฟเฟอร์โปรโตคอลจะใช้ "snake case" (request_id) แต่โปรโตคอล JSON จะใช้ "camel case" (requestId) เอกสารนี้ใช้ camel case ในตัวอย่าง JSON แต่ใช้ snake case เมื่อพูดถึงฟิลด์โดยไม่คำนึงถึงโปรโตคอล
{
"arguments" : ["--some_argument"],
"inputs" : [
{ "path": "/path/to/my/file/1", "digest": "fdk3e2ml23d"},
{ "path": "/path/to/my/file/2", "digest": "1fwqd4qdd" }
],
"requestId" : 12
}
คุณใช้ฟิลด์ verbosity ที่ไม่บังคับเพื่อขอเอาต์พุตการแก้ไขข้อบกพร่องเพิ่มเติมจาก Worker ได้ Worker จะเป็นผู้กำหนดสิ่งที่ต้องแสดงและวิธีแสดง ค่าที่สูงขึ้นจะแสดงเอาต์พุตที่ละเอียดมากขึ้น การส่งแฟล็ก --worker_verbose ไปยัง Bazel จะตั้งค่าฟิลด์ verbosity เป็น 10 แต่คุณสามารถใช้ค่าที่น้อยกว่าหรือมากกว่าได้ด้วยตนเองเพื่อให้แสดงเอาต์พุตในปริมาณที่แตกต่างกัน
ฟิลด์ sandbox_dir ที่ไม่บังคับใช้โดย Worker ที่รองรับ
แซนด์บ็อกซ์แบบมัลติเพล็กซ์เท่านั้น
การตอบสนองต่อคำขอทำงาน
WorkResponse มีรหัสคำขอ รหัสออกเป็น 0 หรือไม่ใช่ 0 และข้อความเอาต์พุตที่อธิบายข้อผิดพลาดที่พบในการประมวลผลหรือการดำเนินการคำขอ Worker ควรบันทึก stdout และ stderr ของเครื่องมือที่เรียกใช้และรายงานผ่าน WorkResponse การเขียนลงใน stdout ของกระบวนการ Worker ไม่ปลอดภัย เนื่องจากจะรบกวนโปรโตคอล Worker
การเขียนลงใน stderr ของกระบวนการ Worker ปลอดภัย แต่ระบบจะรวบรวมผลลัพธ์ในไฟล์บันทึกต่อ Worker แทนที่จะระบุการดำเนินการแต่ละรายการ
{
"exitCode" : 1,
"output" : "Action failed with the following message:\nCould not find input
file \"/path/to/my/file/1\"",
"requestId" : 12
}
ฟิลด์ทั้งหมดไม่บังคับตามมาตรฐานของ protobuf อย่างไรก็ตาม Bazel กำหนดให้ WorkRequest และ WorkResponse ที่เกี่ยวข้องต้องมีรหัสคำขอเดียวกัน ดังนั้นคุณต้องระบุรหัสคำขอหากไม่ใช่ 0 นี่คือ WorkResponse ที่ถูกต้อง
{
"requestId" : 12,
}
request_id ที่เป็น 0 แสดงถึงคำขอ "ซิงเกิลเพล็กซ์" ซึ่งใช้เมื่อไม่สามารถประมวลผลคำขอนี้แบบขนานกับคำขออื่นๆ ได้ เซิร์ฟเวอร์รับประกันว่า Worker ที่กำหนดจะได้รับคำขอที่มี request_id เป็น 0 เท่านั้น หรือ request_id มากกว่า 0 เท่านั้น ระบบจะส่งคำขอซิงเกิลเพล็กซ์แบบอนุกรม เช่น เซิร์ฟเวอร์จะไม่ส่งคำขออื่นจนกว่าจะได้รับการตอบกลับ (ยกเว้นคำขอยกเลิก โปรดดูด้านล่าง)
หมายเหตุ
- บัฟเฟอร์โปรโตคอลแต่ละรายการจะมีขนาดเป็นรูปแบบ
varintนำหน้า (ดูMessageLite.writeDelimitedTo()) - คำขอและการตอบกลับ JSON จะไม่มีตัวบ่งชี้ขนาดนำหน้า
- คำขอ JSON มีโครงสร้างเดียวกับ protobuf แต่ใช้ JSON มาตรฐานและใช้ camel case สำหรับชื่อฟิลด์ทั้งหมด
- เพื่อให้คงคุณสมบัติความเข้ากันได้แบบย้อนกลับและไปข้างหน้าแบบเดียวกับ protobuf ไว้ได้ Worker JSON ต้องยอมรับฟิลด์ที่ไม่รู้จักในข้อความเหล่านี้ และใช้ค่าเริ่มต้นของ protobuf สำหรับค่าที่ขาดหายไป
- Bazel จัดเก็บคำขอเป็น protobuf และแปลงเป็น JSON โดยใช้ รูปแบบ JSON ของ protobuf
การยกเลิก
Worker สามารถเลือกที่จะอนุญาตให้ยกเลิกคำขอทำงานก่อนที่จะเสร็จสิ้นได้
ซึ่งมีประโยชน์อย่างยิ่งในการดำเนินการแบบไดนามิก ซึ่งการดำเนินการในเครื่องอาจถูกขัดจังหวะเป็นประจำโดยการดำเนินการระยะไกลที่เร็วกว่า หากต้องการอนุญาตให้ยกเลิก ให้เพิ่ม supports-worker-cancellation: 1 ลงในฟิลด์ execution-requirements (ดูด้านล่าง) และตั้งค่าแฟล็ก --experimental_worker_cancellation
คำขอยกเลิก คือ WorkRequest ที่ตั้งค่าฟิลด์ cancel (และ
ในทำนองเดียวกัน การตอบกลับการยกเลิก คือ WorkResponse ที่ตั้งค่าฟิลด์ was_cancelled
) ฟิลด์อื่นเพียงฟิลด์เดียวที่ต้องอยู่ในคำขอยกเลิกหรือการตอบกลับการยกเลิกคือ request_id ซึ่งระบุคำขอที่จะยกเลิก ฟิลด์ request_id จะเป็น 0 สำหรับ Worker แบบซิงเกิลเพล็กซ์ หรือ request_id ที่ไม่ใช่ 0 ของ WorkRequest ที่ส่งก่อนหน้านี้สำหรับ Worker แบบมัลติเพล็กซ์ เซิร์ฟเวอร์อาจส่งคำขอยกเลิกสำหรับคำขอที่ Worker ตอบกลับไปแล้ว ในกรณีนี้ต้องละเว้นคำขอยกเลิก
ข้อความ WorkRequest ที่ไม่ใช่การยกเลิกแต่ละข้อความต้องได้รับการตอบกลับ 1 ครั้ง ไม่ว่าจะยกเลิกหรือไม่ก็ตาม เมื่อเซิร์ฟเวอร์ส่งคำขอยกเลิกแล้ว Worker อาจตอบกลับด้วย WorkResponse ที่ตั้งค่า request_id และตั้งค่าฟิลด์ was_cancelled เป็น "จริง" ระบบยังยอมรับการส่ง WorkResponse ปกติด้วย แต่จะละเว้นฟิลด์ output และ exit_code
เมื่อส่งการตอบกลับสำหรับ WorkRequest แล้ว Worker จะต้องไม่แตะต้องไฟล์ในไดเรกทอรีการทำงาน เซิร์ฟเวอร์มีอิสระในการล้างไฟล์ รวมถึงไฟล์ชั่วคราว
การสร้างกฎที่ใช้ Worker
คุณจะต้องสร้างกฎที่สร้างการดำเนินการให้ Worker ดำเนินการด้วย การสร้างกฎ Starlark ที่ใช้ Worker ก็เหมือนกับการสร้างกฎอื่นๆ
นอกจากนี้ กฎยังต้องมีการอ้างอิงถึง Worker เอง และการดำเนินการที่กฎสร้างขึ้นต้องเป็นไปตามข้อกำหนดบางประการ
การอ้างอิงถึง Worker
กฎที่ใช้ Worker ต้องมีฟิลด์ที่อ้างอิงถึง Worker เอง ดังนั้นคุณจะต้องสร้างอินสแตนซ์ของกฎ \*\_binary เพื่อกำหนด Worker หาก Worker ชื่อ MyWorker.Java กฎที่เกี่ยวข้องอาจเป็นดังนี้
java_binary(
name = "worker",
srcs = ["MyWorker.Java"],
)
ซึ่งจะสร้างป้ายกำกับ "worker" ที่อ้างอิงถึงไบนารีของ Worker จากนั้นคุณจะกำหนดกฎที่ ใช้ Worker กฎนี้ควรกำหนดแอตทริบิวต์ที่อ้างอิงถึงไบนารีของ Worker
หากไบนารีของ Worker ที่คุณสร้างอยู่ในแพ็กเกจชื่อ "work" ซึ่งอยู่ที่ระดับบนสุดของบิลด์ คำจำกัดความแอตทริบิวต์อาจเป็นดังนี้
"worker": attr.label(
default = Label("//work:worker"),
executable = True,
cfg = "exec",
)
cfg = "exec" ระบุว่าควรสร้าง Worker ให้ทำงานบนแพลตฟอร์มการดำเนินการของคุณ
แทนที่จะเป็นแพลตฟอร์มเป้าหมาย (เช่น ใช้ Worker เป็นเครื่องมือระหว่างบิลด์)
ข้อกำหนดการดำเนินการของ Worker
กฎที่ใช้ Worker จะสร้างการดำเนินการให้ Worker ดำเนินการ การดำเนินการเหล่านี้มีข้อกำหนด 2-3 ข้อ
ฟิลด์ "arguments" ฟิลด์นี้ใช้รายการสตริง ซึ่งทั้งหมดแต่ไม่ใช่รายการสุดท้ายเป็นอาร์กิวเมนต์ที่ส่งไปยัง Worker เมื่อเริ่มต้น องค์ประกอบสุดท้ายในรายการ "arguments" คืออาร์กิวเมนต์
flag-file(นำหน้าด้วย @) Worker จะอ่านอาร์กิวเมนต์จากแฟล็กไฟล์ที่ระบุตาม `WorkRequest` แต่ละรายการ กฎของคุณสามารถเขียนอาร์กิวเมนต์ที่ไม่ใช่การเริ่มต้นสำหรับ Worker ลงในแฟล็กไฟล์นี้ได้ฟิลด์ "execution-requirements" ซึ่งใช้พจนานุกรมที่มี
"supports-workers" : "1","supports-multiplex-workers" : "1", หรือทั้ง 2 อย่างฟิลด์ "arguments" และ "execution-requirements" จำเป็นสำหรับการดำเนินการทั้งหมดที่ส่งไปยัง Worker นอกจากนี้ การดำเนินการที่ควรดำเนินการโดย Worker JSON ต้องมี
"requires-worker-protocol" : "json"ในฟิลด์ ข้อกำหนดการดำเนินการ"requires-worker-protocol" : "proto"ยังเป็น ข้อกำหนดการดำเนินการที่ถูกต้องด้วย แม้ว่าจะไม่จำเป็นสำหรับ Worker proto เนื่องจากเป็นค่าเริ่มต้นคุณยังตั้งค่า
worker-key-mnemonicในข้อกำหนดการดำเนินการได้ด้วย ซึ่งอาจมีประโยชน์หากคุณใช้ไฟล์ปฏิบัติการซ้ำสำหรับประเภทการดำเนินการหลายประเภทและต้องการแยกความแตกต่างของการดำเนินการตาม Worker นี้ระบบควรบันทึกไฟล์ชั่วคราวที่สร้างขึ้นระหว่างการดำเนินการลงในไดเรกทอรีของ Worker ซึ่งจะช่วยให้ใช้แซนด์บ็อกซ์ได้
สมมติว่ามีการกำหนดกฎที่มีแอตทริบิวต์ "worker" ที่อธิบายไว้ข้างต้น นอกเหนือจากแอตทริบิวต์ "srcs" ที่แสดงอินพุต แอตทริบิวต์ "output" ที่แสดงเอาต์พุต และแอตทริบิวต์ "args" ที่แสดงอาร์กิวเมนต์การเริ่มต้นของ Worker การเรียก ctx.actions.run อาจเป็นดังนี้
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"]
)
ดูอีกตัวอย่างได้ที่ การติดตั้งใช้งาน Worker แบบถาวร
ตัวอย่าง
ฐานโค้ด Bazel ใช้ Worker คอมไพเลอร์ Java, นอกเหนือจาก Worker JSON ตัวอย่าง ที่ใช้ในการทดสอบการผสานรวม
คุณสามารถใช้ โครงสร้างพื้นฐาน ของ Worker เพื่อเปลี่ยนเครื่องมือที่ใช้ Java ให้เป็น Worker ได้โดยการส่งการเรียกกลับที่ถูกต้อง
ดูตัวอย่างกฎที่ใช้ Worker ได้ที่การทดสอบการผสานรวม Worker ของ Bazel
ผู้มีส่วนร่วมภายนอกได้ติดตั้งใช้งาน Worker ในภาษาต่างๆ มากมาย โปรดดูการติดตั้งใช้งาน Worker แบบถาวรของ Bazel ในหลายภาษา ดูตัวอย่างอื่นๆ ได้อีกมากมายใน GitHub