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

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

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

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

การสร้าง Worker

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

  • อ่าน WorkRequests จาก stdin
  • เขียน WorkResponses (และเฉพาะ WorkResponse เท่านั้น) ลงใน stdout
  • ยอมรับแฟล็ก --persistent_worker แรปเปอร์ต้องจดจำแฟล็กบรรทัดคำสั่ง --persistent_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_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 เท่านั้น ระบบจะส่งคำขอซิงเกิลเพล็กซ์แบบอนุกรม เช่น หากเซิร์ฟเวอร์ไม่ส่งคำขออื่นจนกว่าจะได้รับการตอบกลับ (ยกเว้นคำขอให้ยกเลิก โปรดดูด้านล่าง)

หมายเหตุ

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

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

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

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

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

นอกจากนี้ กฎต้องมีการอ้างอิงถึง Worker เอง และการดำเนินการที่กฎสร้างขึ้นต้องเป็นไปตามข้อกำหนดบางประการ

การอ้างอิงถึง Worker

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

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

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

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

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

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

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

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

  • ฟิลด์ "arguments" ฟิลด์นี้ใช้รายการสตริง ซึ่งทั้งหมดแต่ไม่ใช่รายการสุดท้ายเป็นอาร์กิวเมนต์ที่ส่งไปยังผู้ปฏิบัติงานเมื่อเริ่มต้น องค์ประกอบสุดท้ายในรายการ "arguments" คืออาร์กิวเมนต์ flag-file (นำหน้าด้วย @) ผู้ปฏิบัติงานจะอ่านอาร์กิวเมนต์จากแฟล็กไฟล์ที่ระบุตาม `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"]
 )

ดูอีกตัวอย่างได้ที่ การใช้ผู้ปฏิบัติงานแบบถาวร

ตัวอย่าง

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

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

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

ผู้มีส่วนร่วมภายนอกได้ใช้ผู้ปฏิบัติงานในภาษาต่างๆ มากมาย โปรดดูการใช้งานผู้ปฏิบัติงานแบบถาวรของ Bazel ในหลายภาษา ดูตัวอย่างเพิ่มเติมได้ใน GitHub !