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.jsonqui 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 Notebooklocust_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
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
-
Créer un Service Principal Databricks. Pour obtenir des instructions, consultez Ajouter des Service Principals à votre compte.
-
Accorder les autorisations :
- Accédez à la page de votre endpoint de recherche IA.
- Cliquez sur Autorisations .
- Accordez au Service Principal les autorisations Can Query .
-
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 .
-
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_idet stockez le secret OAuth sous la formeservice_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 |
|---|---|---|
| Nom de votre endpoint de recherche IA | Nom de votre endpoint |
| Nom complet de l'index ( | Votre nom d'index |
| Table source pour échantillonner les requêtes ( | Votre table d'entrée d'index |
| Colonne de texte à utiliser pour les embeddings gérés | Laissez tel quel |
| 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. |
| Nombre de requêtes à échantillonner pour le test |
|
| Liste, séparée par des virgules, de nombres de clients simultanés à tester |
|
| 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. |
|
| Nom de votre Secret Scope Databricks | Votre nom de portée |
| Chemin du Workspace vers le script |
|
| (Facultatif) Table Delta pour stocker les résultats dans ( |
|
| Nom ou commentaire pour identifier cette exécution pour une analyse ultérieure | Une étiquette descriptive |
|
|
|
| ( |
|
| ( |
|
| ( |
|
| Nombre de résultats à renvoyer par query |
|
| Liste de colonnes séparées par des virgules à renvoyer dans les résultats de la query (par exemple, | 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) |
|
|
Intégrations autogérées (Delta Sync ou index d'accès direct au vecteur avec vecteurs précalculés) |
|
|
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 |
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 :
- start à
max_target_qps / 2(250 dans l'exemple). - 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). - 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.
- Répétez jusqu’à
exploration_stepsétapes (default 8) ou jusqu’à ce que la plage de recherche se réduise à 5 % demax_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] |
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 |
|---|---|
| Vous connaissez déjà la plage de fonctionnement attendue et souhaitez caractériser les performances à des niveaux de simultanéité spécifiques. |
| 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 |
| 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 |
| 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.
- Random sampling from input table
- Sample from production logs
- Synthetic queries
Le code suivant échantillonne des queries aléatoires de votre table d'entrée d'index.
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")
Les échantillons de code suivants sont proportionnels aux requêtes de production.
# Sample proportionally from production queries
production_queries = pd.read_csv("queries.csv")
# Take stratified sample maintaining frequency distribution
def create_test_set(df, n_queries=1000):
# Group by frequency buckets
df['frequency'] = df.groupby('query_text')['query_text'].transform('count')
# Stratified sample
high_freq = df[df['frequency'] > 100].sample(n=200) # 20%
med_freq = df[df['frequency'].between(10, 100)].sample(n=300) # 30%
low_freq = df[df['frequency'] < 10].sample(n=500) # 50%
return pd.concat([high_freq, med_freq, low_freq])
test_queries = create_test_set(production_queries)
test_queries.to_json("input.json", orient="records", lines=True)
Si vous n'avez pas encore de production Logs, vous pouvez générer des requêtes synthétiques diverses.
# Generate diverse queries programmatically
import random
# Define query templates and variations
templates = [
"find {product} under ${price}",
"best {product} for {use_case}",
"{adjective} {product} recommendations",
"compare {product1} and {product2}",
]
products = ["laptop", "headphones", "monitor", "keyboard", "mouse", "webcam", "speaker"]
prices = ["500", "1000", "1500", "2000"]
use_cases = ["gaming", "work", "travel", "home office", "students"]
adjectives = ["affordable", "premium", "budget", "professional", "portable"]
diverse_queries = []
for _ in range(1000):
template = random.choice(templates)
query = template.format(
product=random.choice(products),
product1=random.choice(products),
product2=random.choice(products),
price=random.choice(prices),
use_case=random.choice(use_cases),
adjective=random.choice(adjectives)
)
diverse_queries.append(query)
print(f"Generated {len(set(diverse_queries))} unique queries")
Étape 4. Testez votre charge utile
Avant d'exécuter le test de charge complet, validez votre charge utile :
- Dans le Workspace Databricks, accédez à votre Endpoint de recherche IA.
- Dans la barre latérale gauche, cliquez sur Déploiement .
- Sélectionnez votre Endpoint.
- Cliquez sur **Utiliser** → **Query**.
- Collez votre contenu
input.jsondans la zone de query. - 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 :
-
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
PermissionErrorau 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. -
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 :
- Créez un Job avec le Notebook comme tâche.
- Définissez les valeurs des widgets comme parameters de Job. Aucune modification de code n'est nécessaire.
- Attachez le Job à un cluster à nœud unique avec de nombreux cœurs de CPU (Locust bénéficie des Worker parallèles).
- 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 |
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 |
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 |
Fonctions d'assistance pour le calcul du point de fonctionnement et de la taille du Endpoint
- Find the optimal point
- Size recommendation formula
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.
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()
def calculate_endpoint_size(target_qps, optimal_capacity_percent=0.65):
"""
Calculate required endpoint capacity
Args:
target_qps: Your expected peak production QPS
optimal_capacity_percent: Target utilization (default 65%)
Returns:
Required maximum endpoint QPS
"""
required_max_qps = target_qps / optimal_capacity_percent
# Add 20% safety margin for unexpected bursts
recommended_max_qps = required_max_qps * 1.2
return {
"target_production_qps": target_qps,
"operate_at_capacity": f"{optimal_capacity_percent*100:.0f}%",
"required_max_qps": required_max_qps,
"recommended_max_qps": recommended_max_qps,
"burst_capacity": f"{(1 - optimal_capacity_percent)*100:.0f}% headroom"
}
# Example
result = calculate_endpoint_size(target_qps=200)
print(f"Target production QPS: {result['target_production_qps']}")
print(f"Size endpoint for: {result['recommended_max_qps']:.0f} QPS")
print(f"Operate at: {result['operate_at_capacity']}")
print(f"Available burst capacity: {result['burst_capacity']}")
# Output:
# Target production QPS: 200
# Size endpoint for: 369 QPS
# Operate at: 65%
# Available burst capacity: 35% headroom
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 voisinsembedding_gen_time: temps passé à générer l'embedding de query sur l'Endpoint de service de modèlereranker_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.
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 :
- Sélectionnez la ligne qui répond le mieux à vos exigences de latence.
- 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 :
- Veuillez attendre que l'endpoint soit prêt. Cela peut prendre plusieurs minutes.
- Exécutez le test de validation final dans le notebook.
- 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 :
- Le service principal dispose des permissions Can Query sur l'endpoint de recherche IA (pas seulement l'index).
- Le Secret Scope contient des clés
service_principal_client_idetservice_principal_client_secretvalides. - 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 analysedebug_infoà partir des réponses) commelocust_script_path. - Certaines configurations d'Endpoint pourraient ne pas renvoyer
debug_info. Les index d'embedding auto-gérés renvoient généralementann_timeetresponse_time, mais pasembedding_gen_timeoureranker_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_resultssimilaire à 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 :
- Utiliser des Service Principals OAuth (pas des PATs) – amélioration de 100 ms
- Réduire
num_results— La récupération de 100 résultats est plus lente que 10 - Optimiser les filtres — Les filtres complexes ou trop restrictifs ralentissent les query
- 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 :
- Augmenter la capacité de l'endpoint - Montez en charge votre endpoint.
- Implémentez la limitation du débit côté client ou répartissez les query dans le temps
- Utiliser le regroupement de connexions – Réutiliser les connexions
- Ajoutez une logique de nouvelle tentative — Utilisez l'interruption exponentielle (déjà une partie du Python SDK)