mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-22 22:48:11 +02:00
Arrêt du moteur : attendre tout l'arbre, pas seulement le chef
En conteneur, userSvcStop n'attendait que le processus `loki serve`. Avec Strata, le moteur qui tient la VRAM (cache d'experts, ~9 Go) est un autre processus qui finit de s'arrêter après le serveur Python : la relance suivante démarrait llama-server sur une carte encore pleine (« cudaMalloc failed: out of memory » à l'encodeur de vision) et Strata mesurait la RAM disponible avec l'ancien moteur encore chargé (mode mmap choisi à tort). stopProcTree relève l'arbre (session, groupe et descendants) avant tout signal, SIGTERM puis SIGKILL aux survivants, et ne revient qu'une fois tout parti. Diagnostic : une pile d'appels citant load_model ne passe plus pour une nouvelle tentative (la cause mémoire était perdue), et le chat donne l'échec de chargement au lieu de « il démarre, réessaie ». Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014wfx8uW1un1WwWJ6xzAbkV
This commit is contained in:
6 files changed
+295
-13
No files matched your search
@@ -0,0 +1,83 @@
|
||||
//go:build linux
|
||||
|
||||
package loki
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestParseProcStat(t *testing.T) {
|
||||
st, ok := parseProcStat("4242 (python3 (x) y) S 4200 4100 4000 0 -1 4194560 0 0 0 0 0 0 0 0 20 0 1 0 987654 0 0")
|
||||
if !ok || st.state != "S" || st.ppid != 4200 || st.pgrp != 4100 || st.sid != 4000 || st.start != "987654" {
|
||||
t.Fatalf("lecture fausse : %+v ok=%v", st, ok)
|
||||
}
|
||||
if _, ok := parseProcStat("4242 (tronqué"); ok {
|
||||
t.Error("ligne sans parenthèse fermante acceptée")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcTreeOf(t *testing.T) {
|
||||
tab := map[int]procStat{
|
||||
100: {ppid: 1, pgrp: 100, sid: 100, start: "a"}, // chef (Setsid)
|
||||
101: {ppid: 100, pgrp: 100, sid: 100, start: "b"},
|
||||
200: {ppid: 101, pgrp: 200, sid: 200, start: "c"}, // moteur dans sa propre session
|
||||
201: {ppid: 200, pgrp: 200, sid: 200, start: "d"},
|
||||
300: {ppid: 1, pgrp: 300, sid: 300, start: "e"}, // étranger
|
||||
400: {ppid: 1, pgrp: 100, sid: 100, start: "f"}, // orphelin rattaché à PID 1
|
||||
500: {ppid: 100, pgrp: 100, sid: 100, start: "g"}, // nous-mêmes
|
||||
}
|
||||
got := map[int]bool{}
|
||||
for _, r := range procTreeOf(tab, 100, 500) {
|
||||
got[r.pid] = true
|
||||
}
|
||||
for _, pid := range []int{100, 101, 200, 201, 400} {
|
||||
if !got[pid] {
|
||||
t.Errorf("PID %d oublié", pid)
|
||||
}
|
||||
}
|
||||
if got[300] || got[500] {
|
||||
t.Errorf("hors de l'arbre pris : %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
// Le chef meurt vite, son enfant (dans une autre session, comme le moteur de
|
||||
// Strata) traîne à s'arrêter : stopProcTree ne revient qu'une fois tout parti.
|
||||
func TestStopProcTreeWaitsForChildren(t *testing.T) {
|
||||
// Le chef lance un enfant qui ignore SIGTERM dans sa propre session, écrit
|
||||
// son PID, puis attend.
|
||||
pidFile := t.TempDir() + "/child"
|
||||
script := `setsid sh -c 'trap "" TERM; echo $$ > ` + pidFile + `; while :; do sleep 0.1; done' & wait`
|
||||
cmd := exec.Command("sh", "-c", script)
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
|
||||
if err := cmd.Start(); err != nil {
|
||||
t.Skip("sh indisponible :", err)
|
||||
}
|
||||
go cmd.Wait() // pas de zombie pour le chef
|
||||
var child int
|
||||
for i := 0; i < 50 && child == 0; i++ {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
if b, err := os.ReadFile(pidFile); err == nil {
|
||||
child, _ = strconv.Atoi(strings.TrimSpace(string(b)))
|
||||
}
|
||||
}
|
||||
if child == 0 {
|
||||
t.Fatal("l'enfant n'a pas démarré")
|
||||
}
|
||||
st, ok := readProcStat(child)
|
||||
if !ok || st.sid == cmd.Process.Pid {
|
||||
t.Fatalf("l'enfant devait avoir sa propre session : %+v", st)
|
||||
}
|
||||
ref := procRef{pid: child, start: st.start}
|
||||
if left := stopProcTree(cmd.Process.Pid, 300*time.Millisecond, 5*time.Second); len(left) != 0 {
|
||||
t.Fatalf("survivants : %v", left)
|
||||
}
|
||||
if procRefAlive(ref) {
|
||||
t.Fatal("l'enfant tourne encore au retour de stopProcTree")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user