• ระบบเดิมที่ใช้งานมานาน 13 ปี และรองรับการแจ้งเตือนระดับหลายพันล้านครั้งต่อปี เริ่มชนปัญหา timeout เมื่อครีเอเตอร์รายใหญ่เพียงรายเดียวสร้างการแจ้งเตือนได้เป็นหลักล้านรายการ ทำให้ Patreon ออกแบบใหม่ด้วยชั้น fanout ที่สร้างการแจ้งเตือนล่วงหน้าต่อผู้รับแต่ละราย
  • ใช้ fanout สองขั้นตอน โดยแบ่งรายชื่อผู้รับเป็นแบตช์เล็ก ๆ และประมวลผลฟีดในแอป, พุช, และอีเมลแยกจากกัน เพื่อให้ขยายแนวนอนได้และแยกผลกระทบเมื่อช่องทางใดช่องทางหนึ่งล่ม
  • สำหรับครีเอเตอร์รายใหญ่ ฟีดในแอปและพุช เร็วขึ้น 80% ส่วนอีเมล เร็วขึ้น 55% และเมื่อจำนวนผู้ชมเพิ่มขึ้น 4 เท่า อัตราการเพิ่มขึ้นของ latency แบบ end-to-end ก็ลดจากเดิม 186% เหลือ 33% และ 60% ตามลำดับ
  • ใช้ Notification Factory และ API เดียว send_fanout_notifications เพื่อทำให้การกรอง, การตรวจสอบสิทธิ์, และการสร้าง payload เป็นมาตรฐานเดียวกัน พร้อมติดตามเวลาในแต่ละขั้นและผลลัพธ์รายผู้รับ ทำให้การปฏิบัติการและการดีบักง่ายขึ้น
  • มี 10 ทีมและวิศวกรมากกว่า 30 คนช่วยกันย้ายการแจ้งเตือนกว่า 200 รายการ โดย การสร้างดราฟต์ด้วย AI และเส้นตายที่ผู้นำกำหนดอย่างชัดเจนช่วยลดงานซ้ำและลดการต้องรันสองระบบคู่ขนานเป็นเวลานาน

ครีเอเตอร์รายใหญ่เผยให้เห็นข้อจำกัดด้านการขยายระบบ

  • Patreon ส่ง การแจ้งเตือนหลายพันล้านครั้งต่อปี ให้แฟน ๆ เมื่อครีเอเตอร์เผยแพร่โพสต์หรือเริ่มไลฟ์สตรีม
  • จากการเปิดตัวสมาชิกฟรีและการเติบโต ทำให้จำนวนแฟนต่อครีเอเตอร์พุ่งสูงขึ้น และต้นปี 2025 ก็เริ่มเห็นคอขวดด้านประสิทธิภาพอย่างรุนแรง โดยเฉพาะกับครีเอเตอร์รายใหญ่
  • เดิมการแจ้งเตือนโพสต์ทำงานด้วยงานอะซิงก์เดียวที่ดึงผู้รับทั้งหมดมา กรองตามการตั้งค่ารายผู้ใช้ สร้าง payload ให้แต่ละคน แล้วส่งต่อไปยังระบบส่งฟีดในแอป, พุช, และอีเมล
  • สำหรับครีเอเตอร์รายใหญ่ งานเดียวต้องรับผิดชอบการสร้าง การแจ้งเตือนหลายล้านรายการ จึงเกิด timeout อย่างต่อเนื่อง และทำให้ส่งการแจ้งเตือนไปยังผู้ชมจำนวนมากได้อย่างเสถียรยากขึ้น
  • โฟลว์เดิมมีข้อจำกัดเชิงโครงสร้างอยู่สองอย่าง
    • ต้นทุนการสร้างต่อผู้รับถูกรวมอยู่ในงานอะซิงก์งานเดียว ทำให้ ขยายแนวนอน ไม่ได้ และมีเพดาน throughput ที่ชัดเจน
    • ฟีดในแอป, พุช, และอีเมลเชื่อมกันแน่นเกินไป ความล้มเหลวของช่องทางหนึ่งจึงสามารถขัดขวางช่องทางอื่นได้ด้วย
  • หลายทีมผลิตภัณฑ์แก้ปัญหาระยะสั้นด้วยการสร้างวิธีขยายเฉพาะสำหรับการแจ้งเตือนปริมาณสูงแต่ละแบบ แต่สิ่งนี้ทำให้แพลตฟอร์มซับซ้อนขึ้น และทำให้การพัฒนา, การปฏิบัติการ, และการดีบักยากขึ้น
  • จึงตัดสินใจคงระบบส่งเดิมไว้ แต่เพิ่ม ชั้น fanout กลางไว้ข้างหน้าเพื่อให้ได้ความสามารถในการขยายแนวนอน, การแยกช่องทาง, และการสังเกตการณ์ระบบ

เงื่อนไขการออกแบบ 6 ข้อของแพลตฟอร์ม fanout

  • ชั้นใหม่ถูกออกแบบให้แบ่งรายชื่อผู้รับขนาดใหญ่เป็นแบตช์เล็ก ๆ สร้างการแจ้งเตือนต่อผู้รับ แล้วส่งต่อไปยังระบบฟีดในแอป, พุช, และอีเมลเดิม
  • แพลตฟอร์มนี้กำหนดข้อกำหนดไว้ 6 ข้อดังนี้
    • เป็นมิตรกับนักพัฒนา: ไม่ควรต้องแก้ปัญหาเรื่องการขยายและความเชื่อถือได้ใหม่ทั้งหมดทุกครั้งที่มีการแจ้งเตือนปริมาณสูง และควรลดการใช้งานผิดพลาดพร้อมรองรับกรณีใช้งานที่หลากหลาย
    • ขยายแนวนอน: ต้องแบ่งรายชื่อผู้รับเป็นแบตช์เล็ก ๆ โดยอัตโนมัติและประมวลผลแบบอะซิงก์ได้
    • แยกช่องทาง: ต้องให้ฟีดในแอป, พุช, และอีเมลทำงานอย่างอิสระ เพื่อไม่ให้ปัญหาจากช่องทางหนึ่งลามไปอีกช่องทาง
    • จัดการตามลำดับความสำคัญ: การแจ้งเตือนที่ไวต่อเวลาต้องใช้คิวและ worker pool เฉพาะ แยกจากการแจ้งเตือนทั่วไป
    • สังเกตการณ์ได้: ต้องตรวจสอบผลการกรองและการส่ง รวมถึงวัด latency ตลอดวงจรชีวิตของการแจ้งเตือนได้
    • ขยายต่อได้: ต้องเพิ่มความสามารถในอนาคต เช่น การสร้างรายชื่อผู้รับ โดยไม่ต้องรีแฟกเตอร์ครั้งใหญ่ทั้งระบบอีก

แยก business logic ด้วย Notification Factory

  • เพื่อแยก orchestration ของแพลตฟอร์มออกจาก business logic เฉพาะของแต่ละการแจ้งเตือน จึงนำ abstraction ชื่อ Notification Factory มาใช้
  • แต่ละ factory ทำหน้าที่เป็นพิมพ์เขียวของการประมวลผลการแจ้งเตือน โดยกำหนดสิ่งต่อไปนี้
    • ลำดับความสำคัญของการประมวลผล
    • การตั้งค่าการแจ้งเตือนที่ต้องใช้
    • วิธีกรองผู้รับ
    • การตรวจสอบสิทธิ์เฉพาะของการแจ้งเตือน
    • วิธีสร้าง payload แยกตามช่องทาง
  • แพตเทิร์นนี้อิงจาก เอนจินงานแบบกระจายภายในของ Patreon และใช้แนวคิดร่วมกันหลายส่วน
  • โมเดล Notification กำหนดอาร์กิวเมนต์ของงานที่เก็บข้อมูลเฉพาะของการแจ้งเตือนนั้นและใช้ร่วมกันโดยผู้รับทั้งหมด
  • คลาสย่อยของ BaseNotificationFactory ที่เชื่อมต่อกันจะทำหน้าที่เป็นตัวประมวลผลงาน พร้อมมีการตั้งค่าและเมธอดสำหรับกรองผู้รับและสร้างการแจ้งเตือน
  • เพื่อให้งาน fanout ซึ่งรับเพียง payload การแจ้งเตือนที่ถูก serialize แล้ว สามารถค้นหา business logic ที่ต้องรันได้ จึงใช้ registry สำหรับแมประหว่างโมเดล Notification แต่ละตัวกับคลาส factory ที่สอดคล้องกัน
  • ระหว่างรองรับประเภทการแจ้งเตือนที่หลากหลาย ก็ยังต้องป้องกันข้อผิดพลาดในการติดตั้งใช้งานที่พบบ่อยและการดึงข้อมูลที่ไม่มีประสิทธิภาพด้วย
    • Patreon มีทั้งชนิดของเรกคอร์ดผู้ใช้และวิธีกรองหลายแบบ จึงออกแบบอินเทอร์เฟซผู้รับให้เป็นแบบทั่วไป
    • และมี hook ที่ชัดเจนสำหรับการใช้งานการตั้งค่าผู้ใช้และการตรวจสอบสิทธิ์เฉพาะของการแจ้งเตือน

API เดียวและ fanout สองขั้นตอน

  • ในอดีต แต่ละช่องทางใช้ API สำหรับส่งแยกกัน ทำให้โฟลว์ของฟีดในแอป, พุช, และอีเมลต่างกันได้ และทีมผลิตภัณฑ์ก็ต้องคิดถึงพฤติกรรมแยกตามช่องทางเร็วเกินไป
  • หลังการออกแบบใหม่ จุดเริ่มต้นถูกรวมเป็น API เดียวคือ send_fanout_notifications
    • ฝั่งที่เรียกใช้จะส่ง payload ของการแจ้งเตือน, รายชื่อผู้รับ, และ timestamp ที่ใช้วัดความหน่วงของการส่ง
    • API จะค้นหา factory ของการแจ้งเตือนนั้น กำหนดขนาดแบตช์และลำดับความสำคัญ จากนั้นกระจายงานแยกตามช่องทางอย่างอิสระ
    • ความล้มเหลวหรือความล่าช้าของช่องทางหนึ่งจะไม่ขัดขวางช่องทางอื่น
  • ขั้นแรกมีหน้าที่ ขยายการสร้างการแจ้งเตือน
    • แบ่งรายชื่อผู้รับทั้งหมดออกเป็นแบตช์เล็ก ๆ
    • แต่ละแบตช์ถูกประมวลผลโดยงาน FanoutNotifications
    • งานนี้จะกรองผู้รับ, รันการตรวจสอบสิทธิ์เฉพาะของการแจ้งเตือน, แล้วใช้ Notification Factory เพื่อสร้าง payload แยกตามช่องทาง
  • ขั้นที่สองรับผิดชอบ ขยายการส่งต่อเพื่อส่งมอบ
    • นำการแจ้งเตือนที่สร้างแล้วไป fanout ต่ออีกครั้งเป็นงานส่งเฉพาะสำหรับฟีดในแอป, พุช, และอีเมล
  • ตัวประมวลผลงาน FanoutNotifications ซึ่งเป็นแกนหลัก อยู่ระหว่าง API และระบบส่งตามช่องทางเดิม โดยเปลี่ยนงานขนาดยักษ์งานเดียวให้เป็นงานเล็กจำนวนมากที่ประมวลผลแยกกันได้

การสังเกตการณ์และการดีบักระดับผู้รับ

  • การแจ้งเตือนอาจล้มเหลวหรือถูกข้ามได้จากหลายเหตุผล เช่น ผู้ใช้ปิดการตั้งค่าไว้, ไม่ผ่านการตรวจสอบสิทธิ์, หรือไม่สามารถส่งไปยังอีเมลนั้นได้
  • บนแพลตฟอร์มเดิม หากต้องการตรวจสอบผลลัพธ์ของการแจ้งเตือนรายการหนึ่ง จำเป็นต้องนำล็อกและข้อมูลจากหลายระบบมาประกอบกัน และยังไม่มีการติดตามการทำงานรายผู้รับแบบครบถ้วน ทำให้การสืบสวนอาจใช้เวลาหลายชั่วโมง
  • แพลตฟอร์มใหม่ถูกออกแบบมาเพื่อตอบคำถามต่อไปนี้ได้
    • การส่งการแจ้งเตือนใช้เวลานานเท่าไร
    • รายชื่อผู้รับเริ่มต้นมีขนาดเท่าไร
    • มีกี่คนที่ถูกกรองออก และเพราะเหตุใด
    • แต่ละช่องทางส่งสำเร็จกี่รายการ
    • เพราะเหตุใดผู้ใช้คนหนึ่งจึงได้รับหรือไม่ได้รับการแจ้งเตือนรายการหนึ่ง
  • มีการนำ โมเดลข้อมูลด้านเวลาและล็อก ที่ส่งต่อได้ตลอดทั้งแพลตฟอร์มมาใช้
    • บันทึก timestamp ในแต่ละขั้นของโฟลว์การแจ้งเตือน
    • สร้าง metrics และ events ทั้งสำหรับผลรวมและผลลัพธ์รายผู้รับ
    • นักพัฒนาสามารถแนบคุณสมบัติการล็อกเพื่อรวมเอนทิตีที่เกี่ยวข้องไว้ในการติดตามการทำงานและ events ได้
  • เมื่อปรับปรุงด้านการสังเกตการณ์, การแจ้งเตือน, แดชบอร์ด, และเครื่องมือดีบักแล้ว วิศวกรผลิตภัณฑ์และทีมซัพพอร์ตก็สามารถตรวจสอบปัญหาได้มากขึ้นโดยไม่ต้องพึ่งวิศวกรแพลตฟอร์ม

ย้ายการแจ้งเตือนมากกว่า 200 รายการ

  • เนื่องจากแพตเทิร์น Notification Factory เปลี่ยนวิธีพัฒนาและดูแลการแจ้งเตือนโดยตรง จึงไม่สามารถย้ายด้วยแค่ เครื่องมือแปลงโค้ดอัตโนมัติ (codemod) ได้
  • จำเป็นต้องคง business logic, การพึ่งพาข้อมูล, กฎด้านสิทธิ์, และพฤติกรรมแยกตามช่องทางของการแจ้งเตือนแต่ละรายการไว้เหมือนเดิม
  • จึงสร้าง AI skill ขึ้นจากเอกสารการย้ายและ PR ตัวอย่างการย้ายแบบทำมือที่เป็นต้นแบบ
    • นักพัฒนาสามารถรัน /migrate-notif-fanout <notif_name> เพื่อสร้างผลลัพธ์การย้ายรอบแรกได้
    • มันช่วยได้มากกับงานซ้ำ ๆ เช่น การสร้างโมเดลการแจ้งเตือน, การเชื่อม factory, การแก้จุดเรียกใช้, และการใช้แพตเทิร์นของแพลตฟอร์ม
  • AI ไม่ได้แทนที่การตัดสินใจของวิศวกร เพราะมันอาจพลาด business logic ที่ละเอียดอ่อน, เกิดภาพหลอน, หรือสร้างโค้ดซ้ำที่ยังต้องเก็บงานต่อ
  • มีการย้ายการแจ้งเตือนมากกว่า 200 รายการ และด้วยความร่วมมือขนาดใหญ่รวมถึงการใช้ AI ก็ทำให้ส่วนที่เหลืออีก 80% เสร็จภายใน 6 สัปดาห์

ผลลัพธ์ด้านประสิทธิภาพและผลต่อการพัฒนาผลิตภัณฑ์

  • เมื่อย้ายการสร้างการแจ้งเตือนต่อผู้รับไปไว้ในงานที่ขยายแนวนอนได้ ก็สามารถสร้าง การแจ้งเตือนหลายล้านรายการ ต่อครีเอเตอร์หนึ่งรายได้อย่างเสถียรโดยไม่เกิด timeout
  • ประสิทธิภาพของการแจ้งเตือนปริมาณสูงสำหรับครีเอเตอร์รายใหญ่ดีขึ้นอย่างมาก
    • การแจ้งเตือนแบบพุชและฟีดในแอป เร็วขึ้น 80%
    • การแจ้งเตือนทางอีเมล เร็วขึ้น 55%
    • เมื่อจำนวนผู้ชมเพิ่มขึ้น 4 เท่า latency แบบ end-to-end ของพุชและฟีดในแอปเพิ่มขึ้น 33% ส่วนอีเมลเพิ่มขึ้น 60%
    • ภายใต้เงื่อนไขเดียวกัน แพลตฟอร์มเดิมมีอัตราการเพิ่มขึ้นของ latency ถึง 186%
  • ทีมผลิตภัณฑ์ไม่จำเป็นต้องออกแบบโซลูชันขยายเฉพาะสำหรับการแจ้งเตือนปริมาณสูงแต่ละแบบอีกต่อไป และความเร็วในการพัฒนาก็ดีขึ้น
  • แพลตฟอร์มใหม่นี้รองรับการเปิดตัวสำคัญในปี 2025 เช่น Lives, Quips และการดึงครีเอเตอร์รายใหญ่เข้ามา
  • หากไม่มีการออกแบบ fanout ใหม่ การเปิดตัวเหล่านี้คงชนกับคอขวดเดิม และต้องใช้เวลาวิศวกรรมเพิ่มเพื่อสร้างโซลูชันขยายแบบเฉพาะกิจ
  • metrics ด้านเวลาในแต่ละขั้นช่วยให้เห็นว่ามีการใช้เวลาไปกับส่วนใด และช่วยปรับโครงสร้างพื้นฐานให้สมดุลระหว่างความเร็วกับต้นทุนได้

การรันคู่ขนาน 9 เดือนและการประสานงานระดับองค์กร

  • ส่วนที่ยากที่สุดคือการแก้ระบบ legacy ที่กำลังใช้งานอยู่ พร้อมประสานการย้ายของหลายทีมไปพร้อมกัน
  • มีการ รันระบบการแจ้งเตือนสองระบบคู่ขนานเป็นเวลา 9 เดือน
    • การเปลี่ยนแปลงทุกอย่างบนแพลตฟอร์มต้องเข้ากันได้ทั้งกับเส้นทางเดิมและเส้นทาง fanout ใหม่
    • ขอบเขตที่อาจเกิดบั๊กและเหตุขัดข้องจึงกว้างขึ้น และยิ่งย้ายนาน ความเสี่ยงในการปฏิบัติการก็ยิ่งเพิ่ม
    • มีเหตุขัดข้องขนาดเล็กเกิดขึ้นหลายครั้งจากปัญหาความเข้ากันได้ จึงยิ่งจำเป็นต้องย้ายให้เสร็จอย่างรวดเร็วและปลอดภัย
  • ช่วงแรกใช้แนวทางจากล่างขึ้นบน โดยให้แต่ละทีมผลิตภัณฑ์จัดลำดับความสำคัญของการย้ายการแจ้งเตือนเอง
    • ผ่านไป 6 เดือน ย้ายได้เพียงประมาณ 20%
    • ความคืบหน้าช้าเพราะถูกเบียดด้วยโรดแมปผลิตภัณฑ์และลำดับความสำคัญอื่นของแต่ละทีม
  • หลังจากผู้นำฝ่ายวิศวกรรมตกลงกันว่าจะให้เสร็จใน ไตรมาส 1 ปี 2026 และกำหนดเส้นตายที่ชัดเจนพร้อมยกระดับเป็นลำดับความสำคัญทั้งองค์กร การย้ายที่เหลือก็เสร็จภายใน 6 สัปดาห์
  • งานทั้งหมดมี 10 ทีมและวิศวกรมากกว่า 30 คน เข้าร่วม
    • ใช้ตัวติดตามใน Notion และระบบอัตโนมัติในการจัดการความคืบหน้า
    • สื่อสารกับผู้มีส่วนได้ส่วนเสียอย่างสม่ำเสมอ และกระจายงานไปหลายทีม
    • ทีมแพลตฟอร์มทำการย้ายประมาณครึ่งหนึ่ง ก่อนส่งต่อการ deploy และเก็บงานให้ทีมเจ้าของการแจ้งเตือน
    • กระจายภาระงานเพื่อไม่ให้ทีมที่มีการแจ้งเตือนจำนวนมากรับภาระหนักเกินไป
  • มีการจัด สัปดาห์ย้ายการแจ้งเตือน พร้อม office hours, ลีดเดอร์บอร์ด, ของรางวัล, และ happy hour เพื่อเร่งความคืบหน้าและฉลองการทำงานเสร็จ
  • ยังเผื่อ buffer โดยทำการส่งมอบให้เสร็จก่อนเส้นตายรวม 2 สัปดาห์
    • เพื่อช่วยทีมที่ยังต้องการการสนับสนุนเพิ่มเติม และค้นหางานที่ตกหล่นใน codebase
    • ในการตรวจรอบสุดท้ายพบงานย้ายใหม่ที่จำเป็นอีก 4 รายการและรายการค้างอีกหลายจุด แต่ก็ปิดงานได้โดยกระทบกำหนดการเพียงเล็กน้อย

บทเรียนจากการย้ายแพลตฟอร์มขนาดใหญ่

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

    • ก่อนหน้านี้ การตอบคำถามเรื่องการแจ้งเตือนจากทีมอื่นและการสืบสวนปัญหาการส่งกินสัดส่วนมากของงาน on-call ของวิศวกรแพลตฟอร์ม
    • จึงต้องปรับปรุงการสังเกตการณ์, การแจ้งเตือน, แดชบอร์ด, และเครื่องมือดีบักไปพร้อมกัน เพื่อให้ทีมผลิตภัณฑ์และซัพพอร์ตตรวจสอบได้เอง และลดภาระ on-call ของทีมแพลตฟอร์ม
    • ต้องคิดถึงไม่ใช่แค่คอขวดปัจจุบัน แต่รวมถึง คอขวดถัดไป ด้วย
    • โปรเจกต์เดียวไม่อาจแก้ปัญหาการขยายของการแจ้งเตือนทั้งหมดได้
    • การออกแบบให้รองรับความสามารถในอนาคตช่วยลดโอกาสที่จะต้องรีแฟกเตอร์ครั้งใหญ่ใหม่อีกเมื่อปริมาณการแจ้งเตือนเพิ่มขึ้น

คอขวดถัดไปและแผนในอนาคต

  • แม้การสร้างการแจ้งเตือนจะขยายได้แล้ว แต่คอขวดถัดไปคือ การสร้างรายชื่อผู้รับ
  • เมื่อกลุ่มเป้าหมายของการแจ้งเตือนยังคงใหญ่ขึ้น การดึงผู้รับจำนวนหลายล้านคนอย่างมีประสิทธิภาพก็จะยิ่งสำคัญขึ้น
  • มีแผนจะรวมการสร้างรายชื่อผู้รับเข้าไว้ใน Notification Factory เพื่อให้แพลตฟอร์มรับผิดชอบโฟลว์การแจ้งเตือนแบบ end-to-end ได้มากขึ้น
  • การตั้งค่าการแจ้งเตือนก็จะได้รับการปรับปรุงทั้งสำหรับนักพัฒนาและแฟน ๆ
    • นักพัฒนาจะได้รับการสนับสนุนให้สร้างและจัดการการตั้งค่าใหม่ได้ง่ายขึ้น
    • ส่วนแฟน ๆ จะเข้าใจและควบคุมความชอบด้านการแจ้งเตือนได้ง่ายขึ้น

ยังไม่มีความคิดเห็น

ยังไม่มีความคิดเห็น