การสร้างผู้ปฏิบัติงานถาวร

รายงานปัญหา ดูแหล่งที่มา

ผู้ปฏิบัติงานถาวรสามารถทำให้บิลด์ของคุณเร็วขึ้นได้ หากคุณมีการดำเนินการซ้ำๆ ในบิลด์ที่มีค่าใช้จ่ายในการเริ่มต้นสูงหรือจะได้รับประโยชน์จากการแคชแบบข้ามการดำเนินการ คุณอาจลองใช้ผู้ปฏิบัติงานที่ต่อเนื่องของคุณเองเพื่อดำเนินการดังกล่าว

เซิร์ฟเวอร์ Bazel สื่อสารกับผู้ปฏิบัติงานโดยใช้ stdin/stdout โดยรองรับการใช้บัฟเฟอร์โปรโตคอลหรือสตริง JSON

การติดตั้งใช้งานผู้ปฏิบัติงานมี 2 ส่วนดังนี้

การทำให้ผู้ปฏิบัติงาน

ผู้ปฏิบัติงานถาวรจะรักษาคุณสมบัติที่กำหนดดังต่อไปนี้

  • โดยจะอ่าน WorkRequests จาก stdin
  • โดยจะเขียน WorkResponses (และเฉพาะ WorkResponses) ไปยัง stdout
  • และยอมรับแฟล็ก --persistent_worker Wrapper ต้องจดจำแฟล็กบรรทัดคำสั่ง --persistent_worker และทำให้ตัวเองคงอยู่ถาวรเมื่อผ่านแฟล็กนั้นแล้วเท่านั้น มิเช่นนั้น จะต้องทำการคอมไพล์แบบช็อตเดียวและออก

หากโปรแกรมของคุณรองรับข้อกำหนดเหล่านี้ ก็สามารถใช้เป็นโปรแกรมทำงานถาวรได้

คำของาน

WorkRequest จะมีรายการอาร์กิวเมนต์สำหรับผู้ปฏิบัติงาน รายการคู่ของไดเจสต์เส้นทางซึ่งแสดงอินพุตที่ผู้ปฏิบัติงานเข้าถึงได้ (ไม่ได้บังคับใช้เกณฑ์นี้ แต่คุณใช้ข้อมูลนี้สำหรับการแคชได้) และรหัสคำขอซึ่งเป็น 0 สำหรับผู้ปฏิบัติงานแบบ Singleplex

หมายเหตุ: แม้ว่าข้อกำหนดบัฟเฟอร์โปรโตคอลจะใช้ "Snake Case" (request_id) แต่โปรโตคอล JSON ใช้ "camel Case" (requestId) เอกสารนี้ใช้ลักษณะตัวพิมพ์ Camel ในตัวอย่าง JSON แต่ใช้ลักษณะตัวพิมพ์แบบงูเมื่อพูดถึงช่องโดยไม่คำนึงถึงโปรโตคอล

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

ช่อง verbosity (ไม่บังคับ) จะใช้เพื่อขอผลลัพธ์การแก้ไขข้อบกพร่องเพิ่มเติมจากผู้ปฏิบัติงานได้ ทั้งหมดนี้ขึ้นอยู่กับพนักงานว่าจะสร้างสรรค์ผลงานอย่างไรและอย่างไร ค่าที่สูงขึ้นบ่งบอกถึงเอาต์พุตที่มีความละเอียดมากขึ้น การส่งแฟล็ก --worker_verbose ไปยัง Blazel จะตั้งค่าช่อง verbosity เป็น 10 แต่คุณจะใช้ค่าที่น้อยกว่าหรือใหญ่กว่าด้วยตนเองสำหรับจำนวนเอาต์พุตที่ต่างกันได้

ช่อง sandbox_dir (ไม่บังคับ) จะใช้โดยผู้ปฏิบัติงานที่รองรับแซนด์บ็อกซ์ Multiplex เท่านั้น

คำตอบเกี่ยวกับงาน

WorkResponse ประกอบด้วยรหัสคำขอ รหัสการออกที่เป็น 0 หรือไม่ใช่ 0 และสตริงเอาต์พุตที่อธิบายข้อผิดพลาดที่พบในการประมวลผลหรือการดำเนินการตามคำขอ ช่อง output มีคำอธิบายสั้นๆ โดยอาจมีการเขียนบันทึกที่สมบูรณ์ไปยัง stderr ของผู้ปฏิบัติงาน เนื่องจากผู้ปฏิบัติงานอาจเขียน WorkResponses ไปยัง stdout เท่านั้น จึงเป็นเรื่องปกติที่ผู้ปฏิบัติงานจะเปลี่ยนเส้นทาง 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 หมายถึงคำขอ "Singleplex" ใช้เมื่อดำเนินการตามคำขอนี้ควบคู่ไปกับคำขออื่นๆ ไม่ได้ เซิร์ฟเวอร์จะรับประกันว่าผู้ปฏิบัติงานจะได้รับคำขอที่มี request_id 0 หรือ request_id มากกว่า 0 เท่านั้น ระบบจะส่งคำขอ Singleplex เป็นอนุกรม ตัวอย่างเช่น หากเซิร์ฟเวอร์ไม่ได้ส่งคำขออื่นจนกว่าจะได้รับการตอบสนอง (ยกเว้นคำขอยกเลิก โปรดดูด้านล่าง)

Notes

  • บัฟเฟอร์โปรโตคอลแต่ละรายการจะมีความยาวของบัฟเฟอร์ในรูปแบบ varint (ดู MessageLite.writeDelimitedTo()
  • คำขอและการตอบกลับ JSON จะไม่มีตัวระบุขนาดนำหน้า
  • คำขอ JSON รักษาโครงสร้างเดียวกันกับ protobuf แต่ใช้ JSON มาตรฐานและใช้ตัวพิมพ์เล็ก/ใหญ่สำหรับชื่อช่องทั้งหมด
  • เพื่อคงคุณสมบัติความเข้ากันได้แบบย้อนหลังและไปข้างหน้าเหมือนกับ protobuf ผู้ปฏิบัติงาน JSON ต้องยอมรับช่องที่ไม่รู้จักในข้อความเหล่านี้ และใช้ค่าเริ่มต้นของ protobuf เพื่อหาค่าที่หายไป
  • Bazel จัดเก็บคำขอในฐานะ protobuf และแปลงเป็น JSON โดยใช้ รูปแบบ JSON ของ protobuf

การยกเลิก

ผู้ปฏิบัติงานสามารถเลือกที่จะอนุญาตให้ยกเลิกคำของานก่อนที่จะเสร็จสิ้นได้ ซึ่งจะเป็นประโยชน์อย่างยิ่งเมื่อใช้ร่วมกับการดำเนินการแบบไดนามิก ซึ่งการดำเนินการในเครื่องอาจหยุดชะงักได้อย่างสม่ำเสมอโดยการดำเนินการระยะไกลที่เร็วกว่า หากต้องการอนุญาตการยกเลิก ให้เพิ่ม supports-worker-cancellation: 1 ลงในช่อง execution-requirements (ดูด้านล่าง) และตั้งค่าสถานะ --experimental_worker_cancellation

คำขอยกเลิกคือ WorkRequest ที่มีชุดช่อง cancel (และในทำนองเดียวกัน การตอบกลับการยกเลิกคือ WorkResponse ที่มีชุดช่อง was_cancelled) อีกช่องเดียวที่ต้องอยู่ในคำขอยกเลิกหรือการตอบกลับคือ request_id ซึ่งเป็นการระบุว่าคำขอใดจะยกเลิก ช่อง request_id จะเป็น 0 สำหรับผู้ปฏิบัติงานแบบ Singleplex หรือ request_id ที่ไม่ใช่ 0 ของ WorkRequest ที่ส่งก่อนหน้านี้สำหรับผู้ปฏิบัติงาน Multiplex เซิร์ฟเวอร์อาจส่งคำขอยกเลิกสำหรับคำขอที่ผู้ปฏิบัติงานตอบกลับแล้ว ซึ่งในกรณีนี้คำขอยกเลิกจะต้องถูกละเว้น

ข้อความ WorkRequest ที่ไม่ยกเลิกแต่ละข้อความต้องตอบเพียงครั้งเดียว ไม่ว่าจะถูกยกเลิกหรือไม่ก็ตาม เมื่อเซิร์ฟเวอร์ส่งคำขอยกเลิก ผู้ปฏิบัติงานอาจตอบกลับด้วย WorkResponse ที่มีการตั้งค่า request_id และตั้งค่าช่อง was_cancelled เป็น "จริง" การส่ง WorkResponse ปกติก็ยอมรับได้เช่นกันแต่ระบบจะไม่สนใจช่อง output และ exit_code

เมื่อส่งการตอบกลับสำหรับ WorkRequest แล้ว ผู้ปฏิบัติงานต้องไม่แตะไฟล์ในไดเรกทอรีที่ใช้งานได้ เซิร์ฟเวอร์มีอิสระในการล้างไฟล์ต่างๆ ซึ่งรวมถึงไฟล์ชั่วคราว

กฎที่ใช้ผู้ปฏิบัติงาน

นอกจากนี้ คุณยังต้องสร้างกฎที่กำหนดให้ผู้ปฏิบัติงานต้องทำ การสร้างกฎ Starlark ที่ใช้ผู้ปฏิบัติงานก็เหมือนกับการสร้างกฎอื่นๆ

นอกจากนี้ กฎดังกล่าวยังต้องมีข้อมูลอ้างอิงไปยังตัวผู้ปฏิบัติงาน และมีข้อกำหนดบางอย่างสำหรับการทำงานที่ผู้ปฏิบัติงานสร้างขึ้นมา

การอ้างอิงถึงผู้ปฏิบัติงาน

กฎที่ใช้ผู้ปฏิบัติงานจะต้องมีช่องข้อมูลที่เกี่ยวข้องกับตัวผู้ปฏิบัติงาน คุณจึงต้องสร้างอินสแตนซ์ของกฎ \*\_binary เพื่อกำหนดผู้ปฏิบัติงาน ถ้าผู้ปฏิบัติงานของคุณมีชื่อว่า MyWorker.Java นี่อาจเป็นกฎที่เกี่ยวข้อง

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

แล้วระบบจะสร้างป้ายกำกับ "worker" ที่อ้างถึงไบนารีของผู้ปฏิบัติงาน จากนั้นคุณจะต้องกำหนดกฎที่ใช้ผู้ปฏิบัติงาน กฎนี้ควรกำหนดแอตทริบิวต์ ที่อ้างอิงไบนารีผู้ปฏิบัติงาน

หากไบนารีของผู้ปฏิบัติงานที่คุณสร้างอยู่ในแพ็กเกจชื่อ "work" ซึ่งอยู่ในระดับบนสุดของบิลด์ นี่อาจเป็นคำอธิบายแอตทริบิวต์

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

cfg = "exec" บ่งชี้ว่าควรสร้างผู้ปฏิบัติงานให้ทำงานบนแพลตฟอร์มการดำเนินการของคุณ ไม่ใช่บนแพลตฟอร์มเป้าหมาย (กล่าวคือ ใช้ผู้ปฏิบัติงานเป็นเครื่องมือในระหว่างการสร้าง)

ข้อกำหนดในการทำงาน

กฎที่ใช้ผู้ปฏิบัติงานจะสร้างการดำเนินการให้ผู้ปฏิบัติงานกระทำ การดำเนินการเหล่านี้ มีข้อกำหนด 2 ข้อ

  • ช่อง "อาร์กิวเมนต์" ซึ่งจะรวบรวมรายการสตริงทั้งหมด ยกเว้นรายการสุดท้ายเป็นอาร์กิวเมนต์ที่ส่งผ่านไปยังผู้ปฏิบัติงานเมื่อเริ่มต้นใช้งาน องค์ประกอบสุดท้ายในรายการ "อาร์กิวเมนต์" คืออาร์กิวเมนต์ flag-file (@-preceded) ผู้ปฏิบัติงานจะอ่านอาร์กิวเมนต์จาก Flagfile ที่ระบุในแบบต่อ WorkRequest กฎของคุณสามารถเขียนอาร์กิวเมนต์ที่ไม่ใช่การเริ่มต้นสำหรับผู้ปฏิบัติงานไปยัง Flagfile นี้ได้

  • ช่อง "execution-requirements" ซึ่งจะใช้พจนานุกรมที่มี "supports-workers" : "1", "supports-multiplex-workers" : "1" หรือทั้ง 2 อย่าง

    คุณต้องระบุข้อมูลในช่อง "อาร์กิวเมนต์" และ "ข้อกำหนดการดำเนินการ" สำหรับการดำเนินการทั้งหมดที่ส่งไปยังผู้ปฏิบัติงาน นอกจากนี้ การดำเนินการที่ผู้ปฏิบัติงาน JSON ควรดำเนินการต้องมี "requires-worker-protocol" : "json" ในช่องข้อกำหนดการดำเนินการด้วย นอกจากนี้ "requires-worker-protocol" : "proto" ยังเป็นข้อกำหนดในการใช้งานที่ถูกต้อง แม้ผู้ปฏิบัติงานโปรโตจะไม่จำเป็น เนื่องจากเป็นค่าเริ่มต้น

    คุณยังตั้งค่า worker-key-mnemonic ในข้อกำหนดการดำเนินการได้ด้วย วิธีนี้อาจเป็นประโยชน์หากคุณนำไฟล์ปฏิบัติการมาใช้ซ้ำสำหรับประเภทการทำงานหลายประเภท และต้องการแยกการทำงานของผู้ปฏิบัติงานคนนี้ออกจากกัน

  • ไฟล์ชั่วคราวที่สร้างขึ้นในระหว่างการดำเนินการควรบันทึกลงในไดเรกทอรีของผู้ปฏิบัติงาน ซึ่งจะเปิดใช้แซนด์บ็อกซ์

สมมติว่าคำจำกัดความของกฎที่มีแอตทริบิวต์ "worker" ที่อธิบายไว้ข้างต้น นอกจากแอตทริบิวต์ "srcs" ที่แสดงอินพุตแล้ว แอตทริบิวต์ "output" ที่แสดงถึงเอาต์พุต และแอตทริบิวต์ "args" ที่แสดงถึงอาร์กิวเมนต์เริ่มต้นผู้ปฏิบัติงาน การเรียกไปยัง 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"]
 )

สำหรับอีกตัวอย่างหนึ่ง โปรดดูการนำผู้ปฏิบัติงานถาวรไปใช้

ตัวอย่าง

ฐานของโค้ด Bazel ใช้ผู้ปฏิบัติงานของคอมไพเลอร์ Java นอกเหนือจากผู้ปฏิบัติงาน JSON ตัวอย่างที่ใช้ในการทดสอบการผสานรวม

คุณสามารถใช้โครงกระดูกในการทำให้เครื่องมือที่ใช้ Java ทำงานหนักได้โดยการส่งผ่านโค้ดเรียกกลับที่ถูกต้อง

ดูตัวอย่างกฎที่ใช้ผู้ปฏิบัติงานได้จากการทดสอบการผสานรวมผู้ปฏิบัติงานของ Bazel

ผู้ให้ข้อมูลภายนอกได้ติดตั้งใช้งานผู้ปฏิบัติงานในภาษาต่างๆ มากมาย โปรดดูการติดตั้งใช้งาน Polyglot สำหรับผู้ปฏิบัติงานถาวรของ Bazel คุณ ค้นหาตัวอย่างอื่นๆ อีกมากมายได้ใน GitHub