Ý chính: Khóa phân tán không thể tự chứng minh một tiến trình vẫn còn quyền sở hữu sau khi bị dừng hoặc mất kết nối.
Thuật ngữ trong bài: Lease: quyền sở hữu có thời hạn; fencing token: số tăng dần để chặn chủ sở hữu cũ; critical section: đoạn xử lý chỉ cho phép một tiến trình thay đổi.
Bài toán thực tế
Một tiến trình có thể bị tạm dừng lâu đến mức Lease hết hạn. Tiến trình khác nhận quyền xử lý, nhưng tiến trình cũ sau đó vẫn chạy tiếp. Tài nguyên đích phải tự từ chối những lần ghi mang quyền sở hữu đã cũ.
Điểm dễ bị bỏ qua
Lease có thể hết hạn trong lúc tiến trình bị dừng bởi GC, mất mạng hoặc tạm treo. Khi tiến trình chạy lại, nó không biết một chủ sở hữu mới đã tiếp quản và vẫn có thể ghi dữ liệu cũ.
Fencing Token giải quyết phần còn thiếu: tài nguyên đích chỉ chấp nhận số thứ tự quyền sở hữu mới hơn, nên tiến trình cũ không thể ghi dù vẫn tiếp tục chạy.
Luồng xử lý
Yêu cầu lease→Coordinator
Cấp mã thứ tự quyền sở hữu→Worker
Xử lý hữu hạn→Tài nguyên
Từ chối token cũ
Thiết kế và triển khai
Ưu tiên partition trách nhiệm sở hữu
Gán aggregate hoặc shard cho một worker và rebalance rõ ràng để giảm số khóa.
Dùng monotonic fencing
Mỗi lần lấy lease nhận token lớn hơn; kho lưu trữ so sánh atomically với token cuối.
Giới hạn đoạn xử lý độc quyền
Đặt thời hạn nhỏ hơn lease duration và ngừng nhận sub-work mới khi renewal không chắc chắn.
Đi sâu vào thiết kế
Thời gian không phải trách nhiệm sở hữu
TTL giới hạn thời gian người khác chờ, nhưng clock không thể thu hồi code đang chạy. Kho lưu trữ-side token bịt khoảng trống đó.
Khóa không nên che product conflict
Hai người sửa cùng document có thể cần optimistic phiên bản và conflict UX, không phải hạ tầng khóa.
Cách triển khai
- Loại bỏ nhu cầu sở hữu chung bằng partition hoặc idempotency trước khi thêm khóa.
- Dùng lease có thời hạn kèm Fencing Token tăng dần cho mỗi lần cấp quyền.
- Buộc tài nguyên được bảo vệ từ chối mọi lần ghi có Fencing Token nhỏ hơn giá trị cuối đã nhận.
Mã minh họa: Cập nhật được bảo vệ bằng Fencing Token
lease = coordinator.acquire(resourceId, ttl = 10s)
result = compute(deadline = lease.expiresAt - 2s)
UPDATE resource
SET value = :result, fence = :token
WHERE id = :id AND fence < :tokenNhận khóa thành công nhưng ghi không kèm Fencing Token vẫn chưa an toàn, vì tiến trình mất quyền có thể tỉnh lại và tiếp tục chạy.
Rủi ro cần tính trước
- Một lần dừng toàn bộ tiến trình kéo dài hơn thời hạn lease.
- Các node điều phối mất quorum và ứng dụng khách không thống nhất ai đang sở hữu quyền.
- Lời gọi dịch vụ bên ngoài hoàn tất sau khi lease đã được chuyển cho tiến trình khác.
Nên theo dõi gì?
| Tín hiệu | Điều tín hiệu cho biết |
|---|---|
| Lỗi nhận và gia hạn lease | Cho biết mức tranh chấp cùng sức khỏe của hệ thống điều phối. |
| Số Fencing Token cũ bị từ chối | Chứng minh tiến trình mất quyền vẫn tiếp tục chạy nhưng đã bị chặn đúng cách. |
| Thời lượng đoạn xử lý độc quyền | Phát hiện công việc đã dài hơn giả định an toàn của lease. |
Cách kiểm chứng
- Tạm dừng khóa holder quá TTL rồi cho chạy tiếp.
- Partition holder khỏi coordinator nhưng vẫn nối được kho lưu trữ.
- Chạy nhiều contender và chứng minh chỉ token tăng dần commit.
Đưa vào production từng bước
Đo đoạn xử lý cần độc quyền và thử idempotency hoặc khóa lạc quan trước. Chỉ thêm Fencing Token cho thao tác thật sự cần một chủ sở hữu, đồng thời giữ đường xử lý tuần tự an toàn để dùng khi có sự cố.
Checklist trước khi vận hành
- Giả định về clock được ghi rõ và giảm tối đa.
- Renewal lỗi phải dừng nhận việc mới trước khi lease hết hạn.
- Vận hành dài có mốc lưu an toàn thay vì một đoạn xử lý độc quyền quá lớn.
Điều cần nhớ
Khóa phân tán chỉ điều phối các tiến trình; Fencing Token tại tài nguyên đích mới bảo vệ tính đúng đắn khi quyền sở hữu đã đổi.
