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 ที่ไม่ใช่คำขอให้ยกเลิกแต่ละข้อความต้องได้รับการตอบกลับ 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 ข้อ
ฟิลด์ "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 ให้เป็น Worker ได้โดยการส่งการเรียกกลับที่ถูกต้อง
หากต้องการดูตัวอย่างกฎที่ใช้ Worker โปรดดูการทดสอบการผสานรวม Worker ของ Bazel
ผู้มีส่วนร่วมภายนอกได้ใช้ Worker ในภาษาต่างๆ มากมาย โปรดดูการใช้งาน Worker แบบถาวรของ Bazel ในหลายภาษา คุณดูตัวอย่างอื่นๆ ได้อีกมากมายใน GitHub !