PAGI 0.002006: อัปเดตสเปกแก้ปัญหาสตรีมมิงและหลุดการเชื่อมต่อ
เจาะลึกการอัปเดต PAGI 0.002006 และ 0.002007 ที่ปรับปรุงสัญญาการเชื่อมต่อ แก้ปัญหาการสตรีม และทำให้แอปพลิเคชันเขียนโค้ดง่ายขึ้น

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง
- PAGI ออก 4 สเปกและ 4 เซิร์ฟเวอร์ซ้อนกันใน 5 วันเพื่อแก้ปัญหาเรื่อง connection disconnect
- แอปปกติอัปเกรดง่ายมากด้วยกฎ 2 ประโยค คือส่งข้อมูลแล้วพังถ้ารูปแบบผิด และห้ามปล่อยตอบสนองค้างไว้
- ปัญหาระหว่างสตรีมข้อมูลเกิดขึ้นเพราะสเปกเดิมไม่เคยระบุชัดเจนว่าเกิดอะไรขึ้นเมื่อไคลเอนต์หลุดขณะรอส่ง
- สเปกใหม่เลือกใช้แนวทางเดียวกับ Kestrel ของ .NET ผ่านอ็อบเจ็กต์ pagi.connection แทนการพ่น Error ทันที
หากคุณติดตามบันทึกการเปลี่ยนแปลง (changelog) ของ PAGI ในช่วงเดือนนี้ จะเห็นการปล่อยอัปเดตอย่างต่อเนื่องถึง 4 สเปกและ 4 เซิร์ฟเวอร์ภายในระยะเวลาเพียง 5 วัน พร้อมด้วยชื่อหัวข้อที่เต็มไปด้วยคำศัพท์เฉพาะอย่าง settlement, transition order และ RST_STREAM บทความนี้จะมาอธิบายว่าความวุ่นวายทั้งหมดนั้นคืออะไร ทำไมการอัปเดตเกือบทั้งหมดจึงไม่กระทบกับแอปพลิเคชันของคุณเลย และส่วนเล็กๆ ที่เกี่ยวข้องกลับช่วยให้โค้ดเรียบง่ายขึ้นกว่าเดิมอย่างไร
สำหรับแอปพลิเคชันแบบขอข้อมูลและรับคำตอบ (request/response) ทั่วไป บันทึกการอัปเกรดของคุณสั้นมากใน 2 ประโยค คือ การส่งข้อมูลที่รออยู่ (awaited sends) สามารถล้มเหลวได้หากอีเวนต์ของคุณผิดรูปแบบหรือไม่เรียงลำดับ จากเดิมที่บางเส้นทางละเลยไปเฉยๆ และคุณต้องส่งคำตอบให้จบเสมอ เพราะการตอบสนองไม่ครบถ้วนตอนนี้จะแสดงความผิดปกติอย่างชัดเจนแทนการค้างเงียบๆ ส่วนที่เหลือทั้งหมดในโพสต์นี้คือการรับประกันที่คุณจะได้รับโดยไม่ต้องเขียนโค้ดเพิ่มเลยสักบรรทัด
จุดเริ่มต้นของการเปลี่ยนแปลงนี้มาจากงานฝั่งเฟรมเวิร์ก ไม่ใช่ฝั่งเซิร์ฟเวอร์ ระหว่างที่กำลังพัฒนาตัวช่วยสตรีมการตอบสนองบน PAGI ทีมงานต้องเผชิญกับปัญหาสภาวะแข่งกัน (async rabbit hole) สุดคลาสสิก ทุกๆ วิธีแก้ปัญหาการตัดการเชื่อมต่อกลับสร้างปัญหาใหม่ตามมา ตัวช่วยดังกล่าวต้องเพิ่มแฟลกสำหรับตรวจสอบว่าการตัดการเชื่อมต่อเข้ามาในระหว่างเรียกฟังก์ชัน send หรือไม่ รวมถึงแฟลกสำหรับเลื่อนการประกาศตัดการเชื่อมต่อจนกว่าการส่งจะเสร็จสิ้น
สาเหตุหลักมาจากตัวสเปก PAGI มีความแม่นยำสูงในกรณีการตัดการเชื่อมต่อบางรูปแบบ แต่กลับไม่ได้ระบุถึงกรณีที่แอปพลิเคชันสตรีมข้อมูลต้องเจอ นั่นคือการที่ Future ของคำสั่งส่งข้อมูลกำลังรออยู่ (เซิร์ฟเวอร์กำลังหน่วงข้อมูลหรือ backpressure) ในจังหวะที่ไคลเอนต์ตัดการเชื่อมต่อพอดี ไม่มีระบุไว้เลยว่ามันจะสิ้นสุดอย่างไร แอปพลิเคชันถูกบังคับให้ต้องรอคำสั่ง send ทุกครั้ง ดังนั้นเซิร์ฟเวอร์ที่ปล่อยให้ค้างเติ่งจึงทำให้แอปมาตรฐานต้องหยุดชะงัก (deadlock) ทุกเฟรมเวิร์กจึงต้องคิดวิธีแก้ของตัวเองขึ้นมา ซึ่งนั่นคือเหตุผลที่ไม่มีวิธีไหนเวิร์กเลย

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง
การจัดการปัญหาการตัดการเชื่อมต่อ (disconnect) ในระบบเว็บแบบอะซิงโครนัสถือเป็นหนึ่งในโจทย์ที่ซับซ้อนที่สุดสำหรับนักพัฒนาเว็บเบื้องหลัง เนื่องจากเครือข่ายไม่ได้ส่งสัญญาณบอกทันทีเสมอไป การที่สเปกอย่าง PAGI เข้ามาสร้างสัญญา (contract) ที่ชัดเจนระหว่างเซิร์ฟเวอร์และแอปพลิเคชัน จึงช่วยลดภาระของนักพัฒนาเฟรมเวิร์กในการเขียนโค้ดรับมือกับบั๊กเฉพาะกิจซ้ำๆ
ก่อนที่จะเขียนข้อกำหนดใหม่ ทีมงานได้ศึกษาแนวทางจากระบบนิเวศอื่นๆ เช่น ASGI ซึ่งเป็นญาติสนิทของ PAGI ที่เลือกแนวทางระบุว่าการส่งข้อมูลบนการเชื่อมต่อที่ปิดไปแล้วควรจะโยน Error ขึ้นมา (raise) ฝั่ง Uvicorn เคยนำไปใช้งานแต่กลับทำให้แอปพลิเคชัน FastAPI ที่ใช้งานจริงพัง จนต้องยกเลิกในเวลาต่อมา ส่วน Kestrel ของ .NET มีแนวทางที่เสถียรกว่า คือหลังไคลเอนต์ตัดการเชื่อมต่อ การเขียนข้อมูลตอบกลับจะถูกละทิ้งเงียบๆ และใช้สัญญาผ่านโทเค็นยกเลิก RequestAborted แทน ซึ่งเทียบเท่ากับอ็อบเจ็กต์ pagi.connection ของ PAGI
ดังนั้น PAGI เวอร์ชัน 0.002006 จึงเข้ามาเติมเต็มหลักการที่มีอยู่เดิม แทนที่จะเป็นการพลิกกลับแนวทาง โดยรูปแบบการใช้งานใหม่สำหรับการสตรีมไฟล์ขนาดใหญ่ช่วยให้หยุดการทำงานทันทีที่ไคลเอนต์หายไป โดยไม่ต้องเขียน try/catch ครอบทุกคำสั่งส่งข้อมูล
"Awaited sends can now fail if your events are malformed or out of sequence and always finish your responses."
PAGI Changelog
ที่มา: Dev.to
พบข้อมูลผิดพลาดในบทความนี้? แจ้งปัญหาบทความนี้
ความคิดเห็น
แสดงความคิดเห็น