Những lời nói dối tôi đã được biết về việc cộng tác chỉnh sửa, Phần 2: Tại sao chúng ta không sử dụng Yjs
Tin tức chung·Hacker News·1 lượt xem

Những lời nói dối tôi đã được biết về việc cộng tác chỉnh sửa, Phần 2: Tại sao chúng ta không sử dụng Yjs

Lies I was told about collaborative editing, Part 2: Why we don't use Yjs

AI Summary

Bài viết này đưa ra một quan điểm trái chiều về các thư viện chỉnh sửa cộng tác phổ biến như Yjs, vốn thường được quảng cáo với lợi ích của CRDT (Conflict-free Replicated Data Types). Tác giả lập luận rằng ngay cả với các lợi ích này, Yjs vẫn có thể **làm hỏng tài liệu một cách âm thầm** và hiện tại chưa phù hợp cho cả việc chỉnh sửa trực tiếp (live collaboration). Tác giả chỉ ra rằng các tính năng quan trọng như optimistic updates, offline editing và provenance hoàn toàn có thể đạt được với **độ phức tạp ít hơn đáng kể** bằng cách sử dụng cách tiếp cận đơn giản hơn, dựa trên cơ chế "authority" từ `prosemirror-collab`. Do đó, các developer nên xem xét giải pháp đơn giản này cho hầu hết các trường hợp sử dụng, bởi nó giúp **tránh được sự phức tạp được nhận thức** cũng như **rủi ro hỏng dữ liệu âm thầm** đi kèm với CRDT. Ngoại trừ những trường hợp mà việc chỉnh sửa peer-to-peer thực sự "masterless" (không có máy chủ trung tâm) là yêu cầu bắt buộc.

Trong phần 1 của loạt bài này, chúng tôi nhận thấy rằng người dùng thường xem các thuật toán chỉnh sửa văn bản cộng tác phổ biến nhất (bao gồm cả thư viện phổ biến nhất, Yjs) là âm thầm làm hỏng tài liệu của họ khi...

Trong phần 1 của loạt bài này, chúng tôi nhận thấy rằng người dùng thường xem các thuật toán chỉnh sửa văn bản cộng tác phổ biến nhất (bao gồm cả thư viện phổ biến nhất, Yjs) là âm thầm làm hỏng tài liệu của họ khi các thuật toán giải quyết xung đột chỉnh sửa trực tiếp. Chúng tôi lập luận rằng, mặc dù điều này có thể phù hợp cho việc chỉnh sửa cộng tác trực tiếp (vì con trỏ hiện diện giúp người dùng tránh xung đột chỉnh sửa trực tiếp), nhưng thuộc tính này khiến chúng nhìn chung hoàn toàn không phù hợp với trường hợp ngoại tuyến vì người dùng sẽ không có khả năng tránh những xung đột đó.

Lần này, trong phần 2, chúng ta sẽ tranh luận rằng các thuật toán phổ biến tương tự—và đặc biệt là Yjs—cũng cũng  hiện không phù hợp với trường hợp cộng tác trực tiếp. Chủ yếu có hai điểm:

Chúng tôi sẽ mô tả một số thách thức cụ thể mà chúng tôi đã gặp phải khi cố gắng đưa Yjs vào trình soạn thảo văn bản sản xuất của mình.

Chúng tôi đề xuất một lựa chọn thay thế ít nổi tiếng hơn cho Yjs vì nó tốt hơn một cách đồng đều trên mọi trục ngoại trừ Chỉnh sửa ngang hàng thực sự không có chủ.

Thời gian demo: giải pháp đơn giản (~40 dòng mã)

Tôi đã nghe lập luận này nhiều lần đến mức tôi không thể đếm được: CRDT rất phức tạp về mặt vận hành, nhưng bạn cần chúng (cần chúng!) để có các bản cập nhật tích cực, các chỉnh sửa trong thời gian mạng bị ngắt kết nối (hoặc ngắt kết nối kéo dài), xuất xứ chi tiết của các chỉnh sửa, đối chiếu ngang hàng, v.v. Tôi muốn thuyết phục bạn rằng tất cả những điều này (ngoại trừ kiến trúc p2p không có chủ thực sự) đều có thể dễ dàng thực hiện được mà không cần CRDT.

Có, dễ dàng có thể thực hiện được: 40 dòng mã (291 nếu bạn nhất quyết muốn đếm giàn giáo React).

Dưới đây, mã này đang chạy dưới dạng bản demo trực tiếp. Bạn có thể sử dụng nút Tạm dừng để mô phỏng việc ngắt kết nối mạng. Chỉnh sửa tài liệu và bỏ tạm dừng để đồng bộ hóa chúng, giống hệt như với CRDT.

Lưu ý: việc hòa giải ngoại tuyến luôn tạo ra kết quả kỳ lạ. Chúng ta đã thảo luận kỹ về vấn đề này trong phần 1. Tất cả thuật toán đối chiếu có khả năng ngoại tuyến (ví dụ:, CRDT, OT và thuật toán này) chọn độ phân giải về cơ bản là ngẫu nhiên. Vấn đề không phải là thuật toán này hoạt động tốt hơn mà là nó thực hiện điều tương tự như CRDT, nhưng với độ phức tạp ít hơn rất nhiều.

Cách thức hoạt động của những điều đơn giản

Thuật toán này sử dụng thư viện prosemirror-collab cực kỳ đơn giản và nhàm chán. Tác giả đã viết về cách thức hoạt động của nó, nhưng nó gần như tầm thường nên tôi cũng sẽ giải thích ở đây:

Đối với mỗi tài liệu, có một thẩm quyền duy nhất nắm giữ nguồn sự thật: tài liệu, các bước được áp dụng và hiện tại phiên bản.

Khách hàng gửi một số bước giao dịch và LastSeenVersion.

Nếu lastSeenVersion không khớp với phiên bản của máy chủ, thì máy khách phải tìm nạp các thay đổi(lastSeenVersion) gần đây, khởi động lại các thay đổi của chính nó lên trên và gửi lại.

Nếu khoảng thời gian khứ hồi bổ sung để khởi động lại các thay đổi không đủ đối với bạn, prosemirror-collab-commit thực hiện gần như tương tự, nhưng nó dựa trên những thay đổi về chính cơ quan có thẩm quyền.

Lưu ý: “Authority” không có nghĩa là “máy chủ tập trung chạy trong AWS”. Bạn có thể thiết lập máy tính xách tay của mình làm cơ quan có thẩm quyền miễn là bạn đang chia sẻ với người khác. Vì vậy, giao thức này có khả năng p2p, nhưng không phải là không có chủ, đó là những gì CRDT cung cấp.

Và thế là xong. Đó là tất cả những gì cần thiết. 40 dòng mã là độ phức tạp cơ bản cho các bản cập nhật lạc quan, chỉnh sửa ngay cả khi mạng không ổn định (hoặc không hoạt động trong khoảng thời gian tùy ý), xuất xứ chi tiết, v.v.

Điều duy nhất còn thiếu là chỉnh sửa ngang hàng thực sự không thành thạo. Nếu bạn cần điều đó, tuyệt vời! Nhưng nếu không thì chi phí là bao nhiêu?

Những thách thức khi triển khai Yjs và CRDT

Đây là phần kém thú vị nhất trong bài viết vì không có cách nào thực sự để nói về vấn đề này mà không tỏ ra đặc biệt quan tâm đến Yjs. Tôi biết mọi người làm việc chăm chỉ về nó. Nhưng… tôi cũng nghĩ rằng với tư cách là một hệ sinh thái, sẽ không thể phát triển nếu chúng ta không thừa nhận mình đang ở đâu hiện tại. Và, dựa trên những gì tôi biết, tôi tin rằng vị trí hiện tại của chúng ta là: một tình thế khó khăn.

“Nhưng mọi người đều đang sử dụng Yjs”

Ok, hãy giải quyết vấn đề này thôi. Có, tôi biết: mọi người khác đang sử dụng Yjs, thư viện cộng tác phổ biến nhất mọi thời đại. Vậy vấn đề chắc chắn là ở chúng ta. Đúng không?

Tôi cũng nghĩ vậy trong một thời gian. Tôi cũng có thể kể cho bạn nghe khoảnh khắc bóng ma nghi ngờ rời khỏi cơ thể tôi và tôi biết tận xương tủy rằng điều này không đúng. Đó là khoảnh khắc tôi thấy y-prosemirror phát hành #113, một báo cáo lỗi dường như vô hại và hiện vẫn đang mở, vô tình tiết lộ rằng Yjs sẽ hủy hoàn toàn và tạo lại toàn bộ tài liệu trên mỗi tài liệu gõ phím.

Đây có phải là tai nạn không? Đáng buồn thay, không. Trong cuộc thảo luận về y-prosemirror chuỗi thông báo từ 6 năm trước, Kevin (tác giả của Yjs) tiết lộ rằng đây là do thiết kế.

Có một số qua lại sau đó. Marijn (tác giả của ProseMirror) đồng tình với giải thích rằng lựa chọn này phá vỡ, giống như, rất nhiều nội dung.

Kevin trả lời để gợi ý rằng, dù có bất kỳ sự cố nào, nó dường như vẫn đủ tốt cho Tiptap. Những người dùng khác gợi ý rằng điều này thực sự phá vỡ nhiều thứ—không, thực sự, đúng vậy. Marijn trả lời cho rằng lý do biện minh cho sự hoàn hảo của chiến lược thay thế mọi thứ có thể không có cơ sở. Có một cách để giải quyết một số vấn đề này, đại loại. vân vân.

Ngoài ra: Vâng, nghiêm túc mà nói, nó phá vỡ rất nhiều thứ. Hiệu suất kém hơn vì mỗi lần nhấn phím về cơ bản khiến bạn phải tạo lại mọi thứ—mọi NodeView, mọi trang trí, tất cả thành phần DOM cho toàn bộ tài liệu. Nó phá vỡ mọi plugin phụ thuộc vào ánh xạ vị trí, ví dụ:, nhận xét và chỉ báo hiện diện cộng tác. Hoàn tác, vị trí con trỏ và quản lý lựa chọn đều trở nên cực kỳ kỳ quặc. Trạng thái của tất cả các tiện ích nhỏ trong tài liệu của bạn sẽ liên tục bị xóa hoàn toàn. Các plugin xem áp dụng thực sự rất chậm vì chúng phải kiểm tra toàn bộ tài liệu thay vì chỉ những gì đã thay đổi. Nhận dạng nút trở nên không ổn định (mặc dù Kevin nói không phải vậy?). Và cứ lặp đi lặp lại.

Tôi vẫn không thể tin được những gì mình đang viết. Việc áp dụng chế độ này có thể hợp lý nếu không có lựa chọn nào khác—nhưng chúng tôi có những lựa chọn đơn giản hơn, nhanh hơn, tốt hơn mà không gặp phải những vấn đề này. Những gì tôi đang thấy ở đây giống như một sai lầm cho thấy sự hiểu lầm cơ bản về những gì người soạn thảo văn bản cần phải hành xử đúng mực, trong bất kỳ tình huống nào. Và đọc nó thực sự rất đau lòng.

Bây giờ, hãy nhìn xem. Tôi hiểu rằng những người bảo trì hiện đang giải quyết vấn đề này. Tôi chân thành hy vọng họ thành công và trong một năm nữa, tôi buộc phải viết một bài khác về việc tất cả những điều này giờ đây đã sai lầm như thế nào. Nhưng dường như đó vẫn chưa phải là nơi chúng ta đang ở. Và kinh nghiệm của chúng tôi là rất khó để chống lại kiến trúc hiện tại.

Yjs khiến việc đạt được mục tiêu về độ trễ hoàn hảo trở nên khó khăn hơn rất nhiều

Mục tiêu của chúng tôi là để trình chỉnh sửa chạy ở tốc độ 60 khung hình/giây. Cho dù có bao nhiêu cộng tác viên, bất kể lô chỉnh sửa lớn đến đâu, bất kể phức tạp đến đâu document: luôn luôn có trường hợp chúng tôi có tối đa ~16 mili giây để thực hiện tất cả công việc của chúng tôi và cả một vòng kết xuất React hoàn chỉnh.

Giống như bảo mật, hiệu suất không xảy ra một cách tình cờ. Cần phải làm việc cẩn thận, có mục tiêu và thường xuyên cảnh giác đối với các trường hợp hồi quy. Dưới đây là danh sách (chưa đầy đủ) những thứ giúp chúng tôi đạt được mục tiêu này. Tất cả đều khó hơn hoặc không thể thực hiện được với Yjs.

Thực hiện giao dịch rất nhanh. Trong điểm chuẩn của chúng tôi, một cỗ máy hiện đại hỗ trợ x.000 ứng dụng ProseMirror Transaction mỗi giây, bao gồm cả thời gian cần thiết để cập nhật DOM. Ngoài ra, ProseMirror giữ ánh xạ vị trí trên các phiên bản tài liệu, do đó, Giao dịch xuất hiện qua mạng có thể được "khởi động lại" và áp dụng cực kỳ nhanh chóng. Như tôi đã đề cập trước đây, Yjs hoàn toàn không có khả năng này: mỗi lần nhấn phím cộng tác sẽ xóa và tạo lại toàn bộ tài liệu từ đầu.

Máy chủ sắp xếp các giao dịch thành các nhóm có độ dài từ 20 bước trở xuống. Khách hàng thường có thể áp dụng 20 đối tượng Bước trong thời gian ít hơn 16 mili giây. Điều này có nghĩa là máy chủ có thể tích lũy một tập hợp thay đổi lớn (ví dụ, với nhiều người chỉnh sửa đồng thời, lỗi mạng, vv.) và nó sẽ vẫn không bao giờ vô tình làm treo chuỗi chính. Điều này hoàn toàn tầm thường trong ProseMirror. Tôi không biết trực giác về cách chúng tôi thực hiện điều này với Yjs.

Giải quyết xung đột không bao giờ xảy ra trên luồng chính. Chúng tôi giải quyết xung đột trên máy chủ; điều này cũng có thể xảy ra trong một chuỗi công việc riêng biệt. Tôi hiểu rằng việc chạy các quy trình đối chiếu Yjs trong một luồng công việc là có thể nhưng đầy thách thức.

Chúng tôi theo dõi những gì trong EditorView gây ra độ trễ. Ví dụ: ngay bây giờ, phần tốn kém nhất trong EditorView của chúng tôi là tính toán vị trí của các dấu mũ hiện diện từ xa. Yjs không có tác động cụ thể đến vấn đề này, ngoài thực tế là việc xây dựng lại toàn bộ EditorView từ đầu trong mỗi lần nhấn phím rõ ràng là tốn kém hơn.

Các bản cập nhật cho đối tượng DOM trong tài liệu có tính gia tăng. Điều này chủ yếu đến từ Reac-prosemirror , tất nhiên, nhưng một lần nữa không là không cần phải nói, vì Yjs thay thế toàn bộ tài liệu trong mỗi lần nhấn phím cộng tác.

Được hiển thị trực quan, quy trình ứng dụng Giao dịch từ xa của chúng tôi trông như thế này:

Đơn giản như cách tiếp cận này, chúng tôi vẫn thường xuyên không đáp ứng được ngân sách hoàn thiện.

Thật đáng thất vọng—và thậm chí bỏ qua vấn đề xóa/tạo lại—quy trình Yjs cũng phức tạp hơn đáng kể. Thứ nhất, CRDT không thể đại diện cho việc chỉnh sửa văn bản đa dạng thức (đó là vấn đề nghiên cứu mở hợp pháp). Thay vào đó, Yjs trình bày các tài liệu ProseMirror bằng cách sử dụng các tiện ích XML của họ. Vì điều này có nghĩa là họ không thể trực tiếp sử dụng các đối tượng Transaction ProseMirror nên việc ghi phải chuyển đổi Transaction thành bản cập nhật XML của Yjs; khách hàng cũng nhận được các bản cập nhật và cần bằng cách nào đó biến bản cập nhật XML của Yjs trở lại thành một Giao dịch và áp dụng nó vào tài liệu ProseMirror.

Tất cả những thứ này đều phải trả phí. Dù giá rẻ nhưng Yjs vẫn nhất quyết thay tài liệu mỗi lần. Tôi thực sự lo lắng khi xem xét quy trình này.

Một lần nữa, tôi hiểu rằng những người bảo trì Yjs đang bắt đầu thực hiện các bản cập nhật chi tiết hơn và thế giới mới sẽ giống như sau.

Điều này chắc chắn gần hơn với những gì chúng tôi mong muốn nhưng chúng tôi sẽ xem nó giúp ích đến mức nào trong thực tế. Điều tôi sẽ nói ngay bây giờ là, do rất khó để có được thứ đơn giản chạy ở tốc độ 60 khung hình/giây, việc thực hiện hồi quy này vẫn còn đáng sợ, đặc biệt nếu chúng ta không cần cấu trúc liên kết p2p thực sự không có chủ.

Yjs mâu thuẫn với các lược đồ tài liệu

Hầu hết mọi người muốn có một bộ quy tắc nhỏ, lành mạnh chi phối cấu trúc của tài liệu, ví dụ, blockquote đó các nút không thể là con của nút code_block. Tài liệu sơ đồ là công cụ chính để thực hiện việc này. Chúng xác định xem Giao dịch có tạo ra EditorState hợp lệ hay không.

Lược đồ tài liệu thường được đóng gói tĩnh như một phần của mã ứng dụng. Trong cài đặt tập trung, máy chủ có thể từ chối các Giao dịch được đề xuất không hợp lệ và ứng dụng có thể xác minh rằng tất cả các máy khách đều sử dụng cùng một phiên bản lược đồ. ví dụ:, đây là những gì tài liệu Tiptap dường như đề xuất họ làm.

Yjs được thiết kế cho các cấu trúc liên kết ngang hàng thực sự không có chủ và các giá trị mặc định của nó nguy hiểm hơn một chút. Chúng phải như vậy—không có thẩm quyền thực sự nào về nội dung của lược đồ!

Nói chung, theo quan điểm của Yjs, không có phiên bản đang chạy nào biết chắc chắn liệu một thay đổi có hoàn toàn không hợp lệ hay không, hay nó chưa nhận được lược đồ mới.

Theo đó, trong quá trình thử nghiệm y-prosemirror v1.3.7 của chúng tôi, khi schema.node() đưa ra một ngoại lệ vì (bao gồm cả vì lược đồ đó không hợp lệ), nút dường như đã bị xóa vĩnh viễn và việc xóa đó đã được phổ biến tới tất cả các nút ngang hàng.

Bạn có thể làm tốt hơn, nhưng bạn phải biết thiết lập nút này trước, ví dụ, Tiptap ít nhất cũng phát hiện được lược đồ không khớp và tạm dừng trình chỉnh sửa và buộc tải lại.

Nếu để yên, điều này đặc biệt tai hại trong quá trình nâng cấp. Nếu bạn bị ngắt kết nối trong một thời gian dài, có bản nâng cấp và mọi người sử dụng tính năng mới thì khi kết nối lại, bạn sẽ âm thầm hủy tất cả dữ liệu mới. Ôi!

Điều này không có nghĩa là bạn không thể giải quyết được vấn đề này (ví dụ:, Tiptap cũng vậy). Tuy nhiên, bạn phải làm việc nhiều hơn để không làm hỏng toàn bộ chân của mình, theo cách rất khó gỡ lỗi.

Yjs khiến việc cấp quyền trở nên khó khăn hơn rất nhiều

Các trình chỉnh sửa tài liệu trong thế giới thực thường sẽ cung cấp nhiều loại quyền có thể được cấp cho những người dùng khác. Rõ ràng là có Trình chỉnh sửa, nhưng rất phổ biến là cũng có Người xem, Người nhận xétĐề xuất để nêu tên một số ví dụ.

Tất cả các tính năng này đều liên quan đến việc cho phép một số người dùng có quyền chọn lọc để chỉnh sửa các phần của tài liệu (ví dụ:, thêm dấu nhận xét vào tài liệu). Thông thường, việc này thường khá đơn giản để thực hiện: bạn xem Giao dịch, xem cách nó thay đổi tài liệu (ví dụ:, nó chỉ đặt một dấu hay nó cũng thay đổi văn bản) và chấp nhận hoặc từ chối dựa trên quyền của người dùng gửi.

Điều này khá khó xử hơn một chút trong Yjs. Vì Yjs ánh xạ Giao dịch đến và từ các bản cập nhật XML nên về cơ bản bạn phải dự đoán hiệu ứng thực sẽ ra sao khi nó được cụ thể hóa dưới dạng Giao dịch và chấp nhận hoặc từ chối dựa trên dự đoán đó. Điều đó không phải là không thể, nhưng nó khó hơn rất nhiều so với vẻ ngoài của nó. Ngoài ra, giống như các lược đồ, Yjs được xây dựng cho cấu trúc liên kết không có thẩm quyền, do đó, nó không có tiện ích gốc nào được tích hợp sẵn cho các quyền, ít nhất là theo như tôi có thể nói.

Có lẽ bạn không chỉ sử dụng CRDT nên tính khả dụng là thực tế không tốt hơn

Một trong những điều tôi thường xuyên nghe về Yjs là nó giúp việc duy trì hoạt động dễ dàng hơn khi máy chủ ngừng hoạt động. Tại thời điểm này, đối với hầu hết các ứng dụng thực tế, tôi sẵn sàng tranh luận rằng điều này không đúng.

Trước hết, các trình soạn thảo văn bản hiện đại gần như thống nhất thực hiện nhiều việc khác ngoài việc chỉ lưu trữ văn bản. Những nội dung như:

Lưu trữ nội dung trong máy chủ phương tiện, ví dụ: hình ảnh bạn dán vào tài liệu

Kiểm tra quyền

Sự hiện diện có thể có hoặc không phải là một dịch vụ riêng biệt

Độ bền (ví dụ, tài liệu có thể được lưu trữ trong S3, các hoạt động trong bộ lưu trữ K/V, v.v.)

Nói chung, không có dịch vụ nào trong số này sẽ là CRDT và nếu bất kỳ dịch vụ nào trong số đó ngừng hoạt động (đặc biệt là các quyền), có thể bạn sẽ muốn ngừng cung cấp lưu lượng truy cập.

Điều này có nghĩa là bạn chủ yếu sử dụng CRDT làm giao thức mạng chứ không phải là chiến lược khả dụng. Bạn chắc chắn có thể làm điều đó, nhưng như tôi đã nói trong suốt bài viết này, nó kém hiệu quả hơn và phức tạp hơn rất nhiều so với các ứng cử viên giao thức mạng thay thế.

Tombstoning làm mất dữ liệu hoặc ngốn hết RAM của bạn

Giải pháp “đơn giản” để cộng tác chỉnh sửa lưu trữ tất cả các bước trong bộ lưu trữ lâu bền. Nếu bạn bị tụt lại phía sau, bạn có thể gọi cho họ API tương đương với changes(lastSeenVersion). Đối với các yêu cầu hợp lý, đây là cách nhanh chóng và hiệu quả để bắt kịp khách hàng và khách hàng có thể quên tất cả các bước sau khi đã kết hợp chúng vào EditorState của mình.

Yjs, được thiết kế cho cấu trúc liên kết p2p thực sự không có chủ, thường không thể dễ dàng quên các bước. Đặc biệt, nếu một mục bị xóa, nó phải giữ lại một “bia mộ”—một điểm đánh dấu ghi lại một mục đã bị xóa. Điều này là do các hoạt động đồng thời tham chiếu ID của một mục nhất định sẽ điều chỉnh không chính xác nếu khách hàng không thể biết rằng mục đó đã bị xóa.

Giải pháp chung là thu thập rác (GC) bia mộ. Nhưng bạn chỉ có thể thực hiện việc này một cách thực sự an toàn khi tất cả các máy ngang hàng đã xóa mục đó—và bạn không thể biết được điều đó vì chúng tôi không thể phân biệt giữa máy khách bị ngắt kết nối và máy khách chạy chậm. Vì vậy, bạn có thể giữ bia mộ lâu hơn (nhai lại bộ nhớ) hoặc bạn có thể quên chúng sau một thời gian tùy ý, điều này sẽ mất dữ liệu. Giao thức cơ bản của Yjs (được gọi là YATA) xử lý bia mộ ở tốc độ ~30 giây.

Nếu bạn không sử dụng cấu trúc liên kết p2p thực sự không có chủ thì đây hoàn toàn là một sự đánh đổi vô nghĩa. changes(lastSeenVersion) giải quyết hoàn toàn vấn đề này và hoàn toàn không có nhược điểm nào. Tất cả những gì bạn cần là một cơ sở dữ liệu để lưu trữ các bước.

CRDT khó gỡ lỗi hơn rất nhiều

Tôi hiện đã triển khai các trình soạn thảo văn bản cộng tác theo hầu hết các cách mà bạn có thể. OT, CRDT, với prosemirror-collab, với prosemirror-collab-commit, có khóa. Điều tôi sẽ nói là ngay cả một lỗi nhỏ trong quá trình triển khai “đơn giản” với prosemirror-collab cũng sẽ khiến bạn tỉnh táo đến tột độ.

Giống như, khi đang nhập, bạn sẽ tạo ra hàng trăm hoặc hàng nghìn bản cập nhật mỗi phút và đôi khi, tài liệu của bạn sẽ thiếu một vài ký tự xuất hiện trên máy chủ.

Nhưng tại sao? Bạn sẽ không biết. Bạn sẽ không ghi lại toàn bộ tài liệu mọi lúc (có thể vậy) nên sẽ rất khó để tìm ra chỉnh sửa chính xác đã làm hỏng tài liệu. Bạn sẽ thêm nhật ký, sau đó bắt đầu công việc kinh doanh của mình và khi bắt đầu lại, bạn sẽ dành 4 giờ để xem nhật ký trước khi nhận ra mình đang thiếu một dòng nhật ký quan trọng khác. Có một lần điều này xảy ra với tôi và vấn đề là tôi đã thêm chờ nhưng đưa ra một số quyết định về dữ liệu tôi đã đọc trước thời điểm hồi hộp, dữ liệu này đôi khi đã lỗi thời vào thời điểm giao dịch hoàn tất.

Trong giải pháp “đơn giản”, bạn có sẵn nhiều công cụ để giúp phát hiện ra những lỗi này. Bạn có thể:

Đính kèm khóa tạm thời cho mọi yêu cầu.

Gửi mỗi yêu cầu ghi hai lần để loại bỏ các cuộc đua.

Tích cực kiểm tra máy chủ để tìm các cuộc đua.

Nhưng nếu bạn đang sử dụng CRDT thì sao? Chà, tất cả những vấn đề này còn khó hơn gấp 100 lần và bạn không có sẵn biện pháp giảm nhẹ nào trong số này. Về mặt định nghĩa, trạng thái chỉ được đảm bảo hội tụ. Vậy làm thế nào để bạn biết liệu có điều gì đó bị phân kỳ tạm thời hay đơn giản là không chính xác? Tất nhiên là bạn không thể. Không hẳn.

… và tôi có thể tiếp tục…

Ở đầu bài viết này, tôi đã nói với bạn rằng mục tiêu của tôi là thuyết phục bạn rằng, trừ khi bạn thực sự cần một cấu trúc liên kết ngang hàng thực sự không có chủ, bạn nên sử dụng giải pháp “đơn giản”. Tại thời điểm này, tôi nghĩ việc viết thêm có lẽ sẽ không giúp tôi thuyết phục được bạn.

Tuy nhiên, tôi muốn để lại cho bạn một điều khác. Và tôi đặc biệt nói chuyện với các tác giả thư viện ở đây: khi thiết kế thư viện, bạn phải bắt đầu với trải nghiệm người dùng cuối mà bạn muốn kích hoạt chứ không phải thuật toán. Đối với chúng tôi, điều đó rất đơn giản. Chúng tôi muốn người dùng có thể cộng tác, chấp nhận tình trạng ngắt kết nối và luôn chạy ở tốc độ 60 khung hình/giây. Chúng tôi muốn người dùng có thể dự đoán điều gì sẽ xảy ra với dữ liệu của họ.

Khi bắt đầu xây dựng công cụ này, chúng tôi cho rằng mọi người đều có những mục tiêu này. Tuy nhiên, ở đầu bên kia của đánh giá này, thật khó để tưởng tượng rằng tất cả chúng ta thực sự đã nghĩ đến điều này. Nếu chúng ta làm vậy, bối cảnh công nghệ trong lĩnh vực này sẽ rất khác.

Phụ lục

Tác giả: antics

#discussion