ที่ Slash, เราเป็นผู้ขับเคลื่อนการใช้จ่ายมูลค่าหลายหมื่นล้านดอลลาร์ทุกปี. ด้วยการเติบโตเกิน 1000% ในช่วง 12 เดือนที่ผ่านมา, เราอยู่ในระหว่างการแก้ปัญหาใหม่ ๆ อย่างต่อเนื่องในขนาดที่ใหญ่ขึ้น. เมื่อปริมาณข้อมูลเพิ่มขึ้นและระบบของเราซับซ้อนมากขึ้น, สมมติฐานที่เราเคยทำเกี่ยวกับความสัมพันธ์ของข้อมูลมักจะล้มเหลว. เราพบปัญหาจากหลายแหล่ง: การเติมกลับที่ไม่ดี, ข้อบกพร่องในโค้ดที่ใช้งานจริง, และข้อผิดพลาดเล็กน้อยระหว่างการย้ายข้อมูล เมื่อเวลาผ่านไป มันกลายเป็นเรื่องยากขึ้นเรื่อยๆ ที่จะตรวจสอบความถูกต้องของข้อมูลของเรา ซึ่งทำให้เราต้องสร้างสิ่งที่เราเรียกว่า Health Checks — ระบบที่ออกแบบมาเพื่อตรวจสอบความถูกต้องของข้อมูลอย่างต่อเนื่องในสภาพแวดล้อมการผลิต
การตรวจสอบสุขภาพเป็นเครื่องมือที่ช่วยให้เราตรวจสอบค่าคงที่ในฐานโค้ดของเรา เมื่อเราสร้างระบบ เราออกแบบระบบเสมอด้วยชุดของค่าคงที่ทั้งที่ชัดเจนและไม่ชัดเจน ซึ่งเป็นกฎหรือสมมติฐานที่เราคาดหวังให้คงอยู่ตลอดเวลา เพื่อให้เราสามารถสร้างระบบที่ซับซ้อนมากขึ้นบนพื้นฐานของระบบเหล่านั้นได้
ตัวอย่างเล็กน้อยของสิ่งที่คงที่: ในบัตรตารางในฐานข้อมูลของเรา ซึ่งแต่ละแถวแทนบัตรเครดิตหนึ่งใบ มีสถานะคอลัมน์ที่หนึ่งในค่าสามารถเป็น "ปิด" และเหตุผลการปิดคอลัมน์ที่สามารถเป็น NULLABLE ได้. ข้อไม่เปลี่ยนแปลงอย่างหนึ่งคือว่าสถานะคอลัมน์จะ "ปิด" **หากและเฉพาะเมื่อ**เหตุผลการปิดฟิลด์นี้ไม่ว่างเปล่า
ตัวอย่างข้างต้นไม่จำเป็นต้องเป็นเงื่อนไขที่เราจะเขียนการตรวจสอบสุขภาพเสมอไป แต่เป็นตัวอย่างของอินแวนต์ที่เราต้องมั่นใจว่ายังคงเป็นจริงในขณะที่ทำงานกับส่วนนี้ของโค้ดเบส
การออกแบบการตรวจสุขภาพเบื้องต้นของเรา
การตรวจสอบสุขภาพครั้งแรกของเราเป็นดังนี้:
เราออกแบบการตรวจสอบสุขภาพในตอนแรกให้ถูกกำหนดโดยการค้นหาด้วยคำสั่ง SQL. เราจะทำการค้นหาด้วยคำสั่ง SQL ต่อตารางเพื่อค้นหา 'ข้อผิดพลาดที่อาจเกิดขึ้น'. แต่เนื่องจากการรันคำสั่ง SQL กับตารางขนาดใหญ่มีค่าใช้จ่ายสูง เราจึงทำการตรวจสอบเหล่านี้กับรีพลิกาสำหรับการอ่านที่มีความสอดคล้องในที่สุด (ในกรณีของเราคือ Snowflake) สำหรับแต่ละผลลัพธ์ที่ได้จากการสอบถาม เราจะทำการตรวจสอบกับฐานข้อมูลหลักที่ใช้งานจริงของเราอีกครั้งเพื่อให้แน่ใจว่าไม่ใช่ผลบวกลวง การออกแบบนี้มีปัญหาอยู่บ้าง:
- เราจำเป็นต้องรักษาสองคำสั่งค้นหา คำสั่งค้นหาแรกจะทำงานกับรีพลิกาสำหรับการอ่านสำหรับทั้งตาราง ผู้ดูแลระบบจะต้องคิดและจัดการกับการแบ่งหน้าสำหรับคำสั่งค้นหาอย่างชัดเจนด้วย คำสั่งค้นหาที่สองจะทำงานกับระบบผลิตสำหรับผลลัพธ์แต่ละรายการที่คำสั่งค้นหาแรกจะคืนค่า
- การค้นหาข้อมูลจากตารางทั้งหมดจะกลายเป็นเรื่องซับซ้อนและยากต่อการอ่าน / ยากต่อการบำรุงรักษา เนื่องจากเราจะพยายามบันทึกตรรกะทางธุรกิจจำนวนมากไว้ในคำสั่ง SQL เพียงคำสั่งเดียว
การตรวจสอบสุขภาพรอบที่สอง
เราตัดสินใจ:
- การตรวจสอบสุขภาพควรดำเนินการกับฐานข้อมูลการผลิตหลักของเราเสมอ มิฉะนั้น เราอาจไม่พบปัญหาที่เกิดขึ้นจริงทุกประการ
- การทำการ "full table scan" ในคำสั่ง SQL เป็นสิ่งที่ควรหลีกเลี่ยงอย่างยิ่ง แต่การทำการ "full table scan" แบบควบคุมโดยการวนลูปผ่านแต่ละแถวและทำการตรวจสอบสุขภาพนั้นสามารถทำได้ — ตราบใดที่โหลดคงที่ สามารถคาดการณ์ได้ และไม่พุ่งสูงขึ้นอย่างฉับพลัน ทุกอย่างมักจะไม่มีปัญหา
- การทำซ้ำข้อมูลในตารางที่ใช้งานจริง (และแยกส่วนนั้นออกไปให้ผู้พัฒนา) ช่วยทำให้ประสบการณ์การใช้งานของเราง่ายขึ้นมาก:
- เราไม่จำเป็นต้องรักษาคำสั่งค้นหา sql ที่ซับซ้อนสองคำสั่งพร้อมการจัดหน้าอีกต่อไป เราเพียงแค่ต้องกำหนดตรรกะของแอปพลิเคชันสำหรับการตรวจสอบสถานะของรายการเดียวเท่านั้น
- การตรวจสอบสุขภาพทุกครั้งจะถูกกำหนดไว้สำหรับทั้งตาราง (ในอนาคต เราอาจเลือกที่จะขยายการตรวจสอบสุขภาพให้สามารถวนซ้ำเฉพาะตามคำจำกัดของดัชนีได้)
บทเรียนสำคัญที่เราได้เรียนรู้ร่วมกันในฐานะทีมตลอดระยะเวลาที่ผ่านมา คือระบบที่สามารถคาดการณ์ได้และสร้างภาระงานอย่างสม่ำเสมอถือเป็นระบบที่เหมาะสมที่สุด ปัญหาหลักที่มักเกิดขึ้นกับระบบผลิตจริงมักเกิดจากการเปลี่ยนแปลงที่ไม่คาดคิดและเกิดขึ้นอย่างฉับพลัน เช่น การเพิ่มขึ้นอย่างรวดเร็วของภาระงานฐานข้อมูล หรือการเปลี่ยนแปลงในตัววางแผนคำสั่งภายในของฐานข้อมูล
แง่มุมที่ขัดกับความเข้าใจทั่วไปเกี่ยวกับการให้ระบบทำงานภายใต้ภาระคงที่ โดยเฉพาะอย่างยิ่งกับฐานข้อมูลและคิว คือมันอาจดูเหมือนเป็นการสิ้นเปลือง ผมเคยเชื่อว่าการไม่สร้างภาระให้กับระบบเลยเป็นค่าเริ่มต้น และทำงานเฉพาะเมื่อจำเป็นในแต่ละวันไม่กี่ครั้งจะดีกว่า อย่างไรก็ตาม วิธีนี้อาจนำไปสู่ปริมาณงานที่พุ่งสูงเป็นช่วงๆ ซึ่งบางครั้งอาจทำให้ระบบของเราเสื่อมประสิทธิภาพโดยไม่คาดคิด ในความเป็นจริง เราพบว่าการมีโหลดคงที่เล็กน้อยที่ทำงานกับฐานข้อมูลอย่างต่อเนื่องเป็นระยะเวลานาน มักจะดีกว่า แม้โหลดคงที่นั้นจะเพิ่มการใช้งาน CPU เพียง 1-5% ตลอด 24 ชั่วโมงก็ตาม เมื่อสามารถคาดการณ์โหลดได้ จะทำให้การตรวจสอบอัตราการใช้งานโดยรวมเป็นเรื่องง่ายขึ้น ความสามารถในการคาดการณ์นี้ช่วยให้เราสามารถขยายระบบในแนวนอนได้อย่างมั่นใจ และวางแผนล่วงหน้าสำหรับปัญหาด้านประสิทธิภาพที่อาจเกิดขึ้นในอนาคต
มีบทความที่น่าสนใจเกี่ยวกับเรื่องนี้โดยทีมงานของ AWS: ความน่าเชื่อถือ การทำงานอย่างต่อเนื่อง และกาแฟแก้วอร่อย
เวอร์ชันที่สองของการตรวจสุขภาพตอนนี้มีลักษณะดังนี้:
ด้วยการออกแบบนี้ ทำให้การเพิ่มการตรวจสอบสุขภาพใหม่บนเอนทิตีเดียวเป็นเรื่องง่ายมาก เนื่องจากแต่ละการตรวจสอบสุขภาพถูกกำหนดให้เป็นฟังก์ชันเดียวแบบอะซิงโครนัส เราสามารถทำการตรวจสอบสุขภาพเหล่านี้ซ้ำไปซ้ำมาบนตารางทั้งหมดได้หลายครั้งต่อวัน โดยไม่จำเป็นต้องแลกเปลี่ยนความถี่ เนื่องจากปริมาณงานคงที่ สำหรับการตรวจสอบสุขภาพข้างต้น แต่ละเอนทิตีจะทำการค้นหาอย่างรวดเร็วไปยังตาราง `LimitRule` การตรวจสอบเหล่านี้ทำงานอย่างต่อเนื่อง ทำให้เกิดภาระงานที่น้อยแต่คงที่ต่อฐานข้อมูลในทุกช่วงเวลา
ระบบนี้ทำงานอย่างไรภายใต้ระบบสามารถอธิบายให้เข้าใจง่ายได้ดังนี้:
- เราทำการตรวจสอบของเราบน Temporal เพื่อให้เราสามารถรับประกันการดำเนินการในที่สุดได้ เราใช้ผลิตภัณฑ์ "schedules" ของ Temporal เพื่อให้แน่ใจว่าเราเรียกใช้ cron ของเราทุกนาที
- ทุกนาที เราจะตรวจสอบเช็คทั้งหมดที่ควรกำหนดเวลาให้ดำเนินการในทันที และนำเช็คเหล่านั้นไปดำเนินการในคิวงาน เราจะควบคุมคิวงานให้เหมาะสมเพื่อไม่ให้มีการส่งข้อมูลไปยังฐานข้อมูลเกินกว่า RPS ที่กำหนดไว้
- แต่ละกิจกรรมที่ทำงานจะดำเนินการงาน "คงที่" บางประเภท ไม่มีความซับซ้อนของเวลาแบบ O(N) หรือความซับซ้อนอื่น ๆ ที่เพิ่มขึ้นตามจำนวนของเอนทิตีทั้งหมดในฐานข้อมูล สิ่งนี้ช่วยให้มั่นใจว่ากิจกรรมทำงานได้อย่างรวดเร็ว คาดการณ์ได้ง่าย และจะไม่ทำให้คิวงานติดขัด
อะไรต่อไป
การตรวจสอบสุขภาพในปัจจุบันเป็นพื้นฐานที่สำคัญในกระบวนการทดสอบของเรา มันเป็นรากฐานสำหรับการทดสอบการตรวจสอบการผลิตอย่างต่อเนื่องของเรา เมื่อเราเติบโตขึ้น การทดสอบทั่วไปก็เพิ่มขึ้นเพื่อรักษาความเสถียรและการทำงานของผลิตภัณฑ์ของเราให้คงที่ เราต้องลองหลายครั้งกว่าจะทำได้ถูกต้อง เราต้องการที่จะนำมาใช้ระบบการทดสอบบัญชีแยกประเภท / การตรวจสอบข้อมูลในระบบการผลิตมาเป็นเวลาสามปีแล้ว อย่างไรก็ตาม จนกระทั่งเมื่อหนึ่งปีครึ่งที่ผ่านมา เราจึงสามารถสร้างระบบขึ้นมาได้ ในภาพรวม บทเรียนที่สำคัญที่เราได้เรียนรู้คือ:
- ส่งอะไรก็ได้ ส่งอะไรก็ได้ และปรับปรุงมันอย่างต่อเนื่องตามเวลา เราไม่สามารถไปถึงการปรับปรุงครั้งที่สองของระบบตรวจสอบสุขภาพของเราได้หากเราไม่ได้ส่งครั้งแรก
- ทำให้เรียบง่าย — เราต้องการให้วิศวกรสร้างและดูแลการตรวจสอบสุขภาพได้ง่ายที่สุดเท่าที่จะเป็นไปได้ การใช้ตรรกะทางธุรกิจที่ซับซ้อนฝังอยู่ในคำสั่ง SQL หลายคำสั่งไม่เคยเป็นเรื่องสนุกในการทำงาน
- รันการตรวจสอบของเราโดยตรงบนระบบผลิต — นั่นคือแหล่งข้อมูลที่ถูกต้องของเรา และหากเราทำการตรวจสอบกับสิ่งอื่นใด มันจะซับซ้อนมากขึ้นโดยธรรมชาติ และมีโอกาสเกิดผลบวกหรือลบที่ผิดพลาดได้มากขึ้น
วันนี้ การตรวจสอบสุขภาพเป็นก้าวแรกที่เราได้ทำเพื่อทดสอบและตรวจสอบระบบของเราให้ดีขึ้นนอกเหนือจากรูปแบบที่ดั้งเดิมมากขึ้นเช่นการทดสอบและการสังเกตการณ์ เมื่อเราเติบโตต่อไป เราจะค่อยๆ สะท้อนและค้นหาวิธีใหม่ ๆ ที่จะทำให้ผลิตภัณฑ์ของเราเสถียร