ความน่าเชื่อถือรันไทม์ของ Rust/Wasm จำเป็นต้องจัดการกับทั้งความตื่นตระหนกและยกเลิกการกู้คืน
เมื่ออินสแตนซ์ Wasm ที่ใช้ร่วมกันเริ่มรับสายเป็นเวลานาน ข้อขัดข้องจะเพิ่มขึ้นจากความล้มเหลวเพียงครั้งเดียวไปสู่การกู้คืนสถานะและปัญหาการแยกข้อบกพร่อง
Wasm ถือได้ว่าเป็นเลเยอร์การย้ายอย่างง่ายดายในตอนแรก: สามารถตั้งโปรแกรมโค้ดได้, เพจสามารถทำงานได้, ประสิทธิภาพก็โอเค และสิ่งต่าง ๆ ดูเหมือนจะใกล้เคียงกัน มันเริ่มยากขึ้นจริงๆ โดยปกติหลังจากผ่านการสาธิตไปแล้ว เมื่อโมดูล เช่น ตัวแก้ไข ตัวเรนเดอร์ และตัวแยกวิเคราะห์เอกสารย้ายจากการทดสอบหน้าเดียวไปเป็นรันไทม์แบบอาศัยระยะยาว โมเดลข้อบกพร่องจะเปลี่ยนไปทันที
ในเวลานี้ ความตื่นตระหนกและการยกเลิกไม่ใช่ข้อยกเว้นในเลเยอร์ภาษาอีกต่อไป สิ่งที่พวกเขาตัดสินใจคือ อินสแตนซ์นี้จะสามารถรับงานต่อๆ ไปได้หรือไม่ สถานะในหน่วยความจำมีการปนเปื้อนหรือไม่ เลเยอร์โฮสต์ควรละทิ้งอินสแตนซ์ทันทีหรือไม่ และจำเป็นต้องเติมพูลอินสแตนซ์หรือไม่ เมื่อทีมงานเคลื่อนที่ย้ายเคอร์เนลที่ทำงานในคอนเทนเนอร์ดั้งเดิมมาเป็นเวลานานไปยังเว็บ การเปลี่ยนแปลงระดับนี้เองที่ประเมินต่ำไปได้ง่ายที่สุด
หลังจากผ่านการสาธิตแล้ว Fault Model เพิ่งเริ่มต้นขึ้น
การขัดข้องในการโทรเพียงครั้งเดียวไม่ใช่เรื่องยากที่จะเข้าใจ การคลิกปุ่มจะทำให้เกิดการโทร Wasm หากล้มเหลว จะมีการรายงานข้อผิดพลาดสำหรับการดำเนินการ รีเฟรชหน้านี้แล้วลองอีกครั้ง ต้นทุนยังคงควบคุมได้
ปัญหาเกิดขึ้นหลังจากที่รันไทม์เริ่มนำอินสแตนซ์กลับมาใช้ใหม่ เมื่ออินสแตนซ์ Wasm เดียวกันเปิดเอกสารหลายชุดอย่างต่อเนื่อง รับเหตุการณ์อินพุตหลายรอบ และผ่านการเรียกบริดจ์ JS หลายครั้ง ขอบเขตของอิทธิพลของความตื่นตระหนกและการยกเลิกจะไม่หยุดอยู่ที่การดำเนินการปัจจุบันอีกต่อไป ความล้มเหลวที่ไม่สมบูรณ์อาจลากคำขอที่ตามมาลงมา
ความเสี่ยงดังกล่าวมักไม่เปิดเผยในวันแรก ในระยะแรก คุณมักจะเห็นเฉพาะรายงานข้อผิดพลาดที่กระจัดกระจาย เช่น การแสดงผลล้มเหลวเป็นครั้งคราว การส่งออกบางอย่างค้าง และเอกสารบางอย่างอยู่ในสถานะที่ไม่ถูกต้องหลังจากถูกปิดและเปิดใหม่ หากคุณตรวจสอบเพิ่มเติม เบาะแสจะค่อยๆ มาบรรจบกันเป็นปรากฏการณ์เดียวกัน แม้ว่าความล้มเหลวจะเกิดขึ้นในสายโซ่การโทร แต่ความเสียหายยังคงอยู่ในอินสแตนซ์ที่ใช้ร่วมกัน
ณ จุดนี้ จุดเน้นของการสนทนาไม่ได้อยู่ที่ “ว่าโค้ด Rust จะตื่นตระหนกหรือไม่” อีกต่อไป แต่เป็น “ว่ารันไทม์นี้มีคุณสมบัติเหมาะสมหรือไม่ที่จะให้บริการการโทรครั้งถัดไปหลังจากการตื่นตระหนก”
จับความตื่นตระหนกได้ ยกเลิกได้เพียงเปลี่ยนอินสแตนซ์เท่านั้น
สิ่งที่สำคัญที่สุดที่ต้องแยกออกจากกันใน Rust/Wasm คือความหมายของความล้มเหลวสองประการของการตื่นตระหนกและการยกเลิก
ความตื่นตระหนกยังมีโอกาสที่จะผ่อนคลายกลับไปตามขอบเขตที่กำหนดไว้ ตราบใดที่ Binding Layer และ Host Layer ตกลงเกี่ยวกับวิธีการกู้คืนล่วงหน้า การเรียกปัจจุบันอาจล้มเหลว และสถานะอื่นๆ ในอินสแตนซ์ก็สามารถรักษาไว้ได้ การทำแท้งไม่ใช่วิธีที่จะไปเลย หมายความว่าการดำเนินการปัจจุบันถึงสถานะที่ไม่สามารถกู้คืนได้ หากคุณยังคงใช้อินสแตนซ์เดิมเพื่อรับคำขอ คุณจะต้องเดิมพันว่าหน่วยความจำและทรัพยากรจะไม่ได้รับความเสียหายครึ่งทาง
เมื่อทั้งสองผสมกันระหว่างรันไทม์ ปัญหาจะเกิดขึ้นอย่างแน่นอนในการประมวลผลครั้งต่อไป:
- Swallow ยกเลิกตามข้อยกเว้นปกติ และกลุ่มอินสแตนซ์จะยังคงใช้ออบเจ็กต์ที่สูญเสียความน่าเชื่อถือต่อไป
- ปฏิบัติต่อความตื่นตระหนกทั้งหมดราวกับว่าอินสแตนซ์จะต้องถูกทำลาย และปริมาณงานจะลดลงโดยไม่จำเป็น
- โฮสต์ JS รู้เพียงว่า “การโทรล้มเหลว” แต่ไม่รู้ว่าจะลองใหม่ สูญเสียอินสแตนซ์ หรือตัดเซสชันปัจจุบันออก
นี่เป็นสิ่งที่สมจริงที่สุดเกี่ยวกับความน่าเชื่อถือรันไทม์ของ Wasm: ต้องกำหนดซีแมนทิกส์การกู้คืนก่อนจึงจะสามารถดำเนินการแยกและกำหนดเวลาในภายหลังได้
หากเลเยอร์การรวมไม่มีซีแมนทิกส์การกู้คืน เลเยอร์โฮสต์จะเข้าสู่สถานะที่ไม่ดีและยอมรับงานต่อไป
สถานที่ที่อันตรายที่สุดสำหรับปัญหาประเภทนี้ไม่ได้อยู่ในรหัสธุรกิจ แต่อยู่ในชั้นที่มีผลผูกพันซึ่งดูเหมือนว่าจะ “ได้รับการดูแลแล้ว” เลเยอร์โฮสต์มักจะเห็นเฉพาะออบเจ็กต์ข้อผิดพลาดที่ส่งออกมา และบันทึกว่าเป็นความล้มเหลวในการโทรตามปกติ บันทึกอยู่ที่นั่นและเพจไม่ขัดข้องทันที แต่ระบบอาจทิ้งสถานะที่ไม่ถูกต้องไว้ภายในอินสแตนซ์
สิ่งที่ต้องแก้ไขจริงๆ ไม่ใช่แค่ลอง/จับ แต่รวมถึงการดำเนินการจัดการหลังจากเกิดความล้มเหลว ลอจิกที่คล้ายกับต่อไปนี้เพิ่งเริ่มเข้าสู่การออกแบบความน่าเชื่อถือ:
async function runWithRecovery(instance, input) {
try {
return await instance.exports.handle(input)
} catch (error) {
if (isAbort(error)) {
pool.replace(instance.id)
}
throw error
}
}
จุดเน้นของโค้ดนี้ไม่ได้อยู่ที่ไวยากรณ์ แต่เป็นการตัดสินง่ายๆ ว่าความล้มเหลวในปัจจุบันได้ทำเครื่องหมายอินสแตนซ์นี้เป็นวัตถุที่ไม่น่าเชื่อถือหรือไม่ หากคำตอบคือใช่ การดำเนินการกู้คืนไม่ควรหยุดอยู่ที่การทิ้งข้อผิดพลาด แต่ควรดำเนินต่อไปจนถึงการกำจัดอินสแตนซ์ การสร้างทรัพยากรขึ้นใหม่ และการร้องขอการตัดโฟลว์
ตราบใดที่เลเยอร์นี้ไม่ได้กำหนดไว้อย่างชัดเจน ระบบจะดูเหมือนว่าจะจัดการกับข้อผิดพลาด แต่จริงๆ แล้วสิ่งที่กำลังทำอยู่คือการนำรันไทม์ที่อาจเสียหายกลับเข้าไปในเส้นทางที่ใช้งานจริง
อินสแตนซ์ที่ใช้ร่วมกันจะขยายปัญหาการกู้คืนให้กลายเป็นปัญหากลยุทธ์การรวมกลุ่ม
หลังจากที่ Wasm ใส่ลงในสินค้าจริงแล้ว แทบจะไม่มี “อินสแตนซ์เดียวจนกว่าเพจจะถูกปิด” โดยทั่วไปคือพูลอินสแตนซ์ พูลผู้ปฏิบัติงาน หรือเอกสารเบื้องหน้าและงานเบื้องหลังที่แชร์ชุดทรัพยากรรันไทม์ ในขั้นตอนนี้ ค่าใช้จ่ายในการฟื้นตัวจากความตื่นตระหนกและการยกเลิกจะเขียนกลยุทธ์การรวมกลุ่มโดยตรง
หากการเริ่มต้นอินสแตนซ์มีราคาแพง ระบบมักจะนำอินสแตนซ์นั้นกลับมาใช้ใหม่ให้ได้มากที่สุด แต่เมื่อมีการนำกลับมาใช้ใหม่แล้ว จะต้องอัปเกรดการแยกข้อผิดพลาดพร้อมกัน:
- สถานะใดที่สามารถหยุดการทำงานได้ในการโทรเพียงครั้งเดียว และจะถูกละทิ้งพร้อมกับการโทรหลังจากเกิดความล้มเหลว
- แคชใดที่ได้รับอนุญาตให้เก็บรักษาไว้ระหว่างการโทร และแคชใดจะต้องใช้ไม่ได้อย่างสมบูรณ์เมื่อพบการยกเลิก
- หลังจากแทนที่อินสแตนซ์แล้ว งานที่อยู่ในคิวจะถูกย้ายอย่างไร การลองใหม่จะทำให้เกิดผลข้างเคียงสองครั้งหรือไม่?
นี่ไม่ใช่คำตอบที่เลเยอร์ภาษาจะส่งโดยอัตโนมัติ เป็นการออกแบบรันไทม์
ด้วยเหตุนี้ หากการถกเถียงเรื่องความน่าเชื่อถือของ Rust/Wasm หยุดแค่เพียง “สามารถจับความตื่นตระหนกได้หรือไม่” ก็เป็นเรื่องง่ายที่จะประเมินปัญหาต่ำไป สิ่งที่ทำให้ช่องว่างต้นทุนการบำรุงรักษากว้างขึ้นจริงๆ คือกลุ่มอินสแตนซ์สามารถรักษาขอบเขตความน่าเชื่อถือที่ชัดเจนหลังจากเกิดความล้มเหลวได้หรือไม่
ขอบเขตที่ใช้บังคับมีความเกี่ยวข้องอย่างมากกับวงจรชีวิต
การออกแบบการบูรณะชุดนี้ไม่จำเป็นสำหรับทุกโครงการ Wasm
หากโมดูลเป็นเพียงเครื่องมือออฟไลน์ที่ทำเพียงครั้งเดียว หรืออินสแตนซ์ทั้งหมดถูกนำกลับมาใช้ใหม่เมื่อเพจถูกทำลาย ความแตกต่างระหว่างความตื่นตระหนกและการยกเลิกจะยังคงอยู่ แต่ประโยชน์ในการกู้คืนจะมีน้อยกว่ามาก บ่อยครั้งเพียงพอที่จะรีเฟรชเพจโดยตรงและรันงานใหม่โดยตรง
เมื่อระบบมีคุณสมบัติดังต่อไปนี้ ความหมายของการกู้คืนจะเปลี่ยนจาก “รายการเพิ่มประสิทธิภาพ” เป็น “รายการโครงสร้างพื้นฐาน” อย่างรวดเร็ว:
- อินสแตนซ์จะคงอยู่เป็นเวลานานและไม่ถูกทำลายพร้อมกับวงจรชีวิตหน้าเดียว
- อินสแตนซ์เดียวกันจะรับสายหลายรอบอย่างต่อเนื่อง
- เลเยอร์โฮสติ้งจำเป็นต้องใช้การรวมกลุ่มเพื่อแลกกับเวลาเริ่มต้นและปริมาณงาน
- ปกป้องสถานะเซสชัน สถานะแคช และงานที่อยู่ในคิวหลังจากเกิดความล้มเหลว
เมื่อทีมเคลื่อนที่ย้ายความสามารถดั้งเดิมไปยังเว็บ ขอบเขตนี้มีแนวโน้มที่จะเกิดขึ้นมากที่สุด ความสัมพันธ์แบบแยกเดี่ยวที่แต่เดิมสร้างไว้ตามค่าเริ่มต้นในกระบวนการแอป มักจะต้องกรอกอีกครั้งหลังจากถึงขอบเขตโฮสต์ JS/Wasm
Wasm ช่วยให้โค้ดเนทีฟเข้าสู่เบราว์เซอร์ได้ง่ายขึ้น แต่ไม่ได้นำซีแมนทิกส์การกู้คืนรันไทม์มาด้วย ทันทีที่ระบบเริ่มแชร์อินสแตนซ์ สถานะใช้ซ้ำ และรับสายระยะยาว ความตื่นตระหนกและการยกเลิกจะต้องถือเป็นเหตุการณ์รันไทม์ที่แตกต่างกันสองเหตุการณ์ แบบแรกสนใจว่าจะวางสายปัจจุบันอย่างไร และแบบหลังสนใจว่าอินสแตนซ์นี้สามารถอยู่ในพูลต่อไปได้หรือไม่ หากไม่ทำการตัดสินก่อน ยิ่งการปลูกถ่ายรหัสประสบความสำเร็จมากเท่าใด การจัดการกับความล้มเหลวที่ตามมาก็จะยิ่งยากขึ้นเท่านั้น
What to read next
Want more posts about iOS?
Posts in the same category are usually the best next step for reading more on this topic.
View same categoryWant to keep following #iOS?
Tags are useful for related tools, specific problems, and similar troubleshooting notes.
View same tagWant to explore another direction?
If you are not sure what to read next, return to the homepage and start from categories, topics, or latest updates.
Back home