การปล่อยโค้ดใหม่ไม่จำเป็นต้องแปลว่าต้องหยุดทุก node แล้วเริ่ม process ใหม่เสมอไป โครงการ open source ชื่อ celld จาก Deno เสนอรูปแบบหนึ่งสำหรับแอปที่เขียนด้วย Cloudflare Workers API: ส่ง deployment manifest ไปยัง object storage แล้วให้ node ที่กำลังทำงานรับเวอร์ชันใหม่มาใช้ระหว่างทาง
ขอแยกชื่อให้ชัดก่อน celld เป็นโครงการของ Deno สำหรับ self-host ไม่ใช่ผลิตภัณฑ์ของ Cloudflare และไม่ได้เป็นคำรับรองอย่างเป็นทางการว่าทุก Workers application ย้ายไปรันที่อื่นได้โดยไม่แก้โค้ด เป้าหมายคือให้ runtime และบริการบางส่วนมีรูปแบบคล้าย Workers บนเครื่องที่เราดูแลเอง โครงการและเอกสาร celld ระบุว่ารองรับ Workers, Durable Objects, KV, Queues, D1, R2, Workflows, Cron Triggers และ static assets ในขอบเขตที่กำหนด
Deploy โดยไม่ restart หมายถึงอะไร
ใน celld หนึ่ง process บนเครื่องหนึ่งเครื่องเรียกว่า node ส่วน node ที่ใช้ bucket เดียวกันรวมเป็น fleet แอปถูก deploy เป็น bundle และ manifest ลง bucket ที่รองรับ เช่น S3-compatible storage, Google Cloud Storage หรือ Azure Blob Storage
เมื่อ deploy เวอร์ชันใหม่ node จะตรวจ deploy/current.json ตามช่วงเวลาที่กำหนด ค่าเริ่มต้นในเอกสารคือทุก 30 วินาที แล้วนำ deployment ใหม่มาใช้โดยไม่ restart process คำขอที่เริ่มทำงานด้วยโค้ดรุ่นเก่าจะทำต่อด้วยรุ่นเดิม ส่วน Durable Object ที่ยัง active จะย้ายไปใช้รุ่นใหม่เมื่อไม่มีงานที่กำลังทำอยู่ หรือเมื่อถึงเวลาบังคับย้ายตามค่าที่ตั้งไว้ ขั้นตอน deploy และการเปลี่ยนเวอร์ชัน
คำว่า “ไม่ restart” จึงหมายถึง ไม่ต้องเริ่ม node process ใหม่เพื่อรับ deployment ไม่ได้หมายความว่าทุก request เปลี่ยนรุ่นพร้อมกัน หรือไม่มีช่วงที่โค้ดสองรุ่นทำงานอยู่ร่วมกัน ช่วงเปลี่ยนผ่านอาจทำให้ Worker รุ่นหนึ่งเรียก Durable Object ที่อยู่คนละรุ่นได้ โค้ดที่คุยกันจึงควรรองรับข้อมูลและรูปแบบคำสั่งของรุ่นก่อนหน้าอย่างน้อยในช่วง rollout
ถ้า cell ยังทำงานนานเกินค่า CELLD_DEPLOY_MAX_AGE_S ซึ่งเอกสารกำหนดค่าเริ่มต้นไว้ 60 วินาที celld อาจบังคับย้าย โดยยกเลิกงานที่กำลังทำและปิด WebSocket แบบปกติด้วยรหัส 1012 ส่วน hibernatable WebSocket มีวิธีจัดการต่างออกไป ทีมจึงควรทดสอบกรณี request ยาวและการ reconnect ด้วย ไม่ใช่ดูแค่ว่า node ยังรันอยู่หรือไม่
แนวคิดนี้มีประโยชน์เมื่อทีมต้อง deploy บ่อย แต่ไม่อยากผูกการปล่อยแอปเข้ากับการ restart fleet ทุกครั้ง อย่างไรก็ดี การอัปเกรดตัว celld เองเป็นอีกงานหนึ่ง ซึ่งต้องทำตามข้อกำหนดของแต่ละเวอร์ชัน การเปลี่ยน runtime binary ไม่ใช่การ deploy แอปแบบเดียวกัน
Bindings คล้ายการส่ง dependency ให้แอป แต่ไม่ใช่ DI container
Cloudflare Workers รับ resource ที่ประกาศไว้ใน configuration ผ่าน env เช่น D1 database, queue หรือ object storage เอกสาร Cloudflare เรียกกลไกนี้ว่า bindings ซึ่งเป็นช่องทางให้ Worker เรียก resource หรือ API ที่ runtime จัดเตรียมไว้ Cloudflare Workers bindings
มองในเชิงออกแบบได้ว่า runtime เป็นผู้จัด resource ให้แอป แต่ไม่ควรเรียกสิ่งนี้ว่า DI container เต็มรูปแบบ มันไม่ได้ประกอบ object graph ของแอปหรือแก้ dependency ให้อัตโนมัติ นักพัฒนายังต้องกำหนดว่า service จะเรียก resource เหล่านั้นอย่างไร
ตัวอย่าง Order API อาจแยก business logic ออกจาก SDK ของ runtime ด้วย interface ของตัวเอง:
interface OrderStore {
save(order: Order): Promise<void>;
}
interface ReviewJobs {
publish(orderId: string): Promise<void>;
}
function createOrderService(deps: {
orders: OrderStore;
reviewJobs: ReviewJobs;
}) {
return {
async submit(order: Order) {
await deps.orders.save(order);
await deps.reviewJobs.publish(order.id);
},
};
}
ใน Worker handler ทีมอาจสร้าง D1OrderStore(env.ORDERS_DB) กับ QueueReviewJobs(env.REVIEW_JOBS) แล้วส่งให้ createOrderService() ส่วน unit test ส่ง fake store และ fake queue เข้ามาแทนได้ จุดที่แยกออกคือ business service รู้จัก interface ของงาน ไม่ต้องรู้ว่าการบันทึกใช้ D1 หรือส่งคิวผ่าน SDK ตัวไหน แต่ adapter และ binding configuration ยังต้องเขียนและทดสอบแยกกัน
ถ้าจะย้าย runtime ในอนาคต ต้องทำ adapter ของ resource ปลายทาง และตรวจพฤติกรรมจริงของแต่ละ service เช่น transaction, retry, ordering, timeout และ error handling ชื่อ binding ที่เหมือนกันไม่ได้รับประกันว่า semantics เหมือนกัน
การแยกขอบเขตแบบนี้ช่วยให้ทีมตกลงเจ้าของงานกันได้ง่ายขึ้น: ทีมแอปดูแลกฎธุรกิจและ interface, ทีม platform ดูแล adapter กับการตั้งค่า runtime, ส่วนทีมโครงสร้างพื้นฐานดูแล node, network และ storage งานที่เปลี่ยน adapter จึงมีจุดทดสอบชัด และการเพิ่ม runtime ใหม่ไม่จำเป็นต้องแก้ business logic ทุกจุด นี่เป็นผลจากการออกแบบของทีม ไม่ใช่ความสามารถที่ celld สร้างให้เอง
