[spip-check] 5 commits

technova69/spip-check | 5 commits

Par Gilles Vincent, le 3 septembre 2026 à 09h21min :

Merge branch ‹ fix/garde-fous-run-store › into ‹ main ›

fix: borner et rendre rejouable la classification

See merge request technova69/spip-check!30

Ajouté
tests/fixtures/http-cycle.php
Modifié
CHANGELOG.md
spip-check.php
src/SpipCheck.php
templates/spip-check.php.tpl
tests/unit/IndexedScannerTest.php
tests/unit/RunStoreTest.php

Détails : https://git.spip.net/technova69/spip-check/-/commit/7b3c0b3bb3b6e54a6d32434829473e803deacc01

==============================
Par gilles, le 3 septembre 2026 à 08h56min :

fix: cesser de materialiser les journaux pour construire un index

Le bornage precedent ne couvrait que les tranches de files.jsonl. Les
passes qui etablissent un index ou un total lisaient toujours leur
journal en entier et le materialisaient : borne haute et total des
candidats, index de dedoublonnage des constats, index des chemins de
confiance. Le run etant rouvert a chaque requete HTTP, aucun de ces
caches ne survit d’un lot au suivant : chaque lot repayait la lecture
integrale, et la memoire restait proportionnelle a tout ce que le run
avait deja produit.

Le parcours des journaux passe par un generateur, de sorte qu’une passe
d’indexation traverse sans retenir. La borne haute et le total des
candidats se lisent desormais sans decoder : l’identifiant est en tete
de ligne, le total est un nombre de lignes.

Mesure sur 20 000 entrees, un lot de 500 par requete, run rouvert a
chaque lot comme le fait le controleur :

avant   1,33 s   16 Mo
apres   0,74 s    4 Mo

Le pic ne bouge plus avec la longueur du journal.

L’assertion chronometrique du lot precedent est remplacee par un
plafond memoire : le meme scenario est rejoue dans un processus limite
a 14 Mo, ou l’implementation precedente s’epuisait avant la fin. Un
seuil en secondes ne distinguait pas les deux implementations d’une
machine chargee – le meme scenario mesure ici entre 0,72 s et 6,4 s
selon ce qui tourne a cote.

Reste une passe lineaire par requete sur le journal des candidats, en
memoire constante : la supprimer demande de persister une position, ce
que seul l’appelant peut faire.

Ajouté
tests/fixtures/http-cycle.php
Modifié
spip-check.php
src/SpipCheck.php
tests/unit/IndexedScannerTest.php

Détails : https://git.spip.net/technova69/spip-check/-/commit/ac5101e0195c9099976d0efe1beeef038fd51ad0

==============================
Par gilles, le 3 septembre 2026 à 08h41min :

fix: borner la lecture des journaux au lot demande

classifyBatch() et scanBatch() lisaient le journal entier avant
d’entrer dans leur boucle bornee. Le budget de temps ne couvrait donc
pas cette lecture, et la memoire retenue etait proportionnelle a tout
ce qui restait a traiter, quel que soit le lot demande. Une etape
decoupee en n lots payait n fois le decodage integral du journal.

recordsAfter() accepte desormais une borne : la lecture s’arrete des
que le lot est plein, et les lignes deja consommees sont reconnues sur
leur identifiant en tete de ligne, sans etre decodees. classifyBatch()
demande une entree de plus que son lot, de quoi savoir s’il en reste
sans lire la suite ; scanBatch() demande son lot, et une seule entree
pour son test de fin de journal ; scanFile() demande la sienne.

Mesure sur un journal de 20 000 entrees traite par lots de 500 :

avant   1,378 s   16 Mo
apres   0,543 s    4 Mo

La memoire ne depend plus du journal. Le temps residuel est le parcours
des lignes deja consommees, qui reste lineaire : le supprimer demande de
retenir une position dans le journal, ce que l’appelant seul peut
persister. Sur le site de reference – 3 489 entrees, sept lots – la
classification complete tient en 68 ms, sans commune mesure avec le
budget de deux secondes.

Modifié
spip-check.php
src/SpipCheck.php
tests/unit/IndexedScannerTest.php
tests/unit/RunStoreTest.php

Détails : https://git.spip.net/technova69/spip-check/-/commit/90bd2fe885edab73c0ccccabd51dbe09d0dfc35d

==============================
Par gilles, le 3 septembre 2026 à 08h20min :

fix: fonder le total des candidats sur le journal, pas sur l’appelant

Deux defauts introduits par la borne haute du journal des candidats,
tous deux signales en relecture de !30 et reproduits avant correction.

Le total des candidats etait accumule par l’appelant, lot apres lot.
Une requete morte entre une inscription et la persistance de son etat
n’ajoutait jamais son retour au total, alors que ses lignes restaient
dans le journal : la reprise, qui refuse desormais les identifiants
deja atteints, ne les recomptait pas davantage. Le total persiste
devenait inferieur au journal, et scanBatch() s’en sert pour decider
qu’elle a fini – l’analyse declarait terminee avec des candidats
jamais lus. Sur 102 candidats dont 5 ecrits avant l’interruption,
l’analyse s’arretait a 100 et laissait 2 fichiers non examines.

Le total est maintenant lu dans le journal des candidats, tenu au fil
des inscriptions comme la borne haute et dans la meme lecture initiale.
L’appelant assigne au lieu d’additionner. Le compte est exact quel que
soit le decoupage des lots, et ne peut plus deriver ni vers le haut ni
vers le bas.

Le second defaut : classifyRecord() s’arretait des que le candidat
etait refuse. Une requete morte entre l’inscription du candidat et
celle de son constat laissait donc le constat introuvable a jamais –
la reprise voyait le candidat et passait a la suivante. La presence
d’un candidat n’atteste pas que la classification de l’entree soit
allee a son terme : les constats sont desormais ecrits dans tous les
cas. Ils sont idempotents, et l’index de dedoublonnage rend la
reecriture d’un constat deja present gratuite.

Mesure inchangee sur le scenario du modele de menace : 1500 scripts
deposes dans IMG/, 63 ms, 1500 constats.

Modifié
spip-check.php
src/SpipCheck.php
templates/spip-check.php.tpl
tests/unit/IndexedScannerTest.php

Détails : https://git.spip.net/technova69/spip-check/-/commit/c36b85a9acc8f5dc9da4deab4f0051b6397abf08

==============================
Par gilles, le 3 septembre 2026 à 00h30min :

fix: borner et rendre rejouable la classification

Trois garde-fous du run store, a la taille de lot actuelle. Ils sont la
condition prealable a toute consolidation des lots : elargir un lot sans
eux multiplie d’autant la fenetre de perte et le cout quadratique.

  • classifyBatch() accepte un budget de 2 secondes, comme indexBatch() et
    scanBatch() en avaient deja un. Le controle a lieu apres l’enregistrement
    d’une entree, jamais avant : un lot rend toujours au moins une entree,
    sans quoi l’etape se rappellerait indefiniment sans avancer. Le corps de
    boucle est extrait dans classifyRecord() pour que les sorties anticipees
    ne court-circuitent plus le controle du budget.
  • appendCandidate() refuse tout identifiant deja atteint. Les candidats
    etant inscrits par identifiants croissants, une borne haute suffit a
    rendre l’inscription idempotente, sans index ni relecture par appel. Une
    requete interrompue avant d’avoir persiste son classify_after est des
    lors rejouable sans dupliquer le journal ni gonfler le compteur.
  • appendFinding() consulte un index en memoire au lieu de relire
    findings.jsonl a chaque appel. Invalidation sur la taille du journal, et
    explicite a chaque reecriture : une reecriture de meme taille ne doit pas
    passer inapercue.

Mesure sur 1500 scripts hostiles deposes dans IMG/, le cas que l’outil
vise : 1246 ms avant, 70 ms apres, pour des constats identiques.

Modifié
CHANGELOG.md
spip-check.php
src/SpipCheck.php
tests/unit/IndexedScannerTest.php
tests/unit/RunStoreTest.php

Détails : https://git.spip.net/technova69/spip-check/-/commit/398f17c987d6ad45d619767f7f82f4d17b31747b