Bạn đã hỏi về NFS. Loại mã này có khả năng bị phá vỡ theo NFS, vì kiểm tra noclobber
liên quan đến hai hoạt động NFS riêng biệt (kiểm tra nếu tệp tồn tại, tạo tệp mới) và hai quy trình từ hai máy khách NFS riêng biệt có thể rơi vào tình trạng chạy đua khi cả hai đều thành công ( cả hai đều xác minh rằng B.part
chưa tồn tại, sau đó cả hai tiến hành tạo thành công, kết quả là họ ghi đè lên nhau.)
Không thực sự phải kiểm tra chung xem liệu hệ thống tập tin bạn đang viết có hỗ trợ một cái gì đó như noclobber
nguyên tử hay không. Bạn có thể kiểm tra loại hệ thống tập tin, cho dù đó là NFS, nhưng đó sẽ là một heuristic và không nhất thiết phải là một sự đảm bảo. Các hệ thống tập tin như SMB / CIFS (Samba) có thể gặp phải vấn đề tương tự. Các hệ thống tập tin phơi bày thông qua FUSE có thể có hoặc không hoạt động chính xác, nhưng điều đó chủ yếu phụ thuộc vào việc thực hiện.
Một cách tiếp cận có thể tốt hơn là tránh va chạm trong B.part
bước này, bằng cách sử dụng tên tệp duy nhất (thông qua hợp tác với các đại lý khác) để bạn không cần phải phụ thuộc noclobber
. Chẳng hạn, bạn có thể bao gồm, như một phần của tên tệp, tên máy chủ của bạn, PID và dấu thời gian (+ có thể là một số ngẫu nhiên.) Vì sẽ có một quy trình duy nhất chạy theo một PID cụ thể tại một máy chủ tại bất kỳ thời điểm nào, điều này nên đảm bảo tính độc đáo.
Vì vậy, một trong những:
test -f B && continue # skip already existing
unique=$(hostname).$$.$(date +%s).$RANDOM
cp A B.part."$unique"
# Maybe check for existance of B again, remove
# the temporary file and bail out in that case.
mv B.part."$unique" B
# mv (rename) should always succeed, overwrite a
# previously copied B if one exists.
Hoặc là:
test -f B && continue # skip already existing
unique=$(hostname).$$.$(date +%s).$RANDOM
cp A B.part."$unique"
if ln B.part."$unique" B ; then
echo "Success creating B"
else
echo "Failed creating B, already existed"
fi
# Both cases require cleanup.
rm B.part."$unique"
Vì vậy, nếu bạn có điều kiện chạy đua giữa hai tác nhân, cả hai sẽ tiến hành hoạt động, nhưng thao tác cuối cùng sẽ là nguyên tử, do đó, B tồn tại với bản sao đầy đủ của A hoặc B không tồn tại.
Bạn có thể giảm kích thước của cuộc đua bằng cách kiểm tra lại sau khi sao chép và trước khi mv
hoặc ln
hoạt động, nhưng vẫn còn một điều kiện cuộc đua nhỏ ở đó. Nhưng, bất kể điều kiện cuộc đua, nội dung của B phải nhất quán, giả sử cả hai quá trình đang cố gắng tạo nó từ A (hoặc bản sao từ tệp hợp lệ làm gốc.)
Lưu ý rằng trong tình huống đầu tiên mv
, khi một chủng tộc tồn tại, quá trình cuối cùng là người chiến thắng, vì đổi tên (2) về cơ bản sẽ thay thế một tệp hiện có:
Nếu newpath đã tồn tại, nó sẽ được thay thế về mặt nguyên tử, do đó không có điểm nào mà quá trình khác cố gắng truy cập newpath sẽ thấy nó bị thiếu. [...]
Nếu newpath tồn tại nhưng hoạt động không thành công vì một số lý do, rename()
đảm bảo để lại một trường hợp của newpath tại chỗ.
Vì vậy, rất có thể các quy trình tiêu thụ B tại thời điểm đó có thể thấy các phiên bản khác nhau của nó (các nút khác nhau) trong quá trình này. Nếu người viết chỉ cố gắng sao chép cùng một nội dung và người đọc chỉ đơn giản là tiêu thụ nội dung của tệp, điều đó có thể ổn, nếu họ nhận được các nút khác nhau cho các tệp có cùng nội dung, họ sẽ rất vui.
Cách tiếp cận thứ hai sử dụng liên kết cứng có vẻ tốt hơn, nhưng tôi nhớ lại việc thực hiện các thử nghiệm với các liên kết cứng trong một vòng lặp chặt chẽ trên NFS từ nhiều khách hàng đồng thời và tính thành công và dường như vẫn còn một số điều kiện chủng tộc ở đó, dường như nếu hai khách hàng đưa ra một liên kết cứng hoạt động cùng một lúc, với cùng một đích, cả hai dường như thành công. (Có thể hành vi này có liên quan đến việc triển khai máy chủ NFS cụ thể, YMMV.) Trong mọi trường hợp, đó có thể là cùng một loại điều kiện chủng tộc, trong đó bạn có thể sẽ nhận được hai nút riêng biệt cho cùng một tệp trong trường hợp nặng đồng thời giữa các nhà văn để kích hoạt các điều kiện chủng tộc. Nếu các nhà văn của bạn nhất quán (cả sao chép A đến B) và độc giả của bạn chỉ tiêu thụ nội dung, điều đó có thể là đủ.
Cuối cùng, bạn đã đề cập đến khóa. Thật không may, khóa rất thiếu, ít nhất là trong NFSv3 (không chắc chắn về NFSv4, nhưng tôi cá là nó cũng không tốt.) Nếu bạn đang xem xét khóa, bạn nên xem xét các giao thức khác nhau để khóa phân tán, có thể nằm ngoài băng tần với bản sao tệp thực tế, nhưng cả hai đều gây rối, phức tạp và dễ xảy ra các vấn đề như bế tắc, vì vậy tôi nên nói rằng tốt hơn nên tránh.
Để có thêm thông tin về chủ đề nguyên tử trên NFS, bạn có thể muốn đọc trên định dạng hộp thư Maildir , được tạo để tránh khóa và hoạt động đáng tin cậy ngay cả trên NFS. Nó làm như vậy bằng cách giữ tên tệp duy nhất ở mọi nơi (vì vậy bạn thậm chí không nhận được B cuối cùng ở cuối.)
Có lẽ hơi thú vị hơn với trường hợp cụ thể của bạn, định dạng Maildir ++ mở rộng Maildir để thêm hỗ trợ cho hạn ngạch hộp thư và làm như vậy bằng cách cập nhật nguyên bản một tệp có tên cố định bên trong hộp thư (vì vậy có thể gần với B. của bạn hơn) để chắp thêm, điều này không thực sự an toàn trên NFS, nhưng có một cách tiếp cận tính toán lại sử dụng một quy trình tương tự như điều này và nó có giá trị như một sự thay thế nguyên tử.
Hy vọng tất cả những gợi ý này sẽ hữu ích!