ปัญหาการโอนเงินไม่ผ่านแต่ผ่านกับ Two Generals' Problem

เมื่อหลายวันก่อนเราไปซื้อชานมไข่มุก เราจ่ายเงินด้วย QR Code ปกติเลย พอกดยืนยัน ปรากฏว่า App ธนาคารบอกว่า เกิดข้อผิดพลาด จะให้เรากดโอนใหม่ แต่สักพัก Line Notification ขึ้นว่ามีเงินออก เครื่อง EDC ก็เขียนว่าได้รับการจ่ายเงินแล้ว เอ๊ะไหงเป็นแบบนั้นละ ปัญหาลักษณะนี้ไม่ได้เกิดขึ้นแค่กับการโอนเงิน แต่เกิดกับหลายเรื่องเยอะมาก ในทางคอมพิวเตอร์ เราเรียนในปัญหาที่เรียกว่า Two Generals' Problem

Two Generals' Problem คืออะไร ?

เพื่อให้เข้าใจปัญหานี้ง่ายขึ้น เราขอยกตัวอย่างเหตุการณ์ดีกว่า สมมุติว่า นายพล A และ B ได้รับมอบหมายจากราชา O ให้บุกเมือง G จากการวางแผนอันสุดยอด แยบยล ได้ออกมาว่า การที่จะทำให้ G แตกได้ เราจะต้องบุกจาก ทั้งสองด้านของ G พร้อม ๆ กัน หากบุกจากเพียงด้านใดด้านหนึ่ง จะทำให้ทีมที่บุก ตุยหมด และแผนการจะล้มเหลวนั่นเอง

ตอนนี้นายพล A และ B ได้นำทัพทหารมารอที่ 2 ฝั่งของเมือง G ละ เข้าเงื่อนไขที่ 1 เรียบร้อย แต่ นายพลทั้งสองจะตกลงกันอย่างไรว่า จะเริ่มบุกเมื่อไหร่ให้การบุกเกิดขึ้นพร้อม ๆ กัน เพื่อให้แผนการณ์สำเร็จ

ก็ง่าย ๆ ใช่มะ งั้น นายพล A สั่งให้พลม้าวิ่ง ไปบอกฝั่งนายพล B ว่า เห้ย ๆ เราจะบุกกันตอน 8 โมงเช้าพรุ่งนี้นะ ผลม้าก็จะวิ่งจากฝั่งทัพนายพล A ผ่านเมือง G ไป ที่อีกฝั่งที่ทัพนายพล B รออยู่ เมื่อถึงก็จะแจ้งสารให้นายพล B แล้วจึงวิ่งผ่านเมืองกลับไปหานายพล A บอกว่า ส่งสารเรียบร้อยแล้วครับ

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

หรืออาจจะส่งถึงนายพล B จริง ๆ แต่ขาวิ่งผ่านเมืองกลับมาหานายพล A เพื่อแจ้งข่าวว่า นายพล B ได้รับสารเรียบร้อยแล้ว อาจจะไม่เกิดขึ้น ก็ทำให้นายพล A ขาดข้อมูลตรงนี้ไป ถ้าเป็นเรา เราจะทำยังไงกับมันดีละ เพื่อการันตีว่าทั้งสองฝั่งสามารถ รับส่งสารได้ 100%

มันทำได้จริงเหรอ ?

เอาเข้าจริง สั้น ๆ คือ เป็นไปไม่ได้ หากเราบอกว่า วิธีการที่เราต้องการ จำเป็นต้องให้ทั้งสองฝั่งได้ข้อมูลตรงกัน 100% กล่าวคือ นายพล A และ B ทราบเวลาในการโจมตีทั้งคู่ ได้ 100% หรือไม่พลาดเลย เราเรียกว่าวิธีการนี้มัน Deterministic

กลับไปในเคสที่พลาดกัน หากสารไม่สามารถส่งได้ หรือ ฝั่งคนส่งไม่สามารถทราบได้ว่าอีกฝั่งได้รับสารหรือยัง การโจมตีย่อมไม่เกิดขึ้นใช่ป่ะ เพราะถ้าเราเลือกที่จะโจมตีหรือไม่ก็ได้ นั่นแปลว่า วิธีการนี้มันไม่ Determisistic แปลว่า อ้าว มันขัดกันเองนิ นั่นจึงทำให้ หากเราต้องการที่จะมั่นใจเลยว่า ทั้งสองฝั่งนั้นจะได้รับสารแน่นอน 100% ผ่านกระบวนการที่เราเล่ามา จึงเป็นเรื่องที่เป็นไปไม่ได้เลย

มันเกี่ยวอะไรกับการโอนเงิน

กลับมาที่ต้นไอเดียบทความนี้คือ การโอนเงิน โดยปกติ เมื่อเรากดโอนเงิน โทรศัพท์ของเราจะต้องติดต่อกลับไปที่ ธนาคาร บอกว่า โอเค เราจะโอนเงินจากบัญชีของเราไปที่ บัญชีปลายทางนี้นะ จากนั้นระบบธนาคารจะทำการโอนเงินตามที่ร้องขอ และเมื่อการโอนเงินเสร็จสมบูรณ์ ธนาคารจะส่งข้อมูลกลับมาที่โทรศัพท์เราว่า โอเค การโอนเงินเสร็จสมบูรณ์

หากเปรียบเทียบใน Two Generals' Problem โทรศัพท์ของเราคือนายพล A และ ธนาคารคือนายพล B และ เมือง G ตรงกลางก็คือ Internet ที่บางครั้ง ข้อมูลอาจมีการตกหล่นระหว่างทางได้

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

การโอนเงิน เป็นการยกตัวอย่างหนึ่งเท่านั้น แต่จริง ๆ แล้ว Two Generals' Problem นั้นถูกนำไปเป็นปัญหาที่เราได้เรียนกันในวิชา Computer Network พูดถึงการสื่อสารระหว่างเครื่องคอมพิวเตอร์ 2 เครื่อง ว่า มันมีกรณีบางตัวที่ทำให้การสื่อสารบางอย่างมันไม่สำเร็จ แล้วเราจะจัดการปัญหานี้อย่างไร

Engineer came to rescue!

ในเมื่อมันเป็นปัญหาที่เราแก้ไม่ได้ เราจะทำอย่างไรละทีนี้ ถ้าเราคิดเร็ว ๆ ถ้าเราฝากความหวังกับ พลม้า มากกว่า 1 คนละ ถ้าเราฝากไว้กับคนเดียวหากเกิดอะไรกับพลม้าคนนั้นก็คือ ชิบหาย แต่ถ้าเราฝากไว้สัก 100 คนละ เราขอแค่วิ่งส่งและกลับมาแค่คนเดียวก็จบแล้ว โอกาสพลาดจริง ๆ ย่อมน้อยกว่าแน่นอนใช่มะ หรือก็คือ วิธีการนี้ เรายอมให้เกิดความไม่แน่นอน ได้

แต่หากเราคิดถึง Worst Case จริง ๆ ว่า หากนายพล A ใช้พลม้า 100 คน และทั้งหมดนั่น ไม่มีใครรอดกลับมารายงานได้สำเร็จเลยละ ใช่ครับ มันเกิดขึ้นได้ใช่มะ ดังนั้นปัญหานี้ก็ยังไม่ถูกแก้ 100% อยู่ดี

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

วิธีการนั้นง่ายมาก คือ เราแค่แปะตัวเลขไว้ที่คนส่ง 1,2,3 ไปเรื่อย ๆ จนถึงคนสุดท้าย ฝั่งนายพล B อาจจะได้รับคนที่ 1 แล้วอยู่ ๆ ได้รับคนที่ 4 เลย ทำให้อนุมานได้ว่า อ่อ คนที่ 2 และ 3 ก็คือ ตุย เรียบร้อยไปแล้ว กลับกันฝั่งนายพล A อาจจะได้รับการยืนยันจาก 4 คนเดียวก็ทำให้นายพล A พอจะเดาได้ละว่า อ่อ โอกาสรอดมีเพียง 25% เท่านั้นนะ นอกจากนั้น นายพล A อาจจะเก็บเวลาที่ พลม้า แต่ละคนออก เมื่อกลับมา เราสามารถเอาเวลาที่กลับมา ลบกับ เวลาตอนออกไป ก็ทำให้เราพอรู้เพิ่มอีกว่า ระยะเวลาที่ พลม้าวิ่งไปกลับใช้เวลาเท่าไหร่ ทำให้กะเวลาในการส่งได้ถูกต้องมากขึ้น มีประสิทธิภาพมากขึ้นนั่นเอง

การแก้ปัญหาลักษณะนี้ ถูกนำไปใช้กับหลักการสื่อสารหลากหลายอย่างมาก ในการโอนเงิน ผู้ออกแบบจะต้องยอมรับว่า มันอาจเกิดความผิดพลาดในการสื่อสารได้เช่นกัน ต้องมีกระบวนการบางอย่างออกมาเพื่อป้องกัน สมมุติว่า เราสั่งโอนเงินไป 100 บาท คำสั่งส่งถึงธนาคารแล้ว ธนาคารตัดเงินเพื่อโอนให้ปลายทางเรียบร้อย แต่ส่งกลับไม่ถึงเรา อี App ก็ดันฉลาด เห็นธนาคารไม่ส่งกลับมา งั้นเราส่งคำสั่งโอนใหม่เลยละกัน นั่นแปลว่า กรณีนี้ เงินที่โอนคือ 200 บาท เกิดจากการโอน 100 บาท 2 รอบ ซึ่งมันไม่ถูกต้อง

การแก้ปัญหาเหมือนเดิมคือ ระบบจะต้องสร้าง Key บางอย่างขึ้นมา เราเรียกว่า Idenpotency Key เช่น Transaction ID โดยมีเงื่อนไขว่า เลขหรือข้อความที่ใช้ระบุมันจะต้อง Unique สมมุติว่า เราส่งไป Transaction ID = 1 คำสั่งคือ โอนเงิน 100 บาท หากเกิดเหตุการณ์ส่งซ้ำอีก ธนาคารจะเห็นว่า อ่อ Transaction ID 1 นี่มันทำไปแล้ว งั้นเราไม่ทำซ้ำแล้วนะ แล้วส่งกลับไปบอกต้นทางว่า เห้ย ๆ นายเราทำไปแล้ว ไม่ต้องส่งซำ้แล้ว

สรุป

ดังนั้นการแก้ปัญหา Two Generals' Problem จริง ๆ คือ การยอมรับให้การสื่อสารเกิดความผิดพลาดขึ้นได้ พร้อมกับการใช้ Idenpotency Key เข้ามาช่วยแก้ปัญหานี้ได้ มันดูเป็นปัญหาที่ ห๋ะ อะไรนะแต่เอาจริง ตอนเรียนก็คือ เห้ย เชี้ย แมร่ง พอไปซื้อชานมแล้วเจอเรื่องโอนเงินเลยทำให้คิดถึงเรื่องนี้ขึ้นมา เลยเอามาเล่าให้อ่านกันนี่แหละ จากชานม กลายมาเป็นเรื่องแบบนี้ได้ไงเนี่ย