SpringBatch-ETL : Pipeline ETL Haute Performance
Migration massive MySQL → MySQL : 73M de lignes à ~1,8M lignes/min, avec un moteur de désérialisation binaire via protocole TCP.
Le Défi
Lors de la migration de plateformes e-commerce legacy vers des systèmes modernes, le volume de données devient vite problématique. SpringBatch-ETL devait migrer 73 millions de lignes à ~1,8M lignes/min tout en gérant une contrainte technique spécifique : la désérialisation de données PHP.
Ces informations, stockées en base dans un format sérialisé propre à PHP, devaient être converties en structures JSON exploitables par Java en plein milieu du flux de migration, sans effondrer les performances du système.
Choix Techniques
Pourquoi Java 21 & Spring Batch ?
- Java 21 : Pour sa gestion moderne de l’asynchronisme et la robustesse du typage sur des transformations complexes.
- Spring Batch : Orchestration native par “chunks” (paquets). Il sécurise le flux avec une gestion de transactions robuste.
Le passage du HTTP au TCP
Au début du projet, j’envisageais de créer une API PHP classique pour déléguer la désérialisation. Cependant, la vitesse de traitement est immédiatement devenue un frein. Le protocole HTTP, bien que standard, impose un overhead (headers, handshake complexe) inadapté pour traiter des millions de requêtes unitaires à la volée.
Je suis donc passé sur un daemon Swoole communiquant directement via le protocole TCP. En éliminant les couches applicatives inutiles, j’ai obtenu un outil de désérialisation performant, capable d’encaisser, additionné au parallélisme de Spring Batch.
Le Problème : L’instabilité des scripts unitaires
Utiliser des scripts PHP isolés lancés en ligne de commande pour chaque ligne à transformer aurait transformé une migration de 2h en un processus de plus de 20h, à cause du temps de boot de l’interpréteur PHP à chaque appel.
La Solution : Micro-service Swoole & Parallélisme
J’ai conçu une architecture hybride où le moteur Java pilote la logique de données tout en déléguant la transformation brute :
- Swoole Daemon : Un conteneur PHP tourne en tâche de fond et maintient une connexion TCP ouverte.
- Composite Processor : Côté Java, le pipeline utilise une chaîne de processeurs. Le premier envoie la donnée brute au daemon via TCP, récupère le JSON, et les processeurs suivants s’occupent du mapping des colonnes et des conversions de locales.
- Multi-threading : Le système traite plusieurs tables en simultané grâce à un pool de threads (
ThreadPoolTaskExecutor), exploitant pleinement les capacités CPU disponibles.
Zoom Technique : Configuration Dynamique
Pour éviter de coder un Job spécifique pour chaque table, j’ai rendu le moteur entièrement générique via le fichier application.yml :
- Mapping déclaratif : Il suffit de lister les tables et les renommages de colonnes (ex:
owner_id->product_id) pour que le Job se construise dynamiquement au démarrage. - Injection d’environnement : Toutes les ressources (mémoire JVM, limites InnoDB, ports Swoole) sont pilotées par un fichier
.env, facilitant le déploiement sur différentes infrastructures.
Ce que j’ai appris
- Optimisation des flux : Impact réel des protocoles réseau (TCP vs HTTP) sur le traitement de données de masse.
- Performance MySQL : Tuning des paramètres
innodb_buffer_pool_sizeetmax_allowed_packetpour supporter des écritures massives en batch. - Interopérabilité : Gérer efficacement la communication entre un runtime Java et un service PHP asynchrone.
Rétrospective & Futur
Ce projet a prouvé sa stabilité sur des migrations critiques en environnement Docker. Cependant, pour passer à l’échelle supérieure, je travaille sur une transition vers le Cloud.
L’objectif est de permettre un scaling horizontal complet : en déployant le moteur sur Kubernetes, je pourrai grâce au mapping déclaratif faire tourner plusieurs instances en parallèle, chacune traitant des tables différentes. Cette approche permettra de réduire drastiquement les temps de migration, même pour des bases de données de plusieurs centaines de millions de lignes.