Duniter 1.2.3 | Fuite mémoire

@cgeek merci mais trop tard, j’ai justement rencontrer le problème hier quand j’ai voulu déployer la 0.2 en prod (a cause du “^” sur la dépendance duniter) et j’ai deviner tout seul :wink:

Smorf :

En gros, ton hypothèse est avérée sur le cas des noeuds miroirs !

Visiblement, même un nœud miroir a quelques appels au moteur de preuve de travail, même s’il n’en demande aucune : ce peut être un changement de puissance CPU, une vérification que rien ne tourne, etc.

Donc il y avait bien alimentation de la variable exchanges même sur ce type de nœud.

1 Like

Bonjour,

Je confirme : sur mon nœud miroir, la consommation mémoire augmente continuellement. À moins de 10% au lancement, je suis à plus de 60% en même pas une journée :

Benoît

[Edit] @cgeek as-tu besoin d’un headdump ?

Attention, il faut se méfier de la mémoire réellement utilisée car Node.js, à l’instar de la JVM, libère ses objets un peu comme il l’entend. Et notamment il ne le fait pas en permanence, mais plutôt quand il n’a plus le choix et qu’il faut libérer de la mémoire.

Oui tu peux m’en envoyer un (par Framadrop). Le mieux étant d’en réaliser un au démarrage du nœud (quelques minutes après le lancement) pour pouvoir comparer les objets créés.

J’ai quand même peur que tu m’envoies un dump de 32Mo, ce qui signifierait (je pense) qu’il n’y a pas de fuite, mais juste une limite d’utilisation trop haute.

Je pense qu’il y a moyen de forcer le maximum de mémoire long terme utilisé par Duniter. Pour ceux qui veulent tester sans attendre, modifiez le fichier /opt/duniter/sources/duniter.sh et remplacez :

$NODE "$DUNITER_DIR/bin/duniter" "$@"

Par :

$NODE --max_old_space_size=300 "$DUNITER_DIR/bin/duniter" "$@"

Puis redémarrez votre nœud à l’aide de restart ou webrestart, selon votre configuration.

Ici, on dit que Node ne pourra pas dépasser 300 Mo de mémoire long terme. Il va donc plus régulièrement nettoyer ses objets.

J’ai testé cette ligne depuis 30 minutes, et on voit clairement le nœud Duniter tourner autour des 100 Mo de mémoire utilisée. :slight_smile:

edit : je me suis ajouté un ticket #978 à ce sujet.

Mon nœud tourne depuis une journée maintenant, je vais continuer de contrôler l’exécution sans le relancer.

@cgeek
La consommation mémoire de mon nœud a continué à grimper. Ce matin, il était éteint. Je l’ai relancé en créant un headdump que je t’ai mis à disposition via Framadrop. J’ai utilisé le ticket #978
Le dump fait bien autour de 32 Mo comme tu t’y attendais.

1 Like

Bon, malgré l’utilisation du flag --max_old_space_size, je constate une lente augmentation de la mémoire utilisée qui dépasse désormais les 400 Mo. Pourtant le heapdump différentiel ne montre rien de particulier.

Je vais attendre davantage encore, mais à tous les coups le problème vient d’un module en C/C++, vu que ce sont les seuls endroits qui peuvent échapper au heapdump.

Je confirme que le bug est toujours présent.

Pour ma part, mon nœud ARM a une augmentation très légère de son utilisation de RAM.
Au démarrage c’est aux alentours de 200-250 Mo, et après trois jours, c’est à 300 Mo au lieu de 800-900 Mo en 1.2.2≤.
C’est déjà un grand progrès !!!

À cette allure, mon nœud devrait pouvoir tenir deux semaines sans redémarrage au lieu de trois jours.

Bon je confirme, j’ai pu reproduire la fuite localement et de façon très visible. Effectivement celle-ci n’apparaît pas dans le heapdump, vu que la fuite provient du module wotb écrit en C++, et donc n’est pas dans JavaScript.

Je continue l’investigation.

Pour info, ma procédure de test est simple : étant donné que la fuite se produit aussi bien sur les nœuds membres que miroirs, je soupçonnais fortement le code qui vérifie la validité d’un bloc. Le test consiste donc à ajouter en boucle le même bloc avec une erreur à la fin, histoire qu’il ne soit pas ajouté et que je puisse le retester intégralement, en boucle. Je constate effectivement que la mémoire :

  • ne bouge pas si je désactive le code propre aux tests de distance dans la WoT (module wotb) : je stagne aux alentours de 150 Mo utilisés, car j’ai explicitement demandé cette valeur
  • je m’envole dans la consommation mémoire sinon
3 Likes

Première fuite trouvée : il manquait un destructeur virtual sur AbstractWoT.h.

Du coup on a beau faire des :

https://github.com/duniter/wotb/blob/master/functions.cc#L78-L80

… le destructeur de des classes filles de AbstractWoT (MemoryWoT et FileWoT) n’est pas appelé, or c’est lui fait le ménage dans la mémoire. :confused:

C’est une erreur de noob il paraît ! Codée par votre serviteur.

Il s’agit de la fuite la plus énorme, très visible. Elle représente à elle seule plus de 95% des fuites restantes en v1.2.3 je dirais.

Mais il en reste visiblement une autre.

5 Likes

En tout cas le colmatage (de la fuite principale) a été rapide.

Pour ce qui est de l’erreur de noob je ne peux pas jeter la pierre ne maîtrisant pas le C++ autant que je le voudrais. :slight_smile:
Concernant les 5% de fuites potentielles, peut-on supposer qu’il s’agisse du même phénomène ? J’image que j’enfonce une porte ouverte…

Bonne journée.

On aurait pu supposer oui, et c’est ce que j’ai fait. Mais j’ai pu me rendre compte que cette fois le C++ n’y est pour rien.

Donc pour ces 5% restants, ce sont en réalité quelques new en JavaScript qui posent problème lors des copies de la WoT en mémoire :

Et :

https://github.com/duniter/duniter/blob/master/app/lib/wot.js#L16

Pour une raison que j’ignore, cela fait grimper la mémoire sans limite. Pourtant le heapdump reste constant. Alors on est peut-être sur un bug de V8 (le moteur JavaScript), ou encore sur un cas aux limites, je ne sais pas bien.

Toujours est-il qu’avec ces solutions, et en sollicitation intensive, je tourne aux environs de :

  • 250 Mo de RAM sans brides (c’est-à-dire en retirant l’option --max_old_space_size=150)
  • 130 Mo de RAM si je lui mets de brides plus fortes (--max_old_space_size=100)
  • 0 Mo de RAM si je descends plus bas (mettons --max_old_space_size=50) : il crash au bout de quelques secondes

Bon, ça me paraît pas mal :slight_smile: je teste encore un peu tout ça, et je ferai une 1.2.4 au plus tard demain.

1 Like

C’est une très grosse avancée. Ils sont apparus quand ces bugs ? Présents depuis toujours ?

L’historique Git te le dira précisément, mais c’est au moins depuis ĞTest : donc plus d’un an et demi.

Est-ce que tu avais envisagé de migrer vers des smartpointers plutôt que ces bons vieux delete ?

Non pas vraiment, je ne connaissais pas leur existence. Peut-être que le prochain à modifier le code pourra le faire, car a priori on ne va plus y toucher avant longtemps.

Bon, finalement, mon nœud sous ARM a occupé 350 Mo de RAM.

Après, on l’a surement pas ressenti, car les règles de distance et les règles de la toile de confiances successives étaient moins restrictives que celle de Ğ1.