Como solucionar consultas lentas do Elasticsearch para uma melhor experiência do usuário

Observação importante para usuários do Elastic Cloud (Elasticsearch Service/ESS): Atualmente, o conteúdo mencionado neste artigo não está disponível no Elastic Cloud. No entanto, queremos coletar as opiniões dos nossos valiosos usuários. Se você tiver interesse em usar este recurso, entre em contato com o suporte da Elastic.

Para qualquer pessoa que use o Elasticsearch® como seu mecanismo de busca, identificar e solucionar problemas de consultas é uma habilidade crucial a ser dominada. Seja em e-commerce, observabilidade ou soluções de busca voltadas para o ambiente de trabalho, um Elasticsearch lento impactará negativamente a experiência do seu usuário.

Para identificar consultas lentas do Elasticsearch, você pode usar o slow log, que captura a execução da consulta em um determinado limite. Definir o limite do slow log corretamente é um desafio por si só. Por exemplo, uma consulta que leva 500 milissegundos sob carga total pode ser aceitável, mas a mesma consulta sob carga baixa pode ser inaceitável. O slow log não diferencia e registra tudo acima de 500 milissegundos. O slow log faz seu trabalho muito bem, então você pode capturar diferentes níveis de granularidade dependendo do valor do limite. O rastreamento, por outro lado, pode analisar todas as consultas, identificando quantas de suas consultas estão dentro de determinados limites.

O monitoramento de performance de aplicação (APM) não se limita mais apenas à sua aplicação. Usando a instrumentação no Elasticsearch, agora podemos adicionar o Elasticsearch como um serviço completo, em vez de uma dependência na sua stack de aplicação. Dessa forma, obtemos uma visão mais detalhada do desempenho do que o log lento pode fornecer.

Para o exemplo a seguir, nosso corpus de dados é o OpenWebText, que fornece aproximadamente 40 GB de texto puro e cerca de 8 milhões de documentos individuais que são executados localmente em um Macbook M1 Max com 32 GB de RAM.

Para começar

A ativação do rastreamento no Elasticsearch é feita com configurações estáticas (configuradas no elasticsearch.yml) e configurações dinâmicas, que podem ser alternadas durante o tempo de execução usando um comando PUT _cluster/settings, onde uma dessas configurações dinâmicas é a taxa de amostragem. Algumas configurações, como a taxa de amostragem, podem ser alternadas durante o tempo de execução. No elasticsearch.yml, queremos definir o seguinte:

Válido para a versão 9.x

telemetry.agent.enabled: true
telemetry.agent.server_url: "url of the APM server"

Válido para as versões 7.x e 8.x

tracing.apm.enabled: true
tracing.apm.agent.server_url: "url of the APM server"

O token secreto (ou chave de API) deve estar no keystore do Elasticsearch. A ferramenta keystore deve estar disponível em <seu diretório de instalação do <your elasticsearch install directory>elasticsearch>/bin/elasticsearch-keystore usando o comando a seguir para as versões 7.x e 8.x elasticsearch-keystore add tracing.apm.secret_token ou tracing.apm.api_key. Para a versão 9.x, use telemetry.secret_token ou telemetry.api_key em vez disso. Depois disso, você precisa reiniciar o Elasticsearch. Mais informações sobre rastreamento podem ser encontradas em nosso documento de rastreamento.

Assim que o APM estiver ativo, podemos ver a visualização do APM no Kibana e observar que o Elasticsearch captura vários endpoints de REST API automaticamente. Aqui, focamos principalmente nas chamadas POST /{index}/_search e vemos o que podemos obter com elas.

captura de tela do Elasticsearch

Ao examinar uma consulta simples diretamente na caixa GET /{index}/_search, vemos a seguinte análise em cascata. Ela contém spans internos que fornecem insights mais profundos sobre o que o Elasticsearch está fazendo nos bastidores. E vemos a duração total de buscar (86 milissegundos).

Amostra de trace

Os metadados que acompanham a consulta incluem informações extensas sobre o cabeçalho HTTP, usuário agent, localização do Node do Elasticsearch (metadados do provedor de serviços em nuvem, hostname, informações do container), algumas informações do sistema e detalhes da URL. Usando algumas informações básicas de transação, podemos criar um gráfico do Lens que plota a duração média da transação e nos permite ver se há uma tendência de alta ou de baixa.

Nossa aplicação de buscar

É bom não precisar mais usar logs lentos! Eu posso determinar a duração da transação e identificar quantas buscas são respondidas abaixo de qualquer limite. No entanto, há um contratempo — o Elasticsearch não captura a consulta enviada, então sabemos que uma consulta levou muito tempo, mas não sabemos qual foi a consulta.

Vamos instrumentar um aplicativo de busca de exemplo. Nesse caso, usaremos um app Flask simples com duas rotas, search_single e search_phrase, que representarão uma consulta match e uma match_phrase no Elasticsearch. Por exemplo, poderíamos usar as seguintes consultas:

{
  "query": {
    "match": {
      "content": "support"
    }
  }
}
And
{
  "query": {
    "match_phrase": {
      "content": "support protest"
    }
  }
}

O código Flask a seguir implementa a rota search_single. A search_phrase é muito semelhante, exceto pelo fato de usar match_phrase em vez de match.

@app.route("/search_single", methods=["GET"])
def search_single():
    query = request.args.get("q", "")
    if not query.strip():
        return jsonify({"error": "No search query provided"}), 400
    try:
        result = es.search(
            index=ES_INDEX, query={"match": {"content": query}}
        )

        hits = result["hits"]["hits"]
        response = []
        for hit in hits:
            response.append(
                {
                    "score": hit["_score"],
                    "content": hit["_source"]["content"],
                }
            )
        
        return jsonify(response)

Com tudo isso preparado, agora posso chamar curl -XGET "http://localhost:5000/search_single?q='microphone'" para buscar o termo microphone.

Adicionamos principalmente o APM à nossa aplicação de busca para observar, mas nossos agentes de APM capturam solicitações de saída e as enriquecem com informações de metadados. Em nosso caso, o span.db.statement contém a consulta do Elasticsearch. E neste caso abaixo, alguém buscou por window.

detalhes da duração

Juntando as peças

No meu serviço Flask, defini o tamanho da consulta como 5.000, o que significa que o Elasticsearch deve me fornecer até 5.000 documentos correspondentes em uma única resposta JSON. Esse é um número grande, e grande parte do tempo é gasto recuperando essa quantidade de documentos do disco. Depois de alterá-lo para os 100 principais documentos, posso identificar rapidamente o que aconteceu no meu dashboard comparando-o.

Olhar para uma transação na visualização de APM e ativar a função de laboratório para o caminho crítico cria uma sobreposição, mostrando-nos onde nossa aplicação está gastando seu tempo.

linha do tempo da visualização do APM

Depois disso, criei um dashboard usando os campos transaction.duration.us, es_query_took, transaction.name. Os filtros KQL gerais contêm service.name, processor.event: transaction, transaction.name: POST /{index}/_search.

Dica lateral: vá para o gerenciamento de data view > selecione seu data view contendo os fluxos de dados de APM > selecione o campotransaction.duration.us > e altere o formato para duration. Agora, ele será renderizado automaticamente em uma saída legível por humanos em vez de microssegundos.

Aproveitando o recurso de anotação do Lens, podemos ver no Lens do meio que a alteração para 100 documentos reduziu bastante a transação média de buscar. Não apenas isso, veja a contagem geral de registros no canto superior direito. Como podemos buscar mais rápido, temos um throughput maior! Eu gosto muito de histogramas, então criei um no meio da linha superior, onde tenho a duração da transação no eixo X e a contagem de registros no eixo Y. Além disso, o APM fornece métricas, para que possamos identificar quanto uso de CPU% está ocorrendo a qualquer momento, bem como o heap da JVM, o uso de non-heap, a contagem de threads e outras informações úteis.

gráficos e tabelas

Conclusão

Este post do blog mostrou como é importante ter o Elasticsearch como uma aplicação instrumentada e identificar gargalos com muito mais facilidade. Além disso, você pode usar a duração da transação como uma métrica para detecção de anomalia, fazer testes A/B para sua aplicação e nunca mais se perguntar se o Elasticsearch parece mais rápido, já que agora você tem dados para responder a essa pergunta. Além disso, todos os metadados coletados de agentes de usuário para consultas ajudam você a solucionar problemas.

Os dashboards e a Data view podem ser importados aqui.

AVISO

Há um problema com a duração das transações dentro do Elasticsearch. Isso foi corrigido na próxima versão 8.9.1. Até lá, as transações usam o relógio incorreto, o que prejudica a duração geral.

O lançamento e o tempo de amadurecimento de todos os recursos ou funcionalidades descritos neste artigo permanecem a exclusivo critério da Elastic. Os recursos ou funcionalidades não disponíveis no momento poderão não ser entregues ou não chegarem no prazo previsto.