Menu Docs
Página inicial do Docs
/ / /
Driver Rust
/

Considerações de desempenho

Nesta página

  • Visão geral
  • Ciclo de vida do cliente
  • Pool de Conexões
  • Configurar tamanho máximo do pool
  • Configurar opções de conexão simultânea
  • Configurar tempo máximo de inatividade
  • Paralelismo
  • Tempo de execução
  • Informações adicionais
  • Documentação da API

Neste guia, você verá como otimizar o desempenho do driver do Rust. Para se conectar ao MongoDB, você deve criar uma instância do Client . Sua instância do Client lida automaticamente com a maioria dos aspectos da conexão, como descobrir a topologia do servidor, monitorar sua conexão e manter um pool de conexões interno. Este guia descreve as melhores práticas para configurar e utilizar sua instância do Client .

Este guia inclui as seguintes seções:

  • O Ciclo de vida do cliente descreve as melhores práticas para criar e gerenciar uma instância Client

  • O Pool de conexões descreve como o pool de conexões funciona no driver

  • Paralelismo fornece código de amostra para executar tarefas paralelas e assíncronas

  • O tempo de execução descreve como gerenciar tempos de execução usando as funcionalidades das caixas tokio e async_std

  • Informações adicionais fornecem links para recursos e documentação da API para os tipos e métodos mencionados neste guia

Recomendamos que você reutilize seu cliente entre sessões e operações. Você pode utilizar a mesma instância do Client para executar múltiplas tarefas, pois o tipo do Client é seguro para uso simultâneo por vários threads. Criar uma nova instância do Client para cada solicitação resulta em desempenho mais lento.

O seguinte código cria um método que aceita um ponteiro para uma instância do Client existente, que permite a você executar muitas solicitações utilizando o mesmo cliente:

// ... Create a client earlier in your code
async fn make_request(client: &Client) -> Result<(), Box<dyn Error>> {
// Use the client to perform operations
Ok(())
}

Cada instância Client tem um pool de conexões integrado para cada servidor em sua topologia do MongoDB. Os pools de conexões abrem soquetes sob demanda para oferecer suporte a solicitações simultâneas ao MongoDB em seu aplicativo.

A configuração padrão de um Client funciona para a maioria dos aplicativos. O seguinte código mostra como criar um cliente com configurações de conexão padrão:

let client = Client::with_uri_str("<connection string>").await?;

Como alternativa, você pode ajustar o pool de conexões para melhor atender às necessidades do seu aplicativo e otimizar o desempenho. Para obter mais informações sobre como personalizar suas configurações de conexão, consulte as seguintes subseções deste guia:

Dica

Para saber mais sobre como configurar um pool de conexões, consulte Ajustando as configurações do pool de conexões no manual do servidor MongoDB.

O tamanho máximo de cada conjunto de conexões é definido pela opção max_pool_size , que padroniza para 10 . Se o número de conexões em uso com um servidor atingir o valor de max_pool_size , a próxima solicitação para esse servidor aguardará até que uma conexão se torne disponível.

Além dos soquetes necessários para dar suporte às solicitações do seu aplicativo, cada instância Client abre mais dois soquetes por servidor em sua topologia MongoDB para monitorar o estado do servidor. Por exemplo, um cliente conectado a um conjunto de réplicas de três nós abre seis soquetes de monitoramento. Se a aplicação usar a configuração padrão para max_pool_size e query somente o nó primário (padrão), poderá haver no máximo 16 conexões totais no pool de conexões. Se a aplicação usar uma preferência de leitura para query os nós secundários, esses pool de conexões crescerão e poderá haver um total de 36 conexões.

Para oferecer suporte a um grande número de solicitações simultâneas do MongoDB em um processo, você pode aumentar o valor da opção max_pool_size . O seguinte código demonstra como especificar um valor para max_pool_size ao instanciar um Client:

let mut client_options = ClientOptions::parse("<connection string>").await?;
client_options.max_pool_size = Some(20);
let client = Client::with_options(client_options)?;

Pool de conexões têm taxa limitada. A opção max_connecting determina o número de conexões que o grupo pode criar em paralelo a qualquer momento. Por exemplo, se o valor de max_connecting for 2, o valor padrão, a terceira solicitação que tenta fazer o check-out simultâneo de uma conexão será bem-sucedida somente quando ocorrer um dos seguintes casos:

  • O pool de conexões termina de criar uma conexão e o número de conexões no pool é menor ou igual ao valor de max_pool_size.

  • Uma conexão existente é verificada novamente no pool.

  • A capacidade do driver de reutilizar conexões existentes melhora devido aos limites de taxa na criação de conexões.

Você pode configurar o número mínimo de conexões simultâneas para cada servidor com a opção min_pool_size , que padroniza para 0. O condutor inicia o pool de ligações com este número de tomadas. Se os soquetes forem fechados e o número total de soquetes, em uso e ociosos, cair abaixo do mínimo, o pool de conexões abrirá mais soquetes até que o mínimo seja atingido.

O seguinte código define as opções max_connecting e min_pool_size ao instanciar um Client :

let mut client_options = ClientOptions::parse("<connection string>").await?;
client_options.max_connecting = Some(3);
client_options.min_pool_size = Some(1);
let client = Client::with_options(client_options)?;

Você pode definir o limite de tempo máximo que uma conexão pode ficar inativa no pool de conexões definindo a opção max_idle_time. Depois que uma conexão fica inativa pelo tempo especificado em max_idle_time, o pool de conexões a remove e a substitui. Esta opção tem como padrão 0, ou sem limite.

Quando o método Client::shutdown() é chamado em qualquer ponto do seu aplicativo, o driver fecha todos os soquetes ociosos e fecha todos os soquetes que estão em uso à medida que são retornados ao pool. A chamada Client::shutdown() fecha somente os soquetes inativos, portanto, não é possível interromper ou encerrar nenhuma operação em andamento usando esse método. O condutor fecha estas tomadas apenas quando o processo é concluído.

O seguinte código define o valor da opção max_idle_time para 90 segundos ao instanciar um Client:

let mut client_options = ClientOptions::parse("<connection string>").await?;
client_options.max_idle_time = Some(Duration::new(90, 0));
let client = Client::with_options(client_options)?;

Se você puder executar operações de dados paralelas, poderá otimizar o desempenho executando tarefas simultâneas e assíncronas. O código abaixo usa o método spawn() do módulo tokio::task para criar tarefas simultâneas separadas para executar operações de inserção:

let client = Client::with_uri_str("<connection string>").await?;
let data = doc! { "title": "1984", "author": "George Orwell" };
for i in 0..5 {
let client_ref = client.clone();
let data_ref = data.clone();
task::spawn(async move {
let collection = client_ref
.database("items")
.collection::<Document>(&format!("coll{}", i));
collection.insert_one(data_ref).await
});
}

Uma instância Client está vinculada à instância do tempo de execução tokio ou async-std no qual você a criou. Se você utilizar uma instância do Client para executar operações em um tempo de execução diferente, você poderá enfrentar comportamentos inesperados ou falhas.

Se usar a macro auxiliar test da caixa tokio ou async_std para testar seu aplicativo, você poderá executar acidentalmente as operações em um tempo de execução diferente do pretendido. Isso ocorre porque essas macros auxiliares criam um novo tempo de execução para cada teste. No entanto, você pode usar uma das seguintes estratégias para evitar esse problema:

  • Anexe o tempo de execução à instância do Client sem utilizar as macros auxiliares do test .

  • Crie uma nova instância Client para cada teste async .

Este exemplo segue a primeira estratégia e cria um tempo de execução global usado apenas para testes. No código a seguir, o método test_list_dbs() usa um cliente que se conecta manualmente a esse tempo de execução para listar os reconhecimento de data center na implantação:

use tokio::runtime::Runtime;
use once_cell::sync::Lazy;
static CLIENT_RUNTIME: Lazy<(Client, Runtime)> = Lazy::new(|| {
let rt = Runtime::new().unwrap();
let client = rt.block_on(async {
Client::with_uri_str("<connection string>").await.unwrap()
});
(client, rt)
});
#[test]
fn test_list_dbs() -> Result<(), Box<dyn Error>> {
let (client, rt) = &*CLIENT_RUNTIME;
rt.block_on(async {
client.list_database_names().await
})?;
Ok(())
}

Implementando a segunda estratégia, o código a seguir cria uma nova instância Client para cada teste executado com tokio::test, garantindo que não haja interações não intencionais entre os tempos de execução:

#[tokio::test]
async fn test_list_dbs() -> Result<(), Box<dyn Error>> {
let client = Client::with_uri_str("<connection string>").await?;
client.list_database_names().await?;
Ok(())
}

Para saber mais sobre como se conectar ao MongoDB, consulte oGuia de Conexão .

Para saber mais sobre os tempos de execução disponíveis para o Rust, consulte o guia sobre API assíncronas e síncronas.

Voltar

Comandos de banco de dados