HN giới thiệu: Triplit - Cơ sở dữ liệu đồng bộ mã nguồn mở chạy trên máy chủ và máy khách
(github.com/aspen-cloud)- Triplit là một cơ sở dữ liệu mã nguồn mở đồng bộ dữ liệu theo thời gian thực giữa máy chủ và trình duyệt, tự định vị là cơ sở dữ liệu full-stack có thể được tích hợp vào ứng dụng dưới dạng gói Typescript
- Xử lý đồng thời việc lưu trữ phía máy chủ và đồng bộ truy vấn phía máy khách; hỗ trợ cập nhật gia tăng, giải quyết xung đột ở cấp thuộc tính, bộ nhớ đệm cục bộ, chế độ ngoại tuyến và tự động kết nối lại
- Hỗ trợ lưu trữ dạng plug-in như SQLite, IndexedDB, LevelDB, Memory, đồng thời cung cấp lưu trữ bền vững phía máy chủ và bảng điều khiển quản trị
- Có thể dùng API truy vấn và thay đổi trong React và vanilla Javascript; được cung cấp dưới dạng monorepo gồm React/Svelte bindings, CLI, Console, Server, v.v.
- Cung cấp đồng thời cập nhật lạc quan để tương tác nhanh, rollback/thử lại các cập nhật thất bại, quyền đọc/ghi được cưỡng chế ở máy chủ và tính năng cộng tác dựa trên CRDT
Triplit cung cấp gì
- Triplit là một cơ sở dữ liệu mã nguồn mở đồng bộ theo thời gian thực dữ liệu giữa máy chủ và trình duyệt
- Cung cấp kho dữ liệu đồng bộ có thể thêm vào ứng dụng dưới dạng gói Typescript
- Lưu dữ liệu trên máy chủ và đồng bộ thông minh các truy vấn của máy khách
- Triplit gọi hình thức này là cơ sở dữ liệu full-stack
- Cũng có video thuyết trình từ cộng đồng Local First: presentation
Tính năng chính
-
Đồng bộ theo thời gian thực
- Hỗ trợ cập nhật gia tăng
- Cung cấp giải quyết xung đột ở cấp thuộc tính
-
Trải nghiệm local-first
- Cung cấp bộ nhớ đệm cục bộ dựa trên cơ sở dữ liệu hoàn chỉnh phía máy khách
- Dùng cập nhật lạc quan để mọi tương tác có cảm giác nhanh
- Hỗ trợ chế độ ngoại tuyến, tự động kết nối lại và đảm bảo tính nhất quán
-
Lưu trữ và vận hành phía máy chủ
- Cung cấp lưu trữ bền vững phía máy chủ
- Bao gồm bảng điều khiển quản trị
- Hỗ trợ nhà cung cấp lưu trữ dạng plug-in như SQLite, IndexedDB, LevelDB, Memory
-
Mô hình dữ liệu và API
- Hỗ trợ truy vấn quan hệ cho các mô hình dữ liệu phức tạp
- Cung cấp an toàn dữ liệu và tự động hoàn thành Typescript thông qua schema
- Cung cấp API đơn giản cho truy vấn và thay đổi trong vanilla Javascript và React
-
Cộng tác và bảo mật
- Cưỡng chế quyền cho cả đọc và ghi trên máy chủ
- Cung cấp tính năng cộng tác/multiplayer dựa trên CRDTs
- Dùng delta patch để giảm lưu lượng mạng và hướng tới độ trễ thấp
- Quản lý rollback và thử lại đối với các cập nhật thất bại
Cấu trúc monorepo
- TriplitDB: DB được thiết kế để chạy trong các môi trường JS như trình duyệt, Node, Deno, React Native; cung cấp các truy vấn nhanh, cập nhật trực tiếp, đồng thời duy trì tính nhất quán với nhiều bên ghi trên mạng
- Client: thư viện trình duyệt để tương tác với TriplitDB cục bộ/từ xa
- CLI: công cụ dòng lệnh để scaffold dự án, chạy môi trường phát triển full-stack, migration máy chủ, v.v.
- React: React binding cho
@triplit/client - Svelte: Svelte binding cho
@triplit/client - Console: ứng dụng để xem và thay đổi dữ liệu dự án Triplit cũng như quản lý schema
- Server: máy chủ Node đồng bộ dữ liệu giữa các Triplit client
- Server-core: thư viện không phụ thuộc giao thức để xây dựng máy chủ Triplit
- Docs: tài liệu Triplit được xây dựng bằng Nextra
- Types: các type dùng chung giữa các dự án Triplit
- UI: thành phần UI dùng chung dựa trên shadcn
Luồng bắt đầu nhanh
- Dự án mới bắt đầu bằng
npm create triplit-app@latest my-app - Với dự án hiện có, cài
@triplit/clilàm dependency phát triển rồi chạynpm run triplit init - Định nghĩa schema trong
my-app/triplit/schema.ts- Ví dụ định nghĩa các trường
id,text,completedtrong collectiontodos completedđược thiết lập là trường Boolean có giá trị mặc định làfalse
- Ví dụ định nghĩa các trường
- Khởi động máy chủ đồng bộ cho phát triển bằng
npm run triplit dev - Máy chủ phát triển xuất ra các biến môi trường cần thiết để ứng dụng đồng bộ với máy chủ
- Trong ví dụ Vite:
VITE_TRIPLIT_SERVER_URL=http://localhost:6543 VITE_TRIPLIT_TOKEN=copied-in-from-triplit-dev
- Trong ví dụ Vite:
Ví dụ dùng React và kiểm tra đồng bộ
- Ví dụ React dùng
TriplitClientvàuseQuery - Client được tạo với schema, URL máy chủ và token
- Đăng ký nhận kết quả truy vấn
todosbằnguseQuery(client.query('todos')) - Khi checkbox thay đổi, dùng
client.updateđể đảo giá trịcompleted - Sau khi khởi động ứng dụng, mở một tab trình duyệt khác để xác nhận dữ liệu được đồng bộ theo thời gian thực
Tài liệu và kênh liên hệ
- Hướng dẫn bắt đầu đầy đủ: getting started guide
- Tutorial chi tiết hơn: building a real-time todo app with Triplit, Vite, and React
- Có thể nhận câu trả lời cho câu hỏi, hỗ trợ khởi đầu và xem trước tính năng mới trên Discord
- Theo dõi các thông báo mới nhất trên Twitter/X
1 bình luận
Ý kiến trên Hacker News
Tôi đã dùng Triplit trong dự án https://github.com/thanhnguyen2187/cryptaa và nó hoạt động như kỳ vọng
Mô hình dữ liệu khá phù hợp với ý tưởng thiên về phân tán/P2P hơn là đặt một DB trung tâm duy nhất làm nguồn chân lý, nhưng self-hosting và ngôn ngữ truy vấn thì còn đáng tiếc
Tài liệu không nói rõ cách tạo token xác thực cho server nên tôi đã tạo token bằng lệnh
devcủa CLI; việc token bị ghi log dạng văn bản thuần trong system service là không tốt về bảo mật, nhưng tôi cho rằng nó giả định đã có vấn đề quyền truy cập lớn hơnDSL truy vấn tùy chỉnh thiếu tính biểu đạt như
UNIQUE,COUNTtrong SQL, nên một số phần tổng hợp phải tự làmGần đây tôi có xem Evolu https://www.evolu.dev/docs, phạm vi và tính năng trông khá giống; Triplit có
.subscribe()còn Evolu thì không, Evolu là SQL có kiểu dựa trên Kysely nên truy vấn quen thuộc và nâng cao hơn, và trên trình duyệt Evolu dùng SQLite trên OPFS trong khi Triplit dường như dùng IndexedDBBài tôi đăng trên Reddit: https://www.reddit.com/r/sveltejs/comments/1dndpj8/cryptaa_a...
Về truy vấn, hiện vẫn chưa có aggregate nhưng đã nằm trong roadmap; tôi nghĩ tận dụng incremental query engine có thể mở ra một hướng rất hay
Ví dụ, với dashboard dữ liệu được cập nhật mỗi giờ, trong các hệ thống hiện có (Postgres, MongoDB, v.v.) mỗi lần đều phải chạy lại truy vấn từ đầu, nhưng nếu xử lý theo cách gần với Materialize, chỉ xử lý dữ liệu mới, thì có thể tiếp tục cập nhật hiệu quả hơn nhiều
Tôi vẫn chưa trực tiếp dùng Evolu, nhưng có thể có người trên Discord đã so sánh: https://triplit.dev/discord
useQueryhoặc theo một cách tách rờiKhi dùng một DB có giao thức đồng bộ offline tuyệt vời như vậy, tôi tò mò việc tiến hóa schema được xử lý ra sao trong tình huống không thể nâng cấp đồng thời các phiên bản client khác nhau
Trước đây tôi từng khổ sở vì vấn đề này trong bối cảnh một ứng dụng sức khỏe trên di động
Nếu cần thì sẽ phải dual-write vào cả hai phiên bản
Nó giống việc live migration không downtime cho các thay đổi phá vỡ tương thích trong SQL DB, nhưng thời điểm chuyển đổi phụ thuộc vào khách hàng nên phải duy trì logic đó lâu hơn
Một bảng điều phối phiên bản mới nhất cũng rất quan trọng; nếu có thay đổi phá vỡ tương thích thì các client tụt hậu cần yêu cầu người dùng nâng cấp
Cũng có thể quyết định cần duy trì dual read/write trong bao lâu dựa trên phiên bản tối thiểu được hỗ trợ trên toàn bộ client
Triplit sẽ hiển thị cảnh báo nếu bạn tạo thay đổi không tương thích ngược; có thể xem trong tài liệu: https://www.triplit.dev/docs/schemas/updating#pushing-the-sc...
Tuy nhiên theo thời gian, một định nghĩa schema lộn xộn với nhiều tên dễ gây nhầm lẫn có thể tự nhiên xuất hiện
Chúng tôi vẫn chưa phát hành giải pháp để sửa chuyện này, nhưng đang làm một số thứ để khiến nó bớt đau hơn; để có bối cảnh về nhiều cách tiếp cận, tài liệu Cambria rất hay: https://www.inkandswitch.com/cambria/
Người dùng có thể đã để điện thoại trong ngăn kéo suốt 2 năm, nên mỗi client chỉ cần tự migration cho mình càng sớm càng tốt
Mọi người sẽ không cần tự phát minh lại cách quản lý migration của riêng mình
Tôi chưa hiểu rõ việc client có thể ghi trực tiếp vào DB thì phù hợp với loại ứng dụng nào, và làm sao có thể trụ được không có logic backend
Tôi cũng có cùng thắc mắc với Supabase và Firestore, nên có vẻ tôi đang bỏ sót điều gì đó
Trong môi trường doanh nghiệp thì tất nhiên ngược lại, và tôi thấy bực khi các cuộc thảo luận bỏ qua điều này
Đặc biệt khi thấy trên tech Twitter có người bênh vực một stack hay cách làm nào đó, quá rõ là họ chỉ từng làm CRUD chứ chưa từng xây dựng business system, nên họ thường không hiểu vì sao các developer nhiều kinh nghiệm không đồng ý
Tôi không nghĩ nó hợp với những thứ có nhiều logic backend
Trong Supabase, ví dụ, có một tính năng gọi là row-level security
Client có thể gửi request tới Supabase, nhưng Supabase sẽ chạy thêm truy vấn ở backend để xác định request đến có được phép hay không
Ví dụ đơn giản, có thể chỉ cho phép đọc, ghi và cập nhật một hàng khi giá trị cột
UserIDbằng với người dùng đã xác thực đang gửi requestĐã lưu cấu hình người dùng vào Triplit, và các cấu hình này cần được quản trị viên quản lý
Người dùng cần cảm thấy như ứng dụng luôn chạy cục bộ, chất lượng Internet cũng thường không tốt, nhưng họ dùng qua nhiều thiết bị và quản trị viên cần xem, quản lý cấu hình của người dùng khác nên cần đồng bộ hóa
Nhìn chung Triplit có trải nghiệm dành cho lập trình viên frontend lẫn hỗ trợ đều rất tuyệt; khi tìm thấy issue hay yêu cầu tính năng, đội ngũ xử lý rất nhanh
Khi có câu trả lời cho triển khai tính sẵn sàng cao, dự định sẽ chuyển cả dữ liệu quan trọng hơn sang thay cho Postgres
Tò mò vì sao lại chọn giấy phép AGPL
Có vẻ đã xem bài trình bày trên YouTube https://www.youtube.com/playlist?list=PLTbD2QA-VMnXFsLbuPGz1... trong server Discord Local First https://localfirstweb.dev/, nên thấy trên Show HN thì rất vui
Vì không dùng TypeScript nên có thể không phải đối tượng chính, và khác với web, chủ yếu dùng local-first cho ứng dụng di động có kết nối không ổn định, với Flutter và backend Rust
Các giải pháp local-first khác như ElectricSQL và PowerSync đồng bộ trực tiếp DB phía client và server, nên độc lập hơn với client/server
Các giải pháp dựa trên CRDT cũng có thể dùng ở client và server qua FFI; ví dụ automerge viết bằng Rust nên phía Flutter có thể dùng FFI qua
flutter_rust_bridge, trên web dùng WASM, và ở backend dùng RustTriplit có vẻ là kiểu đồng bộ client-server cổ điển hơn, lấy server làm nguồn sự thật, thay vì giải quyết không xung đột giữa các client khác nhau
Tò mò vì sao lại chọn giải pháp ở cấp ngôn ngữ thay vì cách tiếp cận tầng DB độc lập hơn với client và server; có vẻ việc hỗ trợ các ngôn ngữ và framework ngoài nền JS trong tương lai sẽ khó
Ngoài ra có vẻ muốn cạnh tranh với Supabase, nhưng Supabase cũng đang thử nghiệm đồng bộ cấp DB của Postgres và CRDT https://news.ycombinator.com/item?id=33931971, nên có thể sẽ bắt kịp
Tuy nhiên khi bắt đầu, quyết định tập trung vào TypeScript thuần; thị trường đủ lớn, tin vào tương lai của PWA, và cho rằng phải tập trung vào đó mới tạo được trải nghiệm tốt nhất
Có lẽ một ngày nào đó sẽ làm thứ gì đó độc lập nền tảng hơn, nhưng thời điểm thì chưa chắc
Các đội ElectricSQL và Supabase đều xuất sắc, thấu đáo và có vẻ sẽ tiếp tục phát triển trong lĩnh vực SQL; đây là khác biệt căn bản nhất trong cách tiếp cận
Triplit cho rằng có thể mang lại trải nghiệm tốt nhất cho lập trình viên bằng cách tránh SQL, và có đủ không gian để hai triết lý cùng tồn tại
Nếu là LWW thì tò mò liệu lượng thông tin ở client có tăng tuyến tính theo số phép toán không
Tức là người dùng càng sửa DB nhiều thì nhật ký thao tác có cứ tiếp tục phình to không, hay có checkpoint; và về mặt dung lượng thì mở rộng thế nào khi người dùng thực hiện hàng triệu thao tác mỗi ngày
Tuy nhiên bản thân LWW register không yêu cầu lưu lịch sử; đó chỉ là cách triển khai hiện tại nhằm giúp cả các client đã offline lâu cũng đồng bộ hiệu quả
Chưa thể nói là đã hoàn toàn đạt tới mức một triệu thao tác mỗi ngày, nhưng có lợi thế là server có thẩm quyền
Trong tương lai, server Triplit có thể theo dõi timestamp đồng bộ cuối cùng của từng client và cắt tỉa lịch sử dần dần, tương tự cách Postgres
VACUUMcác tuple đã chếtSẽ rất tốt nếu có binding Rust để dùng trong Tauri
Kết hợp đà tăng trưởng của Tauri, hỗ trợ thiết bị di động sắp ra mắt, và độ phổ biến gần đây của SQLite, nó có thể lấp khoảng trống cho ứng dụng offline-first và trở thành lựa chọn mặc định của nhiều đội phát triển
ElectricSQL hoạt động ở tầng DB nên độc lập ngôn ngữ, và đang dùng Rust ở server nên binding Rust có thể hoạt động ở cả client lẫn server
Nếu muốn cùng phát triển thì cho biết nhé
Nếu vậy thì Triplit có lẽ sẽ hoạt động ngay
Đã dùng Triplit trong một ứng dụng React Native một thời gian và nó hoạt động rất tốt
Rất khuyến nghị; đây là DB local-first duy nhất đáp ứng tất cả điều kiện tôi cần
Có ngôn ngữ truy vấn hợp lý và sane (không phải SQL), hỗ trợ TypeScript tuyệt vời, hỗ trợ offline, hỗ trợ React Native; thêm việc mã nguồn mở và có thể tự host cũng rất tốt
Tò mò liệu có thể dùng cùng với PostgreSQL DB hiện có không
Chưa sẵn sàng công khai, nhưng dự định sẽ sớm cho mọi người dùng thử
Tôi cũng đang nghiêng về phía dùng nó