Filetable· Article 3 sur 3
Requêtes lentes : du symptôme à la cause, sans deviner
Mesurer chaque requête sans ralentir l'application, reconnaître trois formes, et quatre cas réels où le coupable n'était pas celui qu'on croyait.
5 min de lectureRead in English
Quand une page de Filetable est lente, tout pointe vers la base de données. Souvent à tort. Une page lente peut venir d'une requête mal écrite, d'une requête répétée trop souvent, ou d'une base qui n'y est pour rien. La différence ne se voit pas en lisant le code : elle se mesure.
Le contexte : Postgres, et ce qu'il ne dit pas
Filetable stocke tout dans PostgreSQL : les tables des clients, leurs relations, leurs filtres. C'est un choix assumé : une base relationnelle solide, des transactions, des index, et des colonnes JSON quand il faut de la souplesse.
Postgres sait dire quelles requêtes lui coûtent le plus. Mais il ne sait pas quelle page les a lancées, ni combien de temps l'utilisateur a vraiment attendu : entre l'application et la base, il y a un pool de connexions, un réseau, un processeur partagé. Il faut donc mesurer des deux côtés, et relier les deux : chaque requête lente garde la trace de la page qui l'a émise, et chaque page lente renvoie vers ses requêtes, grâce aux traces distribuées d'OpenTelemetry, le standard ouvert de l'observabilité.
Mesurer
Puis lire : du coût à la cause
- 1Le coût par pagetrié par temps total
- 2Ses requêtes lentescombien, médiane, maximum
- 3La tracechaque appel sur une ligne du temps
Le défi. Mesurer chaque requête sans en ralentir aucune. Trois règles : jamais sur le chemin de l'utilisateur, jamais la valeur des paramètres (ce sont des données clients), et jamais sa propre écriture, sinon la première mesure lente finit par se mesurer elle-même.
Trier par la facture, pas par le record
Le p95, la durée sous laquelle tombent 95 % des appels, désigne la page la plus lente, pas la plus coûteuse. Un export presque jamais appelé peut afficher trente secondes et ne gêner personne ; une lecture de deux secondes, appelée à chaque clic, coûte des heures d'attente par jour. On trie donc par temps total : nombre d'appels × durée moyenne.
Lire la forme, pas les valeurs
Une fois la page trouvée, ses requêtes lentes ont presque toujours l'une de ces trois formes, et chacune pointe vers une cause différente.
Lire la forme, pas les valeurs
La trace confirme
La trace d'une requête HTTP montre chaque appel à la base sur une ligne du temps. On compte plutôt qu'on interprète :
Beaucoup d'appels côte à côte
→ N+1 : une seule requête groupée pour tous les éléments.
Un seul appel, long
→ Requête lourde : lire son plan d'exécution, l'index, les statistiques.
Quatre cas réels
Aucun ne se voyait en lisant le code. Tous se voyaient en quelques minutes dans les mesures.
- × 40la même petite requête dans une seule pageN+1
- Cause
- Une boucle : une requête par élément, toutes lancées d'un coup.
- Correctif
- Une requête groupée. Le nombre d'appels ne dépend plus du nombre d'éléments.
- > 10 spour supprimer une seule lignecascade
- Cause
- Les suppressions en cascade, invisibles dans le plan, parcouraient des tables entières faute d'index.
- Correctif
- Un index sur chaque clé étrangère, et une requête qui liste celles qui en manquent.
- × 400plus de temps à compiler la requête qu'à l'exécuterplanificateur
- Cause
- Le compilateur JIT de Postgres se déclenche sur une estimation trop haute, et ne s'annule pas.
- Correctif
- JIT désactivé : à nos volumes, il ralentissait même les gros calculs.
- × 4000entre la durée vue par l'app et celle vue par Postgresressources
- Cause
- L'attente d'une connexion libre, et un processeur bridé par sa limite.
- Correctif
- Limiter les requêtes en parallèle, et surveiller le bridage du conteneur, pas le CPU de la machine.
Ce que j'en retiens
- La forme avant la valeur. Le nombre d'appels et leur dispersion disent déjà où chercher.
- Demander à Postgres ce qu'il en pense. Quand l'application mesure 400 ms et la base 0,1 ms, le problème n'est pas dans la requête.
- Un commentaire qui défend une boucle n'est pas une preuve. Une requête groupée, mesurée, si.