5 คะแนน โดย GN⁺ 2025-01-17 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • rqlite เป็นฐานข้อมูลเชิงสัมพันธ์แบบกระจายโอเพนซอร์สน้ำหนักเบา เขียนด้วย Go และพัฒนาขึ้นบนพื้นฐานของ SQLite และ Raft
  • เริ่มพัฒนาตั้งแต่ปี 2014 โดยให้ความสำคัญกับความน่าเชื่อถือและคุณภาพเป็นอันดับแรก และแม้ผ่านการพัฒนาและใช้งานจริงมากว่า 10 ปี ก็มีรายงานกรณี panic ในสภาพแวดล้อมโปรดักชันน้อยกว่า 10 ครั้ง
  • การทดสอบระบบแบบกระจายต้องอาศัยการพิจารณาอย่างรอบคอบในหลายชั้น และยึดแนวคิดการรักษาคุณภาพไว้ภายใต้ความเรียบง่าย

พีระมิดการทดสอบ: แนวทางที่มีประสิทธิภาพ

  • การทดสอบของ rqlite ปฏิบัติตาม "พีระมิดการทดสอบ"
    • พีระมิดการทดสอบ: โครงสร้างที่มีการทดสอบหน่วยเป็นฐาน และประกอบด้วยการทดสอบการทำงานร่วมกันรวมถึงการทดสอบ end-to-end (E2E) ในปริมาณน้อยที่สุด
  • ช่วยให้ได้ชุดทดสอบที่มีประสิทธิภาพ ดีบักง่าย และมุ่งเป้าชัดเจน

การทดสอบหน่วย: หัวใจของคุณภาพ

  • การทดสอบหน่วยใช้ทดสอบคอมโพเนนต์ที่แยกเป็นอิสระ และให้สมดุลระหว่างความเร็วกับความแม่นยำ
  • ด้วย SQLite และสถาปัตยกรรมแบบ "shared nothing" ฟังก์ชันส่วนใหญ่จึงครอบคลุมได้ด้วยการทดสอบหน่วย
  • จากโค้ด rqlite ทั้งหมด (ประมาณ 75,000 บรรทัด) การทดสอบหน่วยมีอยู่ราว 27,000 บรรทัด
  • การทดสอบเสร็จสิ้นได้ภายในไม่กี่นาที ทำให้สามารถรันทดสอบได้บ่อยครั้งระหว่างการพัฒนา

การทดสอบระดับระบบ: ตรวจสอบ consensus

  • การทดสอบระดับระบบใช้ตรวจสอบปฏิสัมพันธ์ระหว่างโมดูล consensus ของ Raft กับ SQLite
  • รายการทดสอบหลัก:
    • การจำลองคำสั่ง SQLite ระหว่างโหนด
    • การอ่านข้อมูลภายใต้ระดับความสอดคล้องที่หลากหลาย
    • การกู้คืนจากความล้มเหลวของคลัสเตอร์และการเลือก leader
  • ด้วยโค้ดทดสอบประมาณ 7,000 บรรทัด จึงครอบคลุมปฏิสัมพันธ์ของทั้งการตั้งค่าแบบโหนดเดี่ยวและหลายโหนดอย่างรอบด้าน

การทดสอบ end-to-end: ชั้นที่มีให้น้อยที่สุด

  • การทดสอบ end-to-end ทำหน้าที่เป็น smoke test เพื่อตรวจสอบการเริ่มระบบ การรวมคลัสเตอร์ และการทำงานพื้นฐานของระบบ
  • เขียนด้วย Python และตรวจสอบฟังก์ชันหลักโดยรันคลัสเตอร์ rqlite จริง
  • ตัวอย่าง: การตรวจสอบการสำรองข้อมูลไปยัง AWS S3
  • จำกัดแนวทางไว้ที่โค้ดทดสอบประมาณ 5,000 บรรทัด เพื่อลดต้นทุนการดีบักให้น้อยที่สุด

การทดสอบประสิทธิภาพ: ทดสอบขีดจำกัด

  • การทดสอบประสิทธิภาพประเมินเมตริกต่อไปนี้:
    • ความเร็วสูงสุดของ INSERT
    • การประมวลผลคิวรีพร้อมกัน
    • การเปรียบเทียบการใช้หน่วยความจำ CPU และดิสก์
  • รวมถึงการทดสอบฐานข้อมูล SQLite ขนาดมากกว่า 2GB เพื่อวิเคราะห์การจัดการหน่วยความจำและคอขวดของการเขียนลงดิสก์
  • ช่วยค้นหาปัญหาด้านประสิทธิภาพและรับประกันเสถียรภาพผ่านการปรับแต่ง

บทเรียนที่ได้

  • เริ่มทดสอบตั้งแต่เนิ่นๆ
    • การทดสอบหน่วยเป็นวิธีที่มีประสิทธิภาพที่สุดในการสร้างความเชื่อมั่นต่อระบบ
    • ไม่ควรเลื่อนการเขียนการทดสอบหน่วยระหว่างการพัฒนา เพราะสามารถพบบั๊กได้เร็วกว่าการทดสอบการทำงานร่วมกันหรือ E2E
  • ทำให้โค้ดทดสอบเรียบง่าย
    • ชุดทดสอบไม่ใช่สถานที่สำหรับยึดติดกับการรีแฟกเตอร์ที่ซับซ้อนหรือหลักการ DRY (Don't Repeat Yourself) มากเกินไป
    • สิ่งสำคัญคือการเขียนโค้ดที่เข้าใจง่าย และควรยอมรับโค้ด boilerplate เพิ่มเติมได้
  • ตรวจสอบการทดสอบด้วย
    • ตอนเขียนการทดสอบ ให้สลับผลลัพธ์ที่คาดไว้เป็นตรงกันข้ามชั่วคราวแล้วรันใหม่
    • หากการทดสอบเขียนได้ถูกต้อง กรณีนี้ควรล้มเหลว ซึ่งช่วยป้องกันข้อผิดพลาดในโค้ดทดสอบได้ล่วงหน้า
  • อย่าเพิกเฉยต่อการทดสอบที่ล้มเหลว
    • แม้จะเป็นความล้มเหลวที่เข้าใจยากหรือเกิดขึ้นไม่บ่อย ก็ยังให้ข้อมูลสำคัญเกี่ยวกับซอฟต์แวร์
    • กรณีความล้มเหลวที่ดีบักยากมักเป็นโอกาสในการค้นพบข้อบกพร่องร้ายแรงในโค้ด
  • เพิ่มความเป็น deterministic ให้มากที่สุด
    • สร้างกลไกที่สามารถสั่งให้กระบวนการอัตโนมัติของระบบทำงานแบบแมนนวลได้
    • ตัวอย่าง: ฟังก์ชัน snapshot ของ Raft ปกติทำงานแบบกึ่งอัตโนมัติ แต่ถูกออกแบบให้ trigger ได้อย่างชัดเจนระหว่างการทดสอบ
  • ใช้แนวทางอย่างรอบคอบ (Be Deliberate)
    • เพิ่มการทดสอบการทำงานร่วมกันระดับสูงหรือการทดสอบ E2E เฉพาะเมื่อมีการพิสูจน์ความจำเป็นอย่างชัดเจนแล้วเท่านั้น
    • การทดสอบมากเกินไปอาจทำให้การพัฒนาและการดีบักช้าลง
  • นำไปใช้และทำซ้ำ
    • ในการทดสอบประสิทธิภาพพบว่า การเรียก fsync เป็นคอขวดหลัก จึงบีบอัดรายการล็อกของ Raft ก่อนเขียนลงดิสก์เพื่อเพิ่มประสิทธิภาพการใช้ดิสก์
  • ให้ความสำคัญกับประสิทธิผล
    • รักษาชุดทดสอบที่รันได้ภายในไม่กี่นาที เพื่อให้พัฒนาแบบวนรอบได้อย่างรวดเร็ว
    • นี่คือข้อได้เปรียบสำคัญต่อการดูแลรักษาและทำให้โปรเจกต์โอเพนซอร์สยังคงมีความเคลื่อนไหว

คุณภาพมาก่อน

  • ปฏิบัติตามพีระมิดการทดสอบ และออกแบบให้แต่ละชั้นของการทดสอบมีจุดประสงค์ชัดเจน
  • เมื่อความซับซ้อนของระบบแบบกระจายเพิ่มขึ้น การรักษาความเรียบง่ายของการทดสอบคือหัวใจสำคัญ
  • เป้าหมายคือการสร้างฐานข้อมูลที่เชื่อถือได้และใช้งานดูแลได้ง่าย

1 ความคิดเห็น

 
GN⁺ 2025-01-17
ความคิดเห็นจาก Hacker News
  • เทสต์แรกยากที่สุด แต่ก็คุ้มค่าที่จะเพิ่ม หลังจากนั้นเทสต์ถัดไปจะง่ายขึ้น

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

  • เข้าใจแนวคิด test pyramid แต่บ่อยครั้งก็มักไม่มีครบทุกระดับ จึงเป็นสถานการณ์ที่ต้องรีบปรับปรุง

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

  • กำลังรอรายงาน Jepsen อยู่

  • ชอบในรูปแบบวิดีโอด้วย

  • อิจฉาการตั้งค่าการทดสอบประสิทธิภาพ

  • เคยใช้ rqlite และมันถ่ายทอดความเรียบง่ายออกมาได้ดี

  • กำลังถามความเห็นเกี่ยวกับการทดสอบแบบ deterministic simulation

  • สงสัยว่ามีการใช้ rqlite ในสภาพแวดล้อมจริงหรือไม่

  • rqlite เป็นโปรเจ็กต์ที่แปลกใหม่และอัจฉริยะ