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

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

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

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

การสร้าง Worker

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

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

ฟิลด์ทั้งหมดไม่บังคับตามมาตรฐานของ 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 ที่ไม่ใช่คำขอให้ยกเลิกแต่ละข้อความต้องได้รับคำตอบเพียงครั้งเดียว ไม่ว่าจะยกเลิกหรือไม่ก็ตาม เมื่อเซิร์ฟเวอร์ส่งคำขอให้ยกเลิกแล้ว 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 จะอ่านอาร์กิวเมนต์จากแฟล็กไฟล์ที่ระบุตาม `WorkRequest` แต่ละรายการ กฎของคุณสามารถเขียนอาร์กิวเมนต์ที่ไม่ใช่การเริ่มต้นสำหรับ Worker ลงในแฟล็กไฟล์นี้ได้

  • ฟิลด์ "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