Zapytałeś o NFS. Ten rodzaj kodu najprawdopodobniej noclobber
ulegnie awarii w systemie plików NFS, ponieważ sprawdzenie dotyczy dwóch osobnych operacji NFS (sprawdź, czy plik istnieje, utwórz nowy plik), a dwa procesy z dwóch oddzielnych klientów NFS mogą przejść w stan wyścigu, w którym oba się powiedzie ( oba sprawdzają, że B.part
jeszcze nie istnieje, a następnie oba z powodzeniem go tworzą, w wyniku czego się wzajemnie nadpisują).
Naprawdę nie ma potrzeby ogólnego sprawdzania, czy system plików, w którym piszesz, będzie obsługiwał coś takiego jak noclobber
atomowy, czy nie. Możesz sprawdzić typ systemu plików, czy to NFS, ale byłby to heurystyka i niekoniecznie gwarancja. Systemy plików takie jak SMB / CIFS (Samba) prawdopodobnie cierpią z powodu tych samych problemów. Systemy plików narażone przez FUSE mogą, ale nie muszą zachowywać się poprawnie, ale to zależy głównie od implementacji.
Prawdopodobnie lepszym rozwiązaniem jest uniknięcie kolizji na B.part
etapie, poprzez użycie unikalnej nazwy pliku (dzięki współpracy z innymi agentami), dzięki czemu nie trzeba polegać noclobber
. Na przykład możesz dołączyć jako część nazwy pliku nazwę hosta, identyfikator PID i znacznik czasu (+ ewentualnie liczbę losową). Ponieważ w danym momencie powinien istnieć pojedynczy proces o określonym identyfikatorze PID na hoście, powinno to być gwarantuje wyjątkowość.
Więc jedno z:
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.
Lub:
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"
Więc jeśli masz warunki wyścigu między dwoma agentami, obaj będą kontynuować operację, ale ostatnia operacja będzie atomowa, więc albo B istnieje z pełną kopią A, albo B nie istnieje.
Możesz zmniejszyć rozmiar wyścigu, sprawdzając ponownie po kopii i przed operacją mv
lub ln
, ale nadal istnieje mały warunek wyścigu. Jednak niezależnie od warunków wyścigu zawartość B powinna być spójna, zakładając, że oba procesy próbują utworzyć ją z A (lub kopii z prawidłowego pliku jako źródła).
Zauważ, że w pierwszej sytuacji mv
, gdy wyścig istnieje, ostatni wygrywa ten proces, ponieważ zmiana nazwy (2) atomowo zastąpi istniejący plik:
Jeśli newpath już istnieje, zostanie on zastąpiony atomowo, tak że nie będzie punktu, w którym inny proces próbujący uzyskać dostęp do newpath nie uzna go za brakujący. [...]
Jeśli newpath istnieje, ale operacja z jakiegoś powodu nie powiedzie się, rename()
gwarantuje pozostawienie instancji newpath na miejscu.
Jest więc całkiem możliwe, że procesy zużywające B w tym czasie mogą zobaczyć różne jego wersje (różne i-węzły) podczas tego procesu. Jeśli wszyscy autorzy próbują po prostu skopiować tę samą zawartość, a czytelnicy po prostu zużywają zawartość pliku, to może być w porządku, jeśli otrzymają różne i-węzły dla plików o tej samej zawartości, będą zadowoleni tak samo.
Drugie podejście z wykorzystaniem twardego łącza wygląda lepiej, ale przypominam sobie przeprowadzanie eksperymentów z linkami twardymi w ciasnej pętli na NFS od wielu równoległych klientów i liczenie sukcesu, i nadal zdawały się istnieć pewne warunki wyścigowe, gdzie wydawało się, że dwóch klientów wydało hardlink Operacja w tym samym czasie, z tym samym miejscem docelowym, wydawała się udana. (Możliwe, że to zachowanie było związane z konkretną implementacją serwera NFS, YMMV.) W każdym razie jest to prawdopodobnie ten sam rodzaj wyścigu, w którym możesz uzyskać dwa osobne i-węzły dla tego samego pliku, w przypadkach, gdy jest ciężki współbieżność między pisarzami, aby uruchomić te warunki wyścigu. Jeśli twoi autorzy są konsekwentni (oba kopiują od A do B), a czytelnicy zużywają tylko zawartość, może to wystarczyć.
Wreszcie wspomniałeś o blokowaniu. Niestety blokowanie jest poważnie brakuje, przynajmniej w NFSv3 (nie jestem pewien co do NFSv4, ale założę się, że to też nie jest dobre). Jeśli zastanawiasz się nad blokowaniem, powinieneś rozważyć różne protokoły blokowania rozproszonego, być może poza pasmem z rzeczywiste kopie plików, ale jest to zarówno destrukcyjne, złożone, jak i podatne na problemy, takie jak zakleszczenia, więc powiedziałbym, że lepiej tego unikać.
Aby uzyskać więcej informacji na temat atomowości na NFS, możesz przeczytać w formacie skrzynki pocztowej Maildir , który został stworzony w celu uniknięcia blokad i niezawodnej pracy nawet w NFS. Robi to, utrzymując wszędzie unikalne nazwy plików (dzięki czemu nie dostajesz nawet końcowego B na końcu).
Być może nieco bardziej interesujący dla twojego konkretnego przypadku, format Maildir ++ rozszerza Maildir o obsługę limitu skrzynki pocztowej i robi to poprzez atomową aktualizację pliku o stałej nazwie w skrzynce pocztowej (aby być bliżej twojego B.) Myślę, że Maildir ++ próbuje do dołączania, co nie jest tak naprawdę bezpieczne w systemie plików NFS, ale istnieje metoda przeliczania, która wykorzystuje procedurę podobną do tej i jest ważna jako zamiana atomowa.
Mam nadzieję, że wszystkie te wskaźniki będą przydatne!