web-dev-qa-db-fra.com

Comportement de rsync avec un fichier en cours d’écriture?

Si Apache est en train d'écrire un fichier volumineux et qu'un travail cron rsync s'exécute sur ce fichier, rsync tente-t-il de copier le fichier?

Exemple

  • Apache-1: Le fichier volumineux est-il écrit dans /var/www.
  • Apache-2: Clone d'Apache-1. Toutes les cinq minutes, cron exécute rsync pour obtenir la synchronisation de /var/www.
11
Louis

Si Apache écrit un fichier quelconque à un endroit et ne l'a pas encore terminée , puis rsync démarre, rsync copie ce qui se trouve là. .

Cela signifie que si Apache traite un fichier de 5 Mo, seulement 2 Mo sont écrits et rsync entre en jeu, le fichier partiel de 2 Mo sera copié. Donc, ce fichier semblerait être "corrompu" sur le serveur de destination.

En fonction de la taille des fichiers que vous utilisez, vous pouvez utiliser l'option --inplace dans rsync pour effectuer les opérations suivantes:

Cette option modifie la manière dont rsync transfère un fichier lorsque les données du fichier doivent être mises à jour: au lieu de la méthode par défaut consistant à créer une nouvelle copie du fichier et à le déplacer à la fin, rsync écrit les données mises à jour directement dans la destination. fichier.

L'avantage de ceci est que si un fichier de 5 Mo a seulement 2 Mo copiés lors de la première utilisation, la prochaine exécution sera prise à 2 Mo et continuera à copier le fichier jusqu'à ce que les 5 Mo complets soient en place.

L'inconvénient est que cela pourrait créer une situation dans laquelle une personne accède au serveur Web pendant la copie d'un fichier et voit alors un fichier partiel. À mon avis, rsync fonctionne mieux avec son comportement par défaut consistant à mettre en cache un fichier "invisible", puis à le mettre immédiatement en place. Cependant, --inplace convient aux scénarios dans lesquels des fichiers volumineux et des contraintes de bande passante peuvent empêcher un fichier volumineux d'être facilement copié depuis le début.

Cela dit, vous déclarez ceci; L'accent est à moi:

Toutes les cinq minutes a cron run rsync…

Donc, je suppose que vous avez un script bash en place pour gérer ce travail cron? Eh bien, le problème est que rsync est suffisamment intelligent pour ne copier que les fichiers à copier. Et si vous avez un script qui s'exécute toutes les 5 minutes, il semble que vous essayez d'éviter les étapes rsync les unes sur les autres s'il accélère. En d'autres termes, si vous l'exécutiez toutes les minutes, un ou plusieurs processus rsync risquaient de s'exécuter en raison de la taille du fichier ou de la vitesse du réseau, et le processus suivant serait simplement en concurrence avec celui-ci. une condition de course.

Une façon d'éviter cela consiste à envelopper toute votre commande rsync dans un script bash qui recherche un verrou de fichier; Vous trouverez ci-dessous un framework de script bash que j'utilise dans de tels cas.

Notez que certaines personnes recommanderont d’utiliser flock mais comme flock n’est pas installé sur certains systèmes que j’utilise, et que je saute souvent entre Ubuntu (qui l’a) et Mac OS X (ce qui n’est pas le cas), j’utilise ce simple framework sans réel problème:

LOCK_NAME="MY_GREAT_BASH_SCRIPT"
LOCK_DIR='/tmp/'${LOCK_NAME}.lock
PID_FILE=${LOCK_DIR}'/'${LOCK_NAME}'.pid'

if mkdir ${LOCK_DIR} 2>/dev/null; then
  # If the ${LOCK_DIR} doesn't exist, then start working & store the ${PID_FILE}
  echo $$ > ${PID_FILE}

  echo "Hello world!"

  rm -rf ${LOCK_DIR}
  exit
else
  if [ -f ${PID_FILE} ] && kill -0 $(cat ${PID_FILE}) 2>/dev/null; then
    # Confirm that the process file exists & a process
    # with that PID is truly running.
    echo "Running [PID "$(cat ${PID_FILE})"]" >&2
    exit
  else
    # If the process is not running, yet there is a PID file--like in the case
    # of a crash or sudden reboot--then get rid of the ${LOCK_DIR}
    rm -rf ${LOCK_DIR}
    exit
  fi
fi

L'idée est que le noyau général — où j'ai echo "Hello world!"— est le cœur de votre script. Le reste est essentiellement un mécanisme/logique de verrouillage basé sur mkdir. Une bonne explication du concept est dans cette réponse :

mkdir crée un répertoire s'il n'existe pas encore et, le cas échéant, il définit un code de sortie. Plus important encore, il fait tout cela en une seule action atomique, ce qui le rend parfait pour ce scénario.

Donc, dans le cas de votre processus rsync, je vous recommanderais d’utiliser ce script en modifiant simplement la commande echo en votre commande rsync. Changez également le LOCK_NAME en quelque chose comme RSYNC_PROCESS et vous pourrez continuer.

Désormais, avec votre rsync encapsulé dans ce script, vous pouvez configurer le travail cron pour qu'il s'exécute toutes les minutes sans risque de condition de concurrence extrême lorsque deux processus rsync ou plus se battent pour faire la même chose. Cela vous permettra d'augmenter la vitesse ou les mises à jour rsync, ce qui n'éliminera pas le problème du transfert de fichiers partiels, mais contribuera à accélérer le processus global afin que le fichier complet puisse être copié correctement à un moment donné.

17
JakeGould

Oui - et le fichier peut être corrompu si rsync le lit en même temps que le fichier.

Vous pouvez essayer ceci: https://unix.stackexchange.com/a/2558

Vous pouvez également le script avec lsof:

lsof /path/to file

Un code de sortie de 0 signifie que le fichier est en cours d'utilisation et un code de sortie de 1 signifie qu'il n'y a aucune activité sur ce fichier.

3
rebelshrug