OVH Cloud OVH Cloud

cron qui ne se termine que 24h après

17 réponses
Avatar
Christophe PEREZ
Bonjour,

Sur ma passerelle, j'ai un petit problème, le cron.daily ne se termine
que lorsque le prochain commence.
Par exemple, je reçois aujourd'hui le mail du cron d'hier et non pas
celui de cette nuit.

En cherchant un peu, je constate qu'en ce moment, j'ai un process qui
semble bloquer :
13159 ? S 0:00 crond
21871 ? S 0:00 \_ CROND
21916 ? S 0:00 \_ /usr/sbin/sendmail -FCronDaemon -odi -oem root
21917 ? S 0:00 \_ /usr/sbin/postdrop -r

Pourtant, quand je recevrai le mail (demain), je ne constaterai rien
d'anormal à l'intérieur.

Du coup, je ne sais pas trop vers quoi porter le reste de mes recherches,
je ne sais pas si c'est un pb dans un des scripts, et si oui lequel, ni si
c'est au niveau de postfix (auquel cas ce post serait [HS]).

Comme nous avons déjà parlé ici de nail et de wrapper sendmail, je
précise que cette machine n'est pas du tout concernée, qu'il y a un
postfix dessus qui fonctionne très bien par ailleurs.

Merci d'avance pour vos idées.

--
Christophe PEREZ
Écrivez moi sans _faute !

7 réponses

1 2
Avatar
Nicolas George
no_spam wrote in message :
Bah, je ne pense pas, c'est un peu du hasard: est-ce que le scheduler va
changer de processus actif entre le copy et le truncate et le cas
échéant est-ce que le process qui écrit dans le log va reprendre la
main et écrire dans le log. Ca ne me semble pas très prédictible et
l'incidence de la vitesse de la machine ne me semble pas claire du tout.
Mais comme ça se passe dans le cache, le risque doit en effet être assez
faible si la machine n'est pas trop chargée.


Si c'est bien fait, ils refont un test sur la taille du fichier juste avant
le truncate, et complètent la copie si ça a changé. Avec un sched_yield
juste avant en prime, sur une machine monoprocesseur, on doit avoir une
fiabilité vraiment bonne. Sur un multiprocesseur, en revanche, c'est perdu.

Avatar
no_spam
On Thu, 25 Nov 2004 01:47:04 +0000, Nicolas George wrote:

no_spam wrote in message :
Bah, je ne pense pas, c'est un peu du hasard: est-ce que le scheduler va
changer de processus actif entre le copy et le truncate et le cas
échéant est-ce que le process qui écrit dans le log va reprendre la
main et écrire dans le log. Ca ne me semble pas très prédictible et
l'incidence de la vitesse de la machine ne me semble pas claire du tout.
Mais comme ça se passe dans le cache, le risque doit en effet être assez
faible si la machine n'est pas trop chargée.


Si c'est bien fait, ils refont un test sur la taille du fichier juste avant
le truncate, et complètent la copie si ça a changé. Avec un sched_yield
juste avant en prime, sur une machine monoprocesseur, on doit avoir une
fiabilité vraiment bonne.


Ca ne change pas grand chose. Comme tout syscall impliquant des IO est
susceptible d'entrainer un reschedule, ça ne fait que déplacer le
problème entre le stat (pour vérifier la taille) et le truncate...

Sur un multiprocesseur, en revanche, c'est perdu.


Je ne pensais même pas à çà, mais c'est vrai que l'atomicité
semble impossible à atteindre dans ce cas.


Avatar
Nicolas George
no_spam wrote in message :
Ca ne change pas grand chose. Comme tout syscall impliquant des IO est
susceptible d'entrainer un reschedule, ça ne fait que déplacer le
problème entre le stat (pour vérifier la taille) et le truncate...


Dans ce cas particulier, il s'agit de deux actions qui portent en tout
premier lieu sur l'inode : au retout du stat, on peut être à peu près
certain que l'inode est dans le cache, il ne devrait pas y avoir de
changement de contexte avant le ftruncate, donc l'inode sera toujours dans
le cache. Dans ces conditions, je dirais qu'il y a de bonnes chances pour
qu'on puisse, en examinant le code à fond, affirmer que ça marche dans tous
les cas.

Avatar
no_spam
On Thu, 25 Nov 2004 16:34:12 +0000, Nicolas George wrote:

no_spam wrote in message :
Ca ne change pas grand chose. Comme tout syscall impliquant des IO est
susceptible d'entrainer un reschedule, ça ne fait que déplacer le
problème entre le stat (pour vérifier la taille) et le truncate...


Dans ce cas particulier, il s'agit de deux actions qui portent en tout
premier lieu sur l'inode : au retout du stat, on peut être à peu près
certain que l'inode est dans le cache, il ne devrait pas y avoir de
changement de contexte avant le ftruncate, donc l'inode sera toujours dans
le cache. Dans ces conditions, je dirais qu'il y a de bonnes chances pour
qu'on puisse, en examinant le code à fond, affirmer que ça marche dans tous
les cas.


Ce sont des assertions fausses...
Tu peux très bien être schedulé deux fois, dans cet intervalle:
une première fois au retour du stat, une seconde en faisant l'appel
ftruncate. Si ta machine est chargée et fait beaucoup d'IOs, rien ne
garantit que l'inode sera encore dans le cache après un seul reschedule.
Donc, non, tu ne peux absolument pas garantir que ça marchera.

je viens de regarder le code de logrotate, il est tout sauf safe...
Il y a de gros problèmes dans cette implémentation du copytruncate:
- il n'y a pas de gestion des erreurs EINTR / EGAIN dans les read/write
qui font la copie. Ce sont typiquement des choses qui arrivent sur des
systèmes chargés....
- il n'y a aucun test quel qu'il soit entre le dernier write de la copie
et le ftruncate.
La copie pouvant prendre un certain temps, notement le dernier write,
il n'y a vraiment aucune garantie d'avoir un log valide à l'arrivée.

Je pense toujours que de passer par un mmap serait plus sain...


Avatar
Nicolas George
no_spam wrote in message :
Tu peux très bien être schedulé deux fois, dans cet intervalle:
une première fois au retour du stat, une seconde en faisant l'appel
ftruncate. Si ta machine est chargée et fait beaucoup d'IOs, rien ne
garantit que l'inode sera encore dans le cache après un seul reschedule.


Mais si ! Je parle bien d'une machine monoprocesseur : dans ce cas, après le
retour de stat, il y a un processus et un seul qui s'exécute pendant
quelques instant : logrotate, justement. Et ce qu'il a à faire avant le
ftruncate (tester la valeur de retour de stat, comparer la taille actuelle
avec la taille copiée) prend très largement moins d'un time-slice, de sorte
que le noyau n'aura certainement rien fait entretemps.

Donc je maintiens que c'est plausible.

je viens de regarder le code de logrotate, il est tout sauf safe...


Ah, là je te fais confiance : je n'ai pas regardé, je ne parlais que sur la
possibilité théorique. En pratique, les programmeurs codent comme des porcs,
c'est bien connu.

- il n'y a pas de gestion des erreurs EINTR / EGAIN dans les read/write
qui font la copie. Ce sont typiquement des choses qui arrivent sur des
systèmes chargés....


Euh, une écriture sur le filesystem ne peut pas donner lieu à un EINTR ou un
EAGAIN, il n'y a pas de problème là.

- il n'y a aucun test quel qu'il soit entre le dernier write de la copie
et le ftruncate.
La copie pouvant prendre un certain temps, notement le dernier write,
il n'y a vraiment aucune garantie d'avoir un log valide à l'arrivée.


C'est plus génant.

Je pense toujours que de passer par un mmap serait plus sain...


J'ai du mal à voir pourquoi.

Ceci dit, il y a une manière de faire ça tout à fait fiablement :

mount -o remount,mand /var
chmod g+s /var/log/$file

Je viens de tester, ça marche parfaitement.

Avatar
no_spam
On Thu, 25 Nov 2004 23:56:28 +0000, Nicolas George wrote:

no_spam wrote in message :
Tu peux très bien être schedulé deux fois, dans cet intervalle:
une première fois au retour du stat, une seconde en faisant l'appel
ftruncate. Si ta machine est chargée et fait beaucoup d'IOs, rien ne
garantit que l'inode sera encore dans le cache après un seul reschedule.


Mais si ! Je parle bien d'une machine monoprocesseur : dans ce cas, après le
retour de stat, il y a un processus et un seul qui s'exécute pendant
quelques instant : logrotate, justement. Et ce qu'il a à faire avant le
ftruncate (tester la valeur de retour de stat, comparer la taille actuelle
avec la taille copiée) prend très largement moins d'un time-slice, de sorte
que le noyau n'aura certainement rien fait entretemps.


Tu ne schedule pas uniquement en fin de timeslice mais également à
chaque accès potentiellement bloquant. Si ta machine est chargée, tu
auras très peu de cache donc il est tout à fait plausible que ton inode
soit discardée du cache avant que tu fasse ton second syscall. Il y a
potentiellement pas mal de raison pour celà: même si tu viens de
commencer ton timeslice, il y a des IRQ et des timers qui se déclenchent
de façon complètement asynchrones. Le code qui s'execute pendant ce
temps là peut très bien invalider une partie du cache.

Donc je maintiens que c'est plausible.


Tu ne peux jamais te baser sur de telles afirmations si tu considère que
ton code est sensible pour la sécurité.
Si tu as deux syscall, les modifications ne sont pas atomiques. Tu ne peux
rien y changer, c'est comme celà. Tu ne peux jamais savoir ce qu'il se
passe entre le retour des 2 syscalls.
Je peux te garantir que même dans un environnement avec peu de process,
il peut arriver des surprises si on se repose sur de tels hypothèses...

je viens de regarder le code de logrotate, il est tout sauf safe...


Ah, là je te fais confiance : je n'ai pas regardé, je ne parlais que sur la
possibilité théorique. En pratique, les programmeurs codent comme des porcs,
c'est bien connu.

- il n'y a pas de gestion des erreurs EINTR / EGAIN dans les read/write
qui font la copie. Ce sont typiquement des choses qui arrivent sur des
systèmes chargés....


Euh, une écriture sur le filesystem ne peut pas donner lieu à un EINTR ou un
EAGAIN, il n'y a pas de problème là.


EAGAIN, il n'y a pas de raison, vu que c'est un fd bloquant. EINTR, par
contre peut arriver n'importe quand. Et, dans la pratique, ça arrive,
évidement toujours aux endroits ou on ne l'a pas géré, bien sur ;-)

- il n'y a aucun test quel qu'il soit entre le dernier write de la copie
et le ftruncate.
La copie pouvant prendre un certain temps, notement le dernier write,
il n'y a vraiment aucune garantie d'avoir un log valide à l'arrivée.


C'est plus génant.

Je pense toujours que de passer par un mmap serait plus sain...


J'ai du mal à voir pourquoi.


Tu travailles alors sur de la mémoire virtuelle et tu peux travailler en
mode copy-on-write, ce qui te garanti déjà que ta copie de travail est
saine. Tu peux même gérer assez finement le comportement de la mémoire
virtuelle: bloquer les pages en RAM, par ex, (mais il faut faire attention
à la taille du fichier !) ce qui garantira que les pages ne pourront pas
être discardées entre deux accès. Ca ne rend pas le risque nul, mais
ça le réduit.

Ceci dit, il y a une manière de faire ça tout à fait fiablement :

mount -o remount,mand /var
chmod g+s /var/log/$file

Je viens de tester, ça marche parfaitement.


D'une part, tu ne peux pas forcément faire celà sur une machine de prod.
D'autre part, tu bloque l'autre process pendant tout ce temps, ce qui
n'est pas toujours acceptable: si ta copie dure plusieurs heures, voire
même seulement plusieurs minutes, acceptes tu que tes démons soient
bloqués ? Pas moi.
La seule solution à ce genre de problème, ce serait un syscall qui
ferait un "swap" de fichier en updatant (ou pas, ça pourrait être une
option....) les fd des process qui ont ouvert ce fichier. Dans ce cas, on
peut garantir une opération atomique et qui ne prends pas beaucoup de
temps: il suffit juste de lister les références à l'inode et les faire
pointer sur la nouvelle inode et en tout cas un temps infiniment plus
court que celui de la copie d'un fichier (qui peut être arbitrairement
longue).


Avatar
Nicolas George
no_spam wrote in message :
Si ta machine est chargée, tu
auras très peu de cache donc il est tout à fait plausible que ton inode
soit discardée du cache avant que tu fasse ton second syscall.


Mais scrogneugneu, non ! Entre le retour du stat (où l'inode est forcément
dans le cache), et l'appel à ftruncate, je disais qu'il n'y avait que du
code du processus qui est exécuté : l'inode n'avait donc pas l'occasion de
sortir du cache. Sauf que :

potentiellement pas mal de raison pour celà: même si tu viens de
commencer ton timeslice, il y a des IRQ et des timers qui se déclenchent
de façon complètement asynchrones.


Oui, j'avais complètement oublié les interruptions matérielles, je m'en suis
rendu compte seulement hier soir en me couchant.

Tu ne peux jamais te baser sur de telles afirmations si tu considère que
ton code est sensible pour la sécurité.


Je n'ai pas dit que c'était certain, j'ai dit que c'était plausible. En
toute généralité, il est clair que ni Unix ni même Linux ne garantissent
quoi que ce soit de ce point de vue. Mais Linux, c'est un noyau bien précis,
dont le code est consultable. Or ce code peut très bien avoir certaines
propriétés particulières exploitables (mais pas portables du tout).

EAGAIN, il n'y a pas de raison, vu que c'est un fd bloquant. EINTR, par
contre peut arriver n'importe quand.


Pas sur une opération de filesystem. Même quand physiquement ça prend
plusieurs secondes, ce n'est pas considéré comme un appel système bloquant.
Techniquement, les processus en cours de read/write sur le filesystem sont
en état D.

Tu travailles alors sur de la mémoire virtuelle et tu peux travailler en
mode copy-on-write, ce qui te garanti déjà que ta copie de travail est
saine. Tu peux même gérer assez finement le comportement de la mémoire
virtuelle: bloquer les pages en RAM, par ex, (mais il faut faire attention
à la taille du fichier !) ce qui garantira que les pages ne pourront pas
être discardées entre deux accès. Ca ne rend pas le risque nul, mais
ça le réduit.


Je ne suis pas très convaincu.

D'une part, tu ne peux pas forcément faire celà sur une machine de prod.


Euh, sur une machine de prod, je n'utiliserais de toutes façons pas un
bouzin qui exige ce genre de hack pour la rotation des logs.

D'autre part, tu bloque l'autre process pendant tout ce temps, ce qui
n'est pas toujours acceptable: si ta copie dure plusieurs heures, voire
même seulement plusieurs minutes, acceptes tu que tes démons soient
bloqués ? Pas moi.


Ce n'est pas nécessaire de verrouiller pendant toute la copie : il suffit de
verouiller pour assurer l'atomicité de la vérification que la copie est fini
et de la troncature. Quelque chose comme ça, par exemple :

taille_copiee = 0;
while(1) {
lock(...);
stat(...);
if(taille > taille_copiee) {
unlock(...);
copier(...);
} else {
ftruncate();
unlock(...);
break;
}
}

La seule solution à ce genre de problème, ce serait un syscall qui
ferait un "swap" de fichier en updatant (ou pas, ça pourrait être une
option....) les fd des process qui ont ouvert ce fichier.


Évidemment, ce serait l'idéal. Tu te sens d'aller le défendre dans l'arène ?

1 2