Worker ที่ทำงานต่อเนื่อง จะช่วยให้บิลด์เร็วขึ้น หากคุณมีการดำเนินการซ้ำๆ ในบิลด์ที่มีค่าใช้จ่ายในการเริ่มต้นสูงหรือได้รับประโยชน์จากการแคชการดำเนินการข้าม คุณอาจต้องการติดตั้งใช้งาน Worker ที่ทำงานต่อเนื่องของคุณเองเพื่อดำเนินการเหล่านี้
เซิร์ฟเวอร์ Bazel สื่อสารกับ Worker โดยใช้ stdin/stdout และรองรับการใช้บัฟเฟอร์โปรโตคอลหรือสตริง JSON
การติดตั้งใช้งาน Worker มี 2 ส่วน ได้แก่
- The Worker.
- กฎที่ใช้ Worker
การสร้าง Worker
Worker ที่ทำงานต่อเนื่องต้องเป็นไปตามข้อกำหนดบางประการ ดังนี้
- อ่าน
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 และสตริงเอาต์พุตที่อธิบายข้อผิดพลาดที่พบในการประมวลผลหรือเรียกใช้คำขอ ฟิลด์ output มีคำอธิบายสั้นๆ และระบบอาจเขียนบันทึกแบบสมบูรณ์ลงใน stderr ของ Worker เนื่องจาก Worker เขียนได้เฉพาะ
WorkResponses ลงใน stdout เท่านั้น Worker จึงมักจะเปลี่ยนเส้นทาง stdout
ของเครื่องมือที่ใช้ไปยัง stderr
{
"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 ที่ไม่ใช่คำขอให้ยกเลิกแต่ละข้อความต้องได้รับคำตอบเพียงครั้งเดียว ไม่ว่าจะยกเลิกหรือไม่ก็ตาม เมื่อเซิร์ฟเวอร์ส่งคำขอให้ยกเลิกแล้ว 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 ข้อ
ฟิลด์ "arguments" ฟิลด์นี้ใช้รายการสตริง ซึ่งทั้งหมดแต่ไม่ใช่รายการสุดท้ายเป็นอาร์กิวเมนต์ที่ส่งไปยัง Worker เมื่อเริ่มต้น องค์ประกอบสุดท้ายในรายการ "arguments" คืออาร์กิวเมนต์
flag-file(นำหน้าด้วย @) Worker จะอ่านอาร์กิวเมนต์จาก flagfile ที่ระบุตาม WorkRequest แต่ละรายการ กฎของคุณสามารถเขียนอาร์กิวเมนต์ที่ไม่ใช่การเริ่มต้นสำหรับ Worker ลงใน flagfile นี้ได้ฟิลด์ "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 เพื่อเปลี่ยนเครื่องมือที่ใช้ Java ให้เป็น Worker ได้โดยการส่งการเรียกกลับที่ถูกต้อง
ดูตัวอย่างกฎที่ใช้ Worker ได้ที่การทดสอบการผสานรวม Worker ของ Bazel
ผู้มีส่วนร่วมภายนอกได้ติดตั้งใช้งาน Worker ในภาษาต่างๆ มากมาย โปรดดู การติดตั้งใช้งาน Worker ที่ทำงานต่อเนื่องของ Bazel ในหลายภาษา คุณดูตัวอย่างอื่นๆ ได้อีกมากมายใน GitHub !