Performance · Architecture · 7 min de lecture
Choisir le bon moteur d'exécution, et savoir ce qu'il fait vraiment
Le moteur ne décide pas seulement de la vitesse. Il décide aussi d'où vos données atterrissent, et c'est là que les surprises arrivent.
Dans Dataiku, la plupart des recipes peuvent s'exécuter sur plusieurs moteurs. Le choix est souvent laissé par défaut, et la question ne revient que le jour où un traitement devient trop lent.
C'est une erreur de cadrage. Le moteur ne décide pas seulement de la vitesse d'exécution : il décide aussi d'où le calcul a lieu et de comment le résultat est écrit. Et c'est là que se cachent les vraies surprises.
Le principe : rapprocher le calcul des données
La règle de base tient en une phrase : mieux vaut faire travailler le système qui détient déjà les données que de les déplacer pour les traiter ailleurs.
Quand une recipe lit une table d'une base de données et y réécrit, exécuter le calcul dans cette base évite deux transferts complets. C'est le pushdown SQL, et c'est presque toujours le bon choix quand il est disponible.
À l'inverse, un traitement exécuté sur le DSS lui-même fait transiter toutes les données par l'instance. Sur quelques milliers de lignes, personne ne le remarque. Sur plusieurs dizaines de millions, l'instance devient le goulot d'étranglement de toute la plateforme, y compris pour les autres projets.
Ce qu'on fait par défaut, et les conséquences
Le réflexe courant est de laisser le moteur proposé et de ne s'en occuper qu'en cas de lenteur. Trois conséquences, par ordre de gravité croissante.
La lenteur. La plus visible, et paradoxalement la moins grave : on la constate, on corrige.
La charge sur l'instance. Un traitement mal placé ne ralentit pas seulement lui-même, il ralentit tout le monde. Ce coût est diffus et rarement attribué au bon projet.
L'écriture inattendue des données. Celle-là ne se voit pas du tout, et c'est la plus coûteuse.
Le piège qui m'a coûté le plus de temps
Une table mutualisée, alimentée par une recipe d'empilement, est devenue illisible. Les fichiers étaient bien présents sur le système de fichiers : on pouvait les voir, les compter, vérifier leur taille. Aucune requête SQL ne remontait la moindre ligne. Et aucune erreur, ni à l'écriture, ni à la lecture.
La cause : le moteur Hive, lorsqu'il réalise une union, n'écrit pas directement dans le répertoire cible. Il crée des sous-répertoires, un par branche, et y place les fichiers. Le lecteur regarde le répertoire de la partition, n'y trouve rien, et retourne un résultat vide. De son point de vue, la partition est simplement vide.
Des fichiers visibles sur le disque et une table vide en requête : avant d'analyser les métadonnées, regardez à quel niveau de l'arborescence les fichiers se trouvent réellement.
La correction a tenu en un changement de moteur. Aucune réécriture du traitement : la logique métier n'était pas en cause.
On peut aussi activer la lecture récursive côté lecteur, mais ça déplace le problème. Chaque consommateur doit être configuré, et le prochain retombera dans le piège. Corriger à la source vaut mieux que demander à tous les lecteurs de s'adapter.
Comment je choisis
- Où sont les données ?
- si tout vient d'une même base et y retourne, le pushdown SQL est presque toujours le bon choix
- Quel volume ?
- quelques milliers de lignes tolèrent n'importe quel moteur ; au-delà de quelques millions, le choix devient structurant
- Quel type d'opération ?
- filtres, jointures et agrégations se poussent bien en base ; une logique complexe en code appelle un autre moteur
- Où le résultat sera-t-il écrit ?
- et sous quelle forme exactement, c'est la question qu'on oublie
Avant d'optimiser : regarder où le volume se réduit
Changer de moteur n'est pas toujours la bonne réponse. Avant ça, je regarde la structure du flow.
Le gain le plus fréquent vient du repositionnement des filtres. Filtrer dix millions de lignes à l'étape huit plutôt qu'à l'étape deux fait travailler six étapes pour rien. C'est souvent plus rentable qu'un changement de moteur, et ça ne casse rien.
- Je sais sur quel moteur chaque recipe lourde s'exécute, et pourquoi
- Les traitements qui lisent et écrivent dans la même base sont poussés en base
- Aucun traitement volumineux ne transite inutilement par l'instance DSS
- Les filtres sont positionnés le plus en amont possible
- Après un changement de moteur, j'ai vérifié non seulement la durée mais le contenu produit
- Sur une recipe d'empilement, j'ai vérifié la structure réelle des répertoires écrits
- Les durées d'exécution sont suivies dans le temps, pour repérer les dérives
Une question sur ce sujet ?
Si ce cas recoupe un problème que vous rencontrez, écrivez-moi.