mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-13 18:28:03 +02:00
Chargement de la conversation : compter les reprises, pas les millisecondes
TestLoadConversationAbsenceLegitimeResteRapide vérifie qu'une absence légitime — première installation, rien d'enregistré — ne déclenche aucune reprise. Il le mesurait au chronomètre : « moins de 250 ms », la durée d'une attente de reprise. Le temps de mur ne dit pas ça. Il mêle le coût du PREMIER essai (créer et ouvrir la base bbolt, fsync compris) à l'attente qu'on cherche à exclure. Sur un runner CI chargé, cet essai unique a pris 435 ms : le test a échoué alors qu'aucune reprise n'avait eu lieu, et le même commit passait une minute plus tôt sur un autre runner. LoadConversation compte désormais ses reprises (loadConvRetries), et le test lit ce compteur. Ce qu'il affirme est enfin ce qu'il mesure, et il ne dépend plus de la charge de la machine. Le test de la base illisible vérifie le compteur dans l'autre sens (quatre reprises attendues) : sans ça, un compteur bloqué à zéro rendrait la première assertion vraie sans rien prouver. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0129sffVC43rezAXUMQuzUog
This commit is contained in:
2 files changed
+33
-6
No files matched your search
@@ -8,6 +8,7 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -88,6 +89,18 @@ const (
|
||||
loadConvRetryWait = 250 * time.Millisecond
|
||||
)
|
||||
|
||||
// loadConvRetries compte les ATTENTES de reprise du dernier LoadConversation
|
||||
// (0 = le premier essai a suffi). Il n'existe que pour être observable.
|
||||
//
|
||||
// Le test de non-régression veut vérifier qu'une absence légitime — première
|
||||
// installation, rien d'enregistré — ne déclenche AUCUNE reprise. Il le mesurait
|
||||
// au chronomètre (« moins de 250 ms »), ce qui confond « aucune reprise » avec
|
||||
// « une seule tentative, mais lente » : sur un runner CI chargé, créer et
|
||||
// ouvrir la base bbolt a pris 435 ms et le test a échoué alors qu'aucune reprise
|
||||
// n'avait eu lieu. On compte donc les reprises au lieu de les déduire d'un
|
||||
// temps de mur, qui ne dépend pas que de nous.
|
||||
var loadConvRetries atomic.Int32
|
||||
|
||||
// LoadConversation recharge l'état persisté au démarrage du process. Sans état
|
||||
// enregistré (première fois) on part d'une conversation vide.
|
||||
//
|
||||
@@ -101,11 +114,16 @@ const (
|
||||
func LoadConversation() {
|
||||
var b []byte
|
||||
var err error
|
||||
loadConvRetries.Store(0)
|
||||
retry := func() {
|
||||
loadConvRetries.Add(1)
|
||||
time.Sleep(loadConvRetryWait)
|
||||
}
|
||||
for attempt := 0; attempt < loadConvAttempts; attempt++ {
|
||||
// Sonde : lire la clé de la discussion active en gardant l'erreur. Tant
|
||||
// qu'elle échoue, convEnsureActive ne doit surtout pas être appelée.
|
||||
if _, err = getBytesErr(bkChat, ckActive); err != nil {
|
||||
time.Sleep(loadConvRetryWait)
|
||||
retry()
|
||||
continue
|
||||
}
|
||||
// convEnsureActive reprend au passage le fil unique des versions
|
||||
@@ -113,7 +131,7 @@ func LoadConversation() {
|
||||
if b, err = getBytesErr(bkChat, convKey(convEnsureActive())); err == nil {
|
||||
break
|
||||
}
|
||||
time.Sleep(loadConvRetryWait)
|
||||
retry()
|
||||
}
|
||||
if err != nil {
|
||||
// Toujours en échec : on le DIT au lieu de repartir à vide en silence, et
|
||||
|
||||
@@ -108,6 +108,12 @@ func TestLoadConversationBaseIllisibleNeForgePasDeDiscussion(t *testing.T) {
|
||||
if max := time.Duration(loadConvAttempts)*loadConvRetryWait + 2*time.Second; elapsed > max {
|
||||
t.Fatalf("démarrage retardé de %v par une base illisible (budget %v)", elapsed, max)
|
||||
}
|
||||
// Contrepartie de l'assertion « aucune reprise » du test d'absence légitime :
|
||||
// un compteur bloqué à zéro la rendrait vraie sans rien prouver. Ici les
|
||||
// reprises ont bel et bien lieu, et on le vérifie.
|
||||
if n := loadConvRetries.Load(); int(n) != loadConvAttempts {
|
||||
t.Fatalf("%d reprise(s) comptée(s) sur une base illisible, attendu %d", n, loadConvAttempts)
|
||||
}
|
||||
after, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -130,12 +136,15 @@ func TestLoadConversationAbsenceLegitimeResteRapide(t *testing.T) {
|
||||
t.Setenv("LOKI_HOME", t.TempDir())
|
||||
resetConvForTest()
|
||||
|
||||
start := time.Now()
|
||||
LoadConversation()
|
||||
elapsed := time.Since(start)
|
||||
|
||||
if elapsed >= loadConvRetryWait {
|
||||
t.Fatalf("absence légitime traitée comme une erreur : %v d'attente, aucune reprise ne devait être déclenchée", elapsed)
|
||||
// Mesuré sur le COMPTEUR de reprises, pas au chronomètre : une absence
|
||||
// légitime doit sortir au premier essai. Le temps de mur ne disait pas ça —
|
||||
// il mêlait le coût de l'essai lui-même (création de la base bbolt, fsync)
|
||||
// à l'attente de reprise, et un runner CI chargé l'a fait échouer à 435 ms
|
||||
// alors qu'aucune reprise n'avait été déclenchée.
|
||||
if n := loadConvRetries.Load(); n != 0 {
|
||||
t.Fatalf("absence légitime traitée comme une erreur : %d reprise(s) déclenchée(s), aucune ne devait l'être", n)
|
||||
}
|
||||
conv.mu.Lock()
|
||||
n := len(conv.Messages)
|
||||
|
||||
Reference in new issue
Block a user