GNU/Linux >> Tutoriels Linux >  >> Linux

Comment copier un fichier de manière transactionnelle ?

rsync fait ce travail. Un fichier temporaire est O_EXCL créé par défaut (uniquement désactivé si vous utilisez --inplace ) puis renamed sur le fichier cible. Utilisez --ignore-existing pour ne pas écraser B s'il existe.

En pratique, je n'ai jamais rencontré de problèmes avec cela sur les montages ext4, zfs ou même NFS.


Ne vous inquiétez pas, noclobber est une fonctionnalité standard.


Vous avez posé une question sur NFS. Ce type de code est susceptible de casser sous NFS, car la vérification de noclobber implique deux opérations NFS distinctes (vérifier si le fichier existe, créer un nouveau fichier) et deux processus de deux clients NFS distincts peuvent entrer dans une condition de concurrence où les deux réussissent (les deux vérifient que B.part n'existe pas encore, alors les deux procèdent à sa création avec succès, par conséquent ils s'écrasent mutuellement.)

Il n'y a pas vraiment à faire une vérification générique pour savoir si le système de fichiers sur lequel vous écrivez prendra en charge quelque chose comme noclobber atomiquement ou non. Vous pouvez vérifier le type de système de fichiers, s'il s'agit de NFS, mais ce serait une heuristique et pas nécessairement une garantie. Les systèmes de fichiers comme SMB/CIFS (Samba) sont susceptibles de souffrir des mêmes problèmes. Les systèmes de fichiers exposés via FUSE peuvent ou non se comporter correctement, mais cela dépend principalement de l'implémentation.

Une meilleure approche consiste peut-être à éviter la collision dans le B.part étape, en utilisant un nom de fichier unique (grâce à la coopération avec d'autres agents) afin que vous n'ayez pas besoin de dépendre de noclobber . Par exemple, vous pouvez inclure, dans le cadre du nom de fichier, votre nom d'hôte, PID et un horodatage (+ éventuellement un nombre aléatoire.) Puisqu'il devrait y avoir un seul processus en cours d'exécution sous un PID spécifique sur un hôte à un moment donné, cela devrait garantir l'unicité.

Donc, l'un ou l'autre :

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.

Ou :

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"

Donc, si vous avez une condition de concurrence entre deux agents, ils poursuivront tous les deux l'opération, mais la dernière opération sera atomique, donc soit B existe avec une copie complète de A, soit B n'existe pas.

Vous pouvez réduire la taille de la course en vérifiant à nouveau après la copie et avant le mv ou ln opération, mais il y a toujours une petite condition de course là-bas. Mais, quelle que soit la condition de concurrence, le contenu de B doit être cohérent, en supposant que les deux processus essaient de le créer à partir de A (ou une copie d'un fichier valide comme origine.)

Notez que dans la première situation avec mv , lorsqu'une course existe, le dernier processus est celui qui gagne, puisque rename(2) remplacera atomiquement un fichier existant :

Si nouveauchemin existe déjà, il sera remplacé de manière atomique, de sorte qu'il n'y ait aucun point auquel un autre processus tente d'accéder à nouveauchemin le trouvera manquant. [...]

Si nouveauchemin existe mais l'opération échoue pour une raison quelconque, rename() garantit de laisser une instance de newpath en place.

Il est donc tout à fait possible que les processus consommant B à ce moment-là en voient différentes versions (différents inodes) au cours de ce processus. Si les auteurs essaient tous de copier le même contenu et que les lecteurs consomment simplement le contenu du fichier, cela peut convenir, s'ils obtiennent des inodes différents pour des fichiers avec le même contenu, ils seront tout de même heureux.

La deuxième approche utilisant un lien dur regarde mieux, mais je me souviens avoir fait des expériences avec des liens durs dans une boucle serrée sur NFS à partir de nombreux clients simultanés et compter le succès et il semblait encore y avoir des conditions de concurrence là-bas, où il semblait que si deux clients émettaient une opération de lien dur en même temps, avec le même destination, les deux semblaient réussir. (Il est possible que ce comportement soit lié à l'implémentation particulière du serveur NFS, YMMV.) Dans tous les cas, c'est probablement le même type de condition de concurrence, où vous pourriez finir par obtenir deux inodes distincts pour le même fichier dans les cas où il y a beaucoup simultanéité entre écrivains pour déclencher ces conditions de concurrence. Si vos rédacteurs sont cohérents (tous deux copiant A vers B) et que vos lecteurs ne consomment que le contenu, cela pourrait suffire.

Enfin, vous avez mentionné le verrouillage. Malheureusement, le verrouillage fait cruellement défaut, du moins dans NFSv3 (je ne suis pas sûr de NFSv4, mais je parie que ce n'est pas bon non plus.) Si vous envisagez de verrouiller, vous devriez examiner différents protocoles de verrouillage distribué, éventuellement hors bande avec le des copies de fichiers réelles, mais c'est à la fois perturbateur, complexe et sujet à des problèmes tels que des blocages, donc je dirais qu'il vaut mieux l'éviter.

Pour plus d'informations sur le sujet de l'atomicité sur NFS, vous voudrez peut-être lire sur le format de boîte aux lettres Maildir, qui a été créé pour éviter les verrous et fonctionner de manière fiable même sur NFS. Pour ce faire, il conserve des noms de fichiers uniques partout (afin que vous n'obteniez même pas un B final à la fin.)

Peut-être un peu plus intéressant pour votre cas particulier, le format Maildir ++ étend Maildir pour ajouter la prise en charge du quota de boîte aux lettres et le fait en mettant à jour de manière atomique un fichier avec un nom fixe à l'intérieur de la boîte aux lettres (ce qui pourrait être plus proche de votre B.) Je pense que Maildir ++ essaie à ajouter, ce qui n'est pas vraiment sûr sur NFS, mais il existe une approche de recalcul qui utilise une procédure similaire à celle-ci et qui est valide en tant que remplacement atomique.

Espérons que tous ces pointeurs seront utiles !


Linux
  1. Comment copier récursivement des fichiers par extension de fichier ? ?

  2. Comment renommer un fichier sous Linux ?

  3. Comment rendre le fichier clairsemé?

  4. Comment grep \n dans le fichier

  5. Comment trier un fichier sur place

Comment créer un lien symbolique vers un fichier sous Linux

Comment copier des fichiers avec une extension de fichier spécifique de manière récursive

Comment copier des fichiers et des répertoires dans un terminal Linux

Comment copier un répertoire sous Linux

Comment faire écho dans le fichier

Comment copier un fichier dans le gestionnaire de fichiers