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

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง
- ข้อผิดพลาดทางการเงินส่วนใหญ่ไม่ได้เกิดจากสูตรคำนวณแต่มาจากปัญหา Concurrency
- การอ่านและเขียนข้อมูลพร้อมกันทำให้ยอดเงินสูญหายโดยไม่มีการแจ้งเตือนในระบบ CRUD
- ที่เก็บข้อมูล ledger-core บน GitHub ช่วยจำลองปัญหาเหล่านี้ด้วย 3 กลยุทธ์การจัดการ
- การบังคับใช้ความถูกต้องในตัวสร้างและการใช้หน่วยย่อยที่เป็นจำนวนเต็มช่วยป้องกันข้อผิดพลาด
ในระบบชำระเงิน บั๊กส่วนใหญ่ที่พบไม่ได้มาจากข้อผิดพลาดทางตรรกะ สูตรการคำนวณถูกต้อง การปัดเศษถูกต้อง และชุดการทดสอบทั้งหมดผ่าน แต่เงินกลับสูญหายไปอย่างเงียบๆ ปัญหานี้เกิดขึ้นเมื่อมีผู้ใช้งานสองคนทำธุรกรรมพร้อมกันในเสี้ยววินาทีเดียว
การทำงานร่วมกับระบบธุรกรรมและการชำระเงินเผยให้เห็นว่า ความผิดพลาดลักษณะนี้มีความเงียบเชียบในการแสดงผล ข้อผิดพลาดประเภท Null Reference จะโยนข้อยกเว้นออกมาทันที และสูตรที่ผิดจะปรากฏตั้งแต่การทดสอบรอบแรก ทว่าการอัปเดตที่สูญหายกลับทำให้ตัวเลขผิดเพี้ยนไปเล็กน้อยในบัญชีเดียวและวันเดียว จนกว่ากระบวนการกระทบยอดจะทำงานและพบว่าเงินจำนวนสี่ร้อยดอลลาร์หายไปไหน

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง
เพื่อทำให้ความล้มเหลวดังกล่าวปรากฏชัดเจน จึงมีการพัฒนาที่เก็บข้อมูลขนาดเล็กชื่อ ledger-core บน GitHub ซึ่งเป็นระบบบัญชีแบบบันทึกรายการคู่ที่มีกลยุทธ์การจัดการการทำงานพร้อมกันสามรูปแบบภายใตกินเตอร์เฟซเดียว พร้อมชุดการทดสอบที่พิสูจน์ว่ากลยุทธ์ใดรักษาเงินไว้ได้และกลยุทธ์ใดทำเงินหาย
ปัญหา Race Condition ในระบบการเงินเป็นหนึ่งในภัยเงียบที่น่ากลัวที่สุดสำหรับนักพัฒนา เนื่องจากระบบอัตโนมัติในปัจจุบันมักรันเธรดคู่ขนานจำนวนมากเพื่อรองรับปริมาณธุรกรรมที่สูง การพึ่งพา Unit Test แบบดั้งเดิมจึงไม่เพียงพอ เพราะการทดสอบส่วนใหญ่มักรันแบบซิงโครนัสหรือจำลองสถานการณ์การชนกันของข้อมูลได้ไม่ดีพอ นักพัฒนาจึงต้องออกแบบกลยุทธ์การล็อกข้อมูลหรือใช้กลไก Concurrency Control ที่รัดกุมตั้งแต่ระดับสถาปัตยกรรม
กฎเหล็กเพียงข้อเดียวของระบบบัญชีคือ ผลรวมของยอดคงเหลือทั้งหมดหลังจากทำธุรกรรม N รายการต้องเท่ากับผลรวมก่อนหน้า ธุรกรรมมีหน้าที่ย้ายเงินเท่านั้น โดยจะไม่สร้างหรือทำลายเงินขึ้นมาใหม่
รูปแบบการอ่านข้อมูล คำนวณค่าใหม่ และเขียนกลับแบบดั้งเดิมถือเป็นวิธีที่พบได้ทั่วไปแต่มันใช้งานไม่ได้จริงเมื่อมีเธรดสองเธรดอ่านยอดเงิน 1,000 พร้อมกัน เธรดแรกบวกเพิ่ม 100 และเขียน 1,100 ในขณะที่เธรดที่สองลบออก 50 และเขียน 950 การเขียนครั้งที่สองจะทับลงไป ทำให้การทำธุรกรรมครั้งแรกหายไปอย่างไร้ร่องรอย
"That SpinWait is the honest part. The window exists in real code too. It is just narrower, which means you hit it on a Tuesday in production instead of every time in CI."
ผู้พัฒนา ledger-core
ที่มา: Dev.to
พบข้อมูลผิดพลาดในบทความนี้? แจ้งปัญหาบทความนี้
ความคิดเห็น
แสดงความคิดเห็น