ที่ DailyWatch แผง "วิดีโอที่เกี่ยวข้อง" ของเราเริ่มต้นจากการคิวรี SQLite แบบเรียบง่าย มันทำหน้าที่เชื่อมต่อสามตาราง นับแท็กที่ซ้ำกัน และส่งคืนรายการที่จัดลำดับแล้ว สำหรับแคตตาล็อกขนาดเล็ก แค่นั้นก็เพียงพอแล้ว วิดีโอทำอาหารที่ติดแท็ก "italian" และ "pasta" จะแสดงวิดีโออื่นๆ ที่มีแท็กเดียวกัน และผู้ใช้งานก็คลิกดู ผลลัพธ์ดูมีความเกี่ยวข้องเพราะ metadata สะอาดและคลังข้อมูลยังมีขนาดไม่ลึกมาก

จากนั้นแคตตาล็อกก็เติบโตขึ้น และความคาดหวังของผู้ชมก็เปลี่ยนไป ผู้คนไม่ได้ต้องการแค่เพียงวิดีโอที่มีแท็กเหมือนกันมากขึ้นเท่านั้น พวกเขาต้องการคลิปที่ผู้ชมสี่สิบเปอร์เซ็นต์ดูต่อจากวิดีโอปัจจุบันทันที พวกเขาต้องการช่องเฉพาะกลุ่ม (niche channel) ที่ปรากฏขึ้นซ้ำๆ ในช่วงเวลาการรับชมดึกๆ แบบเดียวกัน แม้ว่าคำอธิบายของช่องนั้นจะไม่ได้กล่าวถึงวิดีโอเริ่มต้นเลย และแท็กของช่องก็มีน้อยมาก สิ่งเหล่านี้คือรูปแบบพฤติกรรม ไม่ใช่การจับคู่ metadata SQLite ไม่สามารถแสดงผลสิ่งเหล่านี้ได้โดยไม่กลายสภาพเป็นโครงสร้างที่ซับซ้อนและดูแลรักษาไม่ได้จากการทำ self-joins และ recursive common table expressions ซ้อนกันหลายชั้น ทุกสัญญาณใหม่ที่เราพยายามสร้างโมเดล ไม่ว่าจะเป็นการดูร่วมกัน (co-viewership) หรือความต่อเนื่องของเซสชัน (session adjacency) ต่างก็เพิ่มความหน่วง (latency) และภาระทางความคิด (mental overhead) เรากำลังเจอปัญหาแบบกราฟที่ถูกบีบให้อยู่ในรูปแบบของ relational database

ผมจึงย้ายเลเยอร์การแนะนำ (recommendation layer) ไปยัง Apache AGE

Apache AGE คือส่วนขยาย (extension) ของ PostgreSQL ที่เพิ่มการคิวรีแบบกราฟด้วย openCypher เข้าไปในฐานข้อมูลที่คุณใช้งานอยู่แล้ว มันไม่ใช่เซิร์ฟเวอร์แยกต่างหาก และไม่ใช่ sidecar มันทำงานอยู่ภายใน Postgres ซึ่งหมายความว่าเราสามารถสร้างเครือข่ายการดูร่วมกัน (co-view network) ได้โดยไม่ต้องตั้งค่า Neo4j cluster แยกต่างหาก หรือต้องเรียนรู้วิธีการปฏิบัติงาน (operational playbook) ใหม่ทั้งหมด สำหรับทีมขนาดเล็กที่ไม่มีวิศวกรด้านความน่าเชื่อถือของฐานข้อมูล (database reliability engineer) ความแตกต่างนี้มีความสำคัญอย่างยิ่ง

ทำไมการใช้ Extension ถึงดีกว่าการใช้ฐานข้อมูลใหม่

การเพิ่มฐานข้อมูลแบบกราฟเข้าไปใน stack ของคุณนั้นดูเหมือนง่ายเมื่ออยู่บนไวท์บอร์ด แต่กลับมีราคาแพงเมื่อใช้งานจริง (production) คุณต้องมีแดชบอร์ดสำหรับตรวจสอบใหม่, ขั้นตอนการสำรองข้อมูลใหม่, connection pools ใหม่ และตรรกะการทำ failover ใหม่ AGE ช่วยเลี่ยงปัญหาทั้งหมดนั้นเพราะมันทำงานอยู่ภายใน Postgres instance เดิมของคุณ

มีเหตุผลในทางปฏิบัติสี่ประการที่ทำให้สิ่งนี้ได้ผลสำหรับเรา

  • ไม่ต้องมีโครงสร้างพื้นฐานใหม่ เนื่องจาก AGE เป็น extension ตารางเวลา pg_dump ปัจจุบัน, ตัวสำรอง (replicas) ที่มีอยู่ และการตรวจสอบสุขภาพ (health checks) มาตรฐานของ Postgres ทั้งหมดจะยังคงทำงานต่อไป คุณไม่จำเป็นต้องโน้มน้าวฝ่ายปฏิบัติการ (operations) ให้มาคอยดูแล data store อีกตัวหนึ่ง
  • การทำงานแบบผสมผสานในคิวรีเดียว AGE ช่วยให้คุณเขียน Cypher ภายใน SQL ได้ คุณสามารถรัน graph traversal เพื่อค้นหาวิดีโอที่น่าจะเป็นไปได้ จากนั้นนำชุดผลลัพธ์นั้นไป join กับตาราง users แบบ relational เพื่อบังคับใช้ข้อจำกัดด้านเนื้อหาตามภูมิภาค หรือ join กับตาราง sponsorships แบบ relational เพื่อลดความสำคัญของบางช่องลง ทั้งหมดนี้จบในรอบเดียว (one round-trip) โดยใช้สองภาษาคิวรีทำงานร่วมกัน
  • ความสามารถในการเคลื่อนย้าย (Portability) Cypher เป็นภาษาคิวรีแบบกราฟที่เป็นมาตรฐานเปิดและมีเอกสารประกอบที่ครบถ้วน หาก DailyWatch เติบโตจนเกินขีดความสามารถของ AGE และจำเป็นต้องย้ายไปยัง Neo4j หรือ Memgraph ในภายหลัง ตรรกะการคิวรีก็จะสามารถถ่ายโอนได้โดยต้องเขียนใหม่เพียงเล็กน้อย คุณจะไม่ถูกผูกขาดอยู่กับภาษาเฉพาะ (proprietary dialect)
  • ความเข้ากันได้ เนื่องจากข้อมูลสุดท้ายจะถูกเก็บไว้ใน PostgreSQL เครื่องมือ PHP หรือ Python ที่คุณมีอยู่จึงไม่ต้องเปลี่ยนแปลง คุณเชื่อมต่อด้วย driver เดิม, จัดการ connection strings แบบเดิม และดึงข้อมูลแถว (rows) ด้วยวิธีเดิม ตรรกะแบบกราฟจะอยู่ที่เลเยอร์ของการคิวรี ไม่ใช่ที่เลเยอร์ของแอปพลิเคชัน

การสร้างโมเดลการดูร่วมกัน (Co-Viewership)

การนำไปใช้งานนั้นตรงไปตรงมา เรากำหนด nodes สำหรับเอนทิตีที่เราสนใจ ได้แก่ Video และ Channel จากนั้นเราจึงกำหนด edges สำหรับความสัมพันธ์ระหว่างกัน โดย edge แบบ PUBLISHED จะเชื่อมต่อ Channel เข้ากับ Video และ edge แบบ CO_VIEWED จะเชื่อมต่อ Video หนึ่งเข้ากับอีกวิดีโอหนึ่ง โดยมีคุณสมบัติ (property) weight ที่แสดงถึงความถี่ที่วิดีโอทั้งสองปรากฏขึ้นในเซสชันการรับชมเดียวกัน

โมเดลนี้สามารถจับสิ่งที่ SQL แบบอิงตามแท็กทำไม่ได้ นั่นคือโครงสร้างที่แฝงอยู่ (implicit structure)