Aller au contenu

Résolution de panne pipeline ETL

Billet #127 : Résolution d'une Panne Complète du Pipeline ETL de Données
Type : Débogage / Correction / Maintenance
Composants concernés : code_source_simule/pipeline.py, code_source_simule/import_data.py, tests/test_pipeline.py


1. Contexte et Symptômes

Une panne silencieuse mais totale du pipeline ETL a été détectée : les prix du marché (fournis par l'API Marketstack) ne se rafraîchissaient plus dans l'interface utilisateur, et les changements de composition du portefeuille (modifications dans le fichier CSV) n'étaient plus reflétés. Le système semblait fonctionner en apparence — les conteneurs tournaient, aucune alerte évidente — mais les données en base stagnaient.


2. Processus d'Investigation

L'investigation a été menée en plusieurs hypothèses successives, chacune étant réfutée ou confirmée par des preuves concrètes.

Hypothèse 1 — Problème de répertoire de travail (config.ini)
Le diagnostic initial pointait vers un chemin relatif config.read('config.ini') dans pipeline.py. Lorsque le script est lancé depuis /app (racine du conteneur), Python cherche config.ini dans ce répertoire, alors que le script se trouve dans /app/code_source_simule/. Cette hypothèse était valide pour l'ancienne version du code.

Hypothèse 2 — Le pipeline ne tourne pas du tout
Réfutée immédiatement grâce aux métriques Marketstack : 596 requêtes API enregistrées entre le 10 et le 13 mars 2026 (149 requêtes exactement par jour ouvrable). Le pipeline s'exécutait bien.

Découverte clé — Décalage entre le code local et la production
L'analyse du volume d'appels API (149 requêtes/jour, soit 1 requête par ticker) révèle que le code en production correspondait à une ancienne version du script — celle qui effectuait un appel par ticker plutôt qu'un appel par batch de 100. Le dernier commit (3c476d1) avait corrigé plusieurs comportements mais n'avait pas été déployé. De plus, ce commit changeait la logique de date (de J-1 au jour courant) sans que les tests soient mis à jour, laissant la CI en rouge sur GitHub.

Découverte en prod — Bug de nettoyage des données
Lors du premier run manuel post-déploiement, une erreur MySQL a interrompu le pipeline :

pymysql.err.DataError: (1366, "Incorrect integer value: '<0.1' for column `historique`.`quantite`")
La colonne No. of Shares du CSV contient des valeurs textuelles comme '<0.1' qui n'étaient pas nettoyées avant l'insertion en base, contrairement à la colonne Price qui bénéficiait déjà de ce traitement.


3. Causes Racines Identifiées

# Cause Fichier Statut
1 Chemin relatif config.read('config.ini') échouant selon le CWD pipeline.py Déjà corrigé dans commit 3c476d1
2 Tests non alignés avec la nouvelle logique de date (J vs J-1) tests/test_pipeline.py Corrigé dans cette session
3 Colonne no_of_shares non nettoyée avant insertion (valeurs type <0.1) pipeline.py Corrigé dans cette session

4. Solutions Implantées

4.1 — Alignement des tests avec la logique de date actuelle
Le pipeline a été modifié (commit 3c476d1) pour traiter la date du jour et non plus J-1. Les deux tests d'intégration qui validaient ce comportement utilisaient encore l'ancienne attente. Ils ont été mis à jour : - date_attendue = date_execution - timedelta(days=1)date_attendue = date_execution - Renommage du test : test_pipeline_requests_and_inserts_data_for_previous_daytest_pipeline_requests_and_inserts_data_for_current_day

4.2 — Suspension temporaire puis restauration de la garde week-end
Afin de permettre une vérification manuelle un samedi, le return None du bloc week-end a été temporairement remplacé par un message d'avertissement, sans bloquer l'exécution. Une fois la validation en production terminée, la logique de blocage des imports le samedi et le dimanche a été restaurée.

4.3 — Nettoyage de la colonne no_of_shares
Ajout dans read_and_clean_csv() d'un nettoyage identique à celui déjà appliqué à la colonne price : suppression des caractères non numériques par regex, puis conversion en float avec repli à 0.0.

if 'no_of_shares' in df.columns:
    df['no_of_shares'] = df['no_of_shares'].astype(str).str.replace(r'[^0-9.]', '', regex=True)
    df['no_of_shares'] = pd.to_numeric(df['no_of_shares'], errors='coerce').fillna(0.0)

4.4 — Renforcement de la suite de tests et de la documentation de couverture
Un test unitaire dédié a été ajouté pour verrouiller la correction CSV des quantités spéciales (<0.1, nombres avec espaces) : - test_read_and_clean_csv_no_of_shares_special_values_are_numeric

Le rapport de couverture a été mis à jour en conséquence : - Pipeline : 42% → 60% - Global : 70% → 76%


5. Vérification et Résultat

  • Suite de tests locale (tests/test_pipeline.py) : 10/10 tests passent après les corrections.
  • Run manuel en prod : Le pipeline s'est exécuté sans erreur, a contacté l'API Marketstack pour 149 tickers, et a écrit les données en base.
  • Interface utilisateur : Les données sont à jour et s'affichent correctement dans le dashboard.
  • Stabilisation post-validation : la garde week-end a été remise en place pour empêcher les imports en dehors des jours ouvrés.

6. Leçons et Points de Vigilance

  1. Un pipeline silencieux n'est pas forcément un pipeline sain. Les métriques externes (logs API, volume de requêtes) sont des sondes de diagnostic essentielles quand les logs applicatifs sont inaccessibles.
  2. Tout changement de comportement métier impose une mise à jour synchrone des tests. Le décalage entre le code corrigé et les tests a maintenu la CI en rouge, brouillant le signal de qualité.
  3. La robustesse des données d'entrée doit être exhaustive. Le même pattern de nettoyage appliqué à price aurait dû l'être dès le départ à toutes les colonnes numériques issues du CSV, dont no_of_shares.