rsync
melakukan pekerjaan ini. File sementara adalah O_EXCL
dibuat secara default (hanya dinonaktifkan jika Anda menggunakan --inplace
) lalu renamed
atas file target. Gunakan --ignore-existing
untuk tidak menimpa B jika ada.
Dalam praktiknya, saya tidak pernah mengalami masalah dengan ini pada mount ext4, zfs, atau bahkan NFS.
Jangan khawatir, noclobber
adalah fitur standar.
Anda bertanya tentang NFS. Kode semacam ini kemungkinan akan rusak di bawah NFS, karena pemeriksaan untuk noclobber
melibatkan dua operasi NFS yang terpisah (periksa apakah file ada, buat file baru) dan dua proses dari dua klien NFS yang terpisah dapat masuk ke kondisi balapan di mana keduanya berhasil (keduanya memverifikasi bahwa B.part
belum ada, lalu keduanya berhasil membuatnya, akibatnya mereka saling menimpa.)
Tidak perlu melakukan pemeriksaan umum apakah sistem file yang Anda tulis akan mendukung sesuatu seperti noclobber
secara atomik atau tidak. Anda dapat memeriksa jenis sistem file, apakah itu NFS, tetapi itu akan menjadi heuristik dan belum tentu merupakan jaminan. Sistem file seperti SMB/CIFS (Samba) cenderung mengalami masalah yang sama. Sistem file yang diekspos melalui FUSE mungkin atau mungkin tidak berperilaku dengan benar, tetapi sebagian besar bergantung pada penerapannya.
Pendekatan yang mungkin lebih baik adalah menghindari tabrakan di B.part
langkahnya, dengan menggunakan nama file yang unik (melalui kerjasama dengan agen lain) sehingga anda tidak perlu bergantung pada noclobber
. Misalnya, Anda dapat menyertakan, sebagai bagian dari nama file, nama host, PID, dan stempel waktu (+mungkin nomor acak.) Karena harus ada satu proses yang berjalan di bawah PID tertentu di host pada waktu tertentu, ini seharusnya menjamin keunikan.
Jadi salah satu dari:
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.
Atau:
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"
Jadi, jika Anda memiliki kondisi balapan antara dua agen, keduanya akan melanjutkan operasi, tetapi operasi terakhir akan bersifat atomik, jadi B ada dengan salinan lengkap A, atau B tidak ada.
Anda dapat mengurangi ukuran balapan dengan mencentang lagi setelah salinan dan sebelum mv
atau ln
operasi, tapi masih ada kondisi balapan kecil di sana. Namun, terlepas dari kondisi balapan, konten B harus konsisten, dengan asumsi kedua proses mencoba membuatnya dari A (atau salinan dari file yang valid sebagai asal.)
Perhatikan bahwa dalam situasi pertama dengan mv
, ketika perlombaan ada, proses terakhir adalah orang yang menang, karena rename(2) secara atomis akan menggantikan file yang ada:
Jika jalur baru sudah ada, itu akan diganti secara atom, sehingga tidak ada titik di mana proses lain mencoba mengakses jalur baru akan menemukannya hilang. [...]
Jika jalur baru ada tetapi operasi gagal karena beberapa alasan,
rename()
jaminan untuk meninggalkan contoh newpath di tempat.
Jadi, sangat mungkin proses yang menggunakan B pada saat itu mungkin melihat versi yang berbeda (inode berbeda) selama proses ini. Jika penulis hanya mencoba menyalin konten yang sama, dan pembaca hanya mengonsumsi konten file, itu mungkin baik-baik saja, jika mereka mendapatkan inode yang berbeda untuk file dengan konten yang sama, mereka akan senang sama saja.
Pendekatan kedua menggunakan hard link looks lebih baik, tetapi saya ingat membuat percobaan dengan hardlink dalam loop ketat pada NFS dari banyak klien bersamaan dan menghitung keberhasilan dan tampaknya masih ada beberapa kondisi balapan di sana, di mana tampaknya jika dua klien mengeluarkan operasi hardlink pada saat yang sama, dengan tujuan yang sama, keduanya tampaknya berhasil. (Ada kemungkinan bahwa perilaku ini terkait dengan implementasi server NFS tertentu, YMMV.) Bagaimanapun, itu mungkin jenis kondisi balapan yang sama, di mana Anda mungkin akan mendapatkan dua inode terpisah untuk file yang sama jika ada banyak konkurensi antara penulis untuk memicu kondisi ras ini. Jika penulis Anda konsisten (keduanya menyalin A ke B), dan pembaca Anda hanya membaca kontennya, itu mungkin sudah cukup.
Terakhir, Anda menyebutkan penguncian. Sayangnya penguncian sangat kurang, setidaknya di NFSv3 (tidak yakin tentang NFSv4, tapi saya yakin itu juga tidak baik.) Jika Anda mempertimbangkan untuk mengunci, Anda harus melihat ke protokol yang berbeda untuk penguncian terdistribusi, mungkin di luar jalur dengan salinan file sebenarnya, tetapi itu mengganggu, rumit, dan rentan terhadap masalah seperti kebuntuan, jadi menurut saya lebih baik dihindari.
Untuk latar belakang lebih lanjut tentang masalah atomisitas pada NFS, Anda mungkin ingin membaca tentang format kotak surat Maildir, yang dibuat untuk menghindari penguncian dan bekerja dengan andal bahkan pada NFS. Itu dilakukan dengan menyimpan nama file unik di mana-mana (sehingga Anda bahkan tidak mendapatkan B terakhir di bagian akhir.)
Mungkin agak lebih menarik untuk kasus khusus Anda, format Maildir++ memperluas Maildir untuk menambahkan dukungan untuk kuota kotak surat dan melakukannya dengan memperbarui file secara atomik dengan nama tetap di dalam kotak surat (sehingga mungkin lebih dekat dengan B Anda.) Saya pikir Maildir++ mencoba untuk menambahkan, yang sebenarnya tidak aman di NFS, tetapi ada pendekatan penghitungan ulang yang menggunakan prosedur yang mirip dengan ini dan valid sebagai penggantian atom.
Semoga semua petunjuk ini bermanfaat!