1 คะแนน โดย GN⁺ 2023-07-31 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • เมื่อวันที่ 8 กรกฎาคม 2023 บัญชีผู้ใช้เก่า ๆ หายไปจากอินสแตนซ์ Mastodon ของ Vivaldi Social และสุดท้ายเกิดเหตุที่ 198 บัญชี ถูกผสานรวมเป็นบัญชีรีโมตบัญชีเดียว
  • สาเหตุไม่ใช่การลบโดยตรงหรือการโจมตี แต่เกิดจาก พฤติกรรมการผสานบัญชี ของ Mastodon ประกอบกับคอนฟิกการจำลองข้อมูล PostgreSQL แบบใช้ Makara ของ Vivaldi Social ทำให้ลำดับการทำงานคลาดเคลื่อน
  • บัญชีดูเหมือนถูกลบ แต่ชื่อผู้ใช้ถูกนำไปจัดสรรใหม่ และรูปอวาตาร์กับภาพเฮดเดอร์ก็หายไปด้วย ทำให้ขอบเขตของปัญหาถูกจำกัดลงว่าเป็น พฤติกรรมภายในแอปพลิเคชัน Mastodon
  • ทีมปฏิบัติการเตรียม rollback ฐานข้อมูลทั้งหมด ขณะเดียวกันก็ทำสคริปต์กู้คืนแบบเลือกเฉพาะควบคู่กันไป เพื่อย้อนคืนบัญชี โพสต์ การติดตาม ผู้ติดตาม และข้อมูลความสัมพันธ์
  • Mastodon v4.1.5 มีการป้องกันไม่ให้ Sidekiq worker ใช้ Makara และการแก้ไขลำดับการผสานบัญชี ดังนั้นผู้ดูแลเซิร์ฟเวอร์ที่ใช้ฐานข้อมูลจำลองควรตรวจสอบเส้นทางการอ่านของ worker

อุบัติเหตุช่วงสุดสัปดาห์ที่บัญชี 198 บัญชีหายไป

  • ราว 17:25 CEST วันเสาร์ที่ 8 กรกฎาคม 2023 แท็บ Vivaldi Social ขอให้ล็อกอินใหม่ และหลังล็อกอินก็พบว่า โฮมไทม์ไลน์ว่างเปล่า
  • อาการเดียวกันเกิดกับบัญชีผู้ดูแลระบบรายอื่นด้วย และเมื่อตรวจสอบฐานข้อมูลพบว่าบัญชีที่ได้รับผลกระทบถูกลบไปแล้ว จากนั้นเมื่อผู้ใช้ล็อกอินอีกครั้งก็ถูกสร้างขึ้นใหม่ราวกับเป็นบัญชีใหม่
  • Vivaldi Social มีแบ็กอัปกลางคืนของวันศุกร์ 23:00 UTC และทีมปฏิบัติการเริ่มคัดลอกไฟล์แบ็กอัปเพื่อตรวจสอบความเป็นไปได้ในการกู้คืน
  • ในการลบบัญชี Mastodon ตามปกติ ชื่อผู้ใช้จะถูกสงวนไว้อย่างถาวรและไม่นำกลับมาใช้ใหม่ แต่ในอุบัติเหตุครั้งนี้ชื่อผู้ใช้เดิมถูกจัดสรรอีกครั้ง จึง ไม่ใช่การลบตามปกติ

การลบยังดำเนินต่ออยู่

  • ตอนแรกบัญชีเก่า ๆ ที่มี ID ต่ำกว่า 142 หายไป และเมื่อถึง 19:10 บัญชีที่มี ID ต่ำกว่า 217 ก็หายไปด้วย ทำให้เห็นว่า การลบกำลังดำเนินอยู่
  • เวลา 19:18 มีการขอความช่วยเหลือจากนักพัฒนา Mastodon และหลังจาก Renaud ตอบกลับ Claire กับ Eugen ก็เข้าร่วมตรวจสอบด้วย
  • เวลา 19:20 เมื่อรีสตาร์ตอินสแตนซ์ Mastodon Docker การลบก็หยุดลง และ ID บัญชีต่ำสุดในฐานข้อมูลกลายเป็น 236
  • บัญชีที่ถูกลบหรือผสานในช่วงเกิดเหตุได้รับการยืนยันในท้ายที่สุดว่ามี 198 บัญชี

จำกัดขอบเขตลงว่าเป็นพฤติกรรมของแอปพลิเคชัน ไม่ใช่การโจมตี

  • ทีมปฏิบัติการและนักพัฒนา Mastodon ตรวจสอบความเป็นไปได้ที่ UserCleanupScheduler จะลบบัญชี “unconfirmed” แต่ตัดออกเพราะผู้ใช้ที่ถูกลบไม่อาจตรงกับเงื่อนไข query ดังกล่าวได้
  • เนื่องจากเพิ่งอัปเกรดเป็น Mastodon 4.1.3 เมื่อ 48 ชั่วโมงก่อนเกิดเหตุ จึงตรวจสอบการเปลี่ยนแปลงระหว่าง v4.1.2 กับ v4.1.3 รวมถึงการเปลี่ยนแปลงที่ Vivaldi เปิดเผย แต่ไม่พบสาเหตุที่เกี่ยวข้อง
  • ใน filesystem รูปอวาตาร์และภาพเฮดเดอร์ ของบัญชีที่ถูกลบก็หายไปด้วย ยืนยันว่าไม่ใช่แค่การลบฐานข้อมูลโดยตรง แต่เป็นแอปพลิเคชัน Mastodon ที่ทำพฤติกรรมการลบ
  • มีการค้นหาร่องรอยการบุกรุกหรือการโจมตีใน log และ filesystem แต่ไม่พบหลักฐาน และไม่พบความเป็นไปได้ของ exploit ที่เกี่ยวข้องกับการแก้ไขความปลอดภัยใน Mastodon v4.1.3
  • คืนวันเสาร์มีการ deploy แพตช์เพิ่ม log สำหรับพฤติกรรมการลบบัญชี และหลังเวอร์ชันแพตช์ถูก deploy เวลา 00:29 CEST ทีมจึงพักผ่อน

เบาะแสชี้ขาด: โพสต์ไปรวมอยู่ที่บัญชีรีโมตบัญชีเดียว

  • วันอาทิตย์ 13:56 มีรายงานว่าหน้าโปรไฟล์ของ Yngve ผู้เชี่ยวชาญด้านความปลอดภัยของ Vivaldi แสดงข้อผิดพลาด HTTP 500 และบัญชีดังกล่าวไม่ได้อยู่ใน 198 บัญชีที่ถูกลบ
  • ใน log มีบัญชีเดียวกันจากอินสแตนซ์ Mastodon รีโมตเดียวกันปรากฏซ้ำ ๆ และในเนื้อหานี้ใช้นามสมมติว่าเป็นบัญชีของ social.example.com
  • query ที่ดึง status ของบัญชีรีโมตดังกล่าวคืนค่า 17,600 แถว
  • เวลา 14:43 การเปรียบเทียบกับแบ็กอัปยืนยันว่า status ทั้งหมดของบัญชีที่ถูกลบทุกบัญชีถูก จัดสรรใหม่ ให้กับผู้ใช้หนึ่งรายของ social.example.com
  • หลัง 15:00 เป็นต้นไป log ของ AccountMergingWorker, Rails console และ query ฐานข้อมูลเพิ่มเติมทำให้สมมติฐานแข็งแรงขึ้นว่า worker ผสานบัญชีกำลังผสานบัญชีทั้งหมดเข้ากับบัญชีรีโมตบัญชีเดียว

สาเหตุราก: การผสานบัญชีกับความล่าช้าของการจำลองข้อมูล PostgreSQL

  • Vivaldi Social ใช้ คอนฟิกการจำลองข้อมูลแบบ 2 เซิร์ฟเวอร์ ของ PostgreSQL และกระบวนการ worker อยู่ในสภาพที่สามารถอ่านฐานข้อมูลจากเซิร์ฟเวอร์ standby ผ่าน Makara ได้
  • สถานการณ์อุบัติเหตุที่ Claire เสนอเมื่อ 17:28 เป็นดังนี้
    • Vivaldi Social ได้รับแจ้งการเปลี่ยนชื่อบัญชีจาก social.example.com
    • เมื่อสร้างบัญชีใหม่ในฐานข้อมูล ฟิลด์ URI ถูกใส่เป็น null
    • หลังจากนั้น URI ของบัญชีใหม่ถูกตั้งค่าเป็นค่าที่ถูกต้องของบัญชีรีโมต
    • การรัน AccountMergingWorker เพื่อผสานข้อมูลจากบัญชีเดิมไปยังบัญชีใหม่ถูกกำหนดคิวผ่าน Redis
    • เพราะความล่าช้าในการจำลองข้อมูลฐานข้อมูล ลำดับระหว่างการตั้งค่า URI กับการกำหนดคิวรัน worker จึงคลาดเคลื่อน ณ เวลาที่อ่านจริง
  • บัญชี local ทั้งหมดของอินสแตนซ์ Mastodon มีค่า URI เป็น null ดังนั้น worker จึง match บัญชีที่มีค่า URI เดียวกันเข้ากับบัญชีรีโมตใหม่ ทำให้บัญชี local ทั้งหมดถูกจับคู่
  • นักพัฒนามองว่าเมื่อโหลดฐานข้อมูลสูงขึ้นจนความล่าช้าในการจำลองข้อมูลยาวนานขึ้น เหตุการณ์แบบนี้จะเกิดได้ง่ายขึ้น
  • ทีมปฏิบัติการและนักพัฒนา Mastodon เห็นว่าคอนฟิกนี้มีความเป็นไปได้สูงมากที่จะเป็นสาเหตุราก

แพตช์และการเปลี่ยนคอนฟิก

  • หลังจำกัดสาเหตุได้แล้ว ทีมปฏิบัติการมุ่งเน้นการกู้คืนข้อมูล และ Claire รับหน้าที่เขียนแพตช์ป้องกันไม่ให้เกิดซ้ำ
  • Hlini รับหน้าที่ใช้แพตช์และเปลี่ยนคอนฟิกการจำลองข้อมูลที่ไม่แนะนำให้ใช้อีกต่อไป
  • ระหว่าง deploy เวลา 17:58 เกิดปัญหา ทำให้มี downtime ทั้งระบบ เพียงครั้งเดียวของสุดสัปดาห์นั้น และเวลา 18:18 Vivaldi Social ก็กลับมาใช้งานได้
  • เวลา 18:44 แพตช์และการเปลี่ยนคอนฟิกถูก deploy สำเร็จ และประเมินว่าอุบัติเหตุเดิมจะไม่เกิดซ้ำ

การกู้คืน: คืนค่าแบบเลือกเฉพาะแทน rollback ทั้งหมด

  • ตอนแรกมีการพิจารณา rollback ฐานข้อมูลทั้งหมด แต่เนื่องจากปัญหาประสิทธิภาพที่ทราบอยู่แล้ว จึงต้องมีกระบวนการซับซ้อนในการแปลงแบ็กอัป .dump เป็น .sql และแก้ไขไฟล์ข้อความขนาด 54GB
  • ทีมปฏิบัติการดำเนินขั้นตอนกู้คืนทั้งหมดควบคู่กับการกู้คืนแบบเลือกเฉพาะ
    • Hlini แก้ไขไฟล์ .sql ขนาด 54GB และเตรียมการกู้คืนทั้งหมด
    • Thomas เขียนสคริปต์กู้คืนบัญชีที่ถูกลบและข้อมูลที่เกี่ยวข้อง
  • ระหว่างเขียนสคริปต์ มีความผิดพลาดที่จัดการ parameter binding ของ PDO query เป็น reference และ Ísak เป็นผู้พบปัญหานี้
  • เวลา 23:04 ส่วนแรกที่แก้ไขเรคคอร์ด user, account และ identity ของผู้ใช้ที่ได้รับผลกระทบทั้ง 198 รายเสร็จสมบูรณ์
  • เวลา 23:55 สคริปต์กู้คืนแบบเลือกเฉพาะ ที่ย้อนคืน status, follows, followers, ข้อมูลความสัมพันธ์ ฯลฯ กลับสู่สภาพก่อนเกิดเหตุเสร็จสมบูรณ์

กู้คืนแบบเลือกเฉพาะเสร็จสิ้นและการแก้ไขตามมา

  • เนื่องจากข้อจำกัดด้านความสัมพันธ์ของฐานข้อมูล การกู้คืนดำเนินเป็น 2 ขั้นตอน
    • ขั้นแรกกู้คืนเรคคอร์ด user/account/identity ของทั้ง 198 คน
    • จากนั้นกู้คืนข้อมูลความสัมพันธ์ส่วนที่เหลือ
  • ในกรณีที่ผู้ใช้บางรายล็อกอินหลังเกิดเหตุและตั้งค่าการติดตามใหม่ เกิด duplicate key error และสคริปต์ถูกแก้ให้ลบเรคคอร์ดเดิมที่ไม่สามารถกู้คืนได้ และเก็บเรคคอร์ดที่ใหม่กว่าไว้
  • เวลา 01:27 CEST วันจันทร์ งานสุดท้ายของสคริปต์เสร็จสิ้น และเวลา 01:40 การทำดัชนีโฮมฟีดใหม่เสร็จสมบูรณ์
  • ผลคือโฮมฟีดของ 198 บัญชีถูกกู้คืน และ ไม่จำเป็นต้อง rollback ทั้งหมด
  • วันจันทร์และวันอังคารมีการแก้ไขปัญหาตามมาเพิ่มเติม
    • ปัญหาล็อกอินของบัญชี 6 บัญชีที่มีสัญลักษณ์ในชื่อผู้ใช้
    • การสูญหายของข้อมูลการตั้งค่าเว็บของ 198 บัญชี
    • ข้อผิดพลาดของตัวนับโปรไฟล์ เช่น จำนวนผู้ติดตามและจำนวนโพสต์
    • บัญชี 4 บัญชีที่มีข้อมูลผิดพลาด

การแก้ไขอย่างเป็นทางการของ Mastodon

ไทม์ไลน์เหตุการณ์ตามเวลา UTC

  • วันเสาร์ 15:15: ข้อความเปลี่ยนชื่อบัญชีจากอินสแตนซ์ภายนอกถูกส่งมายัง Vivaldi Social และงานผสานบัญชีที่ผิดพลาดเริ่มขึ้น
  • วันเสาร์ 15:25: พบสัญญาณแรกของเหตุการณ์
  • วันเสาร์ 17:20: หลังรีสตาร์ต Docker container งานผสานบัญชีหยุดลง; ระหว่าง 15:15 ถึง 17:20 มีบัญชีรวม 198 บัญชี ถูกลบ/ผสาน
  • วันอาทิตย์ 13:00: ระบุสาเหตุรากที่เป็นไปได้
  • วันอาทิตย์ 14:25: ยืนยันสาเหตุราก
  • วันอาทิตย์ 21:55: เริ่มกู้คืนข้อมูล
  • วันอาทิตย์ 23:27: กู้คืนข้อมูลเสร็จสิ้น
  • วันจันทร์ 10:40: แก้ไขบัญชี 6 บัญชีที่มีสัญลักษณ์ในชื่อผู้ใช้
  • วันจันทร์ 11:05: กู้คืนข้อมูลการตั้งค่าเว็บที่สูญหาย
  • วันอังคาร 15:31: แก้ไขค่าตัวนับที่ผิดพลาด
  • วันอังคาร 16:01: แก้ไขบัญชี 4 บัญชีที่มีข้อมูลผิดพลาด

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

 
GN⁺ 2023-07-31
ความคิดเห็นบน Hacker News
  • เป็นบทสรุปย้อนหลังที่ยอดเยี่ยม และโดยเฉพาะอย่างยิ่ง ถ่ายทอดได้ดีว่า ต้นทุนในระดับมนุษย์ อย่างการอดนอนส่งผลต่อการแก้ไขเหตุขัดข้องที่ซับซ้อนมากเพียงใด
    ส่วนที่สะดุดตาที่สุดคือข้อความว่า “บัญชีใหม่ถูกสร้างขึ้นในฐานข้อมูลโดยมีค่า null อยู่ในฟิลด์ URI”
    ทุกครั้งที่เห็นการวิเคราะห์หลังเหตุการณ์เกี่ยวกับฐานข้อมูล แทบทุกครั้ง NULL มักซ่อนตัวอยู่ใกล้ ๆ จุดเกิดเหตุเสมอ ต่อให้ NULL ไม่ใช่ผู้ร้าย ก็ต้องถูกเรียกมาสอบสวนอยู่ดี
    ถ้าจะให้คำแนะนำ คืออย่าพึ่งพา NULL เป็นค่า sentinel และถ้าเป็นไปได้ก็ควรไม่อนุญาตในฐานข้อมูลไปเลย แม้จะดูเหมือนมีข้อดี แต่ไม่กี่ปีต่อมาเมื่อความหมายของโมเดลข้อมูลเปลี่ยนไป ประโยคที่ดูไม่มีพิษภัยบางประโยคซึ่งคาดหวัง NULL หรือ NOT NULL ก็มักถูกหักล้างด้วยบั๊กที่หายากและให้ผลลัพธ์ไม่คาดคิด
    ครั้งนี้เป็นกรณี race condition แต่ถ้าแยกบัญชีโลคัลกับบัญชีรีโมตให้ชัดเจนด้วย type ลำดับการทำงานก็อาจไม่สำคัญ และโค้ดรวมบัญชีก็อาจจำกัดขอบเขตได้แคบลง

    • ผมถึงกับสมัครบัญชีในที่สุดเพื่อจะตอบประเด็นนี้ หวังว่าจะไม่ดูจู่โจมเกินไป
      Null เป็นค่าของข้อมูลที่ถูกต้องสมบูรณ์ และควรถูกปฏิบัติแบบนั้น ค่าเริ่มต้นอย่างการใช้ -1 กับ boolean หรือใช้ค่าว่างกับ string อาจทำให้ระบบที่ควรเกิด error ตอนรันไทม์หากเป็น NULL ดูเหมือนทำงานได้ แต่ไม่ได้หมายความว่าระบบทำงานตามที่คาดหวัง แค่มันเงียบลงเท่านั้น
      เข้าใจแรงจูงใจที่จะกลบ NULL เอาไว้ แต่ “ไม่มี” ก็เป็นสถานะที่ถูกต้องของข้อมูลพอ ๆ กับ “มี” และโดยทั่วไประบบควรถูกเขียนให้รองรับสิ่งนี้
    • ทางเลือกคือ string ว่างหรือ?
      ในกรณีนี้ปัญหาไม่ใช่ NULL ในฐานข้อมูล แต่เป็น NULL ในชั้นแอปพลิเคชัน มากกว่า
      ถ้า NULL เป็นค่าที่บังคับให้ต้องจัดการ คล้าย Maybe monad สุดท้ายก็จะถูกจัดการ และทำให้ต้องคิดถึงมัน ไม่ว่าจะเป็น string ว่าง, string null ของภาษาที่ใช้, หรือค่าพิเศษที่สร้างขึ้นเอง ก็ไม่ได้ต่างกันมากนัก
    • การรวม/ลบรายการซ้ำอัตโนมัติ เป็นหนึ่งในปัญหาที่ยากมาก ซึ่งเมื่อจัดการเรคคอร์ดที่ “คล้ายกัน” ควรให้มนุษย์เข้ามาเกี่ยวข้องให้มากที่สุดเท่าที่ทำได้ มีเคสยกเว้นและ race condition เต็มไปหมด โดยเฉพาะข้อมูลที่ถูก consume แบบอะซิงโครนัสควรส่งต่ออย่างชัดเจนที่สุดเท่าที่เป็นไปได้ และควรผ่านการตรวจสอบหลายชั้นว่า ข้อเท็จจริงจริง ๆ ยังไม่เปลี่ยนไป
      ในหลายกรณี ผู้ทำ implementation ควรนึกถึงประเด็นกังวลและความต้องการด้านปฏิสัมพันธ์แบบที่ conflict ของการ merge ใน Git ต้องการเสียก่อน แล้วค่อยตั้งสมมติฐานแบบลดทอนที่เหมาะกับโดเมนปัญหาจากจุดตั้งต้นนั้น
      เมื่อดูซอร์สของ Mastodon https://github.com/mastodon/mastodon/blob/main/app/workers/a... ดูเหมือนจะไม่มีแม้แต่รายการชัดเจนของ “จะ merge จาก ID ใดบ้าง” ที่ฝั่งซึ่งเริ่มคำขอ merge ส่งให้ตัวดำเนินการ merge แบบอะซิงโครนัส ดังนั้นเรื่องแบบนี้น่าจะเป็นเพียงเรื่องของเวลา
      นี่ไม่ใช่การวิจารณ์ Mastodon ผมเองเคยเขียนลอจิก merge ที่มี race condition แย่กว่านี้มาก และก็ได้รับผลกระทบมาแล้ว อันที่จริง แค่ว่าฟีเจอร์แบบนี้มีอยู่ในโปรเจกต์อาสาสมัครอย่าง https://opencollective.com/mastodon ก็ถือว่าน่าทึ่งแล้ว ถึงอย่างนั้นก็เป็นกรณีที่ควรระวัง
    • ถ้าใช้ JOIN แล้ว NULL เป็นสิ่งหลีกเลี่ยงไม่ได้ เพราะ JOIN เป็นแบบนั้นอยู่แล้ว
      ลึกลงไปกว่านั้น ความจริงนั้นยุ่งเหยิง และฐานข้อมูลก็ไม่อาจปฏิเสธการประมวลผลเพียงเพราะความจริงยุ่งเหยิงได้ ดังนั้น NULL จึงหลีกเลี่ยงไม่ได้ ตัวอย่างเช่น ลองสมมติว่าต้องการโมเดลคำนำหน้านาม คำนำหน้าชื่อ และคำต่อท้ายชื่อ แล้วใช้ข้อมูลนั้นสร้างคำทักทายเต็ม อย่างน้อยก็มีบางคนที่ไม่มีคำต่อท้ายชื่อ ต่อให้ไม่เก็บ NULL ไว้ คุณก็จะได้ NULL จากผลลัพธ์ JOIN ที่ใช้สร้างคำทักทายอยู่ดี
      คุณอาจกำจัดค่า NULL บางค่าได้ แต่กำจัดข้อเท็จจริงที่ว่าในโลกจริง “ไม่เกี่ยวข้อง” หรือ “ไม่ทราบ” มักเป็นค่าที่ถูกต้องไม่ได้ และฐานข้อมูลก็ต้องจัดการสิ่งนี้
    • ต่อให้มี null ฟังก์ชัน merge ก็ควรทำ การตรวจ null หรือการตรวจค่าจริง ในรูปแบบใดรูปแบบหนึ่งอยู่ดี ระดับนี้เหลือเชื่อจริง ๆ
  • กระแสที่ทำให้รู้สึกร่วมได้ตรงนี้คือ เริ่มจาก “มีแบ็กอัปฐานข้อมูลทั้งหมด ก็ restore ทั้งหมดได้เลย” ไปสู่ “การ restore ทั้งหมดยาก มี downtime และผลข้างเคียง” แล้วกลับมาเป็น “น่าจะ restore เฉพาะข้อมูลที่หายไปอย่างชาญฉลาดได้” จากนั้นทำด้วยมือแล้วเจอ error แปลก ๆ สุดท้ายจึง deploy การ restore แบบเลือกเฉพาะที่ทำขึ้นชั่วคราว แล้วปิดงานข้อมูลห้ารายการสุดท้ายที่หายไป พร้อมหวังว่าจะไม่ได้พลาดรายการที่หก
    ไม่ว่าใครจะซ้อม backup/restore ก็มักไหลไปแบบนี้ทุกครั้ง สุดท้ายแล้ว การตัดสินใจว่าจะนำข้อมูลใดกลับมาจากอิมเมจแบ็กอัป มักเป็นเรื่องที่ต้องตัดสินใจใน ระดับแอปพลิเคชัน เสมอ

    • เห็นด้วย มีคำกล่าวว่า “ถ้าคุณไม่ได้ทดสอบแบ็กอัป ก็เท่ากับไม่มีแบ็กอัป”
      แต่ในกรณีนี้ผมไม่ค่อยแน่ใจว่าปัญหาคืออะไร ถ้า restore ทั้งหมดจากแบ็กอัปปกติล่าสุด โพสต์บางส่วนที่ถูกอัปโหลดในช่วงนั้นจะหายไป ซึ่งน่าเสียดายก็จริง แต่ก็เป็นวิธีที่แก้ได้ทันทีแทนการทำด้วยมือและความไม่แน่นอน
  • ประทับใจตรงที่ Renaud, Claire และ Eugen จากทีมพัฒนา Mastodon ช่วยเหลือเกินความคาดหมาย
    ไม่รู้ว่า Vivaldi ให้การสนับสนุนทางการเงินแก่ Mastodon หรือไม่ และก็หาไม่เจอในหน้าผู้สนับสนุน ถ้าไม่ใช่ ก็หวังว่าเหตุการณ์นี้จะทำให้ Vivaldi หรือบริษัทอื่น ๆ ที่ใช้ Mastodon พิจารณา การสนับสนุนหรือสัญญาซัพพอร์ต

    • ปัจจุบัน องค์กรไม่แสวงหากำไร Mastodon ยังไม่มีสัญญาซัพพอร์ตให้บริการ แต่นั่นเป็นไอเดียที่ดี
      การสนับสนุนเปิดรับอยู่ และมีผลมากจริง ๆ การที่โปรเจกต์มีคนทำงานเต็มเวลานั้นสำคัญมาก แต่ตอนนี้ฝั่งเทคนิคมีนักพัฒนาเต็มเวลาเพียง 1 คน และคนดูแล DevOps 1 คน นอกเหนือจาก Eugen ผู้ก่อตั้ง
    • ใน https://joinmastodon.org/sponsors ไม่มีชื่ออยู่ ดังนั้นน่าจะไม่ใช่ผู้สนับสนุน
    • ถึงอย่างนั้น ก็ถือว่าพวกเขามอบอินสแตนซ์ที่ค่อนข้างใหญ่ให้กับ federation ของ Mastodon รวมถึงบุคลากรที่ทำงานอยู่บนนั้นด้วย
  • เป็นหนึ่งใน postmortem ที่ค่อนข้างดีในรอบนาน ๆ ที่ได้อ่าน

    • จำได้ว่า postmortem ของ hachyderm ก็ค่อนข้างดีเหมือนกัน ดีใจที่ผู้คนเปิดเผยอย่างโปร่งใส
  • การที่ข้อ 2 กับ 3 ไม่ได้ถูก ประมวลผลแบบ atomic รู้สึกเหมือนเป็นปัญหา แน่นอนว่าคงมีเหตุผลที่ทำให้การทำแบบนั้นไม่ใช่เรื่องเล็กน้อย แต่ยังไม่ได้ดูโค้ด และคงต้องหาโอกาสดูสักวัน

    • หนึ่งในแพตช์ที่เกี่ยวข้องคือ https://github.com/mastodon/mastodon/commit/13ec425b721c9594...
      ดูเหมือนว่าการทำให้เป็น atomic จะไม่ใช่เรื่องยากเลย
      เพียงแต่ก่อนหน้านี้ไม่จำเป็นต้องทำเท่านั้นเอง หมายความว่า ต่อให้ไม่เป็น atomic ก็ไม่เป็นปัญหา เว้นแต่มีใครตั้งค่าผิด ๆ ให้ sidekiq ไปเชื่อมต่อกับเซิร์ฟเวอร์ฐานข้อมูลที่ข้อมูลเก่า หรือก็คือ replica ซึ่งในกรณีนี้ดูเหมือนการตั้งค่านั้นจะเป็นปัญหาหลัก
  • ครั้งแรกที่ต้องกู้คืน SQL dump ขนาดมหึมา ผมลืมไม่ลงเลยที่เห็น vim พยายามอ่านมันแล้วเกิด segmentation fault จริง ๆ
    ตอนนั้นผมได้ค้นพบเวทมนตร์ของ split(1) หรือการแบ่งไฟล์ออกเป็นชิ้น ๆ จึงแยก dump ขนาดใหญ่ออกเป็นไฟล์ละหนึ่งตาราง
    แน่นอนว่าแม้แต่ตารางเดียวก็อาจใหญ่โตได้ แต่อย่างน้อยไฟล์ก็มีความสม่ำเสมอมากขึ้น ทำให้แปลงคิวรีด้วยเครื่องมืออื่นอย่าง sed หรือ awk ได้ง่ายขึ้น

    • แปลกใจที่ vim เกิด segmentation fault ได้ เคยเห็นว่าเปิดไฟล์ใหญ่แล้วช้า แต่ผมคิดมาตลอดว่ามันน่าจะจัดการอะไรก็ได้ด้วยบัฟเฟอร์แบบเวทมนตร์บางอย่าง อาจจะคิดผิดก็ได้
      อย่างไรก็ตาม ถ้าถึงจุดที่ต้องแก้ไข dump เพื่อกู้คืนข้อมูล แปลว่า ขั้นตอนการกู้คืน น่าจะมีอะไรผิดพลาดอย่างมาก แน่นอนว่าตอนอยู่ในสถานการณ์นั้นจริง ๆ ความรู้นี้ก็ไม่ได้ช่วยอะไรมากนัก
    • เมื่อก่อนเคยดูแลระบบที่มีไฟล์ในโฟลเดอร์หนึ่งมากเกินไป จนแม้แต่คำสั่ง ls ก็รันไม่จบ น่าจะเป็น ext3 หรือ ext2
      วิธีอ้อมคือเขียนสคริปต์ Python ให้ค่อย ๆ ประมวลผลทุกอย่าง แล้วโยกไฟล์ไปยังไดเรกทอรีย่อยตาม prefix ร่วม
  • ตรงประโยคที่ว่า “Claire ขอ stack trace แบบเต็มของรายการ log และสามารถดึงสิ่งนั้นออกมาจาก log ได้ด้วย” ทำเอาเลิกคิ้ว
    นี่อาจเป็นมนตร์ดำขั้นลึก หรือไม่ก็โค้ด/การตั้งค่าที่ทำให้ Xeon กลายเป็นระดับ 286 กันแน่ ไม่ใช่ว่าต่อ request จะกลายเป็นข้อมูลระดับเมกะไบต์หรือ?

    • ตอนดูบัญชีแล้วเกิดข้อผิดพลาด HTTP 500 และหมายถึง stack trace ของ 500 นั้น
      เป็นพฤติกรรมปกติของ Ruby on Rails ถ้าเกิด 500 หรือข้อผิดพลาดที่ไม่รู้จัก มันจะพิมพ์ stack trace ออกมา โดยเนื้อหาก็ประมาณหมายเลขบรรทัดกับ path ของไฟล์
      ผมดูแลแอป Rails ที่ออกแบบค่อนข้างแย่อยู่ เพิ่งตรวจดูเมื่อกี้พบว่า stack trace ของ 500 หนึ่งครั้งมีขนาด 5KiB ส่วนข้อผิดพลาด 500 เกิดประมาณชั่วโมงละครั้งเท่านั้น จึงไม่ถึง 1MiB ต่อวัน
      การเก็บ call stack ไว้ใกล้ ๆ จริง ๆ แล้วค่อนข้างโอเคด้านประสิทธิภาพ พฤติกรรม exception เริ่มต้นของ Java ก็แนบ stack trace มากับทุก exception เช่นกัน แม้จะไม่ได้พิมพ์ออกมาก็ตาม แต่แอป Java ก็ทำงานได้ดี ยังไงก็ต้องรู้วิธีกลับจากการเรียกใช้อยู่แล้ว จึงมี call stack อยู่ และข้อมูลเพิ่มเติมที่ต้องการก็มีแค่ชื่อไฟล์กับ debug symbol ของหมายเลขบรรทัดเท่านั้น สำหรับ Ruby ด้วยลักษณะของภาษา ข้อมูลนั้นก็จำเป็นอยู่แล้ว
    • การ บันทึก stack trace ของข้อผิดพลาดเป็นเรื่องสมเหตุสมผลทีเดียว ตามอุดมคติแล้วไม่ใช่ว่าทุก request จะเกิดข้อผิดพลาด
    • หมายความว่าคุณไม่จับ stack trace ของข้อผิดพลาดบนระบบ production เหรอ? แล้วจะรู้ได้อย่างไรว่าข้อผิดพลาดมาจากไหน?
    • ดูเหมือนจะสับสนระหว่าง stack trace กับ core dump หรืออะไรทำนองนั้น
  • “บัญชี local ทั้งหมดของ Mastodon instance มีค่าในฟิลด์ URI เป็น null จึง match ทั้งหมด” เป็นไปได้อย่างไร?
    NULL = NULL จะประเมินเป็น FALSE SQL ใช้ตรรกะสามค่า หรือให้แม่นคือ weak three-valued logic ของ Kleene และไม่ว่าจะใช้ operator ใดกับ NULL ผลก็เป็น NULL

    • ผมก็สงสัยเหมือนกัน อาจจะ filter ในชั้น application แล้วตรวจความเท่ากันด้วยค่า null ของภาษาที่ใช้ก็ได้
  • ไม่เข้าใจว่าบัญชีที่มีค่า NULL ในคอลัมน์ URI ไป match กับ query ได้อย่างไร NULL ไม่ถูกเปรียบเทียบว่าเท่ากับ NULL นี่เป็น เวทมนตร์ Rails อันน่าสยดสยองหรือเปล่า?

  • ตอนเห็นท่อนที่ว่าผู้ใช้ 6 คนที่มีสัญลักษณ์ใน username ไม่สามารถล็อกอินได้ และแก้ได้ง่ายเพราะเป็นความผิดพลาดของสคริปต์กู้คืน ก็รู้สึกว่า UTF-8 ได้ก่อเรื่องอีกครั้งแล้ว