[Bug] - Corrige l'OOM de l'import INSEE - #59
Merged
Conversation
L'import se faisait tuer par l'OOM killer pendant la phase COMMUNES, à la ligne 37 500 du fichier v_commune_2025.csv qui en compte 37 549 — c'est-à-dire au moment précis du flush final. Deux causes distinctes, toutes deux mesurées. D'abord, importData() ne faisait qu'un seul flush, à la fin du fichier, et n'appelait jamais clear(). L'unit of work conservait donc toutes les lignes du fichier courant, mais surtout, clear() n'étant appelé nulle part, les entités des fichiers déjà traités restaient dans l'identity map pour toute la durée d'un run --target=all, soit environ 95 000 entités. Le coût ne vient pas tant du persist() que du flush : persister 37 500 entités ne prend qu'une soixantaine de mégaoctets, ce qui explique que le processus meure exactement à l'entrée du flush terminal et pas avant. On flush et détache désormais toutes les 500 lignes, selon l'idiome déjà en place dans FileParserService::processFile(). Ensuite, la ligne censée économiser de la mémoire dans la commande n'en économisait aucune. setSQLLogger() est l'API DBAL 2 : elle est dépréciée et se contente de renseigner un champ que le middleware de debug ne lit jamais. Le vrai consommateur en mode debug est le BacktraceDebugDataHolder de doctrine-bundle, qui retient chaque requête exécutée pendant toute la vie du processus — environ 290 Mo sur un import de 95 000 INSERT. Ce middleware est câblé à la construction de la connexion et ne peut pas être retiré à chaud ; la commande avertit donc quand elle tourne en mode debug et renvoie vers --no-debug. La dépendance à EntityManagerInterface devenait inutilisée dans la commande, elle est retirée. Mesuré sur --target=all avec une base MySQL neuve, à nombres de lignes identiques (37 548 communes, 42 314 communes historiques, 13 534 mouvements, 95 COMER, 830 outre-mer historiques, 229 pays, 468 pays historiques) : avant 668 Mo 46 s après (debug actif) 310 Mo 44 s après, avec --no-debug 54 Mo 25 s Le pic ne dépend plus de la taille des fichiers, alors qu'il croissait avec elle jusqu'ici. La ligne de progression affiche maintenant la mémoire occupée, pour qu'une régression se voie pendant l'import. Les imports en masse doivent être lancés avec --no-debug. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problème
app:insee:importse fait tuer par l'OOM killer pendant la phase COMMUNES, à la ligne 37 500 du fichierv_commune_2025.csvqui en compte 37 549 — c'est-à-dire au moment précis du flush final.Causes
Deux causes distinctes, toutes deux mesurées.
1.
InseeService::importData()ne flushait qu'une fois, à la fin du fichier, et n'appelait jamaisclear().L'unit of work conservait toutes les lignes du fichier courant. Surtout,
clear()n'étant appelé nulle part, les entités des fichiers déjà traités restaient dans l'identity map pour toute la durée d'un run--target=all, soit environ 95 000 entités.Le coût ne vient pas tant du
persist()que duflush(): persister 37 500 entités ne prend qu'une soixantaine de mégaoctets, ce qui explique que le processus meure exactement à l'entrée du flush terminal et pas avant.2. La ligne censée économiser de la mémoire dans la commande n'en économisait aucune.
setSQLLogger()est l'API DBAL 2 : elle est dépréciée et se contente de renseigner un champ que le middleware de debug ne lit jamais. Le vrai consommateur en mode debug est leBacktraceDebugDataHolderde doctrine-bundle, qui retient chaque requête exécutée pendant toute la vie du processus — environ 290 Mo sur un import de 95 000 INSERT.Correctifs
clear()toutes les 500 lignes, selon l'idiome déjà en place dansFileParserService::processFile(). Rien dans l'import ne relit une entité persistée, le détachement est donc sans risque.--no-debug.EntityManagerInterfacedevenait inutilisée dans la commande, elle est retirée.Mesures
Sur
--target=all, base MySQL neuve, à nombres de lignes identiques (37 548 communes, 42 314 communes historiques, 13 534 mouvements, 95 COMER, 830 outre-mer historiques, 229 pays, 468 pays historiques) :--no-debugLe pic ne dépend plus de la taille des fichiers, alors qu'il croissait avec elle jusqu'ici.
Vérifications
date_fin1962-07-05).InseeImportTest.Note de déploiement
Les imports en masse doivent être lancés avec
--no-debug.🤖 Generated with Claude Code