Aller au contenu principal

Configurez un test de charge pour les Endpoint de recherche IA

Les tests de charge mesurent les performances d'un Endpoint AI Search sous trafic afin que vous puissiez confirmer sa préparation à la production avant le déploiement. Cette page fournit des conseils, des exemples de code et un Notebook d'exemple pour les tests de charge des Endpoint de recherche AI. Un test de charge peut vous informer sur :

  • Latence à différents niveaux de mise à l'échelle
  • Limites de throughput et goulots d'étranglement (requêtes par seconde, répartition de la latence)
  • Taux d'erreur sous charge soutenue
  • Utilisation des ressources et planification de la capacité

Pour plus d'informations sur les tests de charge et les concepts associés, consultez Tests de charge pour les serving endpoints.

Exigences

Avant de commencer ces étapes, vous devez disposer d'un Endpoint de recherche IA déployé et d'un Service Principal avec les autorisations Can Query sur l'Endpoint. Consultez l'étape 1 : Configurez l'authentification du Service Principal.

Download et importez une copie des fichiers suivants et de l'exemple de notebook dans votre workspace Databricks :

  • input.json. Il s'agit d'un exemple du fichier input.json qui spécifie la charge utile envoyée par toutes les connexions concurrentes à votre endpoint. Vous pouvez avoir plusieurs fichiers si nécessaire. Si vous utilisez l'exemple de notebook, ce fichier est généré automatiquement à partir de la table d'entrée fournie.
  • fast_vs_load_test_async_load.py. Upload ce script vers votre Workspace (par exemple, /Workspace/Users/<your-username>/fast_vs_load_test_async_load.py) et définissez le paramètre de Notebook locust_script_path à son chemin. Ce script gère l’authentification, la livraison de la charge utile et la collecte des métriques de débogage.
  • Le notebook d'exemple suivant, qui exécute les tests de charge. Pour des performances optimales, exécutez ce Notebook sur un cluster à nœud unique avec un grand nombre de cœurs (Locust s’adapte à tous les processeurs disponibles). Une mémoire élevée est recommandée pour les query avec des embeddings pré-générés.

Exemple de notebook et démarrage rapide

Utilisez l'exemple de Notebook suivant pour start. Il prend en charge deux modes d'exploration : un balayage progressif qui teste les niveaux de concurrence spécifiques que vous définissez, et un mode de recherche binaire qui trouve automatiquement le QPS maximal durable (point de rupture) en quelques étapes. Tous les paramètres sont configurés à l'aide de widgets, de sorte que le Notebook peut s'exécuter de manière interactive ou en tant que Job Databricks sans modifications de code.

Notebook de test de charge Locust

Cadre de tests de charge : Locust

Locust est un framework open source de test de charge qui vous permet d'effectuer les opérations suivantes :

  • Faire varier le nombre de connexions client simultanées
  • Contrôlez la vitesse à laquelle les connexions s’établissent.
  • Mesurez les performances de l'Endpoint tout au long du test.
  • Détecter et utiliser automatiquement tous les cœurs de processeur disponibles.

Le Notebook d'exemple utilise le drapeau --processes -1 pour détecter automatiquement les cœurs de CPU et les utiliser pleinement.

Si Locust est limité par le CPU, un message apparaît dans la sortie.

Étape 1 : Configurer l'authentification du Service Principal

important

Pour des tests de performance de type production, utilisez toujours l'authentification du Service Principal OAuth. Les Service Principals offrent un temps de réponse jusqu'à 100 ms plus rapide et des limites de taux de requêtes plus élevées par rapport aux jetons d'accès personnels (PAT).

Créer et configurer un Service Principal

  1. Créer un Service Principal Databricks. Pour obtenir des instructions, consultez Ajouter des Service Principals à votre compte.

  2. Accorder les autorisations :

    • Accédez à la page de votre endpoint de recherche IA.
    • Cliquez sur Autorisations .
    • Accordez au Service Principal les autorisations Can Query .
  3. Créer un secret OAuth.

    • Accédez à la page des détails du Service Principal.
    • Cliquez sur l'onglet tab .
    • Cliquez sur Générer le secret .
    • Définir la durée de vie (recommandé : 365 jours pour les tests à long terme).
    • Copiez immédiatement l'ID client et le secret .
  4. Stockez les identifiants en toute sécurité.

    • Créer un Secret Scope Databricks. Pour obtenir des instructions, consultez Tutoriel : Créer et utiliser un secret Databricks.
    • Comme indiqué dans l'exemple de code suivant, stockez l'ID client du Service Principal sous la forme service_principal_client_id et stockez le secret OAuth sous la forme service_principal_client_secret.
    Python
    # In a Databricks notebook
    dbutils.secrets.put("load-test-auth", "service_principal_client_id", "<CLIENT_ID>")
    dbutils.secrets.put("load-test-auth", "service_principal_client_secret", "<SECRET>")

Étape 2 : Configurez votre test de charge.

Configuration du Notebook

Configurez les paramètres du Notebook à l'aide des widgets en haut du Notebook. Lors de l'exécution du Notebook en tant que Job Databricks, transmettez ces valeurs en tant que paramètres du Job. Aucune modification de code n'est nécessaire.

parameter

Description

Valeur recommandée

endpoint_name

Nom de votre endpoint de recherche IA

Nom de votre endpoint

index_name

Nom complet de l'index (catalog.schema.index)

Votre nom d'index

test_table

Table source pour échantillonner les requêtes (catalog.schema.table)

Votre table d'entrée d'index

query_column

Colonne de texte à utiliser pour les embeddings gérés

Laissez tel quel text ou définissez le nom de votre colonne

embedding_column

Colonne contenant des vecteurs d'intégration précalculés. Uniquement utilisé pour les intégrations autogérées.

Laissez ce champ vide pour des intégrations gérées.

sample_size

Nombre de requêtes à échantillonner pour le test

1000

target_concurrencies

Liste, séparée par des virgules, de nombres de clients simultanés à tester

5,10,20,50

step_duration_seconds

Durée en secondes par niveau de concurrence. Une valeur s'applique à tous les niveaux, ou fournissez-en une par niveau sous forme de liste séparée par des virgules.

300 (5 minutes)

secret_scope_name

Nom de votre Secret Scope Databricks

Votre nom de portée

locust_script_path

Chemin du Workspace vers le script fast_vs_load_test_async_load.py

/Workspace/Users/<your-username>/fast_vs_load_test_async_load.py

output_table

(Facultatif) Table Delta pour stocker les résultats dans (catalog.schema.table). Créé automatiquement lors de la première exécution.

catalog.schema.load_test_results

run_name

Nom ou commentaire pour identifier cette exécution pour une analyse ultérieure

Une étiquette descriptive

exploration_mode

gradual parcourt target_concurrencies dans l'ordre. binary_search trouve automatiquement le point de rupture (voir Exploration du point de rupture).

gradual

max_target_qps

(binary_search uniquement) Limite supérieure pour la recherche QPS

500

exploration_steps

(binary_search uniquement) Nombre maximal d'itérations de recherche binaire

8

error_rate_threshold

(binary_search seulement) Taux d'erreur maximal acceptable (%) pour qu'une étape soit considérée comme un succès

1.0

num_results

Nombre de résultats à renvoyer par query

10

columns_to_return

Liste de colonnes séparées par des virgules à renvoyer dans les résultats de la query (par exemple, id,text). Laissez ce champ vide pour renvoyer toutes les colonnes.

Laissez ce champ vide pour default

parameter

Description

Valeur recommandée

endpoint_name

Nom de votre endpoint de recherche IA

Nom de votre endpoint

index_name

Nom complet de l'index (catalog.schema.index)

Votre nom d'index

test_table

Table source pour échantillonner les requêtes (catalog.schema.table)

Votre table d'entrée d'index

query_column

Colonne de texte à utiliser pour les embeddings gérés

Laissez tel quel text ou définissez le nom de votre colonne

embedding_column

Colonne contenant des vecteurs d'intégration précalculés. Uniquement utilisé pour les intégrations autogérées.

Laissez ce champ vide pour des intégrations gérées.

sample_size

Nombre de requêtes à échantillonner pour le test

1000

target_concurrencies

Liste, séparée par des virgules, de nombres de clients simultanés à tester

5,10,20,50

step_duration_seconds

Durée en secondes par niveau de concurrence. Une valeur s'applique à tous les niveaux, ou fournissez-en une par niveau sous forme de liste séparée par des virgules.

300 (5 minutes)

secret_scope_name

Nom de votre Secret Scope Databricks

Votre nom de portée

locust_script_path

Chemin du Workspace vers le script fast_vs_load_test_async_load.py

/Workspace/Users/<your-username>/fast_vs_load_test_async_load.py

output_table

(Facultatif) Table Delta pour stocker les résultats dans (catalog.schema.table). Créé automatiquement lors de la première exécution.

catalog.schema.load_test_results

run_name

Nom ou commentaire pour identifier cette exécution pour une analyse ultérieure

Une étiquette descriptive

exploration_mode

gradual parcourt target_concurrencies dans l'ordre. binary_search trouve automatiquement le point de rupture (voir Exploration du point de rupture).

gradual

max_target_qps

(binary_search uniquement) Limite supérieure pour la recherche QPS

500

exploration_steps

(binary_search uniquement) Nombre maximal d'itérations de recherche binaire

8

error_rate_threshold

(binary_search seulement) Taux d'erreur maximal acceptable (%) pour qu'une étape soit considérée comme un succès

1.0

num_results

Nombre de résultats à renvoyer par query

10

columns_to_return

Liste de colonnes séparées par des virgules à renvoyer dans les résultats de la query (par exemple, id,text). Laissez ce champ vide pour renvoyer toutes les colonnes.

Laissez ce champ vide pour default

Intégrations gérées vs. autogérées

Le notebook prend en charge à la fois les intégrations gérées (où Databricks génère des intégrations au moment de la query) et les intégrations auto-gérées (où vous transmettez directement des vecteurs pré-calculés). Configurez les paramètres appropriés en fonction de votre type d'index.

Type d'index

Paramètre à définir

Laisser non défini

Intégrations gérées (index Delta Sync avec modèle d'intégration géré par Databricks)

query_column: le nom de colonne de texte à utiliser comme query

embedding_column (laisser vide)

Intégrations autogérées (Delta Sync ou index d'accès direct au vecteur avec vecteurs précalculés)

embedding_column: la colonne contenant les vecteurs d’intégration pré-calculés

query_column

Type d'index

Paramètre à définir

Laisser non défini

Intégrations gérées (index Delta Sync avec modèle d'intégration géré par Databricks)

query_column: le nom de colonne de texte à utiliser comme query

embedding_column (laisser vide)

Intégrations autogérées (Delta Sync ou index d'accès direct au vecteur avec vecteurs précalculés)

embedding_column: la colonne contenant les vecteurs d’intégration pré-calculés

query_column

remarque

Pour les index d'intégration gérée, le test de charge mesure la latence de bout en bout, y compris le temps de génération de l'intégration. Si votre embedding endpoint réduit la mise à l'échelle à zéro, le surcoût du cold-start apparaîtra lors de la première exécution du test. Consultez Identifier le goulot d'étranglement du modèle d'intégration pour savoir comment isoler la latence d'intégration de la latence de recherche.

Pourquoi 5 à 10 minutes ?

Une durée minimale de test de 5 minutes est essentielle.

  • Les queries initiales peuvent inclure des frais généraux de start à froid.
  • Les endpoints ont besoin de temps pour atteindre une performance stable.
  • La mise à l'échelle automatique des Endpoint de déploiement de modèles (si activée) prend du temps à s'activer.
  • Les tests courts manquent les comportements de limitation sous une charge soutenue.

Le tableau suivant présente les durées de test recommandées en fonction de votre objectif de test.

Type de test

Durée du test

Objectifs du test

Test de bon fonctionnement rapide

2 à 3 minutes

Vérifier les fonctionnalités de base.

Base de référence des performances

de 5 à 10 minutes

Métriques fiables à l’état stable

Tests de stress

15 à 30 minutes

Identifier l'épuisement des ressources

Tests d'endurance

de 1 à 4 heures

Dégradation, stabilité de la latence

Type de test

Durée du test

Objectifs du test

Test de bon fonctionnement rapide

2 à 3 minutes

Vérifier les fonctionnalités de base.

Base de référence des performances

de 5 à 10 minutes

Métriques fiables à l’état stable

Tests de stress

15 à 30 minutes

Identifier l'épuisement des ressources

Tests d'endurance

de 1 à 4 heures

Dégradation, stabilité de la latence

Exploration du point de rupture (mode de recherche binaire)

En plus du balayage progressif (exploration_mode=gradual), le notebook prend en charge un mode de recherche binaire automatique qui localise le QPS maximal durable sans que vous ayez à spécifier manuellement les niveaux de concurrence.

Comment cela fonctionne

Définissez exploration_mode=binary_search et spécifiez max_target_qps (par exemple, 500). Le Notebook utilise la loi de Little (concurrency = QPS × avg_latency_sec) pour convertir chaque cible QPS en un niveau de concurrence estimé, puis exécute une recherche binaire comme suit :

  1. start à max_target_qps / 2 (250 dans l'exemple).
  2. Si le taux d'erreur est inférieur à error_rate_threshold (succès), augmentez la borne inférieure et essayez un QPS plus élevé (375, puis 500, et ainsi de suite).
  3. Si le taux d'erreur dépasse le threshold (échec), réduisez la limite supérieure et réessayez à mi-chemin entre le dernier succès et l'échec.
  4. Répétez jusqu’à exploration_steps étapes (default 8) ou jusqu’à ce que la plage de recherche se réduise à 5 % de max_target_qps.

Le tableau suivant montre comment la recherche converge pour un Endpoint hypothétique avec un point d’arrêt d’environ 430 QPS :

Étape

QPS cible

Taux d'erreur

Résultat

Nouvelle plage

1

250

0,1 %

Opération réussie

[250, 500]

2

375

0,3 %

Opération réussie

[375, 500]

3

437

4,5 %

Échec

[375, 437]

4

406

0,8 %

Opération réussie

[406, 437]

5

421

2,1 %

Échec

[406, 421]

Étape

QPS cible

Taux d'erreur

Résultat

Nouvelle plage

1

250

0,1 %

Opération réussie

[250, 500]

2

375

0,3 %

Opération réussie

[375, 500]

3

437

4,5 %

Échec

[375, 437]

4

406

0,8 %

Opération réussie

[406, 437]

5

421

2,1 %

Échec

[406, 421]

Après de 5 à 8 étapes, la recherche converge vers le point de rupture, environ de 406 à 421 QPS dans cet exemple, avec beaucoup moins d'exécutions de test qu'une recherche exhaustive.

Quand utiliser chaque mode

Mode

Quand utiliser

gradual

Vous connaissez déjà la plage de fonctionnement attendue et souhaitez caractériser les performances à des niveaux de simultanéité spécifiques.

binary_search

Vous voulez trouver le QPS maximal durable rapidement, sans connaître à l'avance les niveaux de concurrence.

Mode

Quand utiliser

gradual

Vous connaissez déjà la plage de fonctionnement attendue et souhaitez caractériser les performances à des niveaux de simultanéité spécifiques.

binary_search

Vous voulez trouver le QPS maximal durable rapidement, sans connaître à l'avance les niveaux de concurrence.

Étape 3. Concevez votre jeu de query

Dans la mesure du possible, l'ensemble de query devrait refléter le trafic de production attendu aussi fidèlement que possible. Plus précisément, vous devriez essayer de faire correspondre la distribution attendue des queries en termes de contenu, de complexité et de diversité.

  • Utilisez des requêtes réalistes. N'utilisez pas de texte aléatoire tel que « test query 1234 ».

  • Faites correspondre la distribution attendue du trafic de production. Si vous vous attendez à 80 % de requêtes courantes, 15 % de requêtes de fréquence moyenne et 5 % de requêtes peu fréquentes, votre ensemble de requêtes devrait refléter cette distribution.

  • Faites correspondre le type de query que vous vous attendez à voir en production. Par exemple, si vous vous attendez à ce que les queries de production utilisent la recherche hybride ou des filtres, vous devriez également les utiliser dans votre ensemble de queries.

    Example query using filters:

    JSON
    {
    "query_text": "wireless headphones",
    "num_results": 10,
    "filters": { "brand": "Sony", "noise_canceling": true }
    }

    Exemple de query utilisant la recherche hybride :

    JSON
    {
    "query_text": "best noise canceling headphones for travel",
    "query_type": "hybrid",
    "num_results": 10
    }

Diversité des query et mise en cache

Les Endpoint de recherche AI mettent en cache plusieurs types de résultats de query afin d'améliorer les performances. Cette mise en cache peut affecter les résultats des tests de charge. Pour cette raison, il est important de prêter attention à la diversité de l'ensemble de queries. Par exemple, si vous envoyez à plusieurs reprises le même ensemble de queries, vous testez le cache, et non les performances de recherche réelles.

Utilisation :

Quand :

Exemple

Requêtes identiques ou peu nombreuses

  • Votre trafic de production présente une répétition élevée de query (par exemple, « produits populaires »).

  • Vous testez spécifiquement l'efficacité du cache.

  • Votre application bénéficie de la mise en cache (par exemple, des tableaux de bord avec des queries fixes)

  • Vous souhaitez mesurer les performances en cache dans le meilleur des cas.

Un widget de recommandation de produit qui affiche les « articles tendance » – la même query s'exécute des milliers de fois par heure.

Requêtes diverses

  • Votre trafic de production comprend des queries utilisateur uniques (par exemple, des moteurs de recherche ou des chatbots)

  • Vous souhaitez mesurer les performances non mises en cache dans le pire des cas

  • Vous voulez tester les performances d'analyse d'index, et non les performances du cache.

  • Les queries ont une cardinalité élevée (des millions de variations possibles)

Une recherche e-commerce où chaque utilisateur tape différentes recherches de produits.

Utilisation :

Quand :

Exemple

Requêtes identiques ou peu nombreuses

  • Votre trafic de production présente une répétition élevée de query (par exemple, « produits populaires »).

  • Vous testez spécifiquement l'efficacité du cache.

  • Votre application bénéficie de la mise en cache (par exemple, des tableaux de bord avec des queries fixes)

  • Vous souhaitez mesurer les performances en cache dans le meilleur des cas.

Un widget de recommandation de produit qui affiche les « articles tendance » – la même query s'exécute des milliers de fois par heure.

Requêtes diverses

  • Votre trafic de production comprend des queries utilisateur uniques (par exemple, des moteurs de recherche ou des chatbots)

  • Vous souhaitez mesurer les performances non mises en cache dans le pire des cas

  • Vous voulez tester les performances d'analyse d'index, et non les performances du cache.

  • Les queries ont une cardinalité élevée (des millions de variations possibles)

Une recherche e-commerce où chaque utilisateur tape différentes recherches de produits.

Pour des recommandations supplémentaires, consultez Bonnes pratiques.

Options pour la création d'un ensemble de query

Les onglets de code présentent trois options pour créer un ensemble de query diversifié. Il n'existe pas de solution universelle. Choisissez celui/celle qui vous convient le mieux.

  • (Recommandé) Échantillonnage aléatoire à partir de la table d'entrée de l'index. Ceci est un bon point de départ général.
  • Échantillonnage à partir des logs de production. C'est un bon start si vous avez des logs de production. Gardez à l'esprit que les requêtes changent généralement au fil du temps, alors refresh régulièrement le jeu de test pour le maintenir à jour.
  • Génération de query synthétiques. Ceci est utile si vous n'avez pas de Logs de production ou si vous utilisez des filtres complexes.

Le code suivant échantillonne des queries aléatoires de votre table d'entrée d'index.

Python
import pandas as pd
import random

# Read the index input table
input_table = spark.table("catalog.schema.index_input_table").toPandas()

# Sample random rows
n_samples = 1000
if len(input_table) < n_samples:
print(f"Warning: Only {len(input_table)} rows available, using all")
sample_queries = input_table
else:
sample_queries = input_table.sample(n=n_samples, random_state=42)

# Extract the text column (adjust column name as needed)
queries = sample_queries['text_column'].tolist()

# Create query payloads
query_payloads = [{"query_text": q, "num_results": 10} for q in queries]

# Save to input.json
pd.DataFrame(query_payloads).to_json("input.json", orient="records", lines=True)

print(f"Created {len(query_payloads)} diverse queries from index input table")

Étape 4. Testez votre charge utile

Avant d'exécuter le test de charge complet, validez votre charge utile :

  1. Dans le Workspace Databricks, accédez à votre Endpoint de recherche IA.
  2. Dans la barre latérale gauche, cliquez sur Déploiement .
  3. Sélectionnez votre Endpoint.
  4. Cliquez sur **Utiliser** → **Query**.
  5. Collez votre contenu input.json dans la zone de query.
  6. Vérifiez que l'endpoint renvoie les résultats attendus.

Cela garantit que votre test de charge mesurera des requêtes réalistes, et non des réponses d'erreur.

Étape 5. Exécuter le test de charge

Vérification de la connectivité et préchauffage

Avant le début du test de charge, le notebook effectue deux étapes de configuration :

  1. Vérification de la connectivité : envoie une seule query de sonde à l’aide des informations d’identification du Service Principal. Si l'Endpoint renvoie une erreur 401 ou 403, le notebook échoue immédiatement avec un message clair PermissionError au lieu d'exécuter un test de charge complet qui ne produit que des données d'erreur. Cela permet de gagner du temps lorsque les identifiants ou les autorisations sont mal configurés.

  2. Test d'échauffement (1 minute) : exécute un court test à faible concurrence qui échauffe les caches d'endpoint et valide le flux de requêtes de bout en bout. Les résultats d'échauffement ne sont pas utilisés pour les indicateurs de performance. En mode de recherche binaire, la latence de préchauffage est également utilisée comme référence pour l'estimation de la simultanéité selon la loi de Little.

Série de tests de charge principale

Le notebook exécute une série de tests avec une concurrence client croissante :

  • start : faible simultanéité (par exemple, 5 clients concurrents).
  • Milieu : Concurrence moyenne (par exemple, 10, 20 ou 50 clients)
  • Fin : high concurrency (par exemple, plus de 100 clients)

Chaque test s'exécute pendant la durée configurée dans step_duration_seconds (de 5 à 10 minutes recommandé).

Ce que mesure le Notebook

Le Notebook mesure et rapporte les éléments suivants :

Métriques de latence :

  • P50 (médiane) : La moitié des queries sont plus rapides que cela.
  • P95 : 95 % des requêtes sont plus rapides que cela. Ceci est une métrique SLA clé.
  • P99 : 99 % des requêtes sont plus rapides que cela.
  • Max : Latence dans le pire des cas.

Métriques de throughput :

  • RPS (requêtes par seconde) : requêtes réussies par seconde.
  • Total des requêtes : Nombre de requêtes terminées.
  • Taux de réussite : Pourcentage de query réussies.

Erreurs :

  • query failures by type
  • Messages d'exception
  • Nombre de délais d'expiration

Stockage des résultats

Si le parameter output_table est défini, le notebook stocke une ligne par niveau de concurrence (ou par étape de recherche binaire) dans une table Delta Unity Catalog. Le tableau est créé automatiquement lors de la première exécution, et des données y sont ajoutées lors des exécutions ultérieures. Chaque ligne comprend run_name, exploration_mode, la concurrence, les taux de réussite/échec, les centiles de latence, les RPS et des champs spécifiques à la recherche binaire (bs_step, bs_target_qps, bs_outcome). Cela vous permet de comparer les exécutions au fil du temps à l'aide d'outils SQL ou BI.

Exécution en tant que Job Databricks

Tous les paramètres de Notebook sont définis comme dbutils.widgets, qui sont directement mappés aux paramètres de Job Databricks. Pour planifier ou automatiser les tests de charge :

  1. Créez un Job avec le Notebook comme tâche.
  2. Définissez les valeurs des widgets comme parameters de Job. Aucune modification de code n'est nécessaire.
  3. Attachez le Job à un cluster à nœud unique avec de nombreux cœurs de CPU (Locust bénéficie des Worker parallèles).
  4. Exécuter à la demande ou selon un calendrier pour des tests de référence récurrents.

Étape 6. Interpréter les résultats

Le tableau suivant présente les objectifs de bonne performance :

Métriques

Cible

Commentaire

Latence P95

< 500 ms

La plupart des queries sont rapides.

Latence P99

< 1 s

Performances raisonnables sur les requêtes à longue traîne.

Taux de réussite

> 99,5 %

Faible taux de défaillance.

Latence au fil du temps

Stable

Aucune dégradation observée pendant le test.

Requêtes par seconde

Atteint l'objectif

L'Endpoint peut gérer le trafic attendu

Métriques

Cible

Commentaire

Latence P95

< 500 ms

La plupart des queries sont rapides.

Latence P99

< 1 s

Performances raisonnables sur les requêtes à longue traîne.

Taux de réussite

> 99,5 %

Faible taux de défaillance.

Latence au fil du temps

Stable

Aucune dégradation observée pendant le test.

Requêtes par seconde

Atteint l'objectif

L'Endpoint peut gérer le trafic attendu

Les résultats suivants indiquent de mauvaises performances :

  • P95 > 1s. Indique que les requêtes sont trop lentes pour une utilisation en temps réel.
  • P99 > 3s. La latence sur les requêtes à longue traîne nuira à l'expérience utilisateur.
  • Taux de réussite < 99 %. Trop d'échecs.
  • Augmentation de la latence. Indique un épuisement des ressources ou une fuite de mémoire.
  • Erreurs de limitation de débit (429). Indique qu'une capacité d'endpoint supérieure est requise.

Compromis entre RPS et latence

Le RPS maximum n'est pas le point optimal pour le throughput de production. La latence augmente de manière non linéaire à mesure que vous approchez du throughput maximal. Un fonctionnement au RPS maximum entraîne souvent une latence 2 à 5 fois plus élevée par rapport à un fonctionnement à 60-70 % de la capacité maximale.

L'exemple suivant montre comment analyser les résultats pour trouver le point de fonctionnement optimal.

  • Le RPS maximum est de 480 avec 150 clients simultanés.
  • Le point de fonctionnement optimal est de 310 RPS avec 50 clients simultanés (65 % de capacité).
  • La pénalité de latence au maximum : le P95 est 4,3 fois plus élevé (1,5 s contre 350 ms)
  • Dans cet exemple, la recommandation est de dimensionner l'endpoint pour une capacité de 480 RPS et de fonctionner à environ 310 RPS.

Simultanéité

P50

P95

P99

RPS

Opération réussie

Capacité

5

80 ms

120 ms

150 ms

45

100 %

10 %

10

85 ms

140 ms

180 ms

88

100 %

20 %

20

95 ms

180 ms

250 ms

165

99,8 %

35 %

50

150 ms

350 ms

500 ms

310

99,2 %

65 % ← Équilibre parfait

100

250 ms

800 ms

1,2 s

420

97,5 %

90 % ⚠️ Approche du max

150

450 ms

1,5 s

2,5s

480

95,0 %

100 % ❌ RPS maximal

Simultanéité

P50

P95

P99

RPS

Opération réussie

Capacité

5

80 ms

120 ms

150 ms

45

100 %

10 %

10

85 ms

140 ms

180 ms

88

100 %

20 %

20

95 ms

180 ms

250 ms

165

99,8 %

35 %

50

150 ms

350 ms

500 ms

310

99,2 %

65 % ← Équilibre parfait

100

250 ms

800 ms

1,2 s

420

97,5 %

90 % ⚠️ Approche du max

150

450 ms

1,5 s

2,5s

480

95,0 %

100 % ❌ RPS maximal

Fonctionner au RPS maximum peut entraîner les problèmes suivants :

  • Dégradation de la latence. Dans l'exemple, le P95 est de 350 ms à 65 % de capacité, mais de 1,5 s à 100 % de capacité.
  • Aucune marge pour s'adapter aux pics ou aux rafales de trafic. À 100 % de capacité, tout pic entraîne un délai d'attente. À 65 % de capacité, une augmentation de 50 % du trafic peut être gérée sans problème.
  • Augmentation des taux d'erreurs. Dans l'exemple, le taux de réussite est de 99,2 % à 65 % de capacité, mais de 95,0 % (un taux d'échec de 5 %) à 100 % de capacité.
  • Risque d’épuisement des ressources. À charge maximale, les files d’attente augmentent, la pression mémoire augmente, les Pool de connexion start à se saturer et le délai de récupération après incident augmente.

Le tableau suivant présente les points de fonctionnement recommandés pour différents cas d'utilisation.

Cas d'usage

Capacité cible

Justification

Sensible à la latence (recherche, chat)

50 à 60 % de max

Prioriser la faible latence P95/P99

Équilibré (recommandations)

de 60 à 70 % du maximum

Bon équilibre entre coût et latence

Optimisé en termes de coûts (jobs batch)

70 à 80 % du maximum

Latence plus élevée acceptable

Non recommandé

> 85 % du max

Pics de latence, pas de capacité de rafale

Cas d'usage

Capacité cible

Justification

Sensible à la latence (recherche, chat)

50 à 60 % de max

Prioriser la faible latence P95/P99

Équilibré (recommandations)

de 60 à 70 % du maximum

Bon équilibre entre coût et latence

Optimisé en termes de coûts (jobs batch)

70 à 80 % du maximum

Latence plus élevée acceptable

Non recommandé

> 85 % du max

Pics de latence, pas de capacité de rafale

Fonctions d'assistance pour le calcul du point de fonctionnement et de la taille du Endpoint

Le code suivant trace le QPS par rapport à la latence P95. Dans le graphique, recherchez le point où la courbe start à s'infléchir brusquement vers le haut. Il s'agit du point de fonctionnement optimal.

Python
import matplotlib.pyplot as plt

# Plot QPS vs. P95 latency
qps_values = [45, 88, 165, 310, 420, 480]
p95_latency = [120, 140, 180, 350, 800, 1500]

plt.plot(qps_values, p95_latency, marker='o')
plt.axvline(x=310, color='green', linestyle='--', label='Optimal (65% capacity)')
plt.axvline(x=480, color='red', linestyle='--', label='Maximum (100% capacity)')
plt.xlabel('Queries Per Second (QPS)')
plt.ylabel('P95 Latency (ms)')
plt.title('QPS vs. Latency: Finding the Sweet Spot')
plt.legend()
plt.grid(True)
plt.show()

Identifier le goulot d'étranglement du modèle d'intégration

Si votre index utilise des incorporations gérées, le notebook de test de charge capture le minutage par composant via le paramètre debug_level=1 sur chaque requête. La table des résultats inclut :

  • ann_time: temps passé sur la recherche approximative des plus proches voisins
  • embedding_gen_time: temps passé à générer l'embedding de query sur l'Endpoint de service de modèle
  • reranker_time: temps passé au reranking (si activé)
  • response_time: temps de réponse total de bout en bout

Si embedding_gen_time est systématiquement grand par rapport à ann_time, l'Endpoint d'intégration est le goulot d'étranglement, et non l'Endpoint de recherche IA. Causes courantes :

  • L'Endpoint de déploiement de modèle d'intégration a Monter en charge à zéro activé. Désactivez-le pour les tests de charge en production. Consultez Éviter de monter en charge à zéro pour la production.
  • L'endpoint d'intégration ne dispose pas d'une simultanéité provisionnée suffisante pour le taux de query que vous testez.
  • L'endpoint du modèle d'intégration est partagé avec d'autres charges de travail. Utilisez un endpoint dédié pour les tests de charge.
astuce

Pour isoler les performances de la recherche IA des performances du modèle d'intégration, passez aux intégrations autogérées pour les tests de charge. Transmettez les vecteurs précalculés dans le parameter EMBEDDING_COLUMN au lieu des queries de texte. Cela élimine entièrement la latence d'intégration de la mesure.

Étape 7 : Dimensionnez votre endpoint

Utiliser la recommandation du Notebook

Après avoir analysé les résultats, le notebook vous demande de :

  1. Sélectionnez la ligne qui répond le mieux à vos exigences de latence.
  2. Saisissez le RPS souhaité de votre application.

Le Notebook affiche alors une taille d'Endpoint recommandée. Il calcule la capacité requise en fonction des éléments suivants :

  • Votre RPS cible
  • Latence observée à différents niveaux de concurrence
  • Seuils de taux de réussite.
  • Marge de sécurité (typiquement 2 fois la charge de pointe attendue)

Considérations sur la mise à l'échelle

Endpoint standards :

  • Monter en charge automatiquement pour prendre en charge la taille de l'index
  • Monter en charge manuellement pour supporter le throughput
  • Réduire automatiquement la taille lorsque les index sont supprimés
  • Réduisez manuellement la mise à l'échelle pour réduire la capacité

Endpoints optimisés pour le stockage :

  • Monter en charge automatiquement pour prendre en charge la taille de l'index
  • Réduire automatiquement la taille lorsque les index sont supprimés

Étape 8 : Valider la configuration finale

Après la mise à jour de la configuration de votre Endpoint :

  1. Veuillez attendre que l'endpoint soit prêt. Cela peut prendre plusieurs minutes.
  2. Exécutez le test de validation final dans le notebook.
  3. Confirmez que les performances répondent à vos exigences :
    • RPS ≥ throughput cible
    • La latence P95 respecte le SLA.
    • Taux de réussite > 99,5 %
    • Aucune erreur persistante

Si la validation échoue, essayez ce qui suit :

  • Augmenter la capacité de l'Endpoint
  • Optimiser la complexité des query
  • Vérifier les performances du filtre.
  • Vérifier la configuration de l'endpoint d'intégration

Quand re-tester

Pour maintenir la visibilité des performances, il est conseillé d'exécuter trimestriellement des tests de charge de référence. Vous devriez également tester à nouveau lorsque vous apportez l'une des modifications suivantes :

  • Modifier les modèles ou la complexité des query
  • Mettre à jour l’index de recherche IA
  • Modifier les configurations de filtre
  • Attendez-vous à des augmentations significatives du trafic.
  • Déployer de nouvelles fonctionnalités ou optimisations.
  • Passer des types d'Endpoint standard aux types d'Endpoint optimisés pour le stockage.

Dépannage

Toutes les requêtes échouent avec une latence d'environ 10 ms et des réponses de 240 octets

Cela indique que le Service Principal reçoit une réponse 401/403. Vérifier :

  1. Le service principal dispose des permissions Can Query sur l'endpoint de recherche IA (pas seulement l'index).
  2. Le Secret Scope contient des clés service_principal_client_id et service_principal_client_secret valides.
  3. Le secret OAuth n'a pas expiré.

Le notebook comprend une vérification de la connectivité qui détecte cela avant d'exécuter le test de charge complet.

Exécution de plusieurs Jobs de test de charge sur le même cluster

Si vous exécutez deux jobs de test de charge simultanément sur le même cluster, l’un des jobs peut recevoir des jetons OAuth obsolètes ou subir une contention CPU avec les workers Locust de l’autre job. Pour des résultats fiables, exécutez les jobs de test de charge un par un sur un cluster dédié.

Les graphes de synchronisation des composants sont vides

Les graphes de synchronisation des composants (ann_time, embedding_gen_time, reranker_time) exigent que l'Endpoint renvoie debug_info dans les réponses de query. Si ces graphes sont vides :

  • Vérifiez que vous utilisez le script fast_vs_load_test_async_load.py (qui analyse debug_info à partir des réponses) comme locust_script_path.
  • Certaines configurations d'Endpoint pourraient ne pas renvoyer debug_info. Les index d'embedding auto-gérés renvoient généralement ann_time et response_time, mais pas embedding_gen_time ou reranker_time.

La table de résultats n'est pas interrogeable à partir d'un SQL Warehouse

Le Notebook écrit les résultats de la session Spark du clusters. Si un SQL Warehouse affiche 0 ligne pour une table que le notebook signale comme étant remplie, le problème peut être dû à un délai de synchronisation des métadonnées Unity Catalog. Attendez quelques minutes et réessayez, ou query la table directement depuis un Notebook attaché au même cluster.

Résumé des bonnes pratiques

Configuration de test

  • Exécutez des tests pendant au moins 5 minutes à charge maximale.

  • Utilisez les Service Principal OAuth pour l'authentification.

  • Créez des charges utiles de query réalistes qui correspondent aux queries de production attendues.

  • Testez avec des filtres et des parameters de type production.

  • Incluez une période de préchauffage avant de mesurer.

  • Testez à plusieurs niveaux de concurrence.

  • Suivez les latences P95/P99, et pas seulement les moyennes.

  • Testez les performances mises en cache et non mises en cache.

    Python
    # Conservative approach: Size endpoint for UNCACHED performance
    uncached_results = run_load_test(diverse_queries, duration=600)
    endpoint_size = calculate_capacity(uncached_results, target_rps=500)

    # Then verify cached performance is even better
    cached_results = run_load_test(repetitive_queries, duration=300)
    print(f"Cached P95: {cached_results['p95']}ms (bonus performance)")

Conception de l'ensemble de query

  • Faites correspondre la diversité de vos queries de test à la distribution du trafic réel (queries fréquentes et rares).
  • Utilisez des queries réelles provenant des Logs (anonymisés).
  • Incluez différentes complexités de query.
  • Testez les scénarios mis en cache et non mis en cache et suivez les résultats séparément.
  • Tester avec les combinaisons de filtres attendues.
  • Utilisez les mêmes paramètres que ceux que vous utiliserez en production. Par exemple, si vous utilisez la recherche hybride en production, incluez des requêtes de recherche hybride. Utilisez un paramètre num_results similaire à celui en production.
  • N'utilisez pas de requêtes qui n'apparaîtront jamais en production.

Optimisation des performances

Si les latences sont trop élevées, essayez ce qui suit :

  1. Utiliser des Service Principals OAuth (pas des PATs) – amélioration de 100 ms
  2. Réduire num_results — La récupération de 100 résultats est plus lente que 10
  3. Optimiser les filtres — Les filtres complexes ou trop restrictifs ralentissent les query
  4. Vérifiez l'endpoint d'intégration – assurez-vous qu'il n'est pas mis à l'échelle à zéro ou qu'il dispose d'une bande passante suffisante

Si vous atteignez les limites de débit, essayez ce qui suit :

  1. Augmenter la capacité de l'endpoint - Montez en charge votre endpoint.
  2. Implémentez la limitation du débit côté client ou répartissez les query dans le temps
  3. Utiliser le regroupement de connexions – Réutiliser les connexions
  4. Ajoutez une logique de nouvelle tentative — Utilisez l'interruption exponentielle (déjà une partie du Python SDK)

Ressources supplémentaires

Sur cette page