Duniter 1.2.3 | Fuite mémoire

Version corrective 1.2.3

Version mineure qui corrige un problème de fuite mémoire : certains membres ont ainsi constaté l’arrêt inopiné de leur nœud du fait d’une trop grande utilisation de la RAM de la machine.

A noter que le correctif peut ne pas fonctionner pour les nœuds miroir, mais on peut désormais analyser plus finement le problème.

Un Duniter plus léger !

Fait notable sans être essentiel : le logiciel pèse désormais 75 Mo pour la version Desktop et 35 à 45 Mo pour la version Server. Soit 30% de moins que dans les versions précédentes.

Un Duniter plus local !

Vous ne le saviez peut-être pas, mais le logiciel que vous téléchargiez jusqu’à maintenant était produit sur les plateformes Travis et AppVeyor. Ce sont des machines tierces que nous ne possédons pas, avec toutes les problématiques de confiance que cela pose.

Désormais, ce sont les machines des développeurs qui produisent les nouvelles version du logiciel Duniter : nous sommes donc pleinement maîtres du processus (au matériel près …), ce qui nous permettra très bientôt de signer numériquement le logiciel. Ce sera pour la v1.3.0.

Synchronisation

:white_check_mark: Pas besoin de resynchroniser.

Compatibilité

:white_check_mark: Compatible avec Ğ1.


Mettre à jour sa version

:warning: Attention pour les nœuds ARM : si vous tentez de resynchroniser entièrement la blockchain Ğ1, cela peut prendre une éternité (je constate plus de 12h de chargement actuellement, et ce n’est pas terminé). Mieux vaut ne pas remettre vos données à zéro.

6 J'aime

Quelle méthode de build utilisez-vous du coup ?

J’ai lancé mon noeud sur cette nouvelle version, on verra si la mémoire monte et ce qu’il se passe, vu que c’est une miroir !

C’est du Vagrant.

Les VMs sont stockées sur nos serveurs, nous avons donc la maîtrise de ce qui est déjà installé dessus.

Pour être tout à fait exact, pour le moment je les stocke sur AWS vu les 13Go que représente la machine Windows. Mais dans l’idéal, il faudrait les stocker sur un des serveurs duniter.org.

@elois, @greyzlii, @inso et tous ceux qui ont réalisé / vont réaliser un nœud spécialisé : depuis la v1.2.3, vous risquez de rencontrer une erreur au lancement du nœud si vous mettez à jour Duniter en version 1.2.3 ou supérieur.

Toutefois vous pouvez facilement corriger le problème en vous plaçant dans le dossier de code source de votre nœud et en lançant la commande :

yarn add duniter-crawler duniter-bma duniter-prover duniter-keypair

Ou bien si vous n’avez pas yarn mais npm :

npm install --save duniter-crawler duniter-bma duniter-prover duniter-keypair

La raison est que dans duniter@1.2.2 ou inférieur, la bibliothèque duniter publiée sur NPM avait une dépendance forte sur ces 4 modules de duniter, ce qui était incorrect : on doit pouvoir faire fonctionner le cœur sans ces modules.

Il faut donc les ajouter de façon explicite désormais. Je publierai bientôt un tuto sur le Wiki duniter.org pour bien expliquer cela et proposer une base de développement simple.

2 J'aime

@cgeek
les premieres stats m’ont l’air de bonne augure, même sur mon nœud miroir :

4 J'aime

@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 J'aime

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 J'aime

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 J'aime

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 J'aime

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 :

https://github.com/duniter/wotb/blob/master/index.js#L32

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 J'aime