5 คะแนน โดย GN⁺ 2023-12-02 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • แอปฟรอนต์เอนด์ที่ซับซ้อนเริ่มจากแคชผลลัพธ์ API แล้วค่อย ๆ ต้องแบกรับทั้ง ดัชนีแบบทำเอง และการทำให้แคชหมดอายุ จนสภาพใกล้เคียงกับการสร้างฐานข้อมูลขนาดเล็กขึ้นมาใหม่ในทุกโปรเจกต์
  • ในเฟรมเวิร์กเชิงประกาศอย่าง React เพื่อหลีกเลี่ยงการเรียก API ทุกครั้งที่เรนเดอร์ จึงมักวางแคชไว้ใน state ภายในหรือชั้น Redux และชั้นนี้ก็ค่อย ๆ ทำหน้าที่เป็น ที่เก็บข้อมูลส่วนกลาง
  • โครงสร้างที่เก็บตาม ID และโครงสร้างค้นหาตามวันที่ช่วยให้ค้นหาได้เร็วขึ้น แต่การรักษา ความสอดคล้องกัน ระหว่างหลายโครงสร้างอย่าง CACHE และ ENTRIES_BY_DATE กลายเป็นภาระในการทดสอบและการรีวิวโค้ด
  • การเปลี่ยนแปลงแบบมองโลกในแง่ดีช่วยเพิ่มความเร็วที่ผู้ใช้รู้สึกได้โดยอัปเดต UI ทันทีก่อนเซิร์ฟเวอร์ตอบกลับ แต่ก็ตามมาด้วย ต้นทุนด้านความถูกต้องสอดคล้อง เช่น การทำตรรกะซ้ำทั้งฝั่งไคลเอนต์และเซิร์ฟเวอร์ การติดตามการเปลี่ยนแปลงที่กำลังดำเนินอยู่ การ rollback เมื่อเกิดข้อผิดพลาด และการปรับให้ตรงกันหลังรีสตาร์ตแอป
  • SQLSync พยายามนำเสนอ แคชที่ทนทาน, ดัชนี, constraint, การเปลี่ยนแปลงแบบมองโลกในแง่ดี และ reactive query ภายในสแตกฟรอนต์เอนด์ ด้วยฐานข้อมูลภายในเครื่องที่ใช้ SQLite และการซิงก์คล้าย Git Rebase

กระบวนการที่แคชฟรอนต์เอนด์เติบโตจนกลายเป็นฐานข้อมูล

  • การจัดการข้อมูลฟรอนต์เอนด์อาจเริ่มจาก แคช ง่าย ๆ ที่เก็บผลลัพธ์ API ไว้ในตัวแปรภายในเครื่อง
    • เฟรมเวิร์กเชิงประกาศอย่าง React จะเรนเดอร์ tree ซ้ำหลายครั้งระหว่างการโต้ตอบของผู้ใช้
    • เพื่อไม่ให้ส่งคำขอ API ทุกครั้งที่เรนเดอร์ สามารถใช้ useState และ useEffect เก็บผลลัพธ์คำขอหรือข้อผิดพลาดไว้ใน state ของคอมโพเนนต์ได้
    • ตัวอย่างถูกทำให้ง่ายเพื่อความชัดเจน และในทางปฏิบัติก็มีทางเลือกในการใช้ไลบรารี API ที่ผ่านการพิสูจน์แล้ว
  • แคชสามารถถูกย้ายไปยังชั้นที่สูงขึ้นของ UI tree หรือไปอยู่นอก UI ได้
    • Redux เป็นไลบรารีจัดการ state ของ React ที่รวม state เข้าด้วยกันและประสานการเปลี่ยนแปลงแบบ atomic ตามเวลา
    • ระบบนิเวศของ Redux ได้ขยายไปสู่เครื่องมือและแพตเทิร์นสำหรับจัดการการแคชข้อมูล API
    • รูปแบบการใช้งานเช่นนี้มีเป้าหมายเพื่อรวมตรรกะแคชไว้ส่วนกลาง ประสานการอัปเดต และแชร์ผลลัพธ์แคชระหว่างคอมโพเนนต์
  • ยิ่งชั้นแคชเติบโตขึ้น ก็ยิ่งใกล้เคียงกับ ระบบจัดเก็บข้อมูลส่วนกลาง ที่จัดการข้อมูลอย่างมีประสิทธิภาพให้เข้ากับเอนจินเรนเดอร์และการกระทำของผู้ใช้

ดัชนีแบบทำเองและภาระด้านความสอดคล้องกัน

  • แม้ในฟรอนต์เอนด์ ก็สามารถเก็บข้อมูลที่ได้รับจากเซิร์ฟเวอร์ไว้ใน object ที่ใช้ ID เป็น key เพื่อค้นหาและแก้ไขได้รวดเร็ว
    • ในแอปที่ใช้ REST API มักเกิดการอ่านข้อมูลเป็นชุด แล้วค่อยเสริม object ที่จำเป็นเพิ่มเติมอยู่บ่อยครั้ง
    • หากเก็บ object แยกตาม ID จะทำให้ merge ผลลัพธ์ API เข้ากับแคชได้ง่าย
    • โครงสร้างนี้เหมาะกับการสร้าง อ่าน แก้ไข และลบโดยอิง ID
  • การกรองที่ต้องไล่ดูหลายรายการทำให้ต้องสร้าง ดัชนี แยกต่างหากเพื่อหลีกเลี่ยงการตรวจสอบ entry ทั้งหมด
    • หากสร้างโครงสร้าง ENTRIES_BY_DATE โดยอิงส่วนปี/เดือน/วันของ createdAt ก็จะค้นหา entry ของวันที่เฉพาะเจาะจงได้รวดเร็ว
    • แต่ต้องคอยรักษาความสอดคล้องกันระหว่าง CACHE และ ENTRIES_BY_DATE อย่างต่อเนื่อง
    • การค้นหาช่วงวันที่ต้องเข้าถึงหลายครั้ง และ array ที่เรียงตามวันที่พร้อมตรรกะค้นหา/อัปเดตที่ซับซ้อนกว่าอาจเป็นโครงสร้างที่ดีกว่า
  • เมื่อมีดัชนีมากขึ้น แต่ละดัชนีก็ต้องมีตรรกะสำหรับสร้าง อัปเดต และค้นหา
    • การตรวจสอบความถูกต้องกลายเป็นภาระในการทดสอบและการรีวิวโค้ด
    • หากลืมลบหรืออัปเดตในดัชนีใดดัชนีหนึ่ง อาจเกิดบั๊กที่หาได้ยาก
    • เวลาอาจถูกใช้ไปกับโครงสร้างพื้นฐานเพื่อจัดการความซับซ้อนนี้ มากกว่าฟีเจอร์ใหม่ของแอปพลิเคชัน
  • ดัชนีของฐานข้อมูลจริงซับซ้อนกว่าการเก็บข้อมูลในรูปแบบอื่นอย่างง่าย ๆ มาก
    • ประกอบด้วยองค์ประกอบอย่างการเก็บสถิติ การจัดการเวอร์ชันข้อมูล การควบคุมทรานแซกชัน การล็อก และการทำงานร่วมกับ query optimization

ปัญหาความถูกต้องสอดคล้องที่การเปลี่ยนแปลงแบบมองโลกในแง่ดีเพิ่มเข้ามา

  • การเปลี่ยนแปลงแบบมองโลกในแง่ดี คือการจำลองผลของงานบางอย่างภายในเครื่องก่อนที่เซิร์ฟเวอร์จะตอบกลับ
    • UI สามารถตอบสนองได้ทันทีเหมือนไม่มีความหน่วงของเครือข่าย
    • หากเซิร์ฟเวอร์ตัดสินใจต่างจากที่คาดหรือเกิดข้อผิดพลาด UI อาจต้อง rollback การเปลี่ยนแปลงและขอให้ผู้ใช้แก้ปัญหา
    • สิ่งนี้จะเป็นเครื่องมือที่ทรงพลังเมื่อไคลเอนต์คาดเดาผลลัพธ์ของเซิร์ฟเวอร์ได้ดี สามารถจัดการข้อผิดพลาดที่ฝั่งไคลเอนต์ได้ และตรรกะถูกซิงก์กันอย่างใกล้ชิด
  • โดยทั่วไป optimistic update มีลำดับสี่ขั้นตอน
    • UI สร้างงานเขียนข้อมูลขึ้นมา
    • ใช้สมมติฐานว่าเซิร์ฟเวอร์จะเห็นด้วย แล้วนำการเปลี่ยนแปลงไปใช้กับแคชภายในเครื่องและเรนเดอร์ UI ใหม่ทันที
    • ส่งงานเปลี่ยนแปลงไปยังเซิร์ฟเวอร์แบบ asynchronous
    • merge คำตอบจากเซิร์ฟเวอร์เข้ากับแคชภายในเครื่องเพื่อเขียนทับการเปลี่ยนแปลงแบบมองโลกในแง่ดีเดิม และเรนเดอร์ UI ใหม่หากจำเป็น
  • การรักษาความสอดคล้องกับเซิร์ฟเวอร์ก่อให้เกิดภาระหลายอย่าง
    • ต้องทำตรรกะซ้ำทั้งไคลเอนต์และเซิร์ฟเวอร์เพื่อคาดการณ์ผลลัพธ์
    • ต้องติดตามการเปลี่ยนแปลงแต่ละรายการที่กำลังดำเนินอยู่ เพื่อจัดการข้อผิดพลาดแบบ asynchronous หรือความไม่ตรงกันกับเซิร์ฟเวอร์
    • เพื่อประสบการณ์ผู้ใช้ที่ดีขึ้น อาจต้องทำให้ส่วนของแคชแบบมองโลกในแง่ดีมีความทนทาน เพื่อปรับการเปลี่ยนแปลงให้ตรงกันหลังรีสตาร์ตแอป
  • กระบวนการนี้เพิ่มเวลาในการพัฒนาและต้นทุนการตรวจสอบความถูกต้อง จนอาจทำให้การจัดการข้อมูลมาก่อนการสร้างคุณค่าหรือฟีเจอร์ที่แตกต่างให้ผู้ใช้

ความซับซ้อนของการทำให้แคชหมดอายุแบบ recursive

  • ในแอปที่มีข้อมูลจำนวนมาก ข้อมูลเดียวกันอาจปรากฏอยู่ในหลายตำแหน่งของแคช
    • ตัวอย่างแคชเก็บ projects, tasks, users ไว้ด้วยกัน
    • หลังทำงานเสร็จ หลายส่วนอาจได้รับผลกระทบ เช่น ความคืบหน้าของโปรเจกต์ งานที่มอบหมายให้ผู้ใช้ และข้อมูลงานใหม่
  • เพื่อปรับแคชให้ตรงกับเซิร์ฟเวอร์หลังทำงานเสร็จ อาจต้อง round trip หลายครั้ง
    • แจ้งเซิร์ฟเวอร์ว่างานเสร็จแล้ว
    • รีเฟรชโปรเจกต์เพราะความคืบหน้าของโปรเจกต์เปลี่ยนไป
    • ตรวจสอบว่ามีการมอบหมายงานใหม่หรือไม่
    • หากมีการมอบหมายใหม่ ให้ดึงข้อมูลงานนั้น
  • API ที่ซับซ้อนขึ้นสามารถลดจำนวน round trip ได้ แต่ยังเหลือผลลัพธ์ที่ API หรือตรรกะฝั่งไคลเอนต์ถูกผูกติดกับ โมเดลข้อมูลพื้นฐาน
    • GraphQL เป็นหนึ่งในแนวทางสำหรับปัญหานี้ แต่ไม่ใช่ทางออกที่สมบูรณ์
  • โครงสร้างที่ UI ต้องรู้ว่าแต่ละการเปลี่ยนแปลงเกี่ยวข้องกับส่วนใดของแคช จะเปราะบางมากขึ้นเมื่อขนาดใหญ่ขึ้น
    • ความสัมพันธ์ของข้อมูลและการ aggregation อาจส่งผลต่อหลายส่วนของแคชภายในเครื่อง
    • เมื่อทีมวิศวกรรมใหญ่ขึ้น ปัญหาอาจข้ามขอบเขตทีม และให้ความรู้สึกคล้ายตัวแปร global ที่เปลี่ยนแปลงได้ในโปรเจกต์ซอฟต์แวร์ขนาดใหญ่
  • เมื่อรวมกับการเปลี่ยนแปลงแบบมองโลกในแง่ดี ไคลเอนต์จะยิ่งต้องทำซ้ำตรรกะแบ็กเอนด์มากขึ้นเพื่อคาดการณ์การเปลี่ยนแปลงของเซิร์ฟเวอร์
    • ในตัวอย่าง อาจพยายามลบ task 1 ออกจาก user 1 และคำนวณความคืบหน้าเป็นสัดส่วนใหม่โดยใช้จำนวนงานทั้งหมด
    • ยิ่งทำให้ไคลเอนต์คาดการณ์การเปลี่ยนแปลงซ้อนกันภายในเครื่องมากเท่าไร ไคลเอนต์ก็ยิ่งทำซ้ำสแตกแบ็กเอนด์มากขึ้นเท่านั้น

สแตกฐานข้อมูลฟรอนต์เอนด์ที่ SQLSync เสนอ

  • SQLSync เป็นสแตกฐานข้อมูลที่ปรับให้เหมาะกับฟรอนต์เอนด์ สร้างบน SQLite
    • เอนจินซิงก์อิงแนวคิดจาก Git และระบบแบบกระจาย
    • ออกแบบมาให้ผสานกับเฟรมเวิร์กฟรอนต์เอนด์ยอดนิยมอย่าง React, Vue, Next.js ได้อย่างราบรื่น
    • เป้าหมายคือจัดการปัญหายาก ๆ ด้านการจัดการข้อมูล เพื่อให้นักพัฒนามุ่งเน้นฟีเจอร์เฉพาะของแอปพลิเคชันได้
  • ตัวอย่าง Todo app ใช้ Rust 60 บรรทัด และ SQL query ไม่กี่รายการที่กระจายอยู่ในคอมโพเนนต์ เพื่อสร้าง data layer ทั้งหมด
    • SQLSync ให้แคชที่ทนทาน, ดัชนี/constraint/trigger/query optimization ของ SQLite, การเปลี่ยนแปลงแบบมองโลกในแง่ดี, การทำให้แคชหมดอายุอย่างชาญฉลาด และ reactive query
  • ข้อมูลภายในเครื่องถูกเก็บไว้ในฐานข้อมูล SQLite หนึ่งฐานหรือมากกว่า
    • สร้างดัชนีได้ง่าย และซิงก์กับข้อมูลโดยอัตโนมัติ
    • ฐานข้อมูลสามารถใช้ดัชนีอัตโนมัติเพื่อเร่ง query ได้เหมือนแบ็กเอนด์
    • SQL สามารถอธิบายการค้นหาข้อมูลที่ซับซ้อนได้ และยังใช้ฟีเจอร์อย่าง triggers, foreign keys, constraints, full-text search ได้ด้วย
  • การเปลี่ยนแปลงแบบมองโลกในแง่ดีจัดการด้วย reducer
    • โครงสร้างคล้ายกับ แนวคิดหลักของ Redux
    • reducer สามารถเขียนด้วยภาษาใดก็ได้ที่คอมไพล์เป็น WebAssembly ได้
    • SQLSync รันการเปลี่ยนแปลงบนไคลเอนต์แบบมองโลกในแง่ดี และรันบนเซิร์ฟเวอร์ตามลำดับที่สอดคล้องกันทั่วทั้งระบบ
    • จากนั้นไคลเอนต์จะซิงก์กับเซิร์ฟเวอร์ด้วยกระบวนการที่คล้าย Git Rebase
  • สถาปัตยกรรมนี้มีข้อดีคือทำให้ไม่จำเป็นต้องมี การทำให้แคชหมดอายุแบบ recursive
    • เขียนตรรกะการเปลี่ยนแปลงข้อมูลทั้งหมดไว้ใน reducer ที่แชร์ระหว่างไคลเอนต์กับเซิร์ฟเวอร์ได้ง่าย
    • การเปลี่ยนแปลงข้อมูลทั้งหมดที่เกิดขึ้นระหว่างการเปลี่ยนแปลงจะมองเห็นได้โดยอัตโนมัติ
    • เนื่องจากการซิงก์ทำงานเหมือน Git Rebase แม้เซิร์ฟเวอร์จะทำการเปลี่ยนแปลงต่างจากไคลเอนต์ ไคลเอนต์ก็มีหลักประกันว่าจะไปถึงผลลัพธ์ที่สอดคล้องกันแบบเดียวกัน

งานที่เกี่ยวข้อง

  • “Building data-centric apps with a reactive relational database” ของ Riffle กล่าวถึงแนวคิดการเก็บ state ทั้งหมดของแอปพลิเคชัน รวมถึง UI state ไว้ในฐานข้อมูลแบบ reactive เพียงแห่งเดียว
    • Reactive query ให้โมเดลการคิดที่สะอาดและเข้ากับระบบเชิงประกาศอย่าง React ได้ดี
    • แก้ปัญหาการพัฒนาแอปไคลเอนต์ด้วยแนวคิดจากชุมชนฐานข้อมูล
    • กล่าวถึงประโยชน์ของการโมเดล state ด้วยโมเดลข้อมูลเชิงสัมพันธ์และดัชนีจริง
  • Stepan จาก Instant.db เขียนบทความสองบทความเกี่ยวกับฐานข้อมูลในเบราว์เซอร์
    • Database in the Browser, a Spec
    • A Graph-Based Firebase
    • ทั้งสองบทความกล่าวถึงปัญหาคล้ายกันโดยเน้นความสัมพันธ์ระหว่างสแตกฟรอนต์เอนด์กับแบ็กเอนด์มากกว่า และอธิบายแรงจูงใจที่ทำให้ Instant.db ถูกสร้างขึ้นในฐานะผู้สืบทอดแบบกราฟของ Firebase
  • CR-SQLite ของ Matt Wonlaw เป็นส่วนขยายของ SQLite
    • ใช้ conflict-free replicated data type (CRDT) และ event log แบบ causal order เพื่อ merge ข้อมูลอย่างสอดคล้องกัน
    • ทำให้แอปแบบ peer-to-peer สามารถเก็บข้อมูลใน SQLite และทำงานร่วมกันได้โดยไม่ต้องมีตัวประสานงานส่วนกลาง
    • ยังเป็นตัวอย่างของการรัน SQLite ในเบราว์เซอร์ด้วย
  • Matt Wonlaw กำลังสำรวจแนวคิดที่เกี่ยวข้องด้วย

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

 
GN⁺ 2023-12-02
ความคิดเห็นบน Hacker News
  • ผมรู้จักโปรเจกต์นี้ดี และคนทำก็เป็นเพื่อนผม เลยจะให้เขามาตอบคำถามที่นี่
    เขาเป็น สถาปนิกฐานข้อมูล ที่มีประสบการณ์ SQLsync ถูกสร้างขึ้นเพื่อให้นักพัฒนาฟรอนต์เอนด์สามารถ query และอัปเดตฐานข้อมูลระยะไกลได้ราวกับว่ามันอยู่ในเบราว์เซอร์ทั้งหมด ซึ่งในทางปฏิบัติก็เกือบเป็นแบบนั้นจริง ๆ และด้วย WASM เราสามารถส่งฐานข้อมูล SQLite ทั้งก้อนเข้าไปในเบราว์เซอร์ได้ ประเด็นสำคัญอยู่ที่ อัลกอริทึมแบบ reactive ที่ฉลาดแต่เรียบง่ายสำหรับซิงก์ระหว่างหลายไคลเอนต์
    ถ้ามองว่าส่วนใหญ่ของงานพัฒนาคือการซิงก์ข้อมูล React กับ REST API ก็ถือเป็นกระบวนการซิงก์ชนิดหนึ่งได้ และแนวทางนี้เปิดความเป็นไปได้ใหม่ ๆ ไม่จำเป็นต้องสร้างฐานข้อมูลแบบปรับแต่งเองแปลก ๆ จาก tree ของ object ที่ดึงจาก API แล้ว cache ไว้อีกต่อไป แค่ใช้พลังของฐานข้อมูลเชิงสัมพันธ์เพื่ออัปเดตและ query ในเครื่องได้โดยตรง

    • ผมคิดว่าการย้าย ระบบ query ทั้งหมดมาไว้ที่ฟรอนต์เอนด์คือจุดที่นักพัฒนาฟรอนต์เอนด์จำนวนมากต้องการจริง ๆ พวกเขาต้องการระบบ query ที่ทรงพลังสำหรับข้อมูล ไม่ได้อยากคอยประดิษฐ์ transport layer หรือ REST, GraphQL, *RPC อะไรพวกนี้ขึ้นมาใหม่เรื่อย ๆ
      อย่างไรก็ตาม ในบริษัทเว็บแบบดั้งเดิม การนำมาใช้ทำได้ยากเพราะมีทีมแบ็กเอนด์/ฟรอนต์เอนด์ที่แยกเชี่ยวชาญกันอยู่ เท่ากับเป็นการเอาชั้นฐานข้อมูล แบ็กเอนด์ การส่งข้อมูล และการยืนยันตัวตนออก แล้วแทนด้วยระบบแบบบล็อกเดียว และสถาปนิกระบบส่วนใหญ่ก็มาจากฝั่งแบ็กเอนด์ จึงไม่ค่อยเข้าใจปัญหานี้ มันแตะทั้งสองฝั่งอย่างลึก จึงไม่ค่อยเข้ากับระบบเดิม และสุดท้ายเหมาะกับงานพัฒนาใหม่มากกว่า แบ็กเอนด์ก็ไม่ใช่บริการ AWS หรือ Azure และไม่เป็นมิตรกับ Lambda ด้วย ดังนั้นสถาปนิกประเภทที่ผมเจอส่วนใหญ่คงไม่อยากยุ่ง
      วิธีนี้เคยมีอยู่บ้างแล้วในเทคโนโลยีเก่าอย่าง CouchDB+PouchDB มันเหมาะกับบางกรณีใช้งานค่อนข้างดี แต่ระบบ query ยังไม่เป็นอุดมคติ และวิธีการยืนยันตัวตนกับการกำหนดขอบเขตข้อมูลก็แปลกสำหรับคนส่วนใหญ่ กรณีที่ง่ายที่สุดคือเมื่อข้อมูลเป็นของผู้ใช้รายเดียวอย่างสมบูรณ์ และใช้โมเดลฐานข้อมูลแยกตามผู้ใช้ได้ตรง ๆ หากแบ่งข้อมูลอย่างเข้มงวดด้วย CRDT ก็จะลดปัญหา conflict ได้มาก
      แต่มีปัญหาเรื่องการขยายระบบ CouchDB ต้องการ CPU สูงมากเมื่อมีผู้ใช้เชื่อมต่อ 10,000–100,000 คน และเทคโนโลยีก็เก่าแล้ว แม้ยังมีการดูแลรักษาอยู่ก็ตาม ในแง่การออกแบบระบบ เมื่อเริ่มแชร์ข้อมูลระหว่างผู้ใช้ ความซับซ้อนจะเพิ่มขึ้นอย่างรวดเร็ว จนกลายเป็นแค่ย้ายตำแหน่งของความซับซ้อนแทนที่จะแก้มัน ทำให้ความเหมาะสมลดลง
      แนวทางนี้ก็ดูเหมือนมุ่งไปที่เป้าหมายเดียวกัน แต่มีแนวโน้มสูงว่าจะเจอปัญหาการขยายระบบคล้าย ๆ กัน รอดูว่าจะพัฒนาไปอย่างไร และดูเหมือนเป็นก้าวแรก
    • ฟังดูค่อนข้างคล้าย Couchbase ที่สามารถ query/อัปเดตฐานข้อมูลซึ่งซิงก์กับรีโมต แล้วซิงก์ต่อไปยัง peer ได้ ส่วนการยืนยันตัวตนหรือ business logic ก็สามารถควบคุมได้ง่ายด้วยปลั๊กอิน JavaScript ฝั่งเซิร์ฟเวอร์
    • สงสัยจริง ๆ ว่าทำไมไม่ cache เฉพาะส่วนที่เกี่ยวข้องไว้ใน LocalStorage / SessionStorage
      จำได้ว่าเมื่อก่อน Chrome เคยพยายามใส่ฐานข้อมูล SQL เข้าไปในเบราว์เซอร์แบบตรงตัว แต่ไปได้ไม่ดีนัก แล้ว localStorage ก็กลายเป็นกระแสหลัก ไม่ได้จะลดทอนประโยชน์ของสิ่งนี้นะ แค่โดยปกติผมมักเลือกสิ่งที่เบราว์เซอร์มีให้ ส่วน WASM และความเป็นไปได้ที่จะนำมันเข้ามาในเบราว์เซอร์เมื่อมันสุกงอมหรือมีฟีเจอร์มากขึ้นนั้น ผมคาดหวังไว้มาก
  • บริษัทเก่าที่ผมเคยทำงานใช้ซอฟต์แวร์จัดการโปรเจกต์ที่มีกลไก checkout/checkin สำหรับงานแก้ไข ถ้า checkout โปรเจกต์ ก็จะดาวน์โหลดสำเนาสำหรับแก้ในเครื่อง และเมื่อ checkin ก็อัปโหลดกลับไปที่เซิร์ฟเวอร์ ระหว่าง checkout โปรเจกต์จะถูกล็อก ในยุคของแอป live update ทุกคนรู้สึกว่าวิธีนี้ล้าสมัย
    แต่หลังจากสร้างเว็บแอป SPA มา 10 ปี วิธีซิงก์ข้อมูลแบบนั้นกลับรู้สึกเหมือนมาก่อนกาล

    • การจะรองรับ live update แบบขนานจากผู้ใช้หลายคน หรือจะล็อกให้มีการอัปเดตได้ทีละรายการ ท้ายที่สุดไม่ใช่การตัดสินใจเชิงเทคนิค แต่เป็นการตัดสินใจทางธุรกิจ ประเด็นสำคัญคือกฎธุรกิจที่เหมาะกับแอปพลิเคชันนั้นช่วยให้จัดการ live update พร้อมกันได้หรือไม่
      สุดท้ายก็คือสามารถนำขั้นตอนที่แก้ความไม่สอดคล้องกันระหว่างการอัปเดตหลายรายการพร้อมกันได้อย่างเป็นระบบมาใช้ได้หรือไม่ บางกรณีทำได้ บางกรณีทำไม่ได้ และขึ้นอยู่กับกฎธุรกิจมากกว่าความสามารถทางเทคนิค
      หากตามกฎธุรกิจแล้วไม่สามารถสร้างกลไกการแก้ปัญหาได้ แม้จะมีความสามารถทางเทคนิคในการรองรับการอัปเดตพร้อมกัน ก็จำเป็นต้องมี การล็อก เพื่อให้มีการอัปเดตได้ทีละรายการ
    • ถ้าใช้แนวทางนี้ ปัญหาหลายอย่างจะถูกแก้ และการ implement ก็ง่ายมาก
      แต่ยากที่จะโน้มน้าวว่าจริง ๆ แล้วนี่คือสิ่งที่ต้องการ คนมักหลงอยู่ในภาพลวงตาใหญ่โตว่าทุกอย่างต้องพร้อมใช้งานตลอดเวลา แต่ในความเป็นจริง โดยทั่วไปคนคนหนึ่งเป็นผู้เปลี่ยนแปลงทีละครั้ง และถ้าต้องมีสองคนขึ้นไปทำงานร่วมกัน สุดท้ายก็ต้องคุยหรือสื่อสารกันเพื่อปรับให้ตรงกันอยู่ดี
      แม้ในการพัฒนาแบบกระจายศูนย์เต็มรูปแบบอย่าง Git ก็ยังไม่สามารถแก้ conflict ได้เองอย่างวิเศษอัตโนมัติ หากจะเลือกการเปลี่ยนแปลงที่ถูกต้อง ก็ยังต้องสื่อสารกับคนอื่นและเข้าใจบริบทอยู่ดี
    • นี่แหละคือเหตุผลที่พาราไดม์ ฐานข้อมูลเชิงสัมพันธ์ แบบดั้งเดิมอย่าง MySQL ยังอยู่รอด แม้ผู้คนจะพูดถึงมันในเชิงด้อยกว่าฐานข้อมูลไม่เชิงสัมพันธ์อย่าง NoSQL หรือ MongoDB ก็ตาม ไม่ใช่ว่าจะเอาทุกอย่างมาแทนได้เพียงเพราะมันเร็วหรือดูเท่
      บางอย่างต้องการทางออกที่ผ่านการพิสูจน์แล้ว
    • ฟังดูเหมือน RCS https://en.wikipedia.org/wiki/Revision_Control_System
      จำได้ว่าเมื่อก่อนตอนบริษัทเปลี่ยนจาก RCS ไปเป็น CVS เพื่อนร่วมงานคนหนึ่งหงุดหงิดที่ CVS ไม่รองรับ checkout แบบล็อก
      https://en.wikipedia.org/wiki/Concurrent_Versions_System
    • ผมชอบแนวทางนี้ SQLSync โดยพื้นฐานแล้วก็ทำสิ่งนี้ต่อเนื่องอยู่แล้ว แต่ถ้าประสานงานแบบชัดเจน ก็น่าจะทำรูปแบบ checkin/checkout แบบนั้นได้เช่นกัน
      ผมคิดว่ายุทธศาสตร์ล็อกแบบเจ้าของคนเดียวก็จำลองด้วย SQLSync ได้ เพียงแต่อาจไม่จำเป็น ขึ้นอยู่กับแอป หากเป้าหมายคือ ทำงานออฟไลน์ แล้ว merge เมื่อพร้อม SQLSync ก็ให้แพตเทิร์นนี้มาในตัว หากเป้าหมายคือให้มีไคลเอนต์เดียวเท่านั้นที่แก้ไขได้ ก็ต้องใช้แพตเทิร์นการล็อกจากศูนย์กลาง และสิ่งนี้ก็น่าจะสามารถประสานงานผ่าน SQLSync ได้
  • เรื่องนี้เกี่ยวพันกับหลักการ “สิ่งที่ถูกวัดย่อมถูกบริหารจัดการ” และ ความผิดพลาดจากต้นทุนจม
    ปัญหาที่แท้จริงของฐานข้อมูลคือความซับซ้อน ฟีเจอร์แต่ละอย่างโดยทั่วไปค่อนข้างปลอดภัย แต่เมื่อความเสถียร แคช และดัชนีเริ่มเชื่อมโยงกัน ความซับซ้อนจะพุ่งขึ้นอย่างมาก และโดยปกติแล้วการทำ DB เฉพาะโดเมนขึ้นมาเองก็ไม่สมเหตุสมผล
    แต่พอถึงจุดที่บริษัทตระหนักว่าได้ลงทุนและทุ่มทรัพยากรจำนวนมากไปกับการทำสามฟีเจอร์นั้นแล้ว ในทางการเมืองก็ยากที่จะเสนอให้รื้อทิ้ง และต้นทุนจริงในการกำจัดหนี้ทางเทคนิคในครั้งเดียวก็สูงด้วย
    ผมมองว่าปัญหาที่แท้จริงคือไวยากรณ์ของ SQL ถ้าประสบการณ์การใช้ฐานข้อมูลเชิงสัมพันธ์พื้นฐานนั้นสะดวกเหมือนใช้ไวยากรณ์สไตล์ C ที่คุ้นเคย แทนที่จะเป็นภาษาอังกฤษที่บิดเบี้ยว แรงจูงใจที่จะใช้ DB แทนการทำเองก็คงสูงกว่านี้ ฐานข้อมูล NoSQL เป็นก้าวที่ดีไปในทิศทางนั้น แต่โดยรวมแล้วมักโฟกัสกับ Big Data มากเกินไป มากกว่าประโยชน์ใช้สอยในชีวิตประจำวัน ส่วนอย่าง Redis นั้นยืนระยะได้และถือว่าดี
    การทำให้รัน SQL ได้ง่ายขึ้นเป็นแนวทางที่สมเหตุสมผล แต่ฐานข้อมูลที่ดี ๆ เช่น Postgres ซึ่งผมชอบ ใช้ SQL เป็นภาษาหลัก จึงยากที่จะได้ประสิทธิภาพโดยไม่ใช้ภาษานั้น เราต้องการฐานข้อมูลแบบ PostgresPostSQL จริง ๆ ที่โคลน Postgres ได้สมบูรณ์ แต่ parser หลักรองรับภาษาที่มีไวยากรณ์ดีกว่า

    • ผมไม่แน่ใจว่า SQL ยากตรงไหนกันแน่ ผมคิดว่านักพัฒนาทุกคนควรรู้ SQL ไวยากรณ์ของ SQL ก็ดี และผ่านการพิสูจน์มาแล้วว่ายืนระยะได้นาน แทนที่จะตำหนิ ถ้าใช้เวลาลงไปเรียนจริง ๆ น่าจะมีประโยชน์กว่า
    • SQL ถูกวิจารณ์บ่อย และผมเองก็คิดว่ามีเหตุผลให้วิจารณ์ได้ แต่ทำไมเราถึงสร้างสิ่งที่ดีกว่านี้ไม่ได้?
      ในการเขียนโปรแกรมทั่วไปมีภาษาหลายสิบภาษาใช้งานอยู่และวิวัฒนาการต่อเนื่อง แม้แต่ JavaScript ซึ่งเปลี่ยนได้ยากเพราะเบราว์เซอร์เป็นตัวรันและเราไม่สามารถควบคุมเบราว์เซอร์ของผู้ใช้ได้ ก็ยังวิวัฒนาการผ่าน transpiler และ WebAssembly
      แต่ในโลกฐานข้อมูล โดยแท้จริงแล้วแทบมีแค่ SQL ภาษาเดียว มีทางเลือกอื่นอยู่ แต่ในแง่การใช้งานไม่มีอะไรเข้าใกล้ SQL ได้เลย บางที SQL อาจไม่ได้แย่ขนาดนั้นก็ได้
      เหตุผลอาจเป็นเพราะ relational model นั้นดีจริง ๆ ความพยายามที่จะออกนอกแนวทางนี้มีแนวโน้มจะใช้ได้แค่ใน niche เท่านั้น สไตล์ declarative ก็ดีมากเช่นกัน และถ้าออกนอกแนวทางนี้ก็ยากที่จะประสบความสำเร็จใหญ่ ๆ สุดท้ายถ้าแค่สร้าง SQL ที่ไวยากรณ์ต่างออกไป สำหรับคนส่วนใหญ่ก็คงไม่ใช่การปรับปรุงที่ใหญ่พอให้เปลี่ยนวิธีทำงาน
    • ผมอยากให้ Postgres มี API ที่เสถียรกว่าและอยู่ในระดับต่ำกว่า SQL อาจเป็นรูปแบบคล้ายกับ query plan ที่ได้จาก EXPLAIN
      แอปพลิเคชันที่เขียนโดยเล็งไปที่ API นี้จะสามารถ implement SQL DB ได้ แค่ parse SQL แล้ว implement query planner ที่ปล่อย query plan ที่เข้ากับ API นี้ออกมา
    • ผมสงสัยว่าคุณเคยดูแนวทางการเพิ่ม semantic layer บนฐานข้อมูลสำหรับคนที่อยากหลีกเลี่ยงการเขียน SQL โดยตรงหรือไม่
  • ผมเป็นผู้เขียนเอง ผมเพิ่งไล่อ่านคำถามส่วนใหญ่แบบคร่าว ๆ และจะคอยกลับมาตรวจเป็นระยะว่ามีคำถามไหนตกหล่นไหม ผมก็สงสัยเหมือนกันว่าใครเคยทำวิธีติดตามการถกเถียงบน HN ให้ดีกว่านี้หรือเปล่า
    ดีใจมากกับการพูดคุยจนถึงตอนนี้ บทความแรกไม่ได้โฟกัสว่า SQLSync ทำงานอย่างเป็นรูปธรรมอย่างไร แต่โฟกัสที่แรงจูงใจด้าน วิศวกรรมฟรอนต์เอนด์ ที่ทำให้ผมสร้าง SQLSync ขึ้นมา บทความถัดไปจะพูดถึงวิธีทำงาน

    • สงสัยว่ามีแผนรองรับมือถือไหม ที่ที่ผมอยากลองใช้สิ่งนี้มากที่สุดก็คือมือถือ
  • ไม่ควรทำให้ผู้ใช้มี mental model ที่โลกจริงสามารถทำให้พังได้อย่างรุนแรง หรือพังแบบมองไม่เห็น
    ผมกังวลว่าวิธีซิงก์ฐานข้อมูลแทนโมเดล client-server อาจเป็นหนึ่งในกรณีนั้น กลไกการซิงก์อาจล่มไม่เป็นท่า หรืออาจมีสมมติฐานลึก ๆ บางอย่างที่ไม่เป็นจริง
    ถ้าต้องการ UI ที่เร็ว ผมรู้สึกว่าการสร้างชุด primitive ของ CRDT มาใช้จะปลอดภัยกว่า และส่วนที่เหลือน่าจะคงไว้แค่การส่งฟอร์ม

    • เห็นด้วยครับ หนึ่งในเป้าหมายคือทำให้นักพัฒนาที่ใช้ SQLSync เข้าใจ mental model ได้ง่าย แม้จะมีอคติส่วนตัว แต่ผมรู้สึกว่า โมเดล rebase เข้าใจง่ายกว่า CRDT มาก
  • การซิงก์สถานะระหว่าง client กับ server เป็นปัญหาต้องคำสาป
    ถ้ายอมเสีย user experience ไปเล็กน้อย แล้วกลับไปใช้แนวทางที่ใกล้กับโมเดล PHP/server-side rendering ก็สามารถหลีกเลี่ยงปัญหานี้ทั้งก้อนได้ SPA นั้นดี แต่การส่ง multipart form ก็ยังใช้งานได้อยู่ ด้วย JavaScript เพียงเล็กน้อยก็ขัดเกลาส่วนหยาบ ๆ ที่เหลือได้เกือบหมด
    ในผลิตภัณฑ์เว็บช่วงหลัง ๆ สถานะฝั่ง client มีเพียง authentication claim จาก IdP บุคคลที่สาม, first-party session ID ใน query parameter และเอกสารปัจจุบันเท่านั้น พูดตรง ๆ ผมเองก็ไม่รู้ว่าอย่างแรกถูกเก็บไว้ที่ไหน นั่นเป็นปัญหาของ Microsoft ไม่ใช่ปัญหาของเรา ส่วนสถานะที่เหลือทั้งหมดอยู่บน server
    เราปฏิบัติกับ client เหมือน dumb terminal ที่ทั้งวันมีหน้าที่แค่พิมพ์ input ออกมา ไม่ใช้ first-party cookie หรือ local storage ด้วยซ้ำ วิธีนี้ทำให้ประสบการณ์การพัฒนาสำหรับ iOS/Safari ดีขึ้นมาก
    ดังนั้นผมอยากถามว่า ประสบการณ์ที่ต้องการมอบให้จริง ๆ คืออะไร และทำไมมันถึงสมเหตุสมผลพอที่จะแยกสถานะ client กับ server ออกจากกัน

    • ในอินเทอร์เฟซกราฟิกสำหรับผู้บริโภค optimistic rendering เป็น flow ที่พบได้บ่อยมาก และถ้าทำแบบนั้นก็เท่ากับต้องจัดการสถานะ client/server แม้ทุกวันนี้ยังเห็น loading spinner อยู่บ้างตามที่ต่าง ๆ แต่โดยปกติจะใช้สำหรับโหลดคอนเทนต์แรกเริ่ม ตัวอย่างเช่น Gmail ไม่ได้ให้ผู้ใช้รอเวลาสั่ง archive อีเมล
    • ElectricSQL ก้าวหน้าไปมากในการแก้ปัญหานี้ โดยให้เขียนลง SQLite บน client และรับประกันการซิงก์ไปยัง Postgres
      อ้างอิง: https://news.ycombinator.com/item?id=37584049
  • ดูเหมือนว่าแนวทางที่ใช้ SQLite เป็นฐานแบบ ออฟไลน์/ให้ความสำคัญกับโลคัลก่อน กำลังมาแรงมากช่วงนี้ นี่เป็นบทความที่สามที่อ่านในสัปดาห์นี้ และดูดีทีเดียว
    แต่เมื่อเทียบกับ ElectricSQLhttps://electric-sql.com/ และ PowerSynchttps://powersync.com/ แล้วเป็นอย่างไร?

    • เป็นสาขาที่กำลังร้อนแรงจริง ๆ การได้เห็นแนวทางที่แตกต่างกันออกมาเป็นเรื่องน่าสนใจมาก
      ทั้ง ElectricSQL และ PowerSync ต่างจัดการกับปัญหาที่ยากมากอย่าง การจำลองข้อมูลบางส่วน แนวคิดคือพยายามสร้างวิธีแก้ปัญหาทั่วไปที่ฐานข้อมูลส่วนกลางแบบดั้งเดิมจะซิงก์สองทางเฉพาะสิ่งที่ไคลเอนต์ต้องการ พร้อมทั้งรองรับการเปลี่ยนแปลงแบบมองโลกในแง่ดีและการจัดการความสอดคล้อง/ข้อขัดแย้งที่ตามมา
      ข้อเสียคือความซับซ้อนในการติดตั้งใช้งาน ต้องติดตามอย่างแม่นยำว่าไคลเอนต์แต่ละตัวมีชุดย่อยใดของฐานข้อมูลทั้งหมดอยู่บ้าง จึงจะพุชการเปลี่ยนแปลงไปยังเฉพาะชุดย่อยนั้นได้ อีกทั้งหากต้องการระบุว่าจะดาวน์โหลดชุดย่อยใดของสถานะฐานข้อมูล ก็ต้องมี DSL ใหม่ และต้องเรียนรู้รวมถึงปรับแต่งมันใหม่ด้วย ถึงอย่างนั้นก็ดีใจที่พวกเขากำลังแก้ปัญหาที่ยากมากเหล่านี้ และเมื่อ SQLSync พร้อมรองรับการจำลองข้อมูลบางส่วน ก็น่าจะมีแนวปฏิบัติที่ดีถูกรวบรวมไว้แล้ว
      ในทางกลับกัน ปัจจุบัน SQLSync รองรับเฉพาะ การซิงก์ฐานข้อมูลทั้งก้อน เท่านั้น ไคลเอนต์ทุกตัวจะเห็นมุมมองที่สอดคล้องกันของฐานข้อมูลทั้งหมด อาจสงสัยได้ทันทีว่านี่เป็นความคิดที่ดีหรือไม่ และมันก็ไม่เหมาะกับบางแอป แต่ถ้าคิดถึงแอปการเงินส่วนบุคคล เป้าหมายหลักคือการซิงก์ระหว่างอุปกรณ์ การสำรองข้อมูลบนคลาวด์ และการใช้งานแบบออฟไลน์ ดังนั้นการเก็บฐานข้อมูลทั้งก้อนไว้ในทุกอุปกรณ์อาจเป็นรูปแบบที่ต้องการมากกว่า โมเดลข้อมูลเชิงเอกสารอย่าง Airtable ก็เป็นอีกตัวอย่างได้ หากแยก Airtable แต่ละชุดเป็นฐานข้อมูลต่างหาก ไคลเอนต์ก็สามารถจัดการเองได้ว่าจะสนใจตารางใด
      เมื่อโฟกัสที่การซิงก์ฐานข้อมูลทั้งก้อน เอนจินซิงก์จะเรียบง่ายกว่าวิธีแก้ปัญหาที่รองรับการจำลองข้อมูลบางส่วนมาก หนึ่งในข้อดีคือแบ็กเอนด์เบามาก เดโมปัจจุบัน(https://sqlsync-todo.pages.dev) ทำงานทั้งหมดภายใน Cloudflare Durable Objects โดยใช้พื้นที่จัดเก็บและเวลา CPU เพียงเล็กน้อยมาก
      เพื่อให้ SQLSync รองรับกรณีใช้งานเหล่านี้ได้ ยังมีงานต้องทำอีกมาก และยังคงใกล้เคียงกับต้นแบบอยู่ แต่การทดสอบช่วงแรกให้ผลเชิงบวกมาก
  • ใน แอปมัลติเธเนนต์ขนาดใหญ่ ที่ชุดข้อมูลแต่ละชุดค่อนข้างเล็ก เคยคิดหลายครั้งว่า “แค่ส่งฐานข้อมูลไปยังไคลเอนต์เลยจะเป็นอย่างไร” มันดูเหมือนรูปแบบสถาปัตยกรรมต้องสาปที่หลุดจากมาตรฐานพอสมควร เลยไม่ได้ลงลึกนัก ถ้าได้รู้ว่าตัวเองคิดผิดก็คงดี

    • เคยลองทำแบบนั้นครั้งหนึ่งในแอปนับแคลอรี แม้ฐานข้อมูลจะมีอาหารหลายแสนรายการ แอปก็ยังใช้พื้นที่น้อยกว่าแอปสื่อหรือเกมส่วนใหญ่มาก
    • ผมเองก็สงสัยว่านี่เป็นวิธีต้องสาปหรือเปล่า จนถึงตอนนี้มันดีกว่าที่คาดไว้มาก แอปอย่าง https://sqlsync-todo.pages.dev กลายเป็นเรื่องเล็กน้อยด้วยแพตเทิร์นนี้
      หากจะพิสูจน์อย่างจริงจังยังมีงานต้องทำอีกมาก แต่ค่อนข้างตื่นเต้นที่จะผลักดันต่อและดูว่ามันจะพาไปถึงไหน
    • คำตอบที่ถูกต้องคือย้าย UI กลับไปยังเซิร์ฟเวอร์ที่มีฐานข้อมูลอยู่แล้ว แล้วส่งเพียง HTML ไปยังตัวเรนเดอร์ HTML ฝั่งไคลเอนต์ หรือก็คือเว็บเบราว์เซอร์ บทความทั้งหมดนี้ใกล้เคียงกับประโยคว่า “ฟรอนต์เอนด์ล้ำเส้น ข้ามหน้าผา แล้วตกลงทะเลไปแล้ว”
  • นี่ดูเหมือนเป็นหนึ่งในปัญหาที่จะหายไปหมดหากเลิกใช้ SPA
    หากใช้แนวทางตระกูล Hotwire หรือ htmx คิวรีก็เป็นเพียงคิวรีบนเซิร์ฟเวอร์ และปัญหาในการทำให้คิวรีเหล่านั้นเร็วขึ้นก็เป็นเรื่องที่เข้าใจกันดีกว่ามาก

    • นี่ไม่ใช่ปัญหาเฉพาะเว็บไซต์เท่านั้น ระบบนิเวศของมือถือและเดสก์ท็อปควรขยับไปเป็น thin client แบบเบราว์เซอร์อย่างจริงจังด้วยหรือไม่ แอปเรียบง่ายอย่าง Apple Reminders หรือ Google Tasks ควรทำให้ GUI ค้างเพราะความหน่วงหรือปัญหาการเชื่อมต่อหรือไม่?
    • เพิ่งลองใช้ htmx เมื่อไม่นานมานี้ ความซับซ้อนที่ถูกตัดออกไปและประสิทธิภาพการพัฒนาที่เกิดขึ้นนั้นเหลือเชื่อจริง ๆ การที่สามารถใช้สแต็กที่ต้องการได้ตามเดิมก็เป็นพรอย่างยิ่ง
      ลองใช้ร่วมกับ ocaml + web components แล้ว เป็นประสบการณ์ด้าน ประสิทธิภาพการพัฒนา 10/10 ต้องการแค่เครื่องมือ build ตัวเดียวที่คอมไพล์ได้เร็วกว่าเวลา blink และไม่ต้องเดินสาย mapping JSON ระหว่างฟรอนต์เอนด์กับแบ็กเอนด์ จึง productive มากจริง ๆ
    • พูดตามตรง แนวทางอย่าง Hotwire หรือ Livewire ไม่ได้ตอบสนองทันทีเท่า SPA
      โดยส่วนตัวชอบ InertiaJs https://inertiajs.com มากกว่า มันเป็นระบบ เราเตอร์ฟรอนต์เอนด์ แบบหนึ่งที่ซิงก์สถานะกับเซิร์ฟเวอร์ใน “วิธีแบบเก่า”
    • คล้ายกับการพูดว่า “อย่าสร้างเว็บแอปที่มีปฏิสัมพันธ์มาก” หรือ “อย่ารันบริการที่ต้องใช้มากกว่า VM หนึ่งตัว” เป็นมุมมองที่ไร้สาระ
    • ผมคิดว่าปัญหาจะหายไปไม่ใช่เมื่อเลิกใช้ SPA แต่เมื่อถอยออกจาก ฟีเจอร์ที่มีปฏิสัมพันธ์สูง ซึ่งผลิตภัณฑ์ที่ใช้งานดีมักมี
      โดยเฉพาะอย่างยิ่งหากเป็นผลิตภัณฑ์ที่ต้องทำงานได้แม้ในพื้นที่ที่อินเทอร์เน็ตไม่เสถียร
  • ตอนนี้กำลังเขียนบทความที่คล้ายกันมากเกี่ยวกับ “ฐานข้อมูลแบบฟูลสแต็ก” อยู่ ว่าด้วยแพตเทิร์นที่แอปจำนวนมากสร้างตรรกะของแบ็กเอนด์และฐานข้อมูลซ้ำอีกครั้งในโค้ดไคลเอนต์ฝั่งฟรอนต์เอนด์ แนวทางที่เราแนะนำคือเลือกฐานข้อมูลที่สามารถรันได้ทั้งฝั่งเซิร์ฟเวอร์และไคลเอนต์ แล้วซิงก์ข้อมูลระหว่างกัน
    เหตุผลที่เราไม่ใช้ SQLite ในผลิตภัณฑ์ของเรา พูดตรง ๆ คือเพราะ SQL ไม่ใช่เครื่องมือที่เหมาะกับการ query ข้อมูลของแอปพลิเคชัน มันไม่เข้ากับโครงสร้างข้อมูลที่ต้องการในโค้ดไคลเอนต์ได้ง่าย และแทบทุกฐานข้อมูล SQL ไม่มีวิธี subscribe การเปลี่ยนแปลงของ query โดยไม่ต้อง polling query ซ้ำ ๆ
    ถ้าคุณชอบไอเดียการมีฐานข้อมูลเต็มรูปแบบบนไคลเอนต์ และต้องการการผสานกับ TypeScript/JavaScript อย่างลึกซึ้ง ลองดู https://github.com/aspen-cloud/triplit ที่เรากำลังสร้างอยู่

    • จริง ๆ แล้ว Postgres มีวิธีที่ยอดเยี่ยมในการ subscribe การเปลี่ยนแปลงแบบเรียลไทม์ ผ่าน WAL ไลบรารีโอเพนซอร์สนี้ผมก็ดูแลอยู่เช่นกัน
      https://github.com/cpursley/walex
    • SQLite จริง ๆ แล้วมีกลไกที่จำเป็นสำหรับตรวจจับการเปลี่ยนแปลงผ่าน update hook น่าเสียดายที่ binding ของ SQLite จำนวนมากไม่ได้ expose สิ่งนี้
      ผมใช้งานแบบง่ายมาก ๆ คือเมื่อข้อมูลในตารางพื้นฐานเปลี่ยน ก็รัน query ใหม่โดยอัตโนมัติ อาจไม่ได้มีประสิทธิภาพเท่าการอัปเดตผลลัพธ์แบบ incremental แต่ query ของ SQLite โดยปกติเร็วมากอยู่แล้ว ผมเลยมองว่าไม่ใช่ปัญหาใหญ่
    • ไม่เห็นด้วยกับประเด็นที่ว่ามันไม่เข้ากับโครงสร้างข้อมูลที่ต้องการในโค้ดไคลเอนต์ได้ง่าย การ normalize ข้อมูล สำคัญต่อแอปพลิเคชันฟรอนต์เอนด์แบบ reactive และแทบจะจำเป็นถ้าต้องการให้ข้อมูลทันสมัยอยู่เสมอ งาน CRUD ทั้งหมดก็จัดการได้ง่ายขึ้นมากด้วย
    • Realm Sync กับ Mongo + Kotlin Multiplatform จะครอบคลุมได้แทบทุกแพลตฟอร์ม เช่น เซิร์ฟเวอร์ เว็บ มือถือ และเดสก์ท็อป แน่นอนว่ามีค่าใช้จ่าย ผมสนใจทางเลือกอื่นจริง ๆ และสงสัยว่าเนื้อหานี้จะอยู่ในบทความด้วยหรือไม่