Partilhar via


Configurar um teste de carga para endpoints de pesquisa vetorial

Esta página fornece orientações, código de exemplo e um caderno de exemplo para testes de carga em endpoints de pesquisa vetorial. Os testes de carga ajudam-no a compreender o desempenho e a prontidão para produção de um endpoint de pesquisa vetorial antes de ser implementado para produção. Os testes de carga podem informar-te sobre:

  • Latência em diferentes níveis de escalonamento
  • Limites de rendimento e gargalos (pedidos por segundo, decomposição da latência)
  • Taxas de erro sob carga sustentada
  • Utilização de recursos e planeamento de capacidade

Para obter mais informações sobre testes de carga e conceitos relacionados, consulte Teste de carga para pontos de serviço de extremidade.

Requerimentos

Antes de iniciar estes passos, deve ter um endpoint de pesquisa vetorial implementado e um principal de serviço com permissões Can Query no endpoint. Veja o Passo 1: Configurar a autenticação do principal de serviço.

Transfira e importe uma cópia dos seguintes ficheiros e bloco de notas de exemplo para a sua área de trabalho do Azure Databricks:

  • input.json. Este é um exemplo do input.json ficheiro que especifica a carga útil enviada por todas as ligações concorrentes para o seu endpoint. Podes ter vários ficheiros se necessário. Se usar o caderno de exemplo, este ficheiro é gerado automaticamente a partir da tabela de entrada fornecida.
  • fast_vs_load_test_async_load.py. Carrega este script para o teu espaço de trabalho (por exemplo, /Workspace/Users/<your-username>/fast_vs_load_test_async_load.py) e define o locust_script_path parâmetro do caderno para o seu caminho. Este script trata da autenticação, entrega da carga útil e recolha de métricas de depuração.
  • O seguinte caderno de exemplo, que executa os testes de carga. Para melhor desempenho, execute este notebook num cluster de nó único com um grande número de núcleos (o Locust escala em todos os núcleos de CPU disponíveis). Recomenda-se memória elevada para consultas com embeddings pré-gerados.

Exemplo de caderno e início rápido

Use o seguinte caderno de exemplo para começar. Suporta dois modos de exploração: uma varredura gradual que testa os níveis específicos de concorrência que define, e um modo de pesquisa binária que encontra automaticamente o QPS (ponto de rutura) máximo sustentável em poucos passos. Todos os parâmetros são configurados usando widgets, para que o caderno possa correr de forma interativa ou como um Trabalho Databricks sem edições de código.

Caderno de teste de carga Locust

Obter caderno

Framework de testes de carga: Locust

O Locust é um framework de testes de carga open-source que permite fazer o seguinte:

  • Varie o número de ligações simultâneas ao cliente
  • Controle a velocidade com que as conexões são iniciadas
  • Mede o desempenho dos endpoints ao longo do teste
  • Deteção automática e utilização de todos os núcleos de CPU disponíveis

O portátil de exemplo usa a --processes -1 bandeira para detetar automaticamente os núcleos da CPU e utilizá-los ao máximo.

Se o Locust estiver limitado pelo CPU, aparece uma mensagem na saída.

Passo 1: Configurar a autenticação do principal de serviço

Importante

Para testes de desempenho com características semelhantes à produção, utilize sempre a autenticação do principal de serviço OAuth. As entidades de serviço fornecem um tempo de resposta até 100ms mais rápido e limites de taxa de solicitação mais elevados em comparação com os tokens de acesso pessoal (PATs).

Criar e configurar o principal de serviço

  1. Crie um principal de serviço do Databricks. Para instruções, consulte Adicionar princípios de serviço à sua conta.

  2. Conceder permissões:

    • Navegue até à sua página do ponto final da pesquisa vetorial.
    • Clique em Permissões.
    • Conceda permissões Pode Consultar ao principal do serviço.
  3. Cria um segredo OAuth.

    • Vá à página de detalhes do principal de serviço.
    • Clique no separador Segredos.
    • Clique em Gerar segredo.
    • Defini a vida útil (recomendo 365 dias para testes a longo prazo).
    • Copie imediatamente tanto o ID do Cliente como o Secreto.
  4. Armazene as credenciais de forma segura.

    • Crie um âmbito de segredo no Databricks. Para instruções, veja Tutorial: Criar e usar um segredo Databricks.
    • Como mostrado no seguinte exemplo de código, armazene o ID do cliente principal de serviço como service_principal_client_id e armazene o segredo OAuth como service_principal_client_secret.
    # 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>")
    

Passo 2: Configure o seu teste de carga

Configuração do portátil

Configura os parâmetros do caderno usando os widgets no topo do caderno. Ao executar o notebook como um Databricks Job, passe estes valores como parâmetros do Job. Não são necessárias edições de código.

Parâmetro Description Valor Recomendado
endpoint_name Nome do seu endpoint de pesquisa vetorial O nome do seu endpoint
index_name Nome completo do índice (catalog.schema.index) nome do índice
test_table Tabela de origem para exemplos de consultas a partir de (catalog.schema.table) A sua tabela de entrada de índice
query_column Coluna de texto a usar para incorporações geridas. Deixe como text ou defina como o nome da sua coluna
embedding_column Coluna contendo vetores de incorporação pré-calculados. Apenas utilizado para embeddings geridos por si próprio. Deixe em branco para embeddings gerenciados.
sample_size Número de consultas a amostrar para o teste 1000
target_concurrencies Lista separada por vírgulas de contagens de clientes concorrentes a testar 5,10,20,50
step_duration_seconds Duração em segundos por nível de concorrência. Um valor aplica-se a todos os níveis, ou fornece um por nível como uma lista separada por vírgulas. 300 (5 minutos)
secret_scope_name Nome do seu escopo secreto Databricks Nome do seu âmbito
locust_script_path Caminho do espaço de trabalho para o fast_vs_load_test_async_load.py script /Workspace/Users/<your-username>/fast_vs_load_test_async_load.py
output_table (Opcional) Tabela Delta para armazenar resultados em (catalog.schema.table). Criado automaticamente na primeira execução. catalog.schema.load_test_results
run_name Nomeie ou comente para marcar esta execução para análise posterior Um rótulo descritivo
exploration_mode gradual percorre target_concurrencies por ordem. binary_search encontra automaticamente o ponto de rutura (ver Exploração do ponto de rutura). gradual
max_target_qps (binary_search apenas) Limite superior para a pesquisa QPS 500
exploration_steps (binary_search apenas) Número máximo de iterações de pesquisa binária 8
error_rate_threshold (binary_search apenas) Taxa máxima aceitável de erro (%) para que um passo seja contado como sucesso 1.0
num_results Número de resultados a devolver por consulta 10
columns_to_return Lista separada por vírgulas de colunas a devolver nos resultados da consulta (por exemplo, id,text). Deixe em branco para retornar todas as colunas. Deixe em branco para padrão

Embeddings geridos vs. autogeridos

O caderno suporta tanto embeddings geridos (onde o Databricks gera embeddings em tempo de consulta) como embeddings autogeridos (onde se passam diretamente vetores pré-computados). Configure os parâmetros apropriados com base no seu tipo de índice.

Tipo de índice Parâmetro a definir Deixar indefinido
Incoporações geridas (modelo de incorporação gerido por Databricks no índice Delta Sync) query_column — o nome da coluna de texto a usar como consulta embedding_column (deixar em branco)
Embeddings autogeridos (Delta Sync ou índice de Acesso Vetorial Direto com vetores pré-computados) embedding_column — a coluna que contém vetores de embedding pré-computados query_column

Observação

Para índices de indexação geridos, o teste de carga mede a latência fim-a-fim, incluindo o tempo de geração de indexação. Se o seu ponto final de incorporação escalar para zero, ocorrerá uma sobrecarga inicial na primeira execução do teste. Veja Identifique o gargalo do modelo de embedding para saber como isolar a latência de embedding da latência de pesquisa.

Porquê 5-10 minutos?

Uma duração mínima de 5 minutos é fundamental.

  • As consultas iniciais podem incluir custos de arranque a frio.
  • Os endpoints precisam de tempo para atingir desempenho em estado estável.
  • A escalabilidade automática do modelo que serve os endpoints (se ativada) demora algum tempo a ativar.
  • Testes curtos não detectam comportamentos de limitação sob carga sustentada.

A tabela seguinte mostra as durações recomendadas do teste consoante o seu objetivo.

Tipo de ensaio Duração do teste Objetivos do teste
Teste rápido de fumo 2-3 minutos Verificar a funcionalidade básica
Linha de base de desempenho 5-10 minutos Métricas fiáveis de regime estacionário
Testes de esforço 15 a 30 minutos Identificar o esgotamento de recursos
Testes de resistência 1-4 horas Degradação, estabilidade de latência

Exploração do ponto de rutura (modo de pesquisa binária)

Para além da varredura gradual (exploration_mode=gradual), o portátil suporta um modo automático de pesquisa binária que encontra o QPS máximo sustentável sem necessidade de especificar manualmente os níveis de concorrência.

Como funciona

Defina exploration_mode=binary_search e especifique max_target_qps (por exemplo, 500). O caderno utiliza a Lei de Little (concurrency = QPS × avg_latency_sec) para converter cada alvo de QPS num nível estimado de concorrência, depois executa uma pesquisa binária da seguinte forma:

  1. Comece em max_target_qps / 2 (250 no exemplo).
  2. Se a taxa de erro for inferior error_rate_threshold (sucesso), aumenta o limite inferior e tenta um QPS mais alto (375, depois 500, e assim sucessivamente).
  3. Se a taxa de erro exceder o limiar (falha), baixar o limite superior e tentar a meio caminho entre o último sucesso e o fracasso.
  4. Repita durante até exploration_steps passos (por defeito 8) ou até que o intervalo de pesquisa se reduza a menos de 5% de max_target_qps.

A tabela seguinte mostra como a pesquisa converge para um ponto final hipotético com um ponto de ruptura em torno de 430 QPS:

Step Alvo QPS Taxa de erro Resultado Nova gama
1 250 0,1% SUCESSO [250, 500]
2 375 0,3% SUCESSO [375, 500]
3 437 4.5% FRACASSO [375, 437]
4 406 0,8% SUCESSO [406, 437]
5 421 2.1% FRACASSO [406, 421]

Após 5–8 passos, a pesquisa converge para o ponto de rutura — neste exemplo, aproximadamente 406–421 QPS — com muito menos execuções de teste do que uma varredura exaustiva.

Quando usar cada modo

Mode Quando utilizar
gradual Já conhece o alcance operacional esperado e quer caracterizar o desempenho em níveis específicos de concorrência.
binary_search Queres encontrar o QPS máximo sustentável rapidamente, sem conhecer os níveis de concorrência antecipadamente.

Passo 3. Desenhe o seu conjunto de consultas

Quando possível, o conjunto de consultas deve refletir o tráfego de produção esperado o mais fielmente possível. Especificamente, deve tentar corresponder à distribuição esperada das consultas em termos de conteúdo, complexidade e diversidade.

  • Use consultas realistas. Não uses texto aleatório como "test query 1234".

  • Corresponder à distribuição esperada do tráfego de produção. Se espera 80 consultas% comuns, 15% consultas de frequência média e 5 consultas% infrequentes, o seu conjunto de consultas deve refletir essa distribuição.

  • Corresponde ao tipo de consulta que esperas ver em produção. Por exemplo, se espera que as consultas de produção usem pesquisa híbrida ou filtros, também deve usar esses no seu conjunto de consultas.

    Exemplo de consulta usando filtros:

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

    Exemplo de consulta usando pesquisa híbrida:

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

Diversidade de consultas e cache

Os endpoints de pesquisa vetorial armazenam em cache vários tipos de resultados de consulta para melhorar o desempenho. Esta cache pode afetar os resultados dos testes de carga. Por esta razão, é importante prestar atenção à diversidade do conjunto de consultas. Por exemplo, se enviares repetidamente o mesmo conjunto de consultas, estás a testar a cache, não o desempenho real da pesquisa.

Utilização: Quando: Example
Idênticas ou poucas consultas
  • O seu tráfego de produção tem elevada repetição de consultas (por exemplo, "produtos populares")
  • Estás a testar especificamente a eficácia do cache
  • A sua aplicação beneficia do cache (por exemplo, dashboards com consultas fixas)
  • Queres medir o desempenho em cache no melhor caso
Um widget de recomendação de produto que mostra "itens em tendência" – a mesma consulta é executada milhares de vezes por hora.
Consultas diversas
  • O seu tráfego de produção tem consultas únicas dos utilizadores (por exemplo, motores de busca ou chatbots)
  • Queres medir o desempenho no pior caso sem cache
  • Queres testar o desempenho da análise de índice, não o desempenho da cache
  • As consultas têm alta cardinalidade (milhões de variações possíveis)
Uma pesquisa de comércio eletrónico onde cada utilizador escreve diferentes pesquisas de produtos.

Para recomendações adicionais, consulte Resumo das melhores práticas.

Opções para criar um conjunto de consultas

Os separadores de código mostram três opções para criar um conjunto diversificado de consultas. Não existe uma solução única para todos. Escolhe aquele que funcione melhor para ti.

  • (Recomendado) Amostragem aleatória da tabela de entrada de índice. Este é um bom ponto de partida geral.
  • Amostragem de registos de produção. Este é um bom começo se tiveres registos de produção. Tenha em mente que as consultas normalmente mudam ao longo do tempo, por isso atualize o conjunto de testes regularmente para o manter atualizado.
  • Gerar consultas sintéticas. Isto é útil se não tiver registos de produção ou se estiver a usar filtros complexos.

Amostragem aleatória a partir da tabela de entrada

O código seguinte amostra consultas aleatórias da sua tabela de entrada de índice.

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")

Amostra dos registos de produção

Os seguintes exemplos de código são amostrados proporcionalmente a partir de consultas de produção.

# 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)

Consultas sintéticas

Se ainda não tiver registos de produção, pode gerar consultas sintéticas diversas.

# 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")

Passo 4. Teste a sua carga útil

Antes de executar o teste de carga completa, valide a sua carga útil:

  1. No espaço de trabalho Databricks, navegue até ao seu endpoint de pesquisa vetorial.
  2. Na barra lateral esquerda, clique em Servir.
  3. Seleciona o teu endpoint.
  4. Clique em UsarConsulta.
  5. Cole o seu input.json conteúdo na caixa de consulta.
  6. Verifique se o endpoint retorna os resultados esperados.

Isto garante que o seu teste de carga mede consultas realistas, não respostas de erro.

Passo 5. Executar o teste de carga

Verificação de conectividade e aquecimento

Antes do início do teste de carga, o notebook executa dois passos de configuração:

  1. Verificação de conectividade: Envia uma única consulta de teste usando as credenciais do principal do serviço. Se o endpoint devolver um erro 401 ou 403, o notebook falha de imediato com uma mensagem clara PermissionError em vez de realizar um teste de carga completo que apenas produz dados de erro. Isto poupa tempo quando credenciais ou permissões são mal configuradas.

  2. Teste de aquecimento (1 minuto): Executa um curto teste de baixa concorrência que aquece os caches dos endpoints e valida o fluxo de pedidos de ponta a ponta. Os resultados do aquecimento não são usados para métricas de desempenho. No modo de pesquisa binária, a latência de aquecimento é também usada como base para a estimativa de concorrência da Lei de Little.

Série principal de testes de carga

O notebook executa uma série de testes com uma concorrência crescente de clientes:

  • Início: Baixa concorrência (por exemplo, 5 clientes em simultâneo)
  • Meio: Concorrência média (por exemplo, 10, 20 ou 50 clientes)
  • Fim: Elevada concorrência (por exemplo, mais de 100 clientes)

Cada teste decorre durante a duração configurada em step_duration_seconds (recomenda-se 5-10 minutos).

O que o portátil mede

O caderno mede e reporta o seguinte:

Métricas de latência:

  • P50 (mediana): Metade das consultas são mais rápidas do que isto.
  • P95: 95% de consultas são mais rápidas do que isto. Esta é uma métrica chave de SLA.
  • P99: 99% de consultas são mais rápidas do que esta.
  • Max: Latência do pior cenário.

Métricas de rendimento:

  • RPS (pedidos por segundo): Consultas bem-sucedidas por segundo.
  • Total de consultas: Número de consultas concluídas.
  • Taxa de sucesso: Percentagem de consultas bem-sucedidas.

Erros:

  • Falhas de consulta por tipo
  • Mensagens de exceção
  • Os limites de tempo contam

Armazenamento de resultados

Se o output_table parâmetro estiver definido, o caderno armazena uma linha por nível de concorrência (ou por cada passo de pesquisa binária) numa tabela Delta do Catálogo Unity. A tabela é criada automaticamente na primeira execução e adicionada nas execuções seguintes. Cada linha inclui run_name, exploration_mode, concorrência, taxas de sucesso/falha, percentis de latência, RPS e campos específicos de pesquisa binária (bs_step, bs_target_qps, bs_outcome). Isto permite comparar execuções ao longo do tempo usando ferramentas SQL ou BI.

Executar como um Job Databricks

Todos os parâmetros do notebook são definidos como dbutils.widgets, que correspondem diretamente aos parâmetros de Job do Databricks. Para agendar ou automatizar testes de carga:

  1. Cria um trabalho com o caderno como tarefa.
  2. Defina os valores dos widgets como parâmetros de Trabalho. Não são necessárias edições de código.
  3. Anexe a tarefa a um cluster de nó único, equipado com muitos núcleos de CPU (Locust beneficia de trabalhadores paralelos).
  4. Executar sob demanda ou com um horário para testes de base recorrentes.

Passo 6. Interpretar resultados

A tabela seguinte mostra as metas para bom desempenho:

Métrico Target Comment
Latência P95 < 500ms A maioria das consultas são rápidas
Latência P99 < 1s Desempenho razoável em pesquisas de cauda longa
Taxa de sucesso > 99,5% Baixa taxa de falha
Latência ao longo do tempo Estável Nenhuma degradação observada durante o teste
Consultas por segundo Atinge o alvo O endpoint pode lidar com o tráfego esperado

Os seguintes resultados indicam um desempenho fraco:

  • P95 > 1s. Indica que as consultas são demasiado lentas para utilização em tempo real.
  • P99 > 3s. A latência nas consultas long-tail prejudica a experiência do utilizador.
  • Taxa < de sucesso 99%. Demasiados fracassos.
  • Latência crescente. Indica esgotamento de recursos ou fuga de memória.
  • Erros de limitação de taxa (429). Indica que é necessária uma maior capacidade do endpoint.

Compensação entre RPS e latência

O RPS máximo não é o ponto ótimo para a taxa de produção. A latência aumenta de forma não linear à medida que se aproxima do débito máximo. Operar ao máximo de RPS resulta frequentemente numa latência 2-5x superior comparado com operar a 60-70% da capacidade máxima.

O exemplo seguinte mostra como analisar os resultados para encontrar o ponto operacional ótimo.

  • O RPS máximo é de 480 em 150 clientes concorrentes.
  • O ponto operacional ideal é 310 RPS com 50 clientes simultâneos (capacidade de 65%).
  • A penalização de latência no máximo: P95 é 4,3x superior (1,5s vs. 350ms)
  • Neste exemplo, a recomendação é dimensionar o endpoint para uma capacidade de 480 RPS e operar a ~310 RPS.
Concurrency P50 Pág. 95 Pág. 99 RPS Sucesso Capacidade
5 80ms 120ms 150 milissegundos 45 100% 10%
10 85ms 140ms 180ms 88 100% 20%
20 95ms 180ms 250 milissegundos 165 99,8% 35%
50 150 milissegundos 350ms 500 ms 310 99,2% 65% ← Ponto ideal
100 250 milissegundos 800ms 1.2s 420 97,5% 90% ⚠️ Aproximando-se do máximo
150 450ms 1,5s 2,5s 480 95.0% 100% ❌ Máximo de RPS

Operar ao máximo RPS pode levar aos seguintes problemas:

  • Degradação da latência. No exemplo, o P95 tem 350 ms a 65% de capacidade, mas é 1,5 s a 100% de capacidade.
  • Não há espaço para acomodar picos de tráfego ou picos de carga. Com 100% capacidade, qualquer pico provoca um timeout. Com uma capacidade de 65%, um pico de tráfego de 50% pode ser gerido sem problema.
  • Aumento das taxas de erro. No exemplo, a taxa de sucesso é de 99,2% com capacidade de 65%, mas 95,0% — uma taxa de falha de 5% — a 100% de capacidade.
  • Risco de esgotamento de recursos. Na carga máxima, as filas aumentam, a pressão da memória aumenta, os pools de ligação começam a saturar-se e o tempo de recuperação após incidentes aumenta.

A tabela seguinte mostra os pontos de funcionamento recomendados para diferentes casos de uso.

Caso de uso Capacidade alvo Fundamentação
Sensível à latência (pesquisa, chat) 50-60% do máximo Priorize a baixa latência P95/P99
Equilibrado (recomendações) 60-70% de máximo Bom equilíbrio entre custo e latência
Otimização de custos (tarefas em lote) 70-80% de máximo Latência superior aceitável
Não recomendado > 85% de máximo Picos de latência, sem capacidade de rajada

Funções auxiliares para calcular o tamanho do ponto operacional e do ponto final

Encontrar o ponto ótimo

O código seguinte representa a latência QPS vs P95. No gráfico, procure o ponto onde a curva começa a curvar-se bruscamente para cima. Este é o ponto operacional ideal.

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()

Fórmula de recomendação de tamanho

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

Identificar o gargalo do modelo de embedding

Se o seu índice usar embeddings geridos, o caderno de testes de carga mede os tempos de cada componente através do parâmetro debug_level=1 em cada consulta. A tabela de resultados inclui:

  • ann_time — tempo gasto na pesquisa aproximada do vizinho mais próximo
  • embedding_gen_time — tempo gasto a gerar o vetor de consulta no endpoint de serviço do modelo
  • reranker_time — tempo gasto na reclassificação (se ativado)
  • response_time — tempo total de resposta de ponta a ponta

Se embedding_gen_time for consistentemente grande em relação a ann_time, o ponto final de embedding é o gargalo, não o ponto final da pesquisa vetorial. Causas comuns:

  • O modelo de embedding que serve o endpoint tem o Scale to Zero ativado. Desative-o para testes de carga em ambiente de produção. Consulte Evitar a escalagem para zero na produção.
  • O ponto final de incorporação não tem concorrência predefinida suficiente para a taxa de consulta em teste.
  • O endpoint do modelo de embedding é partilhado com outras cargas de trabalho. Use um endpoint dedicado para testes de carga.

Sugestão

Para isolar o desempenho da pesquisa vetorial do desempenho do modelo de embedding, opte por embeddings autogeridos para testes de carga. Passa vetores pré-computados no EMBEDDING_COLUMN parâmetro em vez de consultas de texto. Isto elimina completamente a latência de embedding da medição.

Passo 7: Dimensione o seu endpoint

Usa a recomendação do caderno

Após analisar os resultados, o caderno pede-lhe para:

  1. Seleciona a linha que melhor corresponde aos teus requisitos de latência.
  2. Introduza o RPS desejado pela sua aplicação.

O caderno mostra então o tamanho recomendado do endpoint. Calcula a capacidade necessária com base no seguinte:

  • O teu RPS alvo
  • Latência observada em diferentes níveis de concorrência
  • Limiares de taxa de sucesso
  • Margem de segurança (tipicamente 2x carga máxima esperada)

Considerações sobre o dimensionamento

Endpoints padrão:

  • Escala automaticamente para suportar o tamanho do índice
  • Escalar manualmente para suportar a taxa de transferência
  • Reduza automaticamente a escala para baixo quando os índices são eliminados
  • Reduza manualmente a escala para reduzir a capacidade

Endpoints otimizados para armazenamento:

  • Escala automaticamente para suportar o tamanho do índice
  • Reduza automaticamente a escala para baixo quando os índices são eliminados

Passo 8: Validar a Configuração Final

Após atualizar a configuração do seu endpoint:

  1. Espera que o endpoint esteja pronto. Isto pode demorar vários minutos.
  2. Executa o teste final de validação no caderno.
  3. Confirme que o desempenho cumpre os seus requisitos:
    • RPS ≥ taxa de transferência objetivo
    • A latência P95 cumpre os SLA
    • Taxa > de sucesso 99,5%
    • Sem erros sustentados

Se a validação falhar, tente o seguinte:

  • Aumentar a capacidade do endpoint
  • Otimizar a complexidade das consultas
  • Revisão do desempenho do filtro
  • Verifique a configuração do endpoint de incorporação

Quando voltar a testar

Para manter a visibilidade de desempenho, é aconselhável fazer testes de carga base trimestrais. Deve também repetir o teste sempre que fizer alguma das seguintes alterações:

  • Alterar padrões de consulta ou complexidade
  • Atualizar o índice de pesquisa vetorial
  • Modificar configurações de filtros
  • Espere aumentos significativos de tráfego
  • Implementar novas funcionalidades ou otimizações
  • Mudança de tipos de endpoint padrão para tipos de endpoint otimizados para armazenamento

Troubleshooting

Todos os pedidos falham com ~10ms de latência e respostas de 240 bytes

Isto indica que o principal do serviço está a receber uma resposta 401/403. Verificar:

  1. O principal do serviço tem permissões Pode Consultar no endpoint de pesquisa vetorial (não apenas no índice).
  2. O âmbito secreto contém chaves válidas service_principal_client_id e service_principal_client_secret.
  3. O segredo OAuth não expirou.

O portátil inclui uma verificação de conectividade que deteta isto antes de executar o teste de carga completa.

Executar múltiplos trabalhos de teste de carga no mesmo cluster

Se executares dois trabalhos de teste de carga em simultâneo no mesmo cluster, um dos trabalhos pode receber tokens OAuth obsoletos ou ter conflitos de CPU com os trabalhadores Locust do outro. Para resultados fiáveis, execute trabalhos de teste de carga um de cada vez num cluster dedicado.

Os grafos de temporização dos componentes estão vazios

Os grafos de temporização de componentes (ann_time, embedding_gen_time, reranker_time) exigem que o endpoint devolva debug_info nas respostas de consulta. Se estes grafos estiverem vazios:

  • Verifique se está a utilizar o script fast_vs_load_test_async_load.py (que analisa debug_info a partir das respostas) como o locust_script_path.
  • Algumas configurações de endpoint podem não devolver debug_info. Os índices de embedding autogeridos normalmente retornam ann_time e response_time mas não embedding_gen_time ou reranker_time.

Tabela de resultados não consultável a partir de um SQL warehouse

O caderno escreve os resultados da sessão Spark do cluster. Se um SQL warehouse mostrar 0 linhas para uma tabela que o caderno reporta como preenchida, o problema pode ser um atraso na sincronização de metadados do Unity Catalog. Espere alguns minutos e tente novamente, ou consulte a tabela diretamente a partir de um caderno ligado ao mesmo cluster.

Resumo das melhores práticas

Configuração de teste

  • Faça testes durante pelo menos 5 minutos na carga máxima.

  • Use princípios de serviço OAuth para autenticação.

  • Crie cargas úteis de consulta realistas que correspondam às consultas de produção esperadas.

  • Teste com filtros e parâmetros semelhantes aos de produção.

  • Inclua um período de aquecimento antes de medir.

  • Teste em múltiplos níveis de concorrência.

  • Acompanhe as latências P95/P99, não apenas as médias.

  • Teste o desempenho tanto em cache como sem cache.

    # 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)")
    

Desenho de conjuntos de consultas

  • Ajuste a diversidade das suas consultas de teste com a distribuição real de tráfego (consultas frequentes e raras).
  • Use consultas reais dos registos (anonimizadas).
  • Inclua diferentes complexidades de consulta.
  • Testa ambos os cenários em cache e sem cache e acompanha os resultados separadamente.
  • Teste com as combinações de filtros esperadas.
  • Usa os mesmos parâmetros que vais usar em produção. Por exemplo, se usar pesquisa híbrida em produção, inclua consultas de pesquisa híbrida. Use um parâmetro semelhante num_results como na produção.
  • Não uses consultas que nunca ocorrerão em produção.

Otimização do desempenho

Se as latências forem demasiado elevadas, experimente o seguinte:

  1. Use princípios de serviço OAuth (não PATs) - melhoria de 100ms
  2. Reduzir num_results - Obter 100 resultados é mais lento do que 10
  3. Otimizar filtros - Filtros complexos ou excessivamente restritivos abrandam as consultas
  4. Verifica o endpoint de embedding - Certifica-te de que não está escalado para zero ou que tem largura de banda suficiente

Se estiver a atingir os limites de taxa, experimente o seguinte:

  1. Aumentar a capacidade do endpoint - Ampliar o seu endpoint
  2. Implementar limitação de taxa do lado do cliente ou distribuir consultas ao longo do tempo
  3. Utilize agrupamento de ligações - Reutilize as ligações
  4. Adicionar lógica de nova tentativa - Usar retrocesso exponencial (que já é parte do SDK Python)

Recursos adicionais