From 9b2e974942f2e3351b69fb4029d976486fd9d3a1 Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Sun, 4 Oct 2026 02:01:11 +0200 Subject: [PATCH] =?UTF-8?q?Moniteur=20:=20corrections=20de=20relecture=20?= =?UTF-8?q?=E2=80=94=20un=20nvidia-smi=20pendu=20ne=20fige=20plus=20les=20?= =?UTF-8?q?jauges?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gpuStatsCached tient son verrou pendant la requête : c'est ce qui fait partager une seule lecture aux onglets. Mais la requête n'était pas bornée, et le code le dit ailleurs (nvidiaGPUCount) : un pilote coincé peut faire pendre nvidia-smi. Avant le cache, chaque nouvelle requête relançait son propre nvidia-smi et les jauges revenaient avec le pilote ; avec le verrou, un seul processus pendu bloquait /api/vram pour tous les onglets jusqu'au redémarrage de Loki. - nvidiaSmiQuery : CommandContext borné à 10 s (large pour une première lecture lente sans mode persistant), WaitDelay d'une seconde. Au-delà, une erreur ordinaire, retentée au délai normal du cache. - Le déchargement profite de la même borne : une lecture pendue devient une mesure ratée, que gpuSettle sait déjà sauter. - Test : la vraie requête, binaire absent du PATH, rend bien exec.ErrNotFound — la seule erreur que le cache garde une minute. Co-Authored-By: Claude Opus 5.5 --- internal/loki/web_vram.go | 18 ++++++++++++++++-- internal/loki/web_vram_test.go | 11 +++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/internal/loki/web_vram.go b/internal/loki/web_vram.go index 41d42ba..c58e201 100644 --- a/internal/loki/web_vram.go +++ b/internal/loki/web_vram.go @@ -13,6 +13,7 @@ package loki import ( + "context" "encoding/json" "errors" "net/http" @@ -36,12 +37,25 @@ type gpuStat struct { // nvidiaSmiQuery lance la requête nvidia-smi. Variable pour que les tests // substituent un faux exécutable et comptent les lancements. +// +// Bornée dans le temps, comme nvidiaGPUCount : un pilote coincé peut faire +// pendre nvidia-smi, et gpuStatsCached tient son verrou pendant la requête. Sans +// borne, un seul nvidia-smi pendu figeait les jauges de TOUS les onglets +// jusqu'au redémarrage de Loki, même une fois le pilote revenu. Dix secondes : +// large pour une première lecture lente (pilote sans mode persistant, plusieurs +// cartes), une erreur ordinaire au-delà — retentée au délai normal. var nvidiaSmiQuery = func() ([]byte, error) { - return hideCmd(exec.Command("nvidia-smi", + ctx, cancel := context.WithTimeout(context.Background(), nvidiaSmiTimeout) + defer cancel() + cmd := hideCmd(exec.CommandContext(ctx, "nvidia-smi", "--query-gpu=name,memory.used,memory.total,utilization.gpu,temperature.gpu", - "--format=csv,noheader,nounits")).Output() + "--format=csv,noheader,nounits")) + cmd.WaitDelay = time.Second // un descendant qui garderait la sortie ouverte ne retient pas Wait + return cmd.Output() } +const nvidiaSmiTimeout = 10 * time.Second + // gpuStats interroge nvidia-smi. Renvoie nil quand il est absent (machine sans // GPU NVIDIA, Mac, CPU seul) : l'absence de mesure n'est pas une erreur, elle // prive juste le déchargement de son bilan chiffré. Toujours une lecture diff --git a/internal/loki/web_vram_test.go b/internal/loki/web_vram_test.go index c211cd7..b08f263 100644 --- a/internal/loki/web_vram_test.go +++ b/internal/loki/web_vram_test.go @@ -170,3 +170,14 @@ func TestGpuSettleSampleFrais(t *testing.T) { t.Errorf("jauges : %d, attendu la lecture en cache 19000", g[0].Used) } } + +// Le cache ne mémorise une minute que l'ABSENCE de nvidia-smi : il faut donc que +// la vraie requête (bornée dans le temps) rende toujours exec.ErrNotFound quand +// le binaire n'est pas dans le PATH — sinon Mac et CPU seul relanceraient une +// recherche toutes les deux secondes. +func TestNvidiaSmiQueryAbsent(t *testing.T) { + t.Setenv("PATH", t.TempDir()) + if _, err := nvidiaSmiQuery(); !errors.Is(err, exec.ErrNotFound) { + t.Fatalf("erreur %v, attendu exec.ErrNotFound", err) + } +}