Crash de Gladys lors de la sauvegarde GladysPlus

Je suis embêté depuis plusieurs semaines avec ma sauvegarde Gladys, mais je n’avais pas pris le temps de faire les vérifications nécessaires.

En fait, chaque nuit quand la sauvegarde se déclenche, Gladys se bloque (plus d’affichage du dashboard, plus de déclenchement de scènes, …), cela dure environ 2h, puis je reçois dans Telegram le message « Gladys vient de redémarrer » (c’est le résultat d’une scène ayant pour déclencheur « Gladys démarre »), et tout fonctionne à nouveau.

Avec l’aide de Claude ce matin, j’ai exploré le souci. Lors d’une étape de son analyse, j’ai répondu que j’avais « une base moyenne (quelques gigas) », mais je ne suis pas sûr d’avoir bien répondu. Dans l’historique de mes sauvegardes, je vois ça :

En première action, Claude m’a suggéré de modifier la RAM allouée à Gladys (avec la commande sudo docker update --memory="6g" --memory-swap="8g" gladys), pour que mon souci ne provoque plus un blocage pendant plusieurs heures. Et effectivement quand j’ai relancé manuellement une sauvegarde, le crash et la remise en route se sont faits en 3-4 minutes. C’est mieux pour ne pas bloquer ma domotique, mais ça ne résoud pas encore le souci de sauvegarde en échec :wink:

Je tourne sur un mini-PC Intel NUC5PPYB, processeur N3700 4 cœurs 1.6GHz, 8Go de RAM, sous Ubuntu 22.04, et Gladys a été installé avec la commande « docker run » standard. Je n’ai pas d’autre chose que Gladys sur ce serveur.

Voilà le rapport final de Claude :

Pic mémoire / OOM-kill pendant la sauvegarde GladysPlus

Résumé

Chaque sauvegarde GladysPlus (automatique 2h-4h ou manuelle) fait grimper la RAM du
conteneur Gladys au point de déclencher un OOM-kill du process Node, suivi d’un
redémarrage automatique du conteneur.

Environnement

  • Gladys en Docker sur Mini PC/NUC
  • RAM totale de l’hôte : 7,7 Go (8 Go non extensible)
  • Base de données : taille moyenne (quelques Go)
  • Conteneur gladys limité manuellement à 6 Go de RAM (docker update --memory)
    pour éviter que l’OOM ne déstabilise tout l’hôte (swap-thrashing observé avant
    la mise en place de cette limite)

Reproduction

Sauvegarde manuelle déclenchée à 09h36.

  • 09:36:01 - RSS avant backup : 4388,77 MB, Heap 233,97/392,50 MB
  • 09:36:01 - DuckDB avant backup : Total 2248,75 MB (BASE_TABLE: 2246,00 MB,
    IN_MEMORY_TABLE: 2,75 MB)
  • 09:36:04 - Ouverture d’une seconde instance DuckDB dédiée au backup
    (duckDbCreateBackupInstance, memory_limit=1024MB, threads=2, READ_ONLY)
    pour exporter vers un dossier Parquet
  • 09:38 - RAM du conteneur : ~6 Go (limite atteinte)
  • 09:40 - Out of memory: Killed process ... (MainThread)
  • 09:41 - Conteneur redémarré, Gladys de nouveau fonctionnel

Sans la limite de 6 Go posée manuellement au niveau Docker, le même phénomène en
conditions réelles (sauvegarde nocturne automatique) a provoqué un épisode de
swap-thrashing sur l’hôte entier de 02h23 à 04h44 (dashboard et scènes
inutilisables), le RSS du process ayant atteint 6774,54 MB avant kill (dmesg :
Out of memory: Killed process 1542 (MainThread) total-vm:30424440kB, anon-rss:6774536kB).

Hypothèse

La nouvelle instance DuckDB créée pour le backup (limitée à 1024 MB) semble
s’additionner à la mémoire déjà occupée par l’instance DuckDB principale
(~2,25 Go), plutôt que de partager/respecter un budget mémoire global du process.
Sur une machine où le process tourne déjà à ~4,4 Go au repos, l’ajout de
~1 Go supplémentaire suffit à dépasser la RAM disponible.

Question pour les mainteneurs

  • Est-il normal que BASE_TABLE reste quasi intégralement en mémoire (2,25 Go)
    au repos, hors sauvegarde, pour une base de taille moyenne ?
  • Le memory_limit=1024MB de l’instance de backup peut-il être abaissé, ou le
    process de backup peut-il streamer/traiter par lots au lieu de charger autant
    en mémoire, pour rester dans une enveloppe totale raisonnable même sur du
    matériel à 8 Go de RAM ?

@pierre-gilles je ne suis pas sûr que tu auras le temps de regarder ça avant tes vacances… Ça attendra ton retour si ce n’est pas possible :wink:

Je laisse @mutmut t’aider :slight_smile: (ou quelqu’un d’autres!)

@StephaneB
j’ai un bout de mes déboires ici : Migration intégration Netatmo : plantage de Gladys - #4 par mutmut
et ensuite là : Surconsommation de mémoire par le backup de gateway? - #18 par mutmut

Pour l’instant, la seule solution que j’ai trouvé pour ne plus être embêté c’est d’augmenter la ram :confused:
Mais comme c’est un cluster proxmox, si le host plante, le lxc avec gladys va basculer sur un autre hôte et là je risque d’avoir des soucis de ram partagée…
En tout cas quand ça freezait, j’arrivais à débloquer la situation en augmentant en live la ram (merci les LXC !), et toutes les tâches en attente se sont bien terminées.

Merci pour ce retour @mutmut.

Je viens de vérifier, ma base de donnée duckdb fait 8Go. Et comme je ne peux malheureusement pas ajouter de RAM (8 Go max sur ce mini pc), je suis coincé…

Est-ce qu’il y a des services dont on sait qu’ils consomment de la RAM et que je pourrais arrêter dans la page ‹ services › de Gladys, le temps de lancer manuellement une sauvegarde ?

Bon, j’ai réussi à faire une sauvegarde en coupant tous les services sauf node-red et mqtt, car je voulais continuer à tracer l’usage RAM et CPU que je fais tourner dans Node-Red (oui, il faudra que je bascule sur l’intégration externe…).

Ça a fait baisser l’usage RAM d’environ -10%, passant d’environ 80% à 70%. J’ai alors lancé manuellement une sauvegarde. Gladys ne s’en est pas sorti, crash OOM. Mais au redémarrage, la RAM n’était utilisée que à 10% environ ! J’ai donc déclenché aussitôt une sauvegarde qui s’est bien passé.

Dans la foulée, j’ai relancé tous les services et provoqué un redémarrage du mini-PC. L’usage RAM s’est stabilisé à 20% environ, top. Il était 19h20.
Sauf qu’à 19h30, j’ai eu un pic de CPU mais surtout une montée de l’usage RAM à 60%, qui n’est pas redescendu depuis !

Est-ce le calcul du suivi d’énergie qui consomme beaucoup de RAM (pourquoi pas) mais ‹ ne la rend pas › ?

Ce n’est pas une fuite, c’est le buffer pool de DuckDB qui se remplit une fois et n’est jamais rendu à l’OS.

DuckDB est ouvert avec memory_limit = 30 % de la RAM (models/index.js:214). Son buffer manager prend la mémoire jusqu’à cette limite au premier gros scan et ne l’évince que sous sa propre pression, jamais pour rendre la RAM à l’OS.
20 % de base + ~30 % de buffer pool + le heap Node qui a grossi ≈ les 60 % observés. Le job de 19h30 est simplement le premier à faire des scans lourds (getDeviceFeatureStates sur une plage large après reboot, plus le DELETE + réinsertion de calculateCostFrom).

Tu peux voir combien DuckDB s’alloue au démarrage avec cette commande :
docker logs gladys 2>&1 | grep "DuckDB initialized with memory_limit"

Tu peux tester une limite plus basse avec la variable DUCKDB_MEMORY_LIMIT=512MB dans ton docker-compose. Mais je ne sais pas si ça peut avoir des impacts négatifs sur le reste.

Mais la deuxième instance (celle qui fait le backup) peut souffrir de cette non éviction.
Je pense qu’il faudrait réduire le pool principal pendant la sauvegarde (pour forcer l’éviction et laisser la place pour la sauvegarde).
C’est du développement à faire