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

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

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

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

การติดตั้งใช้งานผู้ดําเนินการมี 2 ส่วน ได้แก่

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

ผู้ปฏิบัติงานแบบถาวรต้องปฏิบัติตามข้อกำหนดบางประการ ดังนี้

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

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

คำของาน

WorkRequest มีรายการอาร์กิวเมนต์สำหรับเวิร์กเกอร์ รายการคู่เส้นทาง-ข้อมูลสรุปที่แสดงอินพุตที่เวิร์กเกอร์เข้าถึงได้ (ระบบไม่ได้บังคับใช้ แต่คุณใช้ข้อมูลนี้เพื่อแคชได้) และรหัสคำขอ ซึ่งจะเป็น 0 สำหรับเวิร์กเกอร์แบบ 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 (ไม่บังคับ) เพื่อขอเอาต์พุตการแก้ไขข้อบกพร่องเพิ่มเติมจากเวิร์กเกอร์ ขึ้นอยู่กับว่าผู้ปฏิบัติงานจะแสดงผลอะไรและอย่างไร ค่าที่สูงขึ้นบ่งบอกถึงเอาต์พุตที่ละเอียดมากขึ้น การส่ง Flag --worker_verbose ไปยัง Bazel จะตั้งค่าช่อง verbosity เป็น 10 แต่คุณใช้ค่าที่เล็กกว่าหรือมากกว่าได้ด้วยตัวเองสำหรับเอาต์พุตจำนวนต่างๆ

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

การตอบกลับงาน

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

หมายเหตุ

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

การยกเลิก

ผู้ปฏิบัติงานสามารถเลือกที่จะอนุญาตให้ยกเลิกคำของานก่อนทำเสร็จได้ ซึ่งมีประโยชน์อย่างยิ่งเมื่อเกี่ยวข้องกับการดำเนินการแบบไดนามิกที่การดําเนินการในเครื่องอาจถูกขัดจังหวะโดยการดำเนินการระยะไกลที่เร็วกว่าเป็นประจำ หากต้องการอนุญาตการยกเลิก ให้เพิ่ม 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 ข้อ

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

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

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

ดูตัวอย่างอื่นได้ที่การใช้ PersistedWorker

ตัวอย่าง

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

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

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

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