J'ai une petite colle sur un comportement que je n'arrive pas à comprendre. Peut-être que certains d'entre vous pourront m'éclairer.
Tout d'abord la base. J'ai plusieurs machines qui envoient à une machine centrale des fichiers par ftp dans un dossier spécifique pour chaque machine émettrice. Jusque là simple…
Cette machine centrale qui reçoit ces fichiers à un rôle de dispatch des fichiers reçus dans d'autres dossiers en local en fonction de ceux-ci.
Au début, je faisais tourner un cron toutes les 5 min qui scannait l'ensemble des dossiers de réception et faisait le dispatch. Ça marchait très bien mais c'était pas optimum. Notamment je voulais réagir instantanément dés l'arrivée d'un fichier dans un dossier sans attendre les fameuses 5 min.
Pour cela, je me suis tourné vers incrond (produit génial) capable de lancer un script dès qu'il y a un mouvement sur le filesystem. Pour cela j'ai positionné une incrontab sur chacun des dossiers de réception et il faut avouer que cela marche très très bien !!!!
Néanmoins, je n'ai pas abandonné pour autant mon script d'origine.
En effet, il est essentiel que je le conserve car dans le cas ou incrond devait être arrêté, il serait le seul à faire le boulot pour les fichiers qui auront été émis pendant l'arrêt d'incrond. Rappelez vous, incrond ne réagira que sur l'arrivée de nouveaux fichiers, pas sur ceux qui sont arrivés entre temps pendant qu'il ne regardait pas le filesystem ! Pour moi, ce script devient une sorte de script balai…
Voilà mon problème, j'aimerai pouvoir lancer ce script balai même quand incrond est en train de tourner. La plupart du temps, ce script ne devrait rien voir et ne pas prendre en charge de fichiers dans les dossiers de réception car incrond est très rapide et le script de dispatch est très court…
Néanmoins, si je le lance plusieurs fois d'affiler, il arrive quand même à attraper un fichier au vol malgré la rapidité d'incrond :-(. Forcément le traitement de ce script balai se passe mal car entretemps le fichier a disparu car pris en charge par le script lancé par incrond.
Pour éviter ce problème, j'ai une approche simple, le script balai teste la date de création du fichier par rapport à l'heure de maintenant.
=> Si celle-ci est inférieure à 1 min, le fichier sera ignoré, car il pourrait être pris en charge par incrond.
=> Si la différence est supérieure à 1 min, c'est qu'incrond ne l'a jamais vu donc il doit être pris en charge.
Voici un bout du script balai, ci-dessous :
J'ai décrit également la fonction diff_date() qui calcul la différence horaire…
...
# Fonction diff_date calcul la différence entre deux dates
# $1 Date d'orgine
# $2 Date de fin
# $3 affichage différence d,m,h,s ( jours, minutes, heures, secondes)
function diff_date() {
local FD SD P
[[ $# < 2 ]] && return 1
FD=$(date -d "$1" "+%s" 2> /dev/null) || return 1
SD=$(date -d "$2" "+%s" 2> /dev/null) || return 1
case "$3" in
"s") P=1;;
"m") P=60;;
"h") P=3600;;
"d" | "") P=86400;;
*) return 1;;
esac
echo -n $(( ($FD - $SD)/($P) ))
}
...
IGNORE_TIME=1
# Test ci-dessous qui pose problème de temps en temps...
[[ $File =~ ^regexp_qui_valide_name_file$ ]] && (( $(diff_date now "$(stat -c %y $File | sed s/\\..*$//)" m) > $IGNORE_TIME )) && {
...
# Prise en charge du fichier
...
}
Mon soucis vient de la condition qui marche très bien la plupart du temps mais si j'insiste en appelant plusieurs fois celui-ci il arrive qu'elle laisse passer le traitement. Je ne comprends pas pourquoi !!!!
Avez vous une idée, du pourquoi ?
# changer la maniere de faire
Posté par NeoX . Évalué à 4.
incrond doit appeler un script pour faire les actions quand il voit une difference dans un dossier surveillé.
pourquoi ne pas alors utiliser le meme script avec incrond ou crond :
mais pourquoi incrond serait arrété ? c'est un daemon systeme, au meme titre que crond,
si incrond risque d'etre arrété, crond risque aussi de l'etre…
et tu restes avec le meme probleme…
[^] # Re: changer la maniere de faire
Posté par piaff33z . Évalué à 1. Dernière modification le 05 mars 2017 à 10:53.
J'aimerai éviter le principe de lock, je trouve ça lourd à gérer et en plus il faudrait le faire pour chaque fichier reçu :-(… Le calcul d'une date me parait bien plus simple, non !?
mais pourquoi incrond serait arrété ? c'est un daemon systeme, au meme titre que crond,
Et pourquoi pas, à cause d'une maintenance ou autre…
J'ai peut-être pas précisé mais le script balai pourrait être lancé à la main et pas forcément par cron.
Justement après une opération de maintenance pour faire le ménage des dossiers de réception et traiter les fichiers qui ne l'auraient pas été…
[^] # Re: changer la maniere de faire
Posté par piaff33z . Évalué à 1.
=> La variable $File est statique et son contenu ne peut pas varier. Elle a été alimentée à la suite d'un ls dans une boucle. Donc la RegExp va forcément bien se réaliser.
=> Mais, je me suis posé la question de savoir si au moment ou je faisais le test de la date avec stat si le fichier serait encore présent et dans le contraire comment se comporterait la condition !?
Peut-être que mon problème est ici. Néanmoins, si cela devait arriver je devrais avoir une erreur en retour de la commande stat qui devrait dans ce cas-ci planter ma condition !
Pourtant mon script ne me retourne pas d'erreur à ce moment là :-( mais plus loin quand il essaye de dispatcher le fichier qui n'est plus là :-(
[^] # Re: changer la maniere de faire
Posté par NeoX . Évalué à 3.
le lock est le seul moyen d'eviter qu'un fichier soit gerer par les 2 systemes en meme temps.
si tu ne veux pas de fichier lock (un par fichier à transferer)
alors fait l'inverse, elimine le process concurrent.
et puisque tu veux lancer ton script de menage 'au cas ou incrond aurait été arrété'
demande à ton script de stopper incrond,
fait lui faire la synchro
relance incrond
incrond prendra donc les "nouveau fichier" apres la synchro manuel.
[^] # Re: changer la maniere de faire
Posté par piaff33z . Évalué à 1. Dernière modification le 05 mars 2017 à 14:48.
J'ose espérer que d'autres méthodes puissent exister que le lock ;-)
Sur ta solution d'éliminer le process concurrent, c'est une solution mais entre le laps de temps ou tu lances à nouveau incrond et le moment ou il sera totalement opérationnel, tu pourras avoir quelques fichiers qui peuvent passer entre les mailles du filet surtout si les transferts sont nombreux ce qui est mon cas et tu retombes un peu sur le même problème…
Mais sans vouloir obstinément trouver "autre manière de faire" pourquoi ne pas objectivement regarder ce bout de code et voir où le problème pourrait se situer ? :-)
En tout cas merci de participer à ce débat ;-)
[^] # Re: changer la maniere de faire
Posté par NeoX . Évalué à 3.
voila c'est fait
tu as regardé ce qu'il se passait quand tu fais ton stat -c trop souvent ?
tu as lancé ton script avec l'option -x en debut de script,
ou sur le morceau que tu supposes douteux ?
par exemple au moment du calcule de la difference,
tu fait une difference entre :
- $1 MAINTENANT
- $2 le stats -c du fichier
sans unité
tu fais ensuite ($1 - $2) / 1 que tu compares à 1
si c'est >1 alors tu fais le traitement.
de meme, il nous faudrait savoir sur quel evenement tu declenches ta synchro via incrond,
parce que ca peut etre sur un close-write, sur un in-access, ce qui n'est pas la meme chose,
et qui n'a evidemment pas les memes consequences sur le fonctionne final
[^] # Re: changer la maniere de faire
Posté par piaff33z . Évalué à 1.
Tu as raison, je vais pousser mes investigations plus avant et modifier mon script balai et le lancer plus rapidement pour mettre en évidence le problème…
Pour la mécanique incrond…
Sur ta question en ce qui concerne incrond, je peux bien sur préciser les choses.
Mais je tiens à indiquer que la mécanique que je décris fonctionne très bien…
En fait, j'observe chaque dossier sur deux évènements en particulier IN_MOVETO et IN_CLOSEWRITE. Le pourquoi est simple et il est du à la manière dont les fichiers sont transférés par ftp…
Dans une version du logiciel distant qui transmet son fichier, le ftp est initié en créant un fichier file.tmp et à l'issue du téléchargement le fichier et renommer en file. C'est un peu ce qu'on observe quand FF télécharge un fichier ;-), à la différence près que FF crée le nom du fichier avec un contenu vide et le même nom avec une extension .tmp…
Dans ce cas de figure incrond sollicite 2 fois mon script de dispatch sur les 2 évènements.
Dans une seconde version du logiciel, le ftp est différent, le fichier est transmit mais sans l'usage de l'extension .tmp. La fermeture du fichier via l’évènement IN_CLOSEWRITE confirme la fin du transfert et donc incond ne sollicite qu'une seule fois mon script de dispatch qui traite bien sur le fichier car il n'a pas l’extension .tmp.
Que ce soit la méthode 1 ou la méthode 2, tous les fichiers sont traités correctement après qu'ils sont arrivés avec le bon format de nom du fichier et tout ça en parallèle !
Je tiens à préciser que je n'ai pas jugé bon d'avoir une incrontab différente par dossier en fonction de la version du logiciel en face. C'est plus simple quand on génère la incrontab malgré la petite perte d'énergie pour le premier IN_CLOSEWRITE de la 1° méthode…
La mécanique de mon script balai…
Maintenant mon script balai, à un comportement similaire au script de dispatch lancé par incrond à la différence près que lui ne sait pas si des fichiers sont arrivés.
Il est donc obligé de balayer tous les dossiers et rechercher leur présence.
Ici aussi, il a la même mécanique de protection et ignorera les fichiers dont l'extension .tmp est présente et traitera le fichier dans le cas contraire.
Mais le risque est qu'il prenne en charge un fichier qui n'aurait pas été dispatché assez rapidement par le script lancé par incrond…
D'où, mon test sur l'heure de création du fichier (%y de la commande stat) qui indique une arrivée très proche et donc une indication à mon script de ne rien faire…
Dans le cas d'une date d'arrivée assez ancienne il est fort à parier que incrond n'avait pas vu ce fichier et donc là je passe le balai…
En conclusion …
Cette belle mécanique semble très bien marcher mais encore une fois, il arrive qu'un fichier soit traité par mon script balai alors qu'il ne devrait pas l'être :-(.
Je m'en suis rendu compte en lançant plusieurs fois d'affiler et dans 99% des cas il ne voit aucun fichiers car ils ont tous été pris en charge correctement par incrond.
J'ai observé que cela n'arrive uniquement que pour les dossiers dans lequel la méthode 2 du ftp est utilisée. Il a remarqué également quand cela se produit, j'ai un envoie important de ftp dans le dossier ce qui me fait penser qu'incrond est un peu plus lent pour la prise en charge…
Pour rajouter une "cerise sur le gâteau" :-) qui peut peut-être apporter un autre éclairage est que l'arborescence unix ou se produisent les évènements est sous drbd entre deux machines en normal/secours (ça aussi ça marche très bien)…
[^] # Re: changer la maniere de faire
Posté par piaff33z . Évalué à 1.
J'ai modifié mon script balai pour isoler la condition et j'ai trouvé ou était le problème en le lançant plusieurs fois d'affilé.
J'avais raison, le stat se plante parce que le fichier a disparu !
Par contre le plantage du stat valide la condition !!!! :-(
Je vais travailler ce point et faire en sorte que la condition renvoie faux dans ce contexte !!!
En tout cas merci à NeOX et ptit_poulet d'avoir participé à ce fil…
Ps : Je posterai ma solution ;-)
[^] # Re: changer la maniere de faire
Posté par NeoX . Évalué à 2. Dernière modification le 05 mars 2017 à 19:23.
sinon pour trouver les fichiers qui sont plus recent qu'un fichier particulier, ou qu'une date,
tu as
find
-mtime pour la date de modification
-ctime pour la date de creation
ce serait peut-etre plus simple que de faire de stats sur les fichiers, qui vont parfois disparaitre..
tu peux ainsi ecrire un fichier last-run
et prendre tous les fichiers, plus recent que le fichier last-run
et donc la creation date de plus de x minutes…
[^] # Re: changer la maniere de faire
Posté par piaff33z . Évalué à 1.
Bonjour la "cantonnade" comme promis je poste la solution que j'ai trouvé :-) et si ça peut aider quelqu'un à jour…
Pour rappel mon soucis provenait du fait de la disparition d'un fichier qui est en paramètre de la commande ci-dessous :
Cette commande est sensée retourner l'heure de création du fichier. Mais encore une fois si le fichier a disparu la commande stat va se planter !
Cette commande est programmée pour recevoir en entrée un flot :
* si ce flot n'est pas nul, elle l'affiche via un echo
* Si ce flot est nul ou s'il y a un problème en entrée elle évaluera l'ensemble des paramètres comme une seule instruction.
Dans mon contexte cela devient :
Maintenant l'insertion totale dans ma condition décrite précédemment :
Et voili, voilà ça marche comme un charme. J'avoue ça donne un peu le torticolis :-)
Merci encore aux différentes participations même si c'est moi qui est trouvé la soluce ;-)
[^] # Re: changer la maniere de faire
Posté par guppy . Évalué à 1.
C'est de la bricole selon moi. Ca marche pour l'instant, mais pour ma part c'est pas assez déterministe pour passer en production. Qu'est-ce qui va se passer si toi (ou un de tes collègues) modifie le script de traitement pour lui faire faire une opération plus longue qu'une minute ? Ton "script balai" risque d'essayer de traiter un fichier déjà en cours de traitement par incron.
La solution à base de verrou qui t'a déjà été proposée est plus fiable. Plus courte également je pense :
Si on le lance 2 fois sur le même fichier :
[^] # Re: changer la maniere de faire
Posté par piaff33z . Évalué à 1.
Le script de traitement ne sera pas plus long, il n'a pas vocation à l'être. Mais je note que ta remarque a du sens.
Maintenant sur cette solution, elle me convient très bien et elle m'évite justement de gérer la création et la suppression de fichiers lock qui à mon sens est bien plus lourde mais peut-être pour toi plus déterministe => J'en conviens mais question de point de vue ;-)
Dans mon cas, si tout tourne bien avec incrond, il n'est pas nécessaire de gérer des lock car chaque fichier transféré dans les dossiers de réception est bien pris en charge individuellement donc il n'y a pas de concurrence d'accès à un fichier à un moment donné en condition normal de fonctionnement.
Mon soucis est dans des contextes très particuliers
- à la suite d'un arrêt ou reboot du service incrond
- d'une maintenance programmée
- un dossier qui n'a pas été sous surveillance incrond ( pour maintenance )
- …
Dans ces cas, il me faut un script balai qui arrive à deviner s'il ne va pas traiter un fichier qui pourrait être pris en charge par incrond.
Le temps de détection pour moi est arbitraire, je l'ai mis à 1 min mais j'aurai bien pu mettre 10 min si j'ai peur de rentrer en collision avec incrond.
[^] # Re: changer la maniere de faire
Posté par NeoX . Évalué à 2.
non, c'est une question pratique,
en programmation, on a justement inventé le principe des lock ou les mutex, flags, pour eviter les conflits sur une ressource (cpu, ram, disque, fichier, whatelse)
donc si tu as un conflit sur un acces à une ressource, il n'y a pas 50 manieres de faire, il faut :
- detecter que quelqu'un se sert deja de la ressource (verifier la presence du lock)
-- si c'est le cas revenir plus tard
-- sinon faire ton action
[^] # Re: changer la maniere de faire
Posté par totof2000 . Évalué à 2.
sqlite est ton ami …
Tu crées une table avec une colonne contenant le nom de tes fichiers et une colonne avec l'état l'état de chacun d'entre eux. Optionnellement tu peux aussi mettre en place des colones avec date de début de traitement et date de fin de traitement pour pouvoir savoir ce qui se passe (voir plus loin).
Quand tu reçois un fichier, tu remplis ta table avec une nouvelle entrée (nom du fichier), et tu laisses l'état vide (ça veut dire que le fichier est en attente).
Puis toutes les minutes, tu récupère la liste des fichiers dont l'état n'est pas précisé. Pour chacun de ces fichiers, tu déclenches ton traitement, et tu flag le fichier comme étant "en cours de traitement" (tu pourrais même indiquer le PID du process qui exécute le traitement, ça pourrait te servir à controler si le processus qui a traité le fichier est toujours en vie). Enfin quand ton traitement est fini, tu mets à jour le champ avec -1 par exemple pour indiquer que c'est terminé. En bonus, tu peux même ajouter les dates de début/fin de traitement (permetde détecter des anomalies si un traitement n'est pas terminé depuis X minutes ou heures).
Les avantages : tu laisses la base gérer les accès concurrents (voir https://www.sqlite.org/faq.html#q5), tu as une visibilité précise de ce qui se passe, avec un gestionnaire de bases de données assez simple et léger. Tu peux mettre en place des procédures de reprise et de détection d'anomalies assez simplement. Pour les inconvénients, je dirais qu'il faut savoir gérer en ligne de commande les reprises lors des tentatives d'accès à une base busy, mais rien d'insurmontable à mon avis.
[^] # Re: changer la maniere de faire
Posté par NeoX . Évalué à 2.
non, la base gere les access concurrent à SES données,
ca reste ton programme, en fonction de la valeur "etat" qui va dire s'il faut traiter ou pas le fichier
=> ce qui revient donc au meme que le fichier lock
[^] # Re: changer la maniere de faire
Posté par guppy . Évalué à 1.
Il n'y a pas de création de "fichiers lock" ;)
On verrouille tout simplement le fichier que tu veux transférer pour qu'il ne soit pas disponible pour une autre instance du script.
Note également que le verrou se déverrouille tout seul à la fin du sous shell.
Si tu veux traiter un fichier depuis ta console :
worker filename
Depuis ton incrontab :
dirname IN_CLOSE_WRITE worker $@/$#
Version "balai" :
find dirname -print0 | xargs worker
Tu peux lancer tout ça en même temps sur le même répertoire, les fichiers ne seront traités qu'une fois. Et ça même si ton traitement évolue en terme de durée, ou si le système est fortement ralenti pour une raison ou une autre.
Et tout ça avec une dizaine de lignes en plus que ton traitement, pas de calculs bizarres ou d'estimation non déterministe. J'ai l'impression que tu préfères la tienne parce que tu n'es pas à l'aise avec les verrous. Fais des tests, tu verras c'est pas spécialement difficile à utiliser.
[^] # Re: changer la maniere de faire
Posté par NeoX . Évalué à 2.
si tu verrouilles le fichier, tu crees un lock
il n'est pas créé avec un fichier, mais il existe quand meme :D
et le gars, il ne voulait pas faire de 'lock' par concept
# Syncthing ?
Posté par ptit_poulet . Évalué à 0.
Connais-tu Synchting ?
Ça fait exactement ce que tu veux faire avec des possibilités supplémentaires ;)
[^] # Re: Syncthing ?
Posté par piaff33z . Évalué à 1.
Je connais pas mais cela me semble peut-être un peu "too much" pour ce que je veux faire ;-)
Me besoin doit se contenter d'utiliser au maximum d'outils déjà présents sur un système Linux standard.
Incron était déjà un "addone" qui m'a semblé vraiment intéressant et simple à rajouter.
En tout cas merci pour cette idée, j'étudierai peut-être plus profondément cet outil pour découvrir toutes ses possibilités…
# ftp et antivirus
Posté par KiKouN . Évalué à 2.
Certains serveurs ftp, au moins pure-ftpd, te permettent de lancer un script dès réception d'un fichier. C'est normalement utilisé pour passer le fichier a l'antivirus.
[^] # Re: ftp et antivirus
Posté par piaff33z . Évalué à 1.
J'y avais pensé, avec une sorte de procédure de fin de transfert. Mais c'est pas l'option que j'ai prise. Le serveur que j'utilise est PRO_FTPD.
Je trouvais la solution incrond assez élégante et surtout capable de m'ouvrir d'autres usages futures…
# Une minute, ça passe en une seconde !
Posté par moi1392 . Évalué à 2.
C'est une hypothèse à vérifier, mais si ta fonction "date" calcule la date à l'aide du timestamp unix, et que tu ne regardes que les minutes pour comparer deux dates, tu as une différence d'une minute entre la 59ème seconde de la minute précédente et la 0ème seconde de la minute suivante.
Donc ta différence d'une minute est en fait une différence entre la seconde à laquelle le fichier à été créé et la 60ème seconde de la minute en cours.
Si tu n'actives ton balai qu'au bout d'une différence de deux minutes, tu ne devrais plus avoir ce problème.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.