Photo by Joanna Kosinska on Unsplash

หัวข้อด้าน Distributed System เป็นอะไรที่สนุก แต่เข้าใจยากมาก

หนึ่งในเรื่องที่ผมอ่านหลายรอบกว่าจะเก็ต ก็คือเรื่องของ CAP Theorem ซึ่งเป็นแนวคิดในการ Trade-off คุณสมบัติของ Distributed System ที่เราออกแบบ

“CAP” เป็นตัวย่อจาก Consistency, Availability, Partition Tolerant โดย Theorem นี้ได้กล่าวไว้ว่า คุณไม่มีทางที่่จะออกแบบระบบโดยมีคุณสมบัติ 3 ข้อนี้ พร้อมกัน อย่างเก่งก็ได้แค่ 2 ใน 3

แต่ทำไมถึงเป็นอย่างนั้นล่ะ?

วันนี้เราจะมาทำความเข้าใจกับเจ้า Theorem นี้กัน ผ่านการเรื่องราวของบริษัท “สุธีการจำ” !!

Credit: บทความนี้ใช้ไอเดียจากบล็อคของ Kaushik Sathupadi ผมเปลี่ยนลำดับการเล่าเรื่องกับเนื้อหาพอสมควร แต่ใจความเดียวกัน

บริษัทสุธีการจำ

สุธีเป็นคนที่มีความจำเป็นเลิศ ตั้งแต่สมัยเรียน เขาได้ท็อบวิชาท่องจำมาตลอด

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

สุธีจึงตัดสินใจเดินตาม Passion ของตัวเอง โดยเปิดบริษัทสตาร์ทอัพชื่อ “สุธีการจำ”

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

ตัวอย่างเช่น มีคุณลูกค้าชื่ออรุชโทรมา อยากให้จำเบอร์โทรกิ๊ก “085-123-4567” สุธีก็จะบันทึกไว้ในสมอง หลังจากนั้น คุณอรุชสามารถโทรหาบริษัทเพื่อขอเบอร์กิ๊กเมื่อไรก็ได้

ถ้าเราส่องสมองของสุธี เราจะเห็นข้อมูลของลูกค้าที่ถูกจำในรูปแบบตาราง ดังนี้

ชื่อลูกค้า ประโยคที่จำ
อรุช เบอร์น้องกิ๊ก 085-123-4567
กุ๊กไก่ ไหสมบัติของคุณปู่ฝังไว้ที่ 55° 20' 15'' W
ไข่เจียว เจอแฟนครั้งแรกวันที่ 15 ธันวา 2559

คุณภาพของบริการนี้ถูกการันตีด้วยสัญญา 2 อย่างที่ให้กับลูกค้า คือ Consistency กับ Availability

โดยเรื่องของ Consistency สุธีตั้งนิยามว่า เมื่อมีการจำข้อมูลเรียบร้อยแล้ว ลูกค้าคนเดิมที่โทรกลับมา จะได้ประโยคที่จำไว้ถูกต้อง 100%

ส่วน Availability คือเมื่อโทรติด คุณจะได้รับบริการแน่นอน

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

ธุรกิจไปได้สวย

บริษัทสุธีการจำ ได้รับการตอบรับจากลูกค้าเป็นอย่างดี คนโทรหาสุธีเยอะมากจนรับไม่ทัน

แต่ละชั่วโมง สุธีสามารถตอบลูกค้า (ทั้งการจำสิ่งใหม่ๆ และตอบสิ่งที่จำไว้) ได้แค่ประมาณ 60 คน แต่โดยเฉลี่ย มีลูกค้าโทรมาประมาณ 100 คนต่อชั่วโมง ทำให้สูญเสียโอกาสทางธุรกิจไปเยอะมาก

สุธีจึงจ้าง"นักจำ"อีกคนมาช่วย ชื่อนัท

นัทนั้นมีความจำเป็นเลิศเช่นเดียวกับสุธี การได้นัทมาช่วยทำให้สุธีอุ่นใจมาก แต่การเข้ามาของนัท ทำให้เกิดปัญหา Consistency ขึ้น

ลูกค้าไม่ได้ข้อมูลที่ให้จำไว้

เหตุเกิดเมื่อมีลูกค้าอีกคนโทรมาหาสุธีเพื่อทำการจำเบอร์โทรกิ๊ก หลังจากนั้นไม่นาน ลูกค้าคนนั้นก็โทรกลับมาเพื่อถามเบอร์โทร

ในเวลานั้น สุธีกำลังติดสายลูกค้าอีกคนอยู่ นัทจึงรับสายแทน ซึ่งแน่นอน นัทไม่เคยจำเบอร์โทรของลูกค้าคนนี้ จึงตอบกลับไปว่าไม่มีข้อมูล

ลูกค้าคนนี้โกรธมาก และไปโพสต์ด่าในพันทิพย์ ส่งผลกระทบต่อชื่อเสียงของบริษัทสุธีการจำมาก

เรื่องนี้ทำให้สุธีเครียดมาก หลังจากนั่งคิดอยู่สามวันสามคืน สุธีก็ปิ๊งไอเดียขึ้นมาว่า

หลังจากพนักงานคนหนึ่ง(สุธีหรือนัท)รับสายที่ต้องจำข้อมูลเสร็จ จะเดินไปบอกอีกคนให้จำเรื่องนั้นทันที

วิธีนี้การันตีว่าทั้งสุธีกับนัทจะจำข้อมูลของลูกค้าทุกคนได้ ดังนั้น ไม่ว่าใครรับสาย ก็จะสามารถตอบข้อมูลกับไปได้เสมอ

การจ้างนัทเข้ามา เหมือนกับการเพิ่มคอมพิวเตอร์ที่ใช้เก็บข้อมูลเข้ามาอีกหนึ่งเครื่อง ทำให้ระบบเป็น Distributed System ที่ต้องสื่อสารและส่งข้อมูลหากัน

วิธีการแก้ปัญหาโดยให้เครื่องคอมพิวเตอร์ทั้งสองเครื่อง (สุธีกับนัทในที่นี้) จำข้อมูลเดียวกันซ้ำ เป็นเทคนิคที่เรียกว่า Replication ซึ่งใช้การันตี Consistency ในการ Read ข้อมูล โดยไม่ว่า Request จะถูกส่งไปยังเครื่องไหน เราก็จะต้องได้ข้อมูลที่ถูกต้องกลับมาเสมอ

สุธีกับนัทใช้เทคนิคนี้ไป 2-3 วัน ค้นพบว่าผลลัพธ์เป็นที่น่าพอใจมาก บริษัทสามารถรักษาคุณสมบัติ Consistency กับ Availability ได้ตามที่โฆษณาไว้กับลูกค้า

เหตุเกิดเมื่อนัทไปขี้

เหตุเกิดเมื่อวันหนึ่ง นัทปวดท้องกระทันหัน เลยเดินไปขี้

แต่นัทเป็นคนที่รับผิดชอบงาน นัทไม่ได้ไปขี้เปล่าๆ นัทเอาโทรศัพท์ไปด้วย และให้บริการลูกค้าขณะกำลังขี้อยู่นี่แหละ

ระหว่างนั้นเอง สุธีได้รับโทรศัพท์จากลูกค้าอีกคน ให้จำข้อมูลใหม่

ระหว่างที่กำลังคุยกับลูกค้าอยู่ สุธีได้ค้นพบปัญหาอันยิ่งใหญ่…

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

  2. อีกทางเลือกหนึ่ง คือสุธีเลือกที่จะรักษา Consistency เป็นหลัก จึงบอกให้ลูกค้าโทรกลับมาทีหลัง เพราะพนักงานขี้อยู่ ยังจำไม่ได้ แต่หากเลือกทางนี้ สุธีจะผิดสัญญาเรื่อง Availability ทันที

ณ จุดนี้ ผมอยากให้ผู้อ่านลองคิดดู ว่าถ้าหากคุณเป็นสุธี คุณจะมีทางเลือกที่ดีกว่าสองทางนี้ไหม?

ในเคสนี้ สุธีตัดสินใจที่จะเลือก Availability ก่อน

โชคดีที่คราวนี้นัทขี้ไม่นาน กลับมาจำข้อมูลใหม่ทันก่อนลูกค้าคนนั้นโทรมาหาอีกครั้ง

แต่นั่นก็ทำให้สุธีค้นพบว่า เค้าไม่สามารถให้บริการที่มีทั้ง Consistency และ Availability ได้…

หากนัทเดินไปขี้ !!

วกกลับมาที่ CAP Theorem

เหตุการ “นัทไปขี้” นั้นเชื่อมโยงกับตัว “P” ใน CAP Theorem ที่เรียกว่า Partition Tolerant

Partition Tolerant หมายความว่า ระบบยังทำงานได้ แม้ว่าแต่ละเครื่องใน Distrubuted System จะไม่สามารถติดต่อกันได้

ในกรณีข้างต้น หากบริษัทสุธีการจำต้องการจะมี Partition Tolerant สุธีจะต้องหาวิธีให้บริการลูกค้า ในช่วงเวลาที่สุธีกันนัทไม่สามารถติดต่อสื่อสารกันได้

คอมมอนเซนส์ เราพอจะเห็นแล้วว่ามันเป็นไปไม่ได้ (มี Proof ด้วย Contradiction หากใครสนใจ)

CAP Theorem จึงกล่าวไว้ว่า เราเลือกคุณสมบัติได้แค่ 2 ใน 3 อย่าง

การที่นัทต้องไปขี้นั้นเป็นเรื่องที่เลี่ยงไม่ได้ เหมือนกับระบบจริง ที่ Network นั้นไม่ Reliable เราไม่มีทางการันตีได้ว่า ข้อมูลที่ส่งผ่านเน็ตเวิร์คจากเครื่องสองเครื่องจะไปถึงกันได้ตลอด ข้อมูลอาจจะหล่นหายกลางทาง

จุดนี้นี่เอง ที่ทำให้แนวคิดเรื่อง CAP Theorem มีความสำคัญต่อการออกแบบ Distributed System มาก

เพราะเราไม่มีทางที่จะได้ Consistency กับ Availability พร้อมกัน 100% ได้

แล้วจะเลือก Consistency หรือ Availability?

เนื่องจากเราไม่ได้อยู่ในโลกที่มีแค่ขาวกับดำ ในทางปฏิบัติ เราสามารถต่อรอง Requirement ของระบบได้

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

เทคนิคนี้เรียกว่า Asynchronous Replication คือการส่งต่อข้อมูลโดยไม่การันตีว่าเมื่อไรข้อมูลจะถึงเมื่อไร แต่ยังไงก็จะถึงแน่นอน เพราะโน้ตบนโต้ะนัทไม่หายไปไหน

วิธีนี้ทำให้เราได้สิ่งที่เรียกว่า Eventual Consistency คือ เราการันตีว่าระบบจะ Consistency แน่นอน หลังจากเวลาผ่านไปสักพักหนึ่ง

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

การลดเงื่อนไขให้เป็น Eventual Consistency ถือว่าดีมากในกรณีนี้ เพราะลูกค้าส่วนใหญ่ของสุธีไม่โทรกลับมาใน 10 นาทีแน่นอน ทำให้สุธีได้ทั้ง (A)vailability, (P)artition Tolerant, และเกือบจะได้ (C)onsistency

กลับมามองที่ระบบคอมพิวเตอร์ ถ้าเรามั่นใจว่าจะไม่มีการ Read หลัง Write ทันทีทันใด การยอมใช้ Eventual Consistency จะเป็น Trade-off ที่ดีมาก (เช่น โพสต์ฟีดในเฟสบุ้ค ถ้าช้าไปวินึงก็ไม่เป็นไร)

ในทางตรงกันข้าม หากเป็นระบบที่การ Read หลัง Write ทันทีเกิดขึ้นได้ และความแม่นยำของข้อมูลสำคัญมาก แล้วเราไม่สามารถการันตีได้ว่า Request ถัดไปที่ Read จะไปลงที่เซอร์เวอร์เดิมที่เก็บข้อมูลไว้ เราอาจจะต้องเลือก Consistency ก่อน

หากยอมเสีย Response Time แทนล่ะ?

ลองคิดในอีกมุมหนึ่ง เราอาจจะเลือก Trade-off ตัว Response Time แทน

อย่างในตัวอย่างข้างต้น สุธีสามารถให้ลูกค้ารอสาย 10 นาที จนกระทั่งนัทขี้เสร็จและกลับมาจำ จึงค่อยตอบกลับลูกค้าว่าจำเสร็จแล้ว

วิธีนี้ทำให้ลูกค้าต้องรอนานขึ้น (Response Time ช้าลง) แต่การันตีได้ว่ามี Consistency แน่นอน และ Availability ด้วย แค่ช้า(มากๆ)เท่านั้น

แต่วิธีนี้มีข้อเสียเยอะมากกว่านั้น ได้แก่

  • สุธีจะไม่สามารถรับสายใหม่ได้ ต้องรอจนนัทขี้เสร็จ
  • หากนัทท้องเสีย เข้าห้องน้ำนาน (หรือท้องร่วง ต้องไปโรงพยาบาลต่อ) ลูกค้าอาจจะได้รอหลายชั่วโมง

หากเทียบกับระบบคอมพิวเตอร์ คือหลังจากรับ Request มาแล้ว ยังไม่ตอบกลับจนกว่าจะติดต่ออีกเครื่องได้ (รอให้ Network กลับมาทำงานตามปกติ โดยการทำ Retry ไปเรื่อยๆ)

ซึ่งนั่นทำให้เสียประโยชน์ในการ Scale ที่เราควรจะได้จากคอมพิวเตอร์อีกหนึ่งเครื่องไป

และระบบจะไม่สามารถทำงานได้ หากเกิด Partitioning ขึ้น (Network พังนานๆ, หรืออีกเครื่องเกิดล่มไปชั่วคราว)

วิธีนี้ เราก็คงได้แต่ภาวนาว่าจะไม่ให้ Partition เกิดขึ้น หรือสละ P เพื่อให้ได้ C กับ A นั่นเอง

สรุป

เรายกตัวอย่าง Distributed System ทีมีเครื่องอยู่สองเครื่อง (สุธีกับนัท) และแสดงให้เห็นว่า เราไม่สามารถจะได้ Consistency, Availability, และ Partition Tolerant พร้อมๆกันได้

ซึ่งนี่คือสิ่งที่ CAP Theorem บอกนั่นเอง

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

ดังนั้น เราต้องเข้าใจ Requirement ของระบบให้ดี ว่ายอมรับดีเลย์ในการ Consistency ได้แค่ไหน หรือการยอมเสีย Availability ได้ไหม

หรือหากทั้งสองอย่างสำคัญ เราก็ต้องหาวิธีที่ทำให้ไม่เกิด Partitioning ขึ้นได้

แต่คุณจะหวังให้ Network เสถียรตลอดเวลา หรือเครื่องอีกเครื่องไม่ล่มเลยไม่ได้หรอก

หรือคุณห้ามนัทไม่ให้ขี้ได้ ?!

Reference:

Acknowledgement

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