Họ đang đo gì
Bạn có nhận ra đây là quyết định sản phẩm trước khi là quyết định kỹ thuật không.
Trả lời ngắn~30 giây
Từ rẻ tới đắt: last-write-wins (đơn giản, và âm thầm mất dữ liệu); khoá bi quan — ai mở trước thì người sau chỉ đọc (rõ ràng nhưng khó chịu, và cần xử lý khoá bị bỏ quên); phát hiện xung đột bằng version rồi bắt người thua xử lý (đây là mặc định hợp lý cho phần lớn ứng dụng); và cuối cùng là hợp nhất tự động bằng CRDT hoặc OT — cho trải nghiệm kiểu Google Docs, và tốn kém hơn nhiều so với vẻ ngoài.
Giải thích sâu
Bước đầu tiên nên là hỏi mức độ chi tiết của xung đột. Rất nhiều “xung đột” biến mất nếu bạn theo dõi theo TRƯỜNG thay vì theo cả tài liệu: hai người sửa hai trường khác nhau trong cùng một form không hề xung đột, nhưng một API gửi cả object lên sẽ biến nó thành xung đột. Chỉ gửi những trường đã đổi (JSON Patch, hoặc chỉ các trường dirty) loại bỏ phần lớn trường hợp mà không cần cơ chế nào phức tạp.
Nếu chọn phát hiện xung đột, phần khó không phải là phát hiện mà là GIAO DIỆN xử lý nó. Trả về 409 rồi hiện “có người khác vừa sửa, tải lại đi” là cách chắc chắn làm mất công người dùng vừa gõ. Tối thiểu phải hiện được sự khác biệt và giữ lại bản nháp của họ. Đây là chỗ nhiều đội cài đặt đúng phần backend rồi vẫn có sản phẩm khó chịu.
Câu hỏi tiếp theo họ sẽ hỏi
?CRDT có phải luôn tốt hơn không?
Không. Nó hợp nhất được mà không cần server phân xử, nhưng metadata lớn dần theo lịch sử chỉnh sửa, và “hợp nhất được” không có nghĩa là kết quả hợp lý về mặt nghiệp vụ — hai người cùng đặt trạng thái đơn hàng thành hai giá trị khác nhau sẽ hợp nhất thành một giá trị mà không ai muốn.
Trả lời thế này là mất điểm
- Last-write-wins mà không nói rằng nó mất dữ liệu. Nó là lựa chọn hợp lệ; giấu hậu quả thì không.
Nguồn
- Martin Kleppmann, Designing Data-Intensive Applications, ch. 5 — xử lý ghi đồng thời
- MDN — `If-Match` và ETag cho cập nhật có điều kiện