etl : la chaîne passe au quart d'heure et l'agrégat suit #206

Merged
olivier merged 3 commits from lenaic/205-fraicheur-zone-or into develop 2026-09-08 13:55:20 +00:00
Owner

Ferme le #205.

Le défaut

La prévision annoncée H+1 travaillait à trois heures d'écart. Mesuré en production le 08/09 à 12 h 58 :

maintenant                       12:58
dernier seau de mesure_horaire   10:00     179 minutes de retard
prévision émise à 12:35, visant  13:00

La colonne retard_1h_kw du modèle, censée porter la moyenne de l'heure précédant la cible, contenait celle de 10 h pour prédire 13 h.

Deux réglages posés séparément s'additionnaient sans que personne ne les regarde ensemble, et aucun des deux n'était faux seul.

La chaîne ETL tournait une fois par heure, donc les minutes de l'heure H n'arrivaient dans public.mesure qu'à (H+1):27. La 0016 gardait en conséquence un end_offset d'une heure, pour ne pas matérialiser un seau nourri de lignes pas encore chargées. Son commentaire dit l'intention et elle est juste. Le moyen était plus fort que nécessaire : un seau ne se matérialise que s'il tient entièrement dans la fenêtre, donc le seau H, clos à (H+1):00, attendait le passage de (H+2):00.

Le remède, et son ordre

La chaîne passe au quart d'heure : argent :00 :15 :30 :45, or trois minutes derrière, chargement cinq de plus. L'heure H est complète en base à (H+1):08 au pire. La migration 0020 ramène alors end_offset et schedule_interval à quinze minutes, et le seau H devient lisible à (H+1):15.

L'ordre n'est pas indifférent. Toucher l'agrégat en premier matérialiserait des seaux à partir de lignes pas encore chargées. C'est pourquoi les deux partent ensemble, et c'est l'invariant que le nouveau banc tient.

Retard attendu après : 15 à 75 minutes, contre 120 à 180.

Ce que j'ai vérifié plutôt que supposé

La crainte de la 0016 est légitime, il ne suffisait pas de la déclarer infondée. Sur enervision_preprod : une ligne témoin posée dans l'heure EN COURS, puis un refresh_continuous_aggregate sur une fenêtre finissant volontairement trente minutes à l'intérieur de ce seau. Le seau incomplet n'est pas matérialisé, TimescaleDB tronque la fenêtre à la borne de seau inférieure. La préproduction a été remise à l'identique, politique et ligne témoin comprises.

La migration n'appelle pas refresh_continuous_aggregate. J'en avais écrit un. Testé : il refuse de tourner dans une transaction, et run_migrations applique chaque fichier dans une transaction. Il aurait fait échouer le démarrage de l'API, donc le déploiement. Retiré, et le manuel prévient qu'un contrôle joué dans la minute qui suit le déploiement verra encore l'ancien retard.

Le banc a été vu rouge quatre fois avant d'être cru : argent remis à l'heure, end_offset remis à une heure, un refresh ajouté, un chargement collé à :14. Il rougit sur les quatre et ne rougit pas sur la mention en commentaire.

Il déclenchait mon propre banc d'hygiène du #174 (if … | grep -q sous pipefail) et ne le montrait pas, parce que le fichier n'était pas encore suivi par git. Corrigé avant de commettre.

Ce que ça ne fait pas

Le plancher reste de deux seaux d'écart, pas un. Une prévision émise avant le début de son heure cible ne peut pas utiliser l'heure qui la précède, encore en train de se remplir. Descendre à un seul exigerait d'émettre après le début de l'heure visée, donc d'échanger l'avance contre la précision. C'est un arbitrage EC06, posé dans le ticket et volontairement non tranché ici.

La prévision reste horaire : EF-07 en demande une par heure, pas quatre. Elle gagne au passage douze minutes de marge derrière le chargement de :23, contre huit derrière celui de :27.

Ce qu'il faut regarder en relecture

Les minutes de crontab, surtout la marge entre le chargement et le passage de la politique. Et la question de fond : est-ce qu'on veut aussi trancher l'arbitrage avance/précision avant le jury, ou le laisser comme point identifié.

Ferme le #205. ### Le défaut La prévision annoncée H+1 travaillait à trois heures d'écart. Mesuré en production le 08/09 à 12 h 58 : ``` maintenant 12:58 dernier seau de mesure_horaire 10:00 179 minutes de retard prévision émise à 12:35, visant 13:00 ``` La colonne `retard_1h_kw` du modèle, censée porter la moyenne de l'heure précédant la cible, contenait celle de 10 h pour prédire 13 h. Deux réglages posés séparément s'additionnaient sans que personne ne les regarde ensemble, et **aucun des deux n'était faux seul**. La chaîne ETL tournait une fois par heure, donc les minutes de l'heure `H` n'arrivaient dans `public.mesure` qu'à `(H+1):27`. La 0016 gardait en conséquence un `end_offset` d'une heure, pour ne pas matérialiser un seau nourri de lignes pas encore chargées. Son commentaire dit l'intention et elle est juste. Le moyen était plus fort que nécessaire : un seau ne se matérialise que s'il tient entièrement dans la fenêtre, donc le seau `H`, clos à `(H+1):00`, attendait le passage de `(H+2):00`. ### Le remède, et son ordre La chaîne passe au quart d'heure : argent `:00 :15 :30 :45`, or trois minutes derrière, chargement cinq de plus. L'heure `H` est complète en base à `(H+1):08` au pire. La migration `0020` ramène alors `end_offset` et `schedule_interval` à quinze minutes, et le seau `H` devient lisible à `(H+1):15`. **L'ordre n'est pas indifférent.** Toucher l'agrégat en premier matérialiserait des seaux à partir de lignes pas encore chargées. C'est pourquoi les deux partent ensemble, et c'est l'invariant que le nouveau banc tient. Retard attendu après : **15 à 75 minutes**, contre 120 à 180. ### Ce que j'ai vérifié plutôt que supposé **La crainte de la 0016 est légitime**, il ne suffisait pas de la déclarer infondée. Sur `enervision_preprod` : une ligne témoin posée dans l'heure EN COURS, puis un `refresh_continuous_aggregate` sur une fenêtre finissant volontairement trente minutes **à l'intérieur** de ce seau. Le seau incomplet n'est pas matérialisé, TimescaleDB tronque la fenêtre à la borne de seau inférieure. La préproduction a été remise à l'identique, politique et ligne témoin comprises. **La migration n'appelle pas `refresh_continuous_aggregate`.** J'en avais écrit un. Testé : il refuse de tourner dans une transaction, et `run_migrations` applique chaque fichier dans une transaction. Il aurait fait échouer le démarrage de l'API, donc le déploiement. Retiré, et le manuel prévient qu'un contrôle joué dans la minute qui suit le déploiement verra encore l'ancien retard. **Le banc a été vu rouge quatre fois avant d'être cru** : argent remis à l'heure, `end_offset` remis à une heure, un `refresh` ajouté, un chargement collé à `:14`. Il rougit sur les quatre et ne rougit pas sur la mention en commentaire. **Il déclenchait mon propre banc d'hygiène du #174** (`if … | grep -q` sous `pipefail`) et ne le montrait pas, parce que le fichier n'était pas encore suivi par git. Corrigé avant de commettre. ### Ce que ça ne fait pas Le plancher reste de **deux seaux d'écart, pas un**. Une prévision émise avant le début de son heure cible ne peut pas utiliser l'heure qui la précède, encore en train de se remplir. Descendre à un seul exigerait d'émettre après le début de l'heure visée, donc d'échanger l'avance contre la précision. C'est un arbitrage EC06, posé dans le ticket et volontairement non tranché ici. La prévision reste horaire : EF-07 en demande une par heure, pas quatre. Elle gagne au passage douze minutes de marge derrière le chargement de `:23`, contre huit derrière celui de `:27`. ### Ce qu'il faut regarder en relecture Les minutes de crontab, surtout la marge entre le chargement et le passage de la politique. Et la question de fond : est-ce qu'on veut aussi trancher l'arbitrage avance/précision avant le jury, ou le laisser comme point identifié.
lenaic self-assigned this 2026-09-08 13:11:59 +00:00
etl: la chaine passe au quart d'heure et l'agregat suit (#205)
Some checks failed
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 42s
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 7s
Intégration / Workflows — lint et audit de sécurité (pull_request) Failing after 19s
Infra Ansible / Playbooks Ansible valides (pull_request) Failing after 3m0s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m50s
51fa3d7e04
La prevision annoncee H+1 travaillait a trois heures d'ecart. Mesure en
production le 08/09 a 12 h 58 : dernier seau de mesure_horaire a 10 h 00,
pendant que la passe de 12 h 35 visait 13 h 00. La colonne retard_1h_kw du
modele contenait donc la moyenne de 10 h pour predire 13 h.

Deux reglages poses separement s'additionnaient sans que personne ne les
regarde ensemble. Aucun des deux n'etait faux seul.

La chaine ETL tournait une fois par heure : les minutes de l'heure H
n'arrivaient dans public.mesure qu'a (H+1):27. La 0016 gardait donc un
end_offset d'une heure sur mesure_horaire, pour ne pas materialiser un seau
nourri de lignes pas encore chargees. Son commentaire dit l'intention, elle
est juste ; le moyen etait plus fort que necessaire. Un seau se materialise
quand il tient entierement dans la fenetre, donc le seau H, clos a (H+1):00,
attendait le passage de (H+2):00.

La chaine passe au quart d'heure : argent a :00 :15 :30 :45, or trois minutes
derriere, chargement cinq de plus. L'heure H est complete en base a (H+1):08
au pire. La migration 0020 ramene alors end_offset et schedule_interval a
quinze minutes, et le seau H devient lisible a (H+1):15. L'ordre des deux
changements n'est pas indifferent : toucher l'agregat en premier
materialiserait des seaux a partir de lignes pas encore chargees.

Verifie avant d'ecrire la migration, parce que la crainte de la 0016 est
legitime. Sur enervision_preprod, une ligne temoin posee dans l'heure EN COURS
puis un refresh sur une fenetre finissant trente minutes A L'INTERIEUR de ce
seau : le seau incomplet n'est pas materialise, TimescaleDB tronque la fenetre
a la borne inferieure. La preproduction a ete remise a l'identique.

La migration n'appelle pas refresh_continuous_aggregate. Il refuse de tourner
dans une transaction et run_migrations applique chaque fichier dans une
transaction : l'ajouter faisait echouer le demarrage de l'API, donc le
deploiement. Le manuel d'exploitation dit comment le forcer a la main et
previent qu'un controle joue dans la minute qui suit verra encore l'ancien
retard.

Le plancher reste de deux seaux d'ecart, pas un. Une prevision emise avant le
debut de son heure cible ne peut pas utiliser l'heure qui la precede, encore
en train de se remplir. Descendre a un seul exigerait d'emettre apres le debut
de l'heure visee, donc d'echanger l'avance contre la precision : arbitrage
EC06, pose dans le ticket et non tranche ici.

La prevision reste horaire, EF-07 en demande une par heure et pas quatre. Elle
gagne au passage douze minutes de marge derriere le chargement de :23, contre
huit derriere celui de :27.
ci: le banc de fraicheur passe shellcheck (#205)
Some checks failed
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 9s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 24s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 41s
Infra Ansible / Playbooks Ansible valides (pull_request) Failing after 1m56s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m45s
9bf76b5faa
Deux defauts reels, releves par la chaine et non par moi.

SC1087, une ERREUR : « $reglage[[:space:]] » se lit comme une expansion de
tableau. Le motif aurait pu devenir muet sans que rien ne le signale, sur le
controle qui verifie justement que la 0020 tient ses quinze minutes. Accolades
posees.

SC2317 : « derniere_minute » etait definie et jamais appelee. Le calcul se fait
sur place la ou il sert. Retiree plutot que gardee au cas ou.

Verifie avec la meme version que la chaine, koalaman/shellcheck:v0.9.0, sur le
banc et sur les onze autres : aucun n'a de reproche.
olivier requested reviews from olivier and removed review requests for gabriel, justine 2026-09-08 13:34:43 +00:00
ci: vars.yml repasse ansible-lint, une ligne de 103 caracteres (#205)
All checks were successful
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 9s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 24s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 37s
Infra Ansible / Playbooks Ansible valides (pull_request) Successful in 1m35s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m43s
e7597b742b
Le job « Playbooks Ansible valides » sort rouge sur cette branche, et pas sur
le fond du ticket :

  yaml[line-length] group_vars/all/vars.yml:199 -- Line too long
  (103 > 100 characters)

La prose ajoutee sur la mesure des marges a laisse la fin de la phrase
d'origine collee derriere elle, « Aucune des deux ne choisit sa journee --
elles prennent la ». Le paragraphe est replie, le texte est mot pour mot le
meme.

Le niveau « warning » de infra/ansible/.yamllint ne protege pas ici : le
profil moderate d'ansible-lint rend la regle fatale, et c'est bien « 1
violation(s) that are fatal » que le journal annonce.

Verifie : plus aucune ligne au-dela de 100 caracteres dans infra/ansible,
yamllint propre sur les deux arbres, shellcheck propre sur .forgejo/scripts et
tests/ci, et le banc de fraicheur au vert -- toujours rouge, aussi, quand on
remet end_offset a une heure.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
olivier approved these changes 2026-09-08 13:55:16 +00:00
olivier merged commit 10cc241e4a into develop 2026-09-08 13:55:20 +00:00
olivier deleted branch lenaic/205-fraicheur-zone-or 2026-09-08 13:55:20 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
g2/enervision!206
No description provided.