Đừng xây lại cơ sở dữ liệu ở frontend
(sqlsync.dev)- Các ứng dụng frontend phức tạp thường khởi đầu từ bộ nhớ đệm phản hồi API, rồi dần phải tự gánh cả chỉ mục thủ công lẫn việc vô hiệu hóa cache, gần như đi đến trạng thái tái triển khai một cơ sở dữ liệu nhỏ trong mỗi dự án
- Trong các framework khai báo như React, để tránh gọi API ở mỗi lần render, người ta đặt cache trong state cục bộ hoặc tầng Redux, và tầng này dần đảm nhận vai trò kho lưu trữ trung tâm
- Cấu trúc lưu trữ theo ID và cấu trúc truy vấn theo ngày giúp truy xuất nhanh hơn, nhưng việc duy trì tính nhất quán giữa nhiều cấu trúc như
CACHEvàENTRIES_BY_DATElàm tăng gánh nặng cho kiểm thử và review code - Thay đổi lạc quan giúp cập nhật UI ngay trước khi có phản hồi từ server để tăng cảm giác nhanh, nhưng đi kèm chi phí nhất quán dữ liệu như trùng lặp logic client/server, theo dõi các thay đổi đang diễn ra, rollback khi lỗi, và điều phối lại sau khi ứng dụng khởi động lại
- SQLSync muốn cung cấp ngay trong stack frontend một cache bền vững, chỉ mục, ràng buộc, thay đổi lạc quan và truy vấn phản ứng thông qua cơ sở dữ liệu cục bộ dựa trên SQLite và cơ chế đồng bộ tương tự Git Rebase
Quá trình cache frontend phình to thành cơ sở dữ liệu
- Quản lý dữ liệu ở frontend có thể bắt đầu từ một cache đơn giản lưu phản hồi API vào biến cục bộ
- Các framework khai báo như React sẽ render lại cây nhiều lần trong lúc người dùng tương tác
- Để tránh gửi yêu cầu API ở mỗi lần render, có thể dùng
useStatevàuseEffectđể lưu kết quả yêu cầu hoặc lỗi vào state của component - Ví dụ được đơn giản hóa để dễ hiểu; trên thực tế cũng có thể chọn dùng các thư viện API đã được kiểm chứng
- Cache có thể được chuyển lên tầng cao hơn trong cây UI hoặc ra ngoài UI
- Redux là thư viện quản lý state cho React, dùng để hợp nhất state và điều phối các thay đổi nguyên tử theo thời gian
- Hệ sinh thái Redux đã mở rộng với các công cụ và mẫu thiết kế để quản lý việc cache dữ liệu API
- Cách dùng này nhằm tập trung hóa logic cache, điều phối cập nhật và chia sẻ kết quả cache giữa các component
- Khi tầng cache lớn dần, nó tiến gần đến một hệ thống lưu trữ trung tâm xử lý dữ liệu hiệu quả theo cách mà engine render và hành động của người dùng yêu cầu
Chỉ mục thủ công và gánh nặng nhất quán
- Ngay cả ở frontend, dữ liệu nhận từ server cũng có thể được lưu trong object dùng ID làm khóa để truy xuất và sửa đổi nhanh
- Trong các ứng dụng dùng REST API, việc đọc dữ liệu theo lô rồi bổ sung thêm cho các object cần thiết diễn ra khá thường xuyên
- Lưu object theo ID giúp dễ hợp nhất kết quả API vào cache
- Cấu trúc này được tối ưu cho tạo, đọc, sửa, xóa theo ID
- Việc lọc cần quét nhiều mục thường dẫn đến tạo chỉ mục riêng để tránh phải kiểm tra toàn bộ entry
- Có thể tạo cấu trúc
ENTRIES_BY_DATEdựa trên phần năm/tháng/ngày củacreatedAtđể nhanh chóng tìm các entry trong một ngày cụ thể - Nhưng đổi lại, phải liên tục duy trì tính nhất quán giữa
CACHEvàENTRIES_BY_DATE - Truy vấn theo khoảng ngày cần truy cập nhiều lần, và một mảng sắp theo ngày cùng logic truy vấn/cập nhật phức tạp hơn có thể là cấu trúc tốt hơn
- Có thể tạo cấu trúc
- Khi số lượng chỉ mục tăng lên, mỗi chỉ mục đều cần logic tạo, cập nhật và truy vấn riêng
- Việc xác minh tính đúng đắn làm tăng gánh nặng cho kiểm thử và review code
- Chỉ cần quên xóa hoặc cập nhật ở một chỉ mục là có thể tạo ra lỗi khó phát hiện
- Thời gian dành cho hạ tầng quản lý sự phức tạp này có thể còn nhiều hơn thời gian xây dựng tính năng mới cho ứng dụng
- Chỉ mục trong cơ sở dữ liệu thực thụ phức tạp hơn rất nhiều so với việc chỉ lưu dữ liệu dưới một dạng khác
- Chúng bao gồm các yếu tố như thu thập thống kê, quản lý phiên bản dữ liệu, điều khiển giao dịch, khóa, và tương tác với tối ưu hóa truy vấn
Những vấn đề nhất quán do thay đổi lạc quan tạo ra
- Thay đổi lạc quan mô phỏng trước tác động của một thao tác ngay trên máy cục bộ trước khi có phản hồi từ server
- UI có thể phản hồi ngay như thể không có độ trễ mạng
- Nếu server đưa ra quyết định khác dự kiến hoặc xảy ra lỗi, UI có thể phải rollback thay đổi và yêu cầu người dùng sửa vấn đề
- Đây là công cụ mạnh khi client dự đoán tốt kết quả từ server, có thể xử lý lỗi ở phía client và logic giữa hai bên được đồng bộ chặt chẽ
- Cập nhật lạc quan thường diễn ra qua bốn bước
- UI phát sinh thao tác ghi
- Áp dụng thay đổi vào cache cục bộ với giả định server sẽ đồng ý, rồi render lại UI ngay
- Gửi thao tác thay đổi lên server một cách bất đồng bộ
- Hợp nhất phản hồi từ server vào cache cục bộ để ghi đè thay đổi lạc quan trước đó và render lại UI nếu cần
- Để giữ nhất quán với server, phát sinh nhiều gánh nặng
- Phải nhân đôi logic ở cả client và server để dự đoán kết quả
- Muốn xử lý lỗi bất đồng bộ hay sai khác từ server thì phải theo dõi từng thay đổi đang diễn ra
- Để có trải nghiệm người dùng tốt hơn, có thể còn phải làm cho phần cache lạc quan có tính bền vững để điều phối lại thay đổi sau khi ứng dụng khởi động lại
- Quá trình này làm tăng thời gian phát triển và chi phí xác minh tính đúng đắn, khiến việc quản lý dữ liệu có thể lấn át việc tạo ra giá trị cho người dùng hay phát triển các tính năng khác biệt
Sự phức tạp của vô hiệu hóa cache đệ quy
- Trong các ứng dụng có nhiều dữ liệu, cùng một thông tin thường xuất hiện ở nhiều nơi trong cache
- Ví dụ cache lưu cùng lúc
projects,tasks,users - Sau khi hoàn tất một task, nhiều phần có thể bị ảnh hưởng như tiến độ dự án, các task được gán cho người dùng, hay thông tin task mới
- Ví dụ cache lưu cùng lúc
- Để đồng bộ cache với server sau khi hoàn tất task, có thể cần nhiều vòng trao đổi
- Báo cho server rằng task đã hoàn tất
- Làm mới project vì tiến độ dự án đã thay đổi
- Kiểm tra xem có task mới được gán hay không
- Nếu có phân công mới thì truy vấn task đó
- Có thể giảm số vòng trao đổi bằng API phức tạp hơn, nhưng như vậy API hoặc logic client sẽ bị gắn chặt với mô hình dữ liệu nền tảng
- GraphQL là một cách tiếp cận cho vấn đề này, nhưng không phải lời giải hoàn chỉnh
- Một cấu trúc mà UI phải biết phần nào của cache liên quan đến từng thay đổi sẽ ngày càng mong manh khi mở rộng quy mô
- Quan hệ dữ liệu và các phép tổng hợp có thể ảnh hưởng đến nhiều phần của cache cục bộ
- Khi đội ngũ kỹ sư lớn dần, vấn đề có thể vượt qua ranh giới nhóm và gợi cảm giác giống các biến toàn cục có thể bị thay đổi trong những dự án phần mềm lớn
- Khi kết hợp với thay đổi lạc quan, client sẽ sao chép thêm nhiều logic backend hơn để dự đoán thay đổi từ server
- Trong ví dụ, có thể phải xóa task 1 khỏi user 1 và tính lại tiến độ thành tỷ lệ mới bằng tổng số task
- Càng buộc client phải dự đoán nhiều thay đổi lồng nhau ở cục bộ, nó càng trùng lặp nhiều hơn với stack backend
Stack cơ sở dữ liệu frontend mà SQLSync đề xuất
- SQLSync là stack cơ sở dữ liệu tối ưu cho frontend được xây trên SQLite
- Ứng dụng mẫu Todo app triển khai toàn bộ tầng dữ liệu chỉ với 60 dòng Rust và vài truy vấn SQL rải trong các component
- SQLSync cung cấp cache bền vững, chỉ mục/ràng buộc/trigger/tối ưu hóa truy vấn của SQLite, thay đổi lạc quan, vô hiệu hóa cache thông minh và truy vấn phản ứng
- Dữ liệu cục bộ được lưu trong một hoặc nhiều cơ sở dữ liệu SQLite
- Có thể dễ dàng tạo chỉ mục và chúng tự đồng bộ với dữ liệu
- Cơ sở dữ liệu có thể tự động dùng chỉ mục để tăng tốc truy vấn như ở backend
- SQL có thể biểu đạt các truy vấn dữ liệu phức tạp, và cũng có thể dùng các tính năng như triggers, foreign keys, constraints, full-text search
- Thay đổi lạc quan được xử lý bằng reducer
- Cấu trúc tương tự các khái niệm cốt lõi của Redux
- Reducer có thể được viết bằng bất kỳ ngôn ngữ nào có thể biên dịch sang WebAssembly
- SQLSync thực thi thay đổi một cách lạc quan trên client, và thực thi chúng trên server theo một thứ tự nhất quán toàn cục
- Sau đó client đồng bộ với server bằng một quy trình tương tự Git Rebase
- Kiến trúc này có lợi thế là loại bỏ nhu cầu vô hiệu hóa cache đệ quy
- Toàn bộ logic thay đổi dữ liệu được viết trong reducer, thứ dễ chia sẻ giữa client và server
- Mọi thay đổi dữ liệu phát sinh trong quá trình thay đổi đều tự động được phản ánh
- Vì đồng bộ hoạt động như Git Rebase, ngay cả khi server thực hiện thay đổi khác với client, client vẫn được đảm bảo đạt tới cùng một kết quả nhất quán
Công việc liên quan
- “Building data-centric apps with a reactive relational database” của Riffle bàn về ý tưởng lưu toàn bộ state ứng dụng, bao gồm cả state UI, trong một cơ sở dữ liệu phản ứng duy nhất
- Truy vấn phản ứng cung cấp một mô hình tư duy gọn gàng và phù hợp với các hệ thống khai báo như React
- Bài viết giải quyết các vấn đề phát triển ứng dụng client bằng những ý tưởng từ cộng đồng cơ sở dữ liệu
- Nó cũng nói đến lợi ích của việc mô hình hóa state bằng mô hình dữ liệu quan hệ và các chỉ mục thực sự
- Stepan của Instant.db đã viết hai bài về cơ sở dữ liệu trong trình duyệt
- Database in the Browser, a Spec
- A Graph-Based Firebase
- Cả hai bài đều xử lý các vấn đề tương tự nhưng tập trung nhiều hơn vào mối quan hệ giữa stack frontend và backend, đồng thời giải thích động lực tạo ra Instant.db như một bước tiếp nối dựa trên đồ thị của Firebase
- CR-SQLite của Matt Wonlaw là một phần mở rộng cho SQLite
- Nó dùng kiểu dữ liệu sao chép không xung đột (CRDT) và nhật ký sự kiện theo thứ tự nhân quả để hợp nhất dữ liệu một cách nhất quán
- Nhờ đó, các ứng dụng peer-to-peer có thể lưu dữ liệu vào SQLite và cộng tác mà không cần điều phối viên trung tâm
- Đây cũng là một ví dụ về việc chạy SQLite trong trình duyệt
- Matt Wonlaw cũng đang khám phá các ý tưởng liên quan
- incremental computation
- cải thiện khả năng dùng SQL thông qua typed-sql
1 bình luận
Các ý kiến trên Hacker News
Tôi biết rõ dự án này, và người tạo ra nó là bạn tôi, nên tôi sẽ thử bảo anh ấy vào đây trả lời câu hỏi
Anh ấy là một kiến trúc sư cơ sở dữ liệu dày dạn kinh nghiệm. Với SQLsync, anh ấy đã làm cho các lập trình viên frontend có thể truy vấn và cập nhật cơ sở dữ liệu từ xa như thể nó hoàn toàn nằm trong trình duyệt. Thực tế thì gần như là vậy, và nhờ WASM, có thể gửi toàn bộ cơ sở dữ liệu SQLite vào trình duyệt. Điểm cốt lõi nằm ở một thuật toán phản ứng thông minh nhưng đơn giản để đồng bộ giữa nhiều client
Nếu xem một phần lớn công việc phát triển là đồng bộ dữ liệu, thì React và REST API cũng có thể được xem như một dạng quy trình đồng bộ, và cách tiếp cận này mở ra những khả năng mới. Không cần tạo thêm một cơ sở dữ liệu tùy biến kỳ quặc bằng cây đối tượng được lấy từ API rồi cache nữa; chỉ cần dùng sức mạnh của cơ sở dữ liệu quan hệ để cập nhật và truy vấn ngay tại local
Tuy nhiên, ở các công ty web truyền thống, việc áp dụng sẽ khó vì có các đội backend/frontend chuyên biệt. Về cơ bản là bỏ đi các tầng cơ sở dữ liệu, backend, truyền tải, xác thực và thay bằng một hệ thống dạng một khối; mà đa số kiến trúc sư hệ thống xuất thân từ backend nên không hiểu rõ vấn đề này. Vì nó đụng sâu vào cả hai phía nên không hợp lắm với hệ thống hiện có, cuối cùng chỉ phù hợp cho phát triển mới. Backend cũng không phải là dịch vụ AWS hay Azure, cũng không thân thiện với Lambda, nên hầu hết kiểu kiến trúc sư mà tôi gặp sẽ không muốn động vào
Cách này ở một mức độ nào đó đã tồn tại từ trước với công nghệ cũ là CouchDB+PouchDB. Nó khá phù hợp với một số trường hợp sử dụng, nhưng hệ thống truy vấn không lý tưởng, còn cách xác thực và xác định phạm vi dữ liệu thì lạ lẫm với đa số người. Dễ nhất là khi dữ liệu hoàn toàn thuộc về một người dùng đơn lẻ và dùng nguyên mô hình cơ sở dữ liệu theo từng người dùng. Nếu phân vùng dữ liệu mạnh bằng CRDT thì cũng giảm được nhiều vấn đề xung đột
Nhưng có vấn đề về khả năng mở rộng. Khi có 10 nghìn đến 100 nghìn người dùng kết nối, CouchDB đòi hỏi CPU rất cao; công nghệ này cũng đã cũ, dù vẫn được bảo trì. Xét về thiết kế hệ thống, khi bắt đầu chia sẻ dữ liệu giữa người dùng, độ phức tạp tăng vọt, khiến nó giống như chỉ chuyển chỗ độ phức tạp thay vì giải quyết, nên mức độ phù hợp giảm đi
Cách tiếp cận này có vẻ cũng nhắm tới cùng mục tiêu, nhưng nhiều khả năng sẽ gặp các vấn đề mở rộng tương tự. Tôi mong chờ xem nó sẽ phát triển ra sao; hiện trông như một bước khởi đầu
Tôi nhớ trước đây Chrome từng cố đưa một cơ sở dữ liệu SQL đúng nghĩa vào trình duyệt nhưng không thành công lắm, rồi localStorage trở thành lựa chọn chủ đạo. Tôi không có ý hạ thấp tính hữu dụng của nó; chỉ là thông thường tôi có xu hướng chọn thứ trình duyệt cung cấp. Tôi rất kỳ vọng vào khả năng đưa WASM và những gì ngày càng trưởng thành hoặc nhiều tính năng hơn của nó vào trình duyệt
Công ty cũ của tôi từng dùng phần mềm quản lý dự án có cơ chế check-out/check-in cho các thao tác thay đổi. Khi check-out một dự án, bạn tải về một bản sao để chỉnh sửa local; khi check-in thì đẩy lại lên server. Trong trạng thái check-out, dự án bị khóa. Trong thời đại của các ứng dụng cập nhật trực tiếp, ai cũng thấy đó là cách làm lỗi thời
Nhưng sau 10 năm xây dựng web app SPA, tôi lại cảm thấy cách đồng bộ dữ liệu đó như đi trước thời đại
Cuối cùng, vấn đề quy về việc có thể triển khai một quy trình nhất quán để giải quyết bất nhất giữa nhiều cập nhật đồng thời hay không. Có trường hợp làm được, có trường hợp không, và điều này phụ thuộc vào quy tắc nghiệp vụ hơn là năng lực kỹ thuật
Nếu về mặt quy tắc nghiệp vụ không thể triển khai cơ chế giải quyết, thì dù có năng lực kỹ thuật để hỗ trợ cập nhật đồng thời, vẫn cần khóa để mỗi lần chỉ có một cập nhật
Nhưng khó thuyết phục rằng đây mới là điều thực sự mong muốn. Rất dễ rơi vào ảo tưởng lớn rằng mọi thứ phải luôn luôn khả dụng, nhưng trên thực tế thường là một người thay đổi tại một thời điểm, và nếu cần từ hai người trở lên cùng làm thì dù sao họ cũng phải nói chuyện hoặc trao đổi để phối hợp
Ngay cả trong phát triển hoàn toàn phân tán như Git, xung đột cũng không thể được tự động giải quyết một cách thần kỳ. Để chọn thay đổi đúng, vẫn cần trao đổi với người khác và hiểu ngữ cảnh
Có những thứ cần giải pháp đã được kiểm chứng
Tôi nhớ hồi công ty chuyển từ RCS sang CVS, một đồng nghiệp của tôi đã bực mình vì CVS không hỗ trợ check-out có khóa
https://en.wikipedia.org/wiki/Concurrent_Versions_System
Tôi nghĩ chiến lược khóa một chủ sở hữu duy nhất cũng có thể được mô phỏng bằng SQLSync. Tuy nhiên tùy ứng dụng mà có thể không cần. Nếu mục tiêu là làm việc offline rồi hợp nhất khi sẵn sàng, SQLSync cung cấp sẵn pattern này. Nếu mục tiêu là chỉ cho một client được thay đổi, thì cần pattern khóa trung tâm, và khả năng là cũng có thể điều phối việc này thông qua SQLSync
Ở đây có sự đan xen giữa nguyên tắc “cái gì được đo lường thì sẽ được quản lý” và sai lầm chi phí chìm
Vấn đề thật sự của cơ sở dữ liệu là độ phức tạp. Từng tính năng riêng lẻ nhìn chung khá an toàn, nhưng khi độ ổn định, caching và index bắt đầu tương tác với nhau thì độ phức tạp bùng nổ, và thường thì việc triển khai một DB chuyên biệt theo domain là điều không hợp lý
Nhưng đến lúc công ty nhận ra mình đã đầu tư và đổ rất nhiều tài nguyên vào việc triển khai ba tính năng đó, thì về mặt chính trị rất khó khuyên nên gỡ bỏ chúng, và chi phí thực tế để loại bỏ nợ kỹ thuật một lần cũng rất lớn
Tôi cho rằng vấn đề thật sự nằm ở cú pháp SQL. Nếu trải nghiệm dùng cơ sở dữ liệu quan hệ cơ bản dễ chịu như một cú pháp kiểu C quen thuộc thay vì thứ tiếng Anh bị vỡ vụn, thì động lực dùng DB thay vì tự làm sẽ lớn hơn nhiều. Các cơ sở dữ liệu NoSQL là một bước đi tốt theo hướng đó, nhưng nhìn chung lại tập trung quá mức vào big data hơn là tính hữu dụng hằng ngày. Những thứ như Redis đã có chỗ đứng và ổn
Làm cho SQL dễ chạy là một cách tiếp cận hợp lý, nhưng với các cơ sở dữ liệu tốt, ví dụ Postgres mà tôi thích, SQL là ngôn ngữ mặc định nên khó đạt hiệu quả nếu không dùng ngôn ngữ đó. Thực sự cần một cơ sở dữ liệu kiểu PostgresPostSQL, sao chép hoàn hảo Postgres nhưng parser mặc định hỗ trợ một ngôn ngữ có cú pháp tốt
Trong lập trình nói chung có hàng chục ngôn ngữ được sử dụng và liên tục tiến hóa. Ngay cả JavaScript, vốn khó thay đổi vì trình duyệt thực thi nó và ta không kiểm soát được trình duyệt của người dùng, cũng đang tiến hóa thông qua transpiler và WebAssembly
Nhưng trong cơ sở dữ liệu thì thực tế chỉ có một mình SQL. Có các lựa chọn thay thế, nhưng không thứ nào tiệm cận SQL về mức độ sử dụng. Có lẽ SQL không tệ đến vậy
Lý do có thể là vì mô hình quan hệ thật sự rất tốt. Những nỗ lực đi chệch khỏi nó có lẽ chỉ hiệu quả trong các ngách. Phong cách khai báo cũng rất tốt, và nếu rời khỏi nó thì cũng khó đạt thành công lớn. Cuối cùng nếu chỉ tạo ra một SQL với cú pháp khác, thì với đa số người dùng đó không phải là cải tiến đủ lớn để đổi cách làm
Ứng dụng viết nhắm tới API này có thể triển khai một SQL DB. Chỉ cần parse SQL và triển khai một query planner xuất ra kế hoạch truy vấn phù hợp với API này
Tôi là tác giả. Tôi mới chỉ lướt qua phần lớn câu hỏi, và sẽ tiếp tục kiểm tra định kỳ xem có bỏ sót câu nào không. Tôi cũng tò mò liệu có ai đã làm ra cách theo dõi thảo luận HN tốt hơn chưa
Tôi rất vui với cuộc thảo luận cho đến giờ. Bài đầu tiên tập trung vào động cơ kỹ thuật frontend khiến tôi tạo ra SQLSync, hơn là SQLSync cụ thể hoạt động ra sao. Tôi dự định sẽ nói về cách nó hoạt động trong bài tiếp theo
Không nên để người dùng có một mô hình tư duy mà thực tế có thể phá vỡ nghiêm trọng, hoặc phá vỡ theo cách họ không thấy được
Tôi lo rằng cách đồng bộ cơ sở dữ liệu thay vì mô hình client-server có thể là một trường hợp như vậy. Cơ chế đồng bộ có thể đơn giản là tan chảy, hoặc có những giả định sâu không được thỏa mãn
Nếu cần UI nhanh, tôi thấy tạo một tập các primitive CRDT để dùng sẽ an toàn hơn, còn phần còn lại có lẽ nên dừng ở mức gửi form
Đồng bộ trạng thái giữa client và server là một vấn đề bị nguyền rủa
Nếu chấp nhận hy sinh một chút trải nghiệm người dùng và quay về cách gần với mô hình PHP/server-side rendering, ta có thể tránh toàn bộ vấn đề này. SPA thì hay, nhưng gửi multipart form vẫn hoạt động. Chỉ với rất ít JavaScript cũng có thể làm mượt phần lớn các góc cạnh còn lại
Trong các sản phẩm web gần đây, trạng thái phía client chỉ gồm claim xác thực từ IdP bên thứ ba, ID phiên first-party trong query parameter, và tài liệu hiện tại. Thành thật mà nói tôi cũng không biết cái đầu tiên được lưu ở đâu. Đó là vấn đề của Microsoft, không phải của chúng tôi. Tất cả trạng thái còn lại nằm trên server
Tôi đối xử với client như một terminal ngu ngốc chỉ biết phát ra input cả ngày. Cũng không dùng cookie first-party hay local storage. Cách này đã cải thiện đáng kể trải nghiệm phát triển nhắm tới iOS/Safari
Vì vậy tôi muốn hỏi trải nghiệm thực sự muốn cung cấp là gì, và vì sao nó đủ chính đáng để tách trạng thái client và server
Tham khảo: https://news.ycombinator.com/item?id=37584049
Dường như xu hướng offline/local-first dựa trên SQLite đang rất nóng hiện nay. Đây là bài thứ ba tôi đọc trong tuần này, và trông có vẻ hay
Nhưng so với ElectricSQLhttps://electric-sql.com/ và PowerSynchttps://powersync.com/ thì thế nào?
ElectricSQL và PowerSync đều đang xử lý một bài toán rất khó gọi là sao chép một phần. Họ đang cố tạo ra một giải pháp tổng quát để một cơ sở dữ liệu trung tâm truyền thống chỉ đồng bộ hai chiều những gì client cần, đồng thời hỗ trợ các thay đổi lạc quan cùng việc xử lý nhất quán/xung đột kéo theo
Nhược điểm là độ phức tạp khi triển khai. Cần theo dõi chính xác mỗi client đang có tập con nào trong toàn bộ cơ sở dữ liệu, để chỉ đẩy các thay đổi tới tập con đó. Ngoài ra, để chỉ định sẽ tải xuống tập con nào của trạng thái cơ sở dữ liệu, cần một DSL mới, và DSL đó cũng phải được học và tối ưu hóa. Dù vậy, thật đáng mừng khi họ đang giải quyết một vấn đề rất khó, và đến khi SQLSync sẵn sàng hỗ trợ sao chép một phần thì có lẽ các best practice đã được đúc kết
Ngược lại, hiện SQLSync chỉ hỗ trợ đồng bộ toàn bộ DB. Tất cả client sẽ nhìn thấy một view nhất quán của toàn bộ cơ sở dữ liệu. Bạn có thể lập tức nghi ngờ liệu đây có phải ý tưởng hay không, và nó không phù hợp với một số ứng dụng. Nhưng nếu nghĩ đến ứng dụng tài chính cá nhân, mục tiêu cốt lõi là đồng bộ giữa các thiết bị, sao lưu đám mây, khả năng offline, v.v., nên việc toàn bộ DB được lưu trên mọi thiết bị lại có thể chính là cách mong muốn. Mô hình dữ liệu hướng tài liệu như Airtable cũng có thể là một ví dụ. Nếu coi mỗi Airtable là một cơ sở dữ liệu riêng, client có thể tự quản lý việc cần quan tâm đến bảng nào
Khi tập trung vào đồng bộ toàn bộ DB, sync engine trở nên đơn giản hơn nhiều so với các giải pháp hỗ trợ sao chép một phần. Một trong những lợi ích là backend rất nhẹ. Demo hiện tại (https://sqlsync-todo.pages.dev) chạy hoàn toàn trong Cloudflare Durable Objects, chỉ dùng rất ít storage và thời gian CPU
SQLSync vẫn còn nhiều việc phải làm để hiện thực hóa các use case như vậy, và vẫn gần với một prototype, nhưng các thử nghiệm ban đầu rất tích cực
Với các ứng dụng đa tenant lớn, khi từng dataset tương đối nhỏ, tôi đã nhiều lần nghĩ “hay cứ gửi cả cơ sở dữ liệu xuống client thì sao”. Nó trông đủ lệch chuẩn để giống một pattern kiến trúc bị nguyền rủa, nên tôi chưa đào sâu. Sẽ rất vui nếu biết rằng mình đã sai
Còn nhiều việc phải làm để chứng minh cho đàng hoàng, nhưng tôi khá hào hứng muốn tiếp tục đẩy tới để xem nó sẽ đi đến đâu
Đây có vẻ là một trong những vấn đề sẽ biến mất hoàn toàn nếu bỏ SPA
Nếu dùng các giải pháp kiểu Hotwire hay htmx, truy vấn chỉ là truy vấn server, và bài toán làm cho các truy vấn đó nhanh đã được hiểu rõ hơn nhiều
Tôi đã dùng nó cùng ocaml + web components, và đó là trải nghiệm năng suất 10/10. Chỉ cần một build tool biên dịch nhanh hơn cả chớp mắt, và không cần dây nhợ ánh xạ JSON giữa frontend và backend, nên thật sự rất năng suất
Cá nhân tôi thích InertiaJs https://inertiajs.com hơn. Đó là một dạng hệ thống frontend router đồng bộ trạng thái với server theo “cách cũ”
Điều này đặc biệt đúng với sản phẩm cần hoạt động cả ở những khu vực internet không ổn định
Hiện tôi đang viết một bài rất tương tự về “cơ sở dữ liệu full-stack”. Bài viết bàn về mẫu hình trong đó nhiều ứng dụng tạo lại logic của backend và cơ sở dữ liệu trong mã client frontend. Giải pháp chúng tôi khuyến nghị là chọn một cơ sở dữ liệu có thể chạy ở cả phía server lẫn client, rồi đồng bộ giữa hai bên
Lý do chúng tôi không dùng SQLite trong sản phẩm của mình, nói thật là vì SQL không phải công cụ phù hợp để truy vấn dữ liệu ứng dụng. Nó không dễ khớp với cấu trúc dữ liệu mong muốn trong mã client, và hầu như mọi cơ sở dữ liệu SQL đều không có cách subscribe các thay đổi của truy vấn mà không phải polling truy vấn lặp đi lặp lại
Nếu bạn thích ý tưởng đặt một cơ sở dữ liệu hoàn chỉnh ở client và muốn tích hợp sâu với TypeScript/JavaScript, hãy xem https://github.com/aspen-cloud/triplit mà chúng tôi đang phát triển
https://github.com/cpursley/walex
Tôi dùng theo cách rất đơn giản. Khi dữ liệu trong bảng nền thay đổi, truy vấn sẽ tự động được chạy lại. Có thể không hiệu quả bằng việc cập nhật kết quả theo kiểu gia tăng, nhưng truy vấn SQLite thường rất nhanh nên tôi nghĩ đó không phải vấn đề lớn