Dépanner les requêtes Elasticsearch lentes pour une meilleure expérience utilisateur

Pour quiconque utilise Elasticsearch® comme moteur de recherche, l'identification et le dépannage des requêtes sont des compétences essentielles à maîtriser. Qu'il s'agisse de solutions de recherche pour le commerce électronique, l'observabilité ou l'espace de travail, un Elasticsearch lent aura un impact négatif sur l'expérience de vos utilisateurs.
Pour identifier les requêtes Elasticsearch lentes, vous pouvez utiliser le slow log, qui capture l'exécution de la requête à un certain seuil. Définir correctement le seuil du slow log est un défi en soi. Par exemple, une requête qui prend 500 millisecondes sous une charge complète peut être acceptable, mais la même requête sous une charge faible peut être inacceptable. Le slow log ne fait pas de distinction et enregistre tout ce qui dépasse 500 millisecondes. Le slow log remplit très bien son rôle, vous permettant ainsi de capturer différents niveaux de granularité en fonction de la valeur du seuil. Le traçage, quant à lui, peut examiner toutes les requêtes, en identifiant combien de vos requêtes se situent dans certains seuils.
Le suivi des performances applicatives (APM) ne se limite plus à votre seule application. Grâce à l'instrumentation dans Elasticsearch, nous pouvons désormais ajouter Elasticsearch en tant que service à part entière plutôt que comme une dépendance de votre pile applicative. De cette façon, nous obtenons une vue plus nuancée de la performance que ce que le log lent peut fournir.
Pour l'exemple suivant, notre corpus de données est OpenWebText, qui fournit environ 40 Go de texte brut et environ 8 millions de documents individuels qui s'exécutent localement sur un Macbook M1 Max avec 32 Go de RAM.
Premiers pas
L’activation du traçage dans Elasticsearch s’effectue à l’aide de paramètres statiques (configurés dans le fichier elasticsearch.yml) et de paramètres dynamiques, qui peuvent être activés ou désactivés pendant l’exécution à l’aide d’une commande PUT _cluster/settings, où l’un de ces paramètres dynamiques est le taux d’échantillonnage. Certains paramètres, comme le taux d’échantillonnage, peuvent être modifiés pendant l’exécution. Dans le fichier elasticsearch.yml, nous souhaitons définir les éléments suivants :
Valide pour la version 9.x
telemetry.agent.enabled: true
telemetry.agent.server_url: "url of the APM server"Valide pour les versions 7.x et 8.x
tracing.apm.enabled: true
tracing.apm.agent.server_url: "url of the APM server"Le jeton secret (ou clé API) doit se trouver dans le magasin de clés Elasticsearch. L'outil de magasin de clés devrait être disponible dans <votre répertoire<your elasticsearch install directory>d'installation elasticsearch>/bin/elasticsearch-keystore en utilisant la commande suivante pour les versions 7.x et 8.xelasticsearch-keystore add tracing.apm.secret_token outracing.apm.api_key.Pour la version 9.x, veuillez utiliser telemetry.secret_token ou telemetry.api_key à la place. Ensuite, vous devez redémarrer Elasticsearch. Vous trouverez plus d'informations sur le traçage dans notre document sur le traçage.
Une fois APM actif, nous pouvons consulter la vue APM dans Kibana et constater qu'Elasticsearch capture automatiquement divers points de terminaison d'API REST. Ici, nous nous concentrons principalement sur les appels POST /{index}/_search et voyons ce que nous pouvons en tirer.

En examinant une requête simple directement dans la zone GET /{index}/_rechercher, nous voyons la répartition en cascade suivante. Elle contient des spans internes qui fournissent des informations plus approfondies sur ce qu'Elasticsearch fait en coulisses. Et nous voyons la durée globale de cette action de rechercher (86 millisecondes).

Les métadonnées accompagnant la requête incluent des informations détaillées sur l'en-tête HTTP, l'agent utilisateur, l'emplacement du Node Elasticsearch (métadonnées du fournisseur cloud, nom d'hôte, informations sur le conteneur), certaines informations système et les détails de l'URL. À l'aide de quelques informations de transaction de base, nous pouvons créer un graphique Lens qui trace la durée moyenne des transactions et nous permet de voir s'il existe une tendance à la hausse ou à la baisse.
Notre application de rechercher
C'est agréable de ne plus avoir besoin d'utiliser les logs lents ! Je peux déterminer la durée de la transaction et identifier combien de recherches reçoivent une réponse en dessous d'un seuil donné. Cependant, il y a un inconvénient — Elasticsearch ne capture pas la requête envoyée ; nous savons donc qu'une requête a pris beaucoup de temps, mais nous ne savons pas de quelle requête il s'agissait.
Instrumentons une application de recherche exemple. Dans ce cas, nous utiliserons une application Flask simple avec deux routes, search_single et search_phrase, qui représenteront une requête match et match_phrase dans Elasticsearch. Par exemple, nous pourrions utiliser les requêtes suivantes :
{
"query": {
"match": {
"content": "support"
}
}
}
And
{
"query": {
"match_phrase": {
"content": "support protest"
}
}
}Le code Flask suivant implémente la route search_single. La route search_phrase est très similaire, à la différence qu'elle utilise match_phrase au lieu de match.
@app.route("/search_single", methods=["GET"])
def search_single():
query = request.args.get("q", "")
if not query.strip():
return jsonify({"error": "No search query provided"}), 400
try:
result = es.search(
index=ES_INDEX, query={"match": {"content": query}}
)
hits = result["hits"]["hits"]
response = []
for hit in hits:
response.append(
{
"score": hit["_score"],
"content": hit["_source"]["content"],
}
)
return jsonify(response)Une fois tout cela préparé, je peux maintenant appeler curl -XGET "http://localhost:5000/search_single?q='microphone'" pour rechercher le terme microphone.
Nous ajoutons principalement l'APM à notre application de recherche pour observer, mais nos agents APM capturent les requêtes sortantes et les enrichissent avec des informations de métadonnées. Dans notre cas, le span.db.statement contient la requête Elasticsearch. Et dans le cas ci-dessous, quelqu'un a recherché window.

Mise en commun
Dans mon service Flask, j'ai défini la taille de la requête à 5 000, ce qui signifie qu'Elasticsearch devrait me fournir jusqu'à 5 000 documents correspondants dans une seule réponse JSON. Il s'agit d'un nombre important, et une grande partie du temps est consacrée à la récupération de ce volume de documents depuis le disque. Après l'avoir modifié pour afficher les 100 premiers documents, je peux rapidement identifier ce qui s'est passé dans mon tableau de bord en effectuant une comparaison.
L’examen d’une transaction dans la vue APM et l’activation de la fonction labs pour le chemin critique créent une superposition qui nous montre où notre application passe son temps.

Après cela, j'ai créé un tableau de bord en utilisant les champs transaction.duration.us, es_query_took, transaction.name. Les filtres KQL généraux contiennent service.name, processor.event: transaction, transaction.name: POST /{index}/_search.
Astuce : accédez à la gestion des Data view > sélectionnez votre Data view contenant les flux de données APM > sélectionnez le champ transaction.duration.us > et remplacez le format par duration. Il s'affichera désormais automatiquement dans une sortie lisible par l'homme au lieu des microsecondes.
En tirant parti de la fonctionnalité d'annotation de Lens, nous pouvons constater dans le Lens central que le passage à 100 documents a considérablement réduit la durée moyenne des transactions pour rechercher. Ce n'est pas tout, regardez le nombre total d'enregistrements dans le coin supérieur droit. Comme nous pouvons rechercher plus rapidement, nous bénéficions d'un débit plus élevé ! J'apprécie beaucoup les histogrammes, j'en ai donc créé un au milieu de la ligne supérieure, avec la durée de la transaction sur l'axe des x et le nombre d'enregistrements sur l'axe des y. De plus, APM fournit des indicateurs, ce qui nous permet d'identifier le taux d'utilisation du CPU à tout moment, ainsi que l'utilisation du tas JVM, l'utilisation hors tas, le nombre de threads et d'autres informations utiles.

Conclusion
Cet article de blog vous a montré à quel point il est important d'instrumenter vos applications avec Elasticsearch pour identifier les goulots d'étranglement beaucoup plus facilement. Vous pouvez également utiliser la durée des transactions comme métrique pour la détection des anomalies, effectuer des tests A/B pour votre application et ne plus jamais vous demander si Elasticsearch est plus rapide, puisque vous disposez désormais de données pour répondre à cette question. De plus, toutes les métadonnées collectées, des agents utilisateur aux requêtes, vous aident à résoudre les problèmes.
Les tableaux de bord et la Data view peuvent être importés depuis ici.
avertissement
Il existe un problème avec la durée des transactions dans Elasticsearch. Ce problème est corrigé dans la prochaine version 8.9.1. D'ici là, les transactions utilisent une horloge incorrecte, ce qui perturbe la durée globale.
La publication et la date de publication de toute fonctionnalité ou fonction décrite dans le présent article restent à la seule discrétion d'Elastic. Toute fonctionnalité ou fonction qui n'est actuellement pas disponible peut ne pas être livrée à temps ou ne pas être livrée du tout.