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

รายงานปัญหา ดูแหล่งที่มา Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

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

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

การใช้งาน Worker มี 2 ส่วน ดังนี้

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

Persistent Worker จะต้องเป็นไปตามข้อกำหนดต่อไปนี้

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

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

คำของาน

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

หมายเหตุ: แม้ว่าข้อกำหนดของบัฟเฟอร์โปรโตคอลจะใช้ "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_verbose ไปยัง Bazel จะตั้งค่าฟิลด์ verbosity เป็น 10 แต่คุณสามารถใช้ค่าที่เล็กกว่าหรือใหญ่กว่า ด้วยตนเองสำหรับเอาต์พุตจำนวนต่างๆ ได้

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

การตอบกลับที่ทำงาน

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
}

ตามมาตรฐานของ Protobufs ช่องทั้งหมดไม่บังคับ อย่างไรก็ตาม Bazel กำหนดให้ WorkRequest และ WorkResponse ที่เกี่ยวข้องต้องมีรหัสคำขอเดียวกัน ดังนั้นจึงต้องระบุรหัสคำขอหากไม่ใช่ 0 นี่คือ WorkResponse ที่ถูกต้อง

{
  "requestId" : 12,
}

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

หมายเหตุ

  • แต่ละ Protocol Buffer จะมีคำนำหน้าเป็นความยาวในรูปแบบ varint (ดู MessageLite.writeDelimitedTo())
  • คำขอและการตอบกลับ JSON จะไม่มีตัวบ่งชี้ขนาดนำหน้า
  • คำขอ JSON จะมีโครงสร้างเดียวกับ Protobuf แต่ใช้ JSON มาตรฐานและใช้รูปแบบ Camel Case สำหรับชื่อฟิลด์ทั้งหมด
  • เพื่อให้คงคุณสมบัติความเข้ากันได้แบบย้อนกลับและไปข้างหน้าเช่นเดียวกับ 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 สำหรับ Worker แบบ Singleplex หรือ request_id ที่ไม่ใช่ 0 ของ WorkRequest ที่ส่งก่อนหน้านี้ สำหรับ Worker แบบ Multiplex เซิร์ฟเวอร์อาจส่งคำขอยกเลิก สำหรับคำขอที่ Worker ตอบกลับไปแล้ว ในกรณีนี้ ต้องไม่สนใจคำขอยกเลิก

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

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

การสร้างกฎที่ใช้ Worker

นอกจากนี้ คุณยังต้องสร้างกฎที่สร้างการดำเนินการที่ Worker จะต้องทำด้วย การสร้างกฎ Starlark ที่ใช้ 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 เป็นเครื่องมือในระหว่างการสร้าง)

ข้อกำหนดในการดำเนินการกับงาน

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

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

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

    ฟิลด์ "arguments" และ "execution-requirements" เป็นฟิลด์ที่จำเป็นสำหรับ การดำเนินการทั้งหมดที่ส่งไปยังผู้ปฏิบัติงาน นอกจากนี้ การดำเนินการที่ควรดำเนินการโดย JSON Worker ต้องมี "requires-worker-protocol" : "json" ใน ช่องข้อกำหนดในการดำเนินการ "requires-worker-protocol" : "proto" ยังเป็น ข้อกำหนดการดำเนินการที่ถูกต้องด้วย แม้ว่าจะไม่จำเป็นสำหรับผู้ปฏิบัติงาน 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 ใช้ Java compiler workers นอกเหนือจาก example JSON worker ที่ใช้ในการทดสอบการผสานรวม

คุณสามารถใช้โครงสร้างพื้นฐาน เพื่อเปลี่ยนเครื่องมือที่ใช้ Java ให้เป็น Worker ได้โดยส่งการเรียกกลับที่ถูกต้อง

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

ผู้ร่วมให้ข้อมูลภายนอกได้นำโปรแกรมทำงานไปใช้ในภาษาต่างๆ โปรดดูการใช้งานโปรแกรมทำงานที่ทำงานต่อเนื่องของ Bazel ในหลายภาษา คุณดูตัวอย่างอื่นๆ อีกมากมายได้ใน GitHub