• Graft คือ transactional storage engine แบบโอเพนซอร์สที่พยายามผสานความเรียบง่ายของ physical replication เข้ากับประสิทธิภาพของ logical replication แทนที่จะส่ง change log ทั้งหมดให้ไคลเอนต์ทุกตัว
  • จัดการ Volume ที่ประกอบด้วย Page ขนาดคงที่ในหน่วย Snapshot โดยเซิร์ฟเวอร์ไม่ได้ส่งข้อมูลจริง แต่ส่ง graft ซึ่งเป็น bitset ที่บีบอัดของดัชนี page ที่เปลี่ยนแปลง
  • ไคลเอนต์ดู graft แล้วดึงมาเฉพาะ page ที่จำเป็น และสามารถเลือกใช้ prefetching แบบ Leap, prefetching เฉพาะโดเมน หรือ proactive fetching ที่ดึงการเปลี่ยนแปลงทั้งหมดมาได้
  • มีเป้าหมายเพื่อทำ partial replication ได้แม้ใน สภาพแวดล้อมที่มีข้อจำกัด เช่น เบราว์เซอร์ แอปมือถือ serverless function และ embedded environment โดยใช้งาน object storage และ edge server
  • consistency model คือ Serializable Snapshot Isolation โดยจะปฏิเสธ commit ที่อิง Snapshot เก่า และให้ไคลเอนต์จัดการด้วยวิธีใดวิธีหนึ่งระหว่าง reset/replay, merge หรือ Volume fork

ปัญหา replication ที่ Graft ต้องการแก้

  • partial replication ดูเหมือนง่ายหากซิงค์เฉพาะข้อมูลที่ต้องใช้ แต่ในการออกแบบจริง replication แต่ละแบบมีต้นทุนที่ชัดเจน
    • logical replication ติดตามการเปลี่ยนแปลงทุกอย่างได้อย่างละเอียด แต่ทำให้ strong consistency ซับซ้อนขึ้น
    • physical replication หลีกเลี่ยงความซับซ้อนนั้นได้ แต่ต้องซิงค์การเปลี่ยนแปลงทั้งหมดแม้บางส่วนจะถูกทิ้งในภายหลัง
  • Graft คือ transactional storage engine แบบโอเพนซอร์สที่สร้างขึ้นโดยมีเป้าหมายคือ lazy synchronization, partial replication, strong consistency, horizontal scalability และ durability ของ object storage
  • จุดเริ่มต้นมาจากประสบการณ์กับ SQLSync
    • SQLSync เป็น database stack ที่ปรับให้เหมาะกับ frontend สร้างบน SQLite และใช้แนวคิดจาก Git กับ distributed systems ใน sync engine
    • SQLSync มีโครงสร้างที่ replicate change log ทั้งหมดไปยังไคลเอนต์ทุกตัว ซึ่งใช้ได้ดีบนเซิร์ฟเวอร์ แต่ไม่เหมาะกับสภาพแวดล้อม edge และเบราว์เซอร์
  • เป้าหมายของ Graft คือให้ไคลเอนต์ซิงค์ตามจังหวะที่ต้องการ ดึงเฉพาะสิ่งที่จำเป็น และ replicate ข้อมูลใด ๆ ที่ edge และอุปกรณ์ออฟไลน์ได้ด้วย strong consistency

การออกแบบที่อยู่ระหว่าง full replication กับ schema-aware diff

  • แนวทางเดิมแบ่งได้เป็นสองสายหลัก
    • full replication: ซิงค์ dataset ทั้งหมดไปยังไคลเอนต์แต่ละตัว จึงไม่เหมาะในทางปฏิบัติกับสภาพแวดล้อมที่มีข้อจำกัด เช่น serverless function หรือเว็บแอป
    • schema-aware diff: ติดตามการเปลี่ยนแปลงเชิงตรรกะระดับแถวหรือฟิลด์ เช่น CDC หรือ CRDT แต่ต้องผสานเข้ากับแอปพลิเคชันอย่างลึก และทำให้ครอบคลุมข้อมูลใด ๆ ได้ยาก
  • Graft ไม่ขึ้นกับ schema เหมือน full replication
    • ไม่รู้และไม่สนใจชนิดของข้อมูลที่จัดเก็บ แต่ replicate page ที่บรรจุ byte อยู่
  • ขณะเดียวกันก็ส่งคำอธิบายแบบบีบอัดให้ไคลเอนต์ว่าอะไรเปลี่ยนไปตั้งแต่ซิงค์ครั้งล่าสุด เหมือน logical replication
  • abstraction หลักคือ Volume
    • Volume คือคอลเลกชันแบบ sparse และเรียงลำดับของ Page ขนาดคงที่
    • ไคลเอนต์อ่านและเขียน Volume ที่ Snapshot ใด Snapshot หนึ่งผ่าน transaction API
    • ภายใน Graft จะจัดเก็บและ replicate เฉพาะสิ่งที่จำเป็น และใช้ object storage เป็น backend ที่ทนทานและขยายขนาดได้

lazy synchronization: ตามให้ทันในเวลาที่ไคลเอนต์ต้องการ

  • Graft ออกแบบโดยตั้งสมมติฐานว่า edge client ตื่นขึ้นมาเป็นครั้งคราว เครือข่ายไม่เสถียร และมีเวลารันสั้น
  • ไม่พึ่งพา replication อย่างต่อเนื่อง แต่ให้ไคลเอนต์เลือกเองว่า จะซิงค์เมื่อไร
  • การซิงค์เริ่มจากคำถามว่า “มีอะไรเปลี่ยนไปตั้งแต่ Snapshot ล่าสุด”
  • เซิร์ฟเวอร์ไม่ส่งข้อมูลจริง แต่ตอบกลับด้วย graft ซึ่งเป็น bitset ที่บีบอัดของดัชนี page ที่เปลี่ยนแปลง
    • graft ทำหน้าที่เป็นคำแนะนำสำหรับต่อการเปลี่ยนแปลงใหม่เข้ากับ Snapshot เดิม
    • ไคลเอนต์รู้ได้ว่า page ใดนำกลับมาใช้ซ้ำได้ และ page ใดต้องดึงมาเมื่อจำเป็น
  • เนื่องจาก graft เป็น metadata ของการเปลี่ยนแปลง ไม่ใช่ตัวข้อมูล อำนาจควบคุมว่าจะดึงอะไรและเมื่อไรจึงยังอยู่กับไคลเอนต์

partial replication และ prefetching

  • ในแท็บเบราว์เซอร์ แอปมือถือ และ serverless function การดาวน์โหลด dataset ทั้งหมดเพื่อประมวลผล query บางส่วนทำได้ยาก
  • หลังได้รับ graft ไคลเอนต์จะตัดสินว่า page ใดยังใช้ได้อยู่ และ page ใดต้องดึงมา
  • เพราะดึงมาเฉพาะ page ที่จำเป็นแบบเลือกได้ จึง replicate เฉพาะข้อมูลที่จะใช้งานจริงได้
  • Graft รองรับ prefetching หลายแบบเพื่อลด latency ในการเข้าถึง page
    • general-purpose prefetching: prefetcher ในตัวที่อิงอัลกอริทึม Leap ระบุ access pattern และคาดการณ์การเข้าถึง page ในอนาคต
    • domain-specific prefetching: แอปพลิเคชันใช้ความรู้เกี่ยวกับข้อมูลที่ถูกเรียกดูบ่อย เช่น โปรไฟล์ผู้ใช้ เพื่อดึง page ที่เกี่ยวข้องมาล่วงหน้าได้
    • proactive fetching: หากจำเป็น สามารถดึงการเปลี่ยนแปลงทั้งหมดมา ซึ่งเท่ากับย้อนกลับไปเป็น full replication โดยเฉพาะมีประโยชน์กับ workload ของ Graft ฝั่งเซิร์ฟเวอร์
  • page ถูก host โดยตรงบน object storage จึงใช้เป็นฐาน replication ที่มี durability และ scalability

การ deploy ที่ edge และ embedded client

  • edge replication ของ Graft ไม่ได้มีเป้าหมายแค่ว่าจะซิงค์ข้อมูลใด แต่รวมถึงการวางข้อมูลไว้ในตำแหน่งที่ต้องใช้ด้วย
  • page ถูกให้บริการจาก object storage ผ่าน fleet ของ global edge server
    • hot page ที่ถูกเข้าถึงบ่อยสามารถถูก cache ใกล้กับไคลเอนต์ได้
    • มีเป้าหมายให้ latency ต่ำและตอบสนองได้ดี ไม่ว่าผู้ใช้จะอยู่ที่ใดในโลก
  • Graft client ออกแบบให้เบาและฝังเข้าไปได้
    • มี dependency น้อยและ runtime ขนาดเล็ก
    • สามารถผสานเข้ากับสภาพแวดล้อมอย่างเบราว์เซอร์ อุปกรณ์ แอปมือถือ และ serverless function ได้
  • edge caching ทำให้เกิดปัญหา consistency และ conflict handling ดังนั้น Graft จึงมี strong consistency model มาพร้อมกัน

consistency model และ conflict handling

  • Graft ใช้ Serializable Snapshot Isolation เป็น consistency model
  • ไคลเอนต์ได้รับมุมมองข้อมูลที่ถูก isolate และ consistent ณ Snapshot หนึ่ง โดย read สามารถดำเนินพร้อมกันได้โดยไม่รบกวนกัน
  • write ถูก serialize อย่างเข้มงวด ทำให้ transaction ทั้งหมดมีลำดับที่ consistent ในระดับ global
  • ด้วยคุณสมบัติ offline-first และ delayed replication ไคลเอนต์อาจพยายาม commit โดยอิง Snapshot ที่เก่า
    • หากยอมรับ commit แบบนี้โดยไม่มีเงื่อนไข strict serializability จะเสียหาย
    • Graft จะปฏิเสธ commit นั้นอย่างปลอดภัย และให้ไคลเอนต์เลือกวิธีจัดการ
  • ตัวเลือกทั่วไปของไคลเอนต์มีสามแบบ
    • Reset and replay: ดึง Snapshot ล่าสุดมา แล้วนำ local transaction มา apply ใหม่ก่อนลองอีกครั้ง
      • ข้อมูล global ยังคงอยู่ในสถานะ strict serializable
      • ฝั่ง local จะได้รับประสบการณ์แบบ Optimistic Snapshot Isolation โดย read จะเห็น Snapshot ที่ consistent ภายใน แต่ Snapshot นั้นอาจถูกทิ้งหาก commit ถูกปฏิเสธ
    • Merge: merge local state เข้ากับ Snapshot ล่าสุดของเซิร์ฟเวอร์
      • ในกรณีนี้ consistency model ระดับ global อาจลดลงเป็น snapshot isolation
    • Volume fork: สร้าง Volume ใหม่แบบถาวรเพื่อแยกออกไป
      • ยังคงรักษา global serializability ได้

แอปพลิเคชันที่สร้างได้

  • แอป offline-first: Graft สามารถรับผิดชอบการซิงค์ในแอปที่ทำงานออฟไลน์บางส่วน เช่น โน้ต การจัดการงาน และแอป CRUD
    • เมื่อใช้ร่วมกับ conflict handler ก็สามารถทำฟีเจอร์ multiplayer บนข้อมูลใด ๆ ได้
  • ข้อมูลข้ามแพลตฟอร์ม: แชร์ข้อมูลบนแพลตฟอร์มมือถือ อุปกรณ์ และเว็บ พร้อมลดการผูกติดกับ vendor ได้
  • stateless read replica: เปิด database replica โดยไม่มี local state ดึง metadata ของ Snapshot ล่าสุด แล้ว query ได้ทันที
    • ไม่จำเป็นต้องดาวน์โหลดข้อมูลทั้งหมดหรือ replay log
  • replication สำหรับข้อมูลใด ๆ: Graft มุ่งเน้นที่ page replication จึงไม่ยุ่งกับรูปแบบข้อมูลภายใน page
    • สามารถใช้กับข้อมูลอย่างฐานข้อมูล SQLite, โมเดล AI, ไฟล์ Parquet, Lance และ Geospatial tilesets ได้

SQLite extension libgraft

  • วิธีที่ง่ายที่สุดในตอนนี้ในการใช้ Graft คือ libgraft ซึ่งเป็น native SQLite extension
  • libgraft ใช้ได้ทุกที่ที่ SQLite ทำงาน และ replicate เฉพาะบางส่วนของฐานข้อมูลที่ไคลเอนต์ใช้งานจริง
  • implement SQLite VFS เพื่อ intercept การอ่านและเขียนฐานข้อมูล
  • ให้ semantics ของ transaction และ concurrency แบบเดียวกับที่ SQLite มีใน WAL mode
  • ความสามารถที่มีให้ ได้แก่
    • asynchronous replication กับ object storage
    • delayed partial replication ที่ edge และบนอุปกรณ์
    • Serializable Snapshot Isolation
    • point-in-time restore
  • ดูเอกสารได้ที่ เอกสาร SQLite บน GitHub

การมีส่วนร่วมและแผน managed service

  • Graft พัฒนาแบบเปิดบน GitHub
  • รับ issue, discussion และ Pull Request พร้อมมี contribution guide
  • มี Discord และอีเมลเป็นช่องทางพูดคุย
  • มีแผนเปิดตัว Graft Managed Service และมีลิงก์สำหรับสมัคร waitlist

roadmap

  • Graft ผ่านงานวิจัยมา 1 ปี การ iterate หลายครั้ง และการเปลี่ยนทิศทางครั้งใหญ่หนึ่งครั้ง แต่ยังมีงานเหลืออีกมาก
  • รายการที่วางแผนไว้มีดังนี้
    • รองรับ WebAssembly: ทำให้ใช้ Graft ในเบราว์เซอร์ได้ โดยตั้งเป้ารองรับ official Wasm build ของ SQLite, wa-sqlite และ sql.js
    • ผสาน Graft กับ SQLSync: หลังรองรับ Wasm แล้ว มีแผนแยกชั้น mutation, rebase และ query subscription ของ SQLSync ออกมา แล้ววางบนฐานข้อมูล replication ของ Graft
    • ขยาย client library: ต้องการ native Graft client wrapper สำหรับ Python, JavaScript, Go และ Java
    • low-latency write: ปัจจุบันงาน push จะถูก block จนกว่าจะ commit เข้า object storage อย่างสมบูรณ์
      • ทดลอง S3 express zone
      • วางกลุ่ม consensus ที่ทนทานและ latency ต่ำไว้หน้า object storage
    • garbage collection, checkpointing, compaction: จำเป็นเพื่อเพิ่มประสิทธิภาพ query ให้สูงสุด ลดพื้นที่สูญเปล่า และลบถาวร
    • authentication และ authorization: เป็นงานขอบเขตกว้าง ตั้งแต่บัญชี managed service ไปจนถึงสิทธิ์อ่าน/เขียน Volume แบบละเอียด
    • Volume forking: service สามารถทำ zero-copy fork ได้โดย copy reference ของ Segment ไปยัง Volume ใหม่ แต่ local fork ในปัจจุบันต้อง copy page ทั้งหมด
    • conflict handling: มีแผนให้กลยุทธ์แก้ conflict ในตัวและ extension point โดยกลยุทธ์แรกคือ auto-merge transaction ที่ไม่ทับซ้อนกัน

เปรียบเทียบกับโซลูชัน SQLite replication

  • ข้อมูลเปรียบเทียบรวบรวมจากเอกสารและบทความบล็อก พร้อมระบุข้อสงวนว่าอาจไม่แม่นยำทั้งหมด
  • mvSQLite

    • mvSQLite implement ชั้น VFS แบบ custom ที่เก็บ SQLite page ลงใน FoundationDB โดยตรง
    • Graft กับ mvSQLite คล้ายกันตรงที่ versioning ระดับ page ทำให้ทำ lazy fetch และมุมมองฐานข้อมูลแบบ partial ได้
    • ความแตกต่างคือที่เก็บข้อมูลและวิธีติดตามการเปลี่ยนแปลงของ page
      • mvSQLite พึ่งพา FoundationDB และทุก node ต้องเข้าถึง cluster โดยตรง
      • changeset ที่อิง Splinter ของ Graft เป็นแบบ self-contained จึง deploy ง่ายกว่า และไม่ต้อง query FoundationDB โดยตรงเพื่อรู้ version ของ page ที่เปลี่ยน
  • Litestream

    • Litestream คือโซลูชัน streaming backup ที่ replicate SQLite WAL frame ไปยัง object storage อย่างต่อเนื่อง
    • Graft ผสานเข้ากับกระบวนการ commit ของ SQLite โดยตรงผ่าน custom VFS ทำให้ทำ delayed partial replication และ distributed write ได้
    • ทั้งสอง replicate page ไปยัง object storage และรองรับ point-in-time restore
  • cr-sqlite

    • cr-sqlite คือ SQLite extension ที่เปลี่ยน table เป็น CRDT เพื่อให้ logical replication ระดับแถวทำได้
    • มี automatic conflict resolution แต่ต้องรู้ schema และต้องผสานในระดับแอปพลิเคชัน
    • Graft ไม่ขึ้นกับ schema และเข้ากันได้กับ SQLite extension ใด ๆ รวมถึง custom data structure แต่เพื่อ global serializability แอปพลิเคชันต้องจัดการ conflict resolution อย่างชัดเจน
  • Cloudflare Durable Objects with SQLite Storage

    • การรวม Durable Objects กับ SQLite ทำให้วางฐานข้อมูลที่มี strong consistency และ durability สูงซึ่งครอบด้วย business logic ไว้บนเครือข่าย edge ของ Cloudflare ได้
    • ภายในคล้าย Litestream ตรงที่ replicate SQLite WAL ไปยัง object storage และ checkpoint เป็นระยะ
    • Graft เปิดเผย replication เป็น first-class feature และมุ่งทำ replication กับ edge อย่างมีประสิทธิภาพ
  • Cloudflare D1

    • Cloudflare D1 คือ managed SQLite database ที่เข้าถึงผ่าน HTTP API
    • Graft เป็นโมเดลแบบ distributed ที่ embed ข้อมูลใน client application และ replicate ไปยัง edge โดยตรง
  • Turso & libSQL

    • Turso ให้ managed SQLite database และ embedded replica ผ่าน libSQL
    • Graft แตกต่างด้วย partial replication และการรองรับ data structure ใด ๆ ที่ไม่ขึ้นกับ schema
    • backend service ของ Graft ทำงานระดับ page และปล่อย lifecycle ของ transaction ทั้งหมดให้ไคลเอนต์
  • rqlite & dqlite

    • rqlite และ dqlite กระจาย SQLite ไปยังหลายเซิร์ฟเวอร์ผ่าน consensus ที่อิง Raft และ network protocol
    • โซลูชันเหล่านี้มุ่งซิงค์ชุดของ node ที่มี state และเชื่อมต่อกันอยู่
    • Graft เป็นระบบ stateless ที่สร้างบน object storage และออกแบบมาเพื่อแลกเปลี่ยนข้อมูลกับ edge
  • Verneuil

    • Verneuil replicate SQLite Snapshot ไปยัง read replica แบบ asynchronous ผ่าน object storage โดยให้ความสำคัญกับ reliability
    • ตั้งใจหลีกเลี่ยงกลไกที่ลด replication lag หรือเพิ่ม freshness ให้มากที่สุด
    • Graft ทำงานใกล้เคียงกับ multi-writer distributed database มากกว่า และเน้น selective real-time partial replication

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

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