Deux angles morts fermés. 1. Syntaxe SQL sans conteneur (apps/api/test/sqlSyntax.test.js) Le faux pool vérifiait la FORME du SQL mais ne l'exécutait jamais : une requête syntaxiquement invalide passait tous les tests et n'échouait qu'en production — c'est précisément ce qui s'était produit avec crawler_pending. Chaque requête réellement émise par les 28 routes est désormais parsée avec la grammaire PostgreSQL (node-sql-parser), y compris les SET dynamiques du PATCH et les casts par type de resolve-conflict. 48 tests, aucun conteneur. Limite déclarée explicitement : la sémantique n'est pas validée, et deux requêtes bâties sur jsonb_build_object ne sont pas parsables — le test échoue si une route cesse d'avoir la moindre requête vérifiable, pour éviter qu'il passe au vert à vide. 2. Supervision des services de fond (migration 015) Le crawler et les workers ne sont pas exposés par Traefik : aucune sonde HTTP ne peut les atteindre. Un crawler dont FlareSolverr était injoignable, ou un worker à court de quota Geoapify, restait muet — le seul symptôme était l'absence de données nouvelles, qu'il fallait remarquer soi-même. Chaque service écrit un battement de cœur horaire dans service_health : - crawler : base, FlareSolverr joignable, dernier run terminé < 12 h - geocoder : base, clé Geoapify présente, API joignable, progression < 6 h Une ligne par service, écrasée à chaque contrôle. L'API calcule `stale` en SQL (> 2 h sans écriture) : un service arrêté cesse d'écrire, et son dernier contrôle réussi le ferait sinon passer pour sain indéfiniment. Le Pilotage affiche une carte « Services » et remonte chaque service dégradé ou silencieux en alerte actionnable. Règle appliquée aux sondes : aucune ne peut interrompre le service qu'elle surveille. Toute exception devient un échec de sonde, l'écriture du résultat et la journalisation échouent en silence. Un contrôle de santé qui fait tomber le crawler serait pire que pas de contrôle. Tests : 580 JS (+51), 63 Python (+20), 85 Playwright (+3) Co-Authored-By: Claude <noreply@anthropic.com>
24 lines
1.4 KiB
SQL
24 lines
1.4 KiB
SQL
-- 015_service_health.sql
|
|
--
|
|
-- Supervision des services de fond (crawler, geocoder, groq-worker).
|
|
--
|
|
-- Ils tournent sans être exposés par Traefik : aucun healthcheck HTTP ne peut
|
|
-- les atteindre. Jusqu'ici, un crawler dont FlareSolverr était injoignable, ou
|
|
-- un worker à court de quota Geoapify, restait silencieux — le seul symptôme
|
|
-- était l'absence de données nouvelles, qu'il fallait remarquer soi-même.
|
|
--
|
|
-- Chaque service écrit désormais un battement de cœur avec le résultat de ses
|
|
-- propres sondes (base joignable, dépendance externe joignable, données qui
|
|
-- avancent). Une seule ligne par service, mise à jour en place : l'historique
|
|
-- détaillé est déjà dans crawl_log, on ne veut pas d'une table qui grossit.
|
|
|
|
CREATE TABLE IF NOT EXISTS service_health (
|
|
service TEXT PRIMARY KEY, -- 'crawler' | 'geocoder' | 'groq-worker'
|
|
ok BOOLEAN NOT NULL,
|
|
checks JSONB NOT NULL DEFAULT '{}', -- { "database": true, "flaresolverr": false, ... }
|
|
error TEXT, -- première sonde en échec, lisible
|
|
checked_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
COMMENT ON TABLE service_health IS
|
|
'Battement de cœur des services de fond. Une ligne par service, écrasée à chaque contrôle. Un checked_at trop ancien signale un service arrêté ou bloqué — c''est aussi informatif qu''un ok=false.';
|