[แปล] ไปให้ถึงข้อจำกัดนั้น ข้อควรพิจารณาสำหรับการสร้าง Reliable System

       ระบบที่มีความซับซ้อนมักจะทำงานแบบล้มเหลวอยู่เสมอ นั่นเพราะว่าระบบที่มีความซับซ้อนสูงนั้น มักจะประกอบด้วย ส่วนประกอบต่างๆ ที่แยกจากกัน ซึ่งแต่ละอันสามารถที่จะล้มเหลวได้ในตัวของมันเอง หรือบางครั้งก็ทั้งหมดเลย ในสถาปัตยกรรมแบบ microservice ที่ซึ่งฟังก์ชั่นหนึ่งๆ สามารถที่จะติดต่อกับ service อื่นๆ ได้อย่างอิสระความสามารถที่จะทำงานแบบบ High Availability ขึ้นอยู่กับว่าระบบนั้นสามารถที่จะทำงานได้โดยที่มีเพียงแค่ส่วนประกอบบางส่วนทำงาน (Partially available) อยู่รึเปล่า ซึ่งก็คือแก่นของวิศวกรรมความยืดหยุ่น (Resilience Engineering) นั่นเอง ยกตัวอย่างเช่นถ้าฟังก์ชั่นหนึ่งข้องพึ่งพา service อีก 3 ตัวแต่ละตัวมีค่า Reliability อยู่ที่ 90%, 95% และ 99% ตามลำดับความสามารถในการทำงานได้เพียงแค่บางส่วนนั้นอาจจะมีค่าได้ตั้งแต่ 99.995% reliablity ไปจนถึง 84% reliability (สมมติว่าระบบล่มแยกจากกันหมด) วิศวกรรมความยืดหยุ่นคือระบบที่ออกแบบมาโดยที่การล่มเป็นเรื่องปกติ

       ส่ิงสำคัญสุดถ้าเราจะทำให้ระบบของเรามีความยืดหยุ่นนั้นคือ เราต้องเตรียมรับมือความผิดพลาดที่จะเกิดขึ้นในระบบให้ได้ แต่อีกสิ่งหนึ่งที่สำคัญพอกันคือเราต้องพร้อมที่จะยอมรับมัน กล้าที่จะปฏิเสธ client เวลามีการร้องขอข้อมูลและการที่ระบบล่มโดยที่เรารู้สาเหตุนั้นย่อมดีกว่าการที่เราไม่รู้เลยว่าเกิดอะไรขึ้น ในทางวิศวกรรมวิธีหนึ่งที่ใช้กันมากในการจัดการกับความยืดหยุ่นของระบบนั้นคือการรู้จักกับความดันต้านทาน ยกตัวอย่างง่ายๆ เช่นปั๊มน้ำ ถ้าในท่อที่น้ำเราปั๊มไปมีแรงดันกลับมากๆ ปั๊มนั้นก็ต้องทำงานหนักมากขึ้นกว่าปกติ ไม่ต่างอะไรกับเซิร์ฟเวอร์ที่มีการคุยกันหนักมากๆ ระบบก็จะมีโหลดสูงตามไปด้วย วิธีแก้ปัญหาเรื่องนี้แบบพื้นฐานที่สุดคือการบังคับระบบเราให้มีข้อจำกัดซึ่งเป็นไปได้ในหลายรูปแบบมากตั้งแต่ queue lengths, bandwidth throttling, traffic shaping, message rate limits, max payload sizes และอื่นๆ อีก การกำหนดข้อจำกัด เหล่านี้ไว้ตั้งแต่เนิ่นๆ จะทำให้เราเห็นข้อจำกัดซึ่งปกติมักจะไม่ค่อยเห็นกัน ถึงแม้ในท้ายที่สุดตัวเซิร์ฟเวอร์เองจะถูกจำกัดด้วยหน่วยความจำที่มี แต่การที่เราไม่รู้ลิมิตของระบบมันทำให้เราไม่รู้เลยว่าเมื่อไรที่ระบบจะล่มหรือจะเกิดอะไรขึ้นต่อไป ไม่ต่างอะไรกับการที่มีคนบอกว่าเขารู้ตัวว่าจะหยุดดื่มก็ตอนที่เขาหมดสติไปแล้ว

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

       จินตนาการว่าเรามีระบบที่มีหลายผู้กระทำคุยกัน ผู้กระทำหนึ่งสามารถส่ง message ไปหาผู้กระทำอื่นที่จะประมวลผล message นั้นหรืออาจจะส่ง message อีกก็ได้ มีข้อหนึ่งของ Eight fallacy of distributed computing กล่าวไว้ว่า "the network is homogenous" นั่นหมายความว่าไม่ใช่ผู้กระทำทุกตัวจะใช้ Hardware, Software หรือ Network แบบเดียวกัน เราอาจจะมี Ubuntu เซิร์ฟเวอร์ที่มีแรม 128GB มีแลปท็อปรัน MacOS อยู่บนแรม 16GB มี android รันอยู่บนแรม 2GB หรือ IOT devices แรม 512MB และอื่นๆ อีกที่ทำงานอยู่บน Software และ Network ที่แตกต่างกัน

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

       เราจะทดสอบอะไรก็ตามที่ไม่รู้ข้อจำกัดได้ไง คำตอบคือเราทำไม่ได้ครับ ตอนนี้เรามีทางเลือกสองทางคือ เราจะกำหนดข้อจำกัดให้ชัดเจนหรือเราจะทำให้มันลึกลับแบบเป็นยังไงก็ได้แบบนี้ต่อไป ซึ่งยิ่งเรากำหนดขอบเขตการทำงานเรามากเท่าไรในอดีต ก็ยิ่งทำให้เราทำสอบสิ่งที่ไม่ได้กำหนดไว้ในระดับ production น้อยลงเท่านั้น ทางเลือกที่สองคือเราต้องวัดดวงกับ reliability ของระบบเรา ข้อจำกัดนั้นยังมีอยู่ครับเราแค่ไม่เห็นมัน เมื่อเราไม่รู้ข้อจำกัดของเรา มันเป็นเรื่องง่ายมากที่เราจะ DoS เซอร์วิสเราในโปรดักชั่นเอง ลิมิตจะยิ่งเป็นสิ่งสำคัญมากเมื่อเราต้องยุ่งกับระบบที่อยู่บน Cloud ที่มีผู้ใช้งานหลายคน ซึ่งป้องกันระบบหรือบางครั้งเราเองในการทำให้เซอร์วิสล่มหรือกินทรัพยากรให้หมดไป 

       ในระบบที่มีผู้กระทำหลากหลาย เรามี message ส่งไปยัง mobile device กับ web browser ซึ่งส่วนใหญ่แล้วก็รันอยู่แค่เธรดเดียวหรือมีการจำกัดหน่วยความจำอยู่ ถ้าเราไม่มีลิมิตในขนาดของ message ตัว client นั้นสามารถที่จะทำลายตัวเองได้ง่ายมากโดยการ request ข้อมูลในปริมาณที่มากเกินไปหรือเกินขอบเขตที่จะควบคุมได้ นี่แหละเป็นเหตุผลที่ข้อจำกัดนั้นเป็นสิ่งที่ไม่มีคนเคยบอกแต่มันจะมีผลที่ตามมาเสมอ

       ลองมาดูตัวอย่างเรื่องนี้จากงานทางวิศวกรรมแบบอื่นกันเช่น US National Highway System กระทรวงคมนาคมสหรัฐใช้ Federal Bridge Gross Weight Formula เป็นค่าเฉลี่ยในการป้องกันพาหนะที่มีน้ำหนักมากสร้างความเสียหายให้กับถนนและสะพาน เรื่องนี้เป็นปัญหาทางวิศวกรรมเดียวกัน ต่างกันแค่สาขาและชนิดของโครสร้างพื้นฐาน

       "ในเดือนสิงหาคมปี 2007 สะพานข้ามแม่น้ำมิสซิสซิปปี้บนทางหลวงสาย 35W เกิดถล่มขึ้นก่อให้เกิดการพิจารณาในเรื่องของน้ำหนักของรถบรรทุกและความสัมพันธ์กับความเครียดที่กระทำกับสะพาน ในเดือนพฤศจิกายนปี 2008 National Transportation Safety Board ตัดสินว่าสาเหตุที่สะพานถล่มนั้นมาจากหลากหลายเหตุผลตั้งแต่ความผิดพลาดของ แผ่นโลหะที่เชื่อมเหล็กแต่ละชิ้นเข้าด้วยกัน, การตรวจสอบที่ไม่เพียงพอ และน้ำหนักส่วนเกินของอุปกรณ์การก่อสร้างขนาดหนักรวมกับน้ำหนักของการจราจรในชั่วโมงเร่งด่วน" 

       กระทรวงคมนาคมนั้นอาศัยสถานีวัดน้ำหนัก (Weigh stations) เพื่อให้แน่ใจว่ารถบรรทุกได้ปฏิบัติและบรรทุกน้ำหนักเป็นไปตามข้อกำหนดและมีค่าปรับสำหรับรถบรรทุกที่บรรทุกเกินข้อกำหนดโดยไม่ได้รับอนุญาติ

       "รัฐบาลกลางกำหนดไว้ว่าน้ำหนักบรรทุกสูงสุดที่อนุญาติคือ 80,000 ปอนด์ รถบรรทุกใดๆ ก็ตามที่มีน้ำหนักเกินกว่าข้อกำหนดนี้สามารถวิ่งบนทางหลวงได้ตราบใดที่มีใบอนุญาติบรรทุกเกินน้ำหนัก แต่ใบอนุญาตินี้ต้องออกก่อนการเริ่มต้นเดินทางและจะหมดอายุทันทีเมื่อสิ้นสุดการเดินทาง ใบอนุญาติบรรทุกเกินน้ำหนักนี้จะออกให้กับสินค้าที่ไม่สามารถแบ่งเป็นชิ้นย่อยๆ ที่ต่ำกว่าข้อกำหนดได้ และหรือไม่มีวิธีการขนส่งอื่นๆ นอกจากโดยรถบรรทุก"

       การกำหนดน้ำหนักนั้นมีไว้เพื่อให้ วิศวกรโยธา สามารถกำหนดน้ำหนักที่ถนน, สะพานและสาธารณูปโภคอื่นๆ ที่สร้างขึ้นสามารถรองรับได้ คอมพิวเตอร์ก็ไม่ต่างกัน นี่ถึงเป็นเหตุผลว่าระบบต่างๆ ถึงมีการกำหนดลิมิตเหล่านี้ ยกตัวอย่างเช่น Amazon ประกาศข้อจำกัดของ Simple Queue Service ไว้อย่างชัดเจนว่า in-flight messages นั้นต้องมีขนาดไม่เกิน 120,000 messages สำหรับคิวมาตรฐานและ 20,000 messages สำหรับ FIFO คิว และทุกข้อความต้องมีขนาดไม่เกิน 256KB เซอร์วิสอื่นๆ อย่างเช่น Amazon Kinesis, Apache Kafka, NATS และ Google App Engine pull queues ทั้งหมดกำหนดขนาดของ messages ไว้ไม่เกิน 1MB ข้อจำกัดเหล่านี้ช่วยให้ผู้ดูแลระบบสามารถที่จะออกแบบเพิ่มประสิทธิภาพ Infrastructure, ลดความเสี่ยงในหลายๆ ส่วนของระบบได้ และไม่ต้องพูดถึงเลยว่า มันสามารถทำให้เราวางแผนรับรองความจุของระบบให้ง่ายขึ้นมากแค่ไหน

       ทุกอย่างไม่ว่าจะเป็น queues, message size, queries หรือ traffic ทั้งหมดล้วนเป็น resilience engineering anti-pattern ถ้าไม่มีการเขียนข้อจำกัดไว้อย่างชัดเจน อะไรก็สามารถล่มโดยที่เราไม่สามารถรู้ตัวในทางที่ไม่สามารถเดาได้ จงจำไว้ว่าข้อจำกัดนั้นมีอยู่ มันแค่ซ่อนอยู่ที่ไหนซักแห่ง การที่เราสามารถรู้ถึงข้อจำกัดนั้นอย่างน้อยเราก็สามารถจำกัดความล้มเหลวให้แคบลง และสามารถทำนายได้มากขึ้น, ระยะห่างระหว่างการล้มเหลวแต่ละครั้งที่มากขึ้น และเวลาในการกู้คืนระบบที่สั้นลง แลกกับการเตรียมการที่มากขึ้นและซับซ้อนขึ้นอีกนิดเดียว 

       มันจะดีกว่ามั้ยถ้าเราจะทำให้ข้อจำกัดเหล่านี้มันชัดเจนไปเลยตั้งแต่แรกแล้วหาวิธีควบคุมมันตั้งแต่เนิ่นๆ ดีกว่าไปแถตอนเกิดปัญหาขึ้นแล้วปล่อยให้ระบบอาจจะล่มไม่ทางใดก็ทางหนึ่ง ถ้ามามองดูภายหลังวิธีหลังอาจจะดูเหมือนเราไม่ต้องเตรียมการอะไรมากตอนแรก แต่ก็ต้องแลกมาด้วยความน่าจะเป็นที่จะเกิดปัญหามากขึ้นในระยะยาว การที่เรากำหนดข้อจำกัดให้ developer โดยตรงจะทำให้เขาคิดถึง API และ Business logic ละเอียดขึ้นและออกแบบการมีปฏิสัมพันธ์ระหว่างส่วนต่างๆ ได้ดีขึ้นโดยคำนึงถึง stability, scalability และ performance

แปลและเรียบเรียงจาก http://bravenewgeek.com/take-it-to-the-limit-considerations-for-building-reliable-systems/

Comments