Sie haben nach NFS gefragt. Diese Art von Code wird wahrscheinlich unter NFS beschädigt, da die Prüfung auf noclobber
zwei separate NFS-Vorgänge umfasst (prüfen, ob eine Datei vorhanden ist, neue Datei erstellen) und zwei Prozesse von zwei separaten NFS-Clients möglicherweise in einen Race-Zustand geraten, in dem beide erfolgreich sind ( beide verifizieren, dass es B.part
noch nicht existiert, und beide erstellen es erfolgreich, wodurch sie sich gegenseitig überschreiben.)
Es gibt nicht wirklich eine generische Überprüfung, ob das Dateisystem, in das Sie schreiben, so etwas wie noclobber
atomar unterstützt oder nicht. Sie könnten den Dateisystemtyp überprüfen, ob es sich um NFS handelt, aber das wäre eine Heuristik und nicht unbedingt eine Garantie. Dateisysteme wie SMB / CIFS (Samba) leiden wahrscheinlich unter denselben Problemen. Dateisysteme, die über FUSE verfügbar gemacht werden, können sich möglicherweise korrekt verhalten oder nicht, dies hängt jedoch hauptsächlich von der Implementierung ab.
Ein möglicherweise besserer Ansatz besteht darin, die Kollision im B.part
Schritt zu vermeiden , indem Sie einen eindeutigen Dateinamen verwenden (in Zusammenarbeit mit anderen Agenten), damit Sie sich nicht darauf verlassen müssen noclobber
. Beispielsweise können Sie als Teil des Dateinamens Ihren Hostnamen, Ihre PID und einen Zeitstempel (+ möglicherweise eine Zufallszahl) angeben. Da auf einem Host zu einem bestimmten Zeitpunkt ein einzelner Prozess unter einer bestimmten PID ausgeführt werden sollte, sollte dies der Fall sein Einzigartigkeit garantieren.
Also eines von:
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.
Oder:
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"
Wenn Sie also eine Race-Bedingung zwischen zwei Agenten haben, fahren beide mit der Operation fort, aber die letzte Operation ist atomar, sodass entweder B mit einer vollständigen Kopie von A existiert oder B nicht existiert.
Sie können die Größe des Rennens reduzieren, indem Sie nach der Kopie und vor der Operation mv
oder erneut prüfen ln
, aber es gibt immer noch eine kleine Rennbedingung. Unabhängig von der Rennbedingung sollte der Inhalt von B jedoch konsistent sein, vorausgesetzt, beide Prozesse versuchen, ihn aus A (oder einer Kopie aus einer gültigen Datei als Ursprung) zu erstellen.
Beachten Sie, dass in der ersten Situation mit mv
, wenn ein Rennen existiert, der letzte Prozess derjenige ist, der gewinnt, da das Umbenennen (2) eine vorhandene Datei atomar ersetzt:
Wenn der neue Pfad bereits vorhanden ist, wird er atomar ersetzt, sodass es keinen Punkt gibt, an dem ein anderer Prozess, der versucht, auf den neuen Pfad zuzugreifen , ihn als fehlend ansieht . [...]
Wenn ein neuer Pfad vorhanden ist, der Vorgang jedoch aus irgendeinem Grund fehlschlägt, wird rename()
garantiert, dass eine Instanz des neuen Pfads vorhanden bleibt .
Es ist also durchaus möglich, dass Prozesse, die B zu diesem Zeitpunkt verbrauchen, während dieses Prozesses unterschiedliche Versionen davon (unterschiedliche Inodes) sehen. Wenn die Autoren nur alle versuchen, denselben Inhalt zu kopieren, und die Leser einfach den Inhalt der Datei verbrauchen, ist dies möglicherweise in Ordnung. Wenn sie unterschiedliche Inodes für Dateien mit demselben Inhalt erhalten, sind sie trotzdem glücklich.
Der zweite Ansatz, bei dem ein Hardlink verwendet wird, sieht besser aus, aber ich erinnere mich, dass ich von vielen gleichzeitigen Clients Experimente mit Hardlinks in einer engen Schleife auf NFS durchgeführt und den Erfolg gezählt habe. Dort schien es immer noch einige Rennbedingungen zu geben, unter denen zwei Clients einen Hardlink herausgaben Operation zur gleichen Zeit, mit dem gleichen Ziel, schienen beide erfolgreich zu sein. (Es ist möglich, dass dieses Verhalten mit der jeweiligen NFS-Server-Implementierung YMMV zusammenhängt.) In jedem Fall ist dies wahrscheinlich die gleiche Art von Race-Bedingung, bei der Sie möglicherweise zwei separate Inodes für dieselbe Datei erhalten, wenn es schwer ist Parallelität zwischen Autoren, um diese Rennbedingungen auszulösen. Wenn Ihre Autoren konsistent sind (beide kopieren A nach B) und Ihre Leser nur den Inhalt konsumieren, könnte dies ausreichen.
Schließlich haben Sie das Sperren erwähnt. Leider fehlt das Sperren stark, zumindest in NFSv3 (nicht sicher über NFSv4, aber ich wette, es ist auch nicht gut). Wenn Sie das Sperren in Betracht ziehen, sollten Sie verschiedene Protokolle für das verteilte Sperren prüfen, möglicherweise außerhalb des Bandes mit dem tatsächliche Dateikopien, aber das ist sowohl störend als auch komplex und anfällig für Probleme wie Deadlocks. Daher würde ich sagen, dass es besser ist, dies zu vermeiden.
Weitere Informationen zum Thema Atomizität in NFS finden Sie im Maildir-Postfachformat , das erstellt wurde, um Sperren zu vermeiden und auch unter NFS zuverlässig zu funktionieren. Dies geschieht, indem überall eindeutige Dateinamen beibehalten werden (sodass Sie am Ende nicht einmal ein endgültiges B erhalten).
Vielleicht etwas interessanter für Ihren speziellen Fall, erweitert das Maildir ++ - Format Maildir, um Unterstützung für das Postfachkontingent hinzuzufügen, und zwar durch atomares Aktualisieren einer Datei mit einem festen Namen innerhalb des Postfachs (so dass dies möglicherweise näher an Ihrem B. liegt). Ich denke, Maildir ++ versucht es Anhängen, was unter NFS nicht wirklich sicher ist, aber es gibt einen Neuberechnungsansatz, der ein ähnliches Verfahren verwendet und als atomarer Ersatz gültig ist.
Hoffentlich sind all diese Hinweise nützlich!