diagnosing-endpoint-performance

Par posthog · skills

Diagnostiquer pourquoi un endpoint PostHog est lent ou coûteux et proposer un correctif concret — augmenter le TTL du cache, activer la matérialisation, restructurer les variables ou réécrire la requête. À utiliser quand l'utilisateur dit « cet endpoint est lent », « mon endpoint expire », « on atteint le plafond de coût sur celui-là », ou demande « devrais-je matérialiser ça ? ». Porte sur un seul endpoint nommé, pas sur un audit global du projet.

npx skills add https://github.com/posthog/skills --skill diagnosing-endpoint-performance

Diagnostic de performance des endpoints

Cette skill guide à travers un endpoint spécifique qui est lent, coûteux ou non fiable, et produit une recommandation concrète. C'est la version approfondie de auditing-endpoints (qui identifie les candidats).

Quand utiliser cette skill

  • « Cet endpoint est lent / expire »
  • « Pourquoi mon endpoint atteint-il le plafond de coûts ? »
  • « Dois-je matérialiser X ? »
  • Un endpoint identifié par auditing-endpoints comme une matérialisation défaillante ou un appelant coûteux
  • L'utilisateur a un endpoint spécifique en tête et veut des conseils

Si la question porte sur le projet dans son ensemble (« que dois-je nettoyer ? »), utilisez d'abord auditing-endpoints.

Outils disponibles

Tool Purpose
endpoint-get Configuration complète de l'endpoint : requête, version actuelle, data_freshness_seconds, statut de matérialisation
endpoint-versions Historique de chaque version (requête + état de matérialisation) ; quelle version est actuelle
endpoint-materialization-status Si la matérialisation est éligible, état actuel, dernière exécution, dernière erreur
endpoints-materialization-preview À quoi ressemblerait la requête matérialisée, plus la raison du rejet si non éligible
endpoints-last-execution-times Quand a-t-il été appelé pour la dernière fois (vérification d'activité au niveau de l'endpoint)
execute-sql Requête query_log pour la fréquence d'appels au niveau de l'endpoint et la durée/les octets par appel

L'arbre de décision

Lors de la décision de recommandation, parcourez ces étapes dans l'ordre — la première qui s'applique est la correction la moins coûteuse.

Étape 1 — Est-il mis en cache du tout ?

Récupérez l'endpoint et regardez data_freshness_seconds (cela définit à la fois le TTL du cache et, quand matérialisé, la cadence de rafraîchissement). Si le trafic de l'utilisateur appelle les mêmes paramètres à plusieurs reprises dans cette fenêtre, chaque appel après le premier est un cache hit et effectivement gratuit.

  • Le TTL est à la valeur par défaut (24h / 86400s) et les données n'ont vraiment pas besoin d'être plus fraîches que cela → fait, aucun changement nécessaire.
  • Le TTL est au plancher de 900s (15 min) et l'utilisateur appelle l'endpoint plusieurs fois par minute → augmentez le TTL. C'est presque toujours le mouvement initial le moins cher. (data_freshness_seconds est une énumération : 900, 1800, 3600, 21600, 43200, 86400, 604800 — il n'existe pas de valeur inférieure à 15 minutes.)
  • Le TTL est au plancher parce que les données doivent être fraîches (par ex. tableau de bord en temps réel) → le cache ne servira à rien, passez à l'étape 2.

La forme des variables compte ici : si chaque appel passe des valeurs user_id ou date_from différentes, le cache a de nombreuses clés distinctes et un TTL plus élevé aide moins. Si presque chaque appel utilise la même poignée de combinaisons de paramètres, le cache aide beaucoup.

Étape 2 — Doit-il être matérialisé ?

La matérialisation pré-calcule la requête dans une vue sauvegardée rafraîchie selon un calendrier. Les lectures deviennent quasi-instantanées — au prix d'une obsolescence égale à l'intervalle de rafraîchissement, plus le stockage et le calcul pour la matérialisation elle-même.

Appelez endpoints-materialization-preview. La réponse vous dit :

  • Éligible + transforme clean → fort candidat. Recommandez d'activer, en particulier pour les endpoints avec des formes de filtre prévisibles (variables, répartitions).
  • Non éligible, avec une raison de rejet → impossible de matérialiser. La raison indique souvent l'étape suivante (voir étape 3 — réécriture).
  • Éligible mais la transforme est compliquée (lots de paires de plage, agrégation complexe re-dérivation) → la matérialisation fonctionnera mais peut ne pas épargner beaucoup. Vaut la peine de signaler avant de basculer.

Quand la matérialisation est activée, les appelants doivent passer toutes les variables matérialisées — les appels sans elles sont rejetés (sécurité : empêche de retourner des données non filtrées). Associez la recommandation avec une note sur les variables qui deviennent requises.

Étape 3 — La requête a-t-elle besoin d'être réécrite ?

Si l'endpoint n'est pas éligible pour la matérialisation, la raison du rejet de endpoints-materialization-preview est généralement la piste :

  • Rejet de répartition de cohorte / mode de comparaison → les répartitions de propriété ordinaires se matérialisent bien ; seules les répartitions de cohorte et le mode de comparaison sont bloqués. Remplacez une répartition de cohorte par une répartition de propriété, ou supprimez le mode de comparaison (exposez la fenêtre de comparaison comme une variable à la place).
  • JOINs combinés avec des variables → un JOIN de haut niveau plus un filtre de variable est rejeté pour la matérialisation, car appliquer la variable change la cardinalité des lignes jointes et produit silencieusement des résultats erronés (par ex. les non-correspondances LEFT JOIN perdent la colonne de variable). Restructurez de sorte que la variable filtre une seule table — poussez le filtre dans une sous-requête/CTE qui est ensuite jointe, plutôt que de filtrer sur la jointure. C'est le piège le plus courant « a l'air bien mais ne se matérialisera pas ».
  • « Variables manquantes » / analyse non délimitée → la requête lit trop de données sans filtre. Encouragez l'ajout d'une variable de fenêtre temporelle requise (par ex. date_from, lookback_days).
  • *HogQL avec `/ fonctions non-déterministes** → réduisez les colonnes sélectionnées, remplaceznow()/today()` par une variable quand possible.

Vérifiez endpoint-versions pour voir si la requête a été récemment modifiée. Souvent la régression provenait d'un commit spécifique et le rétablissement de cette version est plus rapide que la réécriture.

Étape 4 — La version lente est-elle même celle qui est appelée ?

Seule la dernière version s'exécute par défaut ; les versions plus anciennes ne s'exécutent que quand un appelant épingle ?version=N. Donc la version à ajuster est presque toujours la version actuelle — sauf si une version plus ancienne épinglée est le responsable. Appelez endpoint-versions et lisez le last_executed_at de chaque version pour voir quelles versions ont été frappées récemment ; une version matérialisée avec un last_executed_at nul ou long-obsolète est une candidate à dématérialiser ou supprimer plutôt qu'à ajuster (confirmez d'abord — ce signal ne compte que les exécutions de clé API et peut être fragmentaire).

Pour la fréquence d'appels au niveau de l'endpoint et le coût par appel, requêtez query_log avec execute-sql — elle porte query_duration_ms, read_rows, et read_bytes, utiles pour confirmer à quel point les appels de l'endpoint sont vraiment lourds :

SELECT count() AS calls, max(query_start_time) AS last_called, avg(query_duration_ms) AS avg_ms
FROM query_log
WHERE name = '<endpoint_name>' AND endpoint LIKE '%/endpoints/%' AND is_personal_api_key_request

Workflow

  1. Identifiez l'endpoint par nom. Si l'utilisateur a donné une URL, analysez le nom de /api/projects/{team_id}/endpoints/{name}/run.
  2. endpoint-get pour récupérer la configuration complète. Remarque : data_freshness_seconds, is_materialized, version actuelle, type de requête.
  3. endpoint-materialization-status pour voir l'état de la matérialisation (échoué ? éligible ?).
  4. endpoint-versions pour voir l'historique des requêtes et quelle version est actuelle (celle que les appelants frappent sauf s'ils épinglent ?version=N).
  5. Si l'utilisateur a donné des symptômes spécifiques (« expire », « limite de mémoire », « très lent »), parcourez l'arbre de décision ci-dessus. Sinon, présentez les quatre points de contrôle et demandez lequel correspond.
  6. Finissez par une recommandation concrète — augmentez le TTL, matérialisez, réécrivez, ou rétablissez — et confirmez avant de l'appliquer.

Exemple d'interaction

Utilisateur : « monthly_active_users expire pour certains appelants »

Étapes de l'agent :
- endpoint-get monthly_active_users → endpoint HogQL, data_freshness_seconds=900,
  is_materialized=false, version actuelle v4
- endpoint-materialization-status monthly_active_users → non matérialisé ; éligible : true
- endpoint-versions monthly_active_users
  → v4 est actuelle ; v1–v3 sont des révisions de requête antérieures (les appelants frappent v4 sauf s'ils épinglent)
- endpoints-materialization-preview → éligible, transforme clean avec une paire de plage

- « Trois choses se détachent :
   1. Le TTL du cache est 900 secondes (le plancher de 15 min), mais les données
      agrègent un mois — un rafraîchissement aussi agressif n'est probablement pas
      nécessaire. Augmenter à 1 heure (3600s) réduirait dramatiquement
      la charge ClickHouse.
   2. C'est éligible pour la matérialisation avec une transforme clean. Si
      les appelants peuvent tolérer une obsolescence de 5-15 minutes, la matérialisation
      rendrait les lectures quasi-instantanées.
   3. v4 est la version actuelle, donc les appelants la frappent par défaut — la
      cible d'ajustement est claire.

   Victoire la plus rapide : augmentez le TTL à 3600s. Plus grande victoire : matérialisez.

   Quel compromis correspond à vos besoins ? Si vous avez besoin d'une fraîcheur en temps réel,
   ni l'un ni l'autre n'aide et nous aurions besoin de réécrire la requête — probablement
   en réduisant la fenêtre d'agrégation. »

Notes importantes

  • Le cache est presque toujours le premier correctif. C'est gratuit, instantanément réversible, et ne change pas la sémantique des données. Résistez à l'envie de sauter à la matérialisation si un TTL plus élevé suffirait.
  • La matérialisation a des coûts cachés. Stockage de la vue matérialisée, calcul de rafraîchissement, et l'exigence que les appelants passent toutes les variables.
  • Ne réécrivez pas la requête sans l'utilisateur. Un changement de requête crée une nouvelle version et peut casser les appelants !!! Surfacez le changement suggéré, obtenez l'approbation, puis appliquez.
  • Trois signaux d'utilisation. Le last_executed_at de endpoint-get est la récence au niveau de l'endpoint ; endpoint-versions donne le propre last_executed_at de chaque version ; query_log (via execute-sql) donne la fréquence d'appels au niveau de l'endpoint et le coût par appel. Tous ne comptent que les appels de clé API personnelle, et la récence par version peut être fragmentaire — confirmez avec l'utilisateur avant d'appeler une version morte.
  • Le correctif « juste » dépend du SLA, pas de la requête. Demandez toujours à l'utilisateur l'obsolescence acceptable avant de recommander la matérialisation. Une vue matérialisée obsolète de 15 minutes est mauvaise pour un tableau de bord en temps réel, indépendamment de son bon marché.
  • Dites à PostHog ce qui manque. Si le diagnostic rencontre une limitation de produit (une règle d'éligibilité, l'énumération TTL, les variables requises), poussez l'équipe via agent-feedback.

Skills similaires