Join us at MongoDB.local London on 7 May to unlock new possibilities for your data. Use WEB50 to save 50%.
Register now >
Docs Menu
Docs Home
/ /

Performance Considerations

In this guide, you can learn how to optimize the performance of the Rust driver. To connect to MongoDB, you must create a Client instance. Your Client instance automatically handles most aspects of connection, such as discovering server topology and monitoring your connection. This guide describes best practices for configuring and using your Client instance.

This guide includes the following sections:

  • Client Lifecycle describes best practices for creating and managing a Client instance.

  • Parallelism provides sample code for running parallel, asynchronous tasks.

  • Runtime describes how to manage runtimes by using functionalities of the tokio and async_std crates.

  • Additional Information provides links to resources and API documentation for types and methods mentioned in this guide.

Tip

You can optimize performance by using connection pools. To learn more, see the Manage Connections with Connection Pools guide.

We recommend that you reuse your client across sessions and operations. You can use the same Client instance to perform multiple tasks, as the Client type is safe for concurrent use by multiple threads. Creating a new Client instance for each request results in slower performance.

The following code creates a method that accepts a pointer to an existing Client instance, which allows you to perform many requests by using the same client:

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

You can optimize performance by running asynchronous, concurrent tasks to execute parallel data operations. The following example uses the spawn() method from the tokio::task module to create separate, concurrent tasks to perform insert operations:

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

A Client instance is bound to the instance of the tokio or async-std runtime in which you created it. If you use a Client instance to perform operations on a different runtime, you might experience unexpected behavior or failures.

If you use the test helper macro from the tokio or async_std crate to test your application, you might unintentionally run operations on a different runtime than the Client instance expects. This occurs because these helper macros create a new runtime for each test. You can use one of the following strategies to avoid this issue:

  • Attach the runtime to the Client instance without using the test helper macros.

  • Create a new Client instance for every async test.

The following example attaches a global runtime to a Client instance for testing. The test_list_dbs() method uses a client that connects to this runtime to list databases in the deployment:

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

The following example creates a new Client instance for each test run with tokio::test, ensuring that there are no unintended interactions between runtimes:

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

To learn more about connecting to MongoDB, see the Create a MongoDB Client guide.

To learn more about the available runtimes for the Rust driver, see the Asynchronous and Synchronous APIs guide.

To learn how to optimize performance by using connection pools, see the Manage Connections with Connection Pools guide.

Back

Connection Pools

On this page