Docs Menu
Docs Home
/ /

Consideraciones sobre el rendimiento

En esta guía, aprenderá a optimizar el rendimiento del controlador de Rust. Para conectarse a MongoDB, debe crear un Client Instancia. Su instancia Client gestiona automáticamente la mayoría de los aspectos de la conexión, como el descubrimiento de la topología del servidor, la monitorización de la conexión y el mantenimiento de un grupo de conexiones interno. Esta guía describe las prácticas recomendadas para configurar y usar su instancia Client.

Esta guía incluye las siguientes secciones:

  • El ciclo devida del Client cliente describe las mejores prácticas para crear y administrar una instancia

  • Elpool de conexiones describe cómo funciona el pool de conexiones en el controlador

  • Parallelism proporciona código de muestra para ejecutar tareas paralelas y asincrónicas

  • Eltiempo de ejecución describe cómo administrar los tiempos de ejecución mediante el uso de las funcionalidades de las cajas tokio y async_std

  • Información adicional proporciona enlaces a recursos y documentación de API para los tipos y métodos mencionados en esta guía.

Se recomienda reutilizar el cliente en sesiones y operaciones. Puedes utilizar la misma instancia Client para realizar múltiples tareas, ya que el tipo Client es seguro para el uso concurrente de múltiples hilos. Crear una nueva instancia Client para cada solicitud genera un desempeño más lento.

El siguiente código crea un método que acepta un puntero a una instancia Client existente, lo que le permite realizar muchas solicitudes utilizando el mismo 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 instancia Client tiene un grupo de conexiones integrado para cada servidor de su topología de MongoDB. Los grupos de conexiones abren sockets bajo demanda para admitir solicitudes simultáneas a MongoDB en su aplicación.

La configuración por defecto para un Client funciona para la mayoría de las aplicaciones. El siguiente código muestra cómo crear un cliente con la configuración de conexión por defecto:

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

Como alternativa, puede ajustar el grupo de conexiones para que se adapte mejor a las necesidades de su aplicación y optimice el rendimiento. Para obtener más información sobre cómo personalizar la configuración de conexión, consulte las siguientes subsecciones de esta guía:

Tip

Para obtener más información sobre cómo configurar un grupo de conexiones, consulte Ajuste de la configuración de su grupo de conexiones en el manual del servidor.

El tamaño máximo de cada grupo de conexiones se establece mediante la opción max_pool_size, cuyo valor predeterminado es 10. Si el número de conexiones en uso a un servidor alcanza max_pool_size, la siguiente solicitud a ese servidor espera hasta que haya una conexión disponible.

Además de los sockets necesarios para atender las solicitudes de su aplicación, cada instancia Client abre dos sockets más por servidor en su topología de MongoDB para supervisar el estado del servidor. Por ejemplo, un cliente conectado a un conjunto de réplicas de tres nodos abre seis sockets de supervisión. Si la aplicación usa la configuración predeterminada para max_pool_size y solo consulta el nodo principal (predeterminado), puede haber un máximo de 16 conexiones en el grupo de conexiones. Si la aplicación usa una preferencia de lectura para consultar los nodos secundarios, estos grupos de conexiones crecen y pueden haber 36 conexiones en total.

Para admitir un gran número de solicitudes simultáneas de MongoDB en un proceso, puede aumentar el valor de la opción max_pool_size. El siguiente código muestra cómo especificar un valor para max_pool_size al instanciar un Client:

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

Los pools de conexiones tienen limitaciones de velocidad. La opción max_connecting determina cuántas conexiones puede crear el pool en paralelo en cualquier momento. Por ejemplo, si el valor de max_connecting es 2, el valor por defecto, la tercera solicitud que intenta obtener una conexión simultáneamente solo tiene éxito cuando ocurre uno de los siguientes casos:

  • El grupo de conexiones termina de crear una conexión y la cantidad de conexiones en el grupo es menor o igual al valor de max_pool_size.

  • Una conexión existente se vuelve a incluir en el grupo.

  • La capacidad del controlador para reutilizar conexiones existentes mejora debido a los límites de velocidad en la creación de conexiones.

Puede establecer el número mínimo de conexiones simultáneas a cada servidor con la opción min_pool_size, cuyo valor predeterminado es 0. El controlador inicializa el pool de conexiones con este número de sockets. Si se cierran los sockets y el número total de sockets, tanto en uso como inactivos, cae por debajo del mínimo, el pool de conexiones abre más sockets hasta alcanzarlo.

El siguiente código establece las opciones max_connecting y min_pool_size al crear una instancia de 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)?;

Puede establecer el tiempo máximo que una conexión puede permanecer inactiva en el pool configurando la opción max_idle_time. Una vez que una conexión ha estado inactiva durante el tiempo especificado en max_idle_time, el pool la elimina y la reemplaza. Esta opción tiene como valor predeterminado 0, es decir, sin límite.

Al llamar al método Client::shutdown() en cualquier punto de la aplicación, el controlador cierra todos los sockets inactivos y todos los que están en uso a medida que se devuelven al grupo. Llamar a Client::shutdown() solo cierra los sockets inactivos, por lo que no se pueden interrumpir ni finalizar operaciones en curso con este método. El controlador cierra estos sockets solo cuando el proceso finaliza.

El siguiente código establece el valor de la opción max_idle_time en 90 segundos al crear una instancia de 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)?;

Si puede ejecutar operaciones de datos en paralelo, puede optimizar el rendimiento ejecutando tareas asincrónicas y simultáneas. El siguiente código utiliza el método spawn() del módulo tokio::task para crear tareas independientes y simultáneas que realizan operaciones de inserción:

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
});
}

Una instancia Client está vinculada a la instancia del entorno de ejecución tokio o async-std en el que la creó. Si usa una instancia Client para realizar operaciones en un entorno de ejecución diferente, podría experimentar comportamientos inesperados o fallos.

Si usa la macro auxiliar test del paquete tokio o async_std para probar su aplicación, podría ejecutar operaciones accidentalmente en un entorno de ejecución distinto al previsto. Esto se debe a que estas macros auxiliares crean un nuevo entorno de ejecución para cada prueba. Sin embargo, puede usar una de las siguientes estrategias para evitar este problema:

  • Adjunte el entorno de ejecución a la instancia Client sin utilizar las macros auxiliares test.

  • Crea una nueva instancia Client para cada prueba async.

Este ejemplo sigue la primera estrategia y crea un entorno de ejecución global que se usa solo para pruebas. En el siguiente código, el método test_list_dbs() utiliza un cliente que se conecta manualmente a este entorno de ejecución para listar las bases de datos en la implementación:

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

Al implementar la segunda estrategia, el siguiente código crea una nueva instancia Client para cada ejecución de prueba con tokio::test, lo que garantiza que no haya interacciones no deseadas entre los tiempos de ejecución:

#[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 obtener más información sobre cómo conectarse a MongoDB, consulte Guía de conexión.

Para obtener más información sobre los tiempos de ejecución disponibles para el controlador Rust, consulte la guía sobre API asincrónicas y sincrónicas.

Volver

Comandos de base de datos

En esta página