Skip to content

Commit 8d11c48

Browse files
pierwillrustagir
andauthored
DOCSP-30561 Performance Considerations (#30)
Co-authored-by: pierwill <pierwill@users.noreply.github.com> Co-authored-by: Rea Rustagi <85902999+rustagir@users.noreply.github.com>
1 parent 3475092 commit 8d11c48

File tree

7 files changed

+155
-0
lines changed

7 files changed

+155
-0
lines changed

source/fundamentals.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Fundamentals
1919
/fundamentals/aggregation
2020
/fundamentals/tracing-logging
2121
/fundamentals/run-command
22+
/fundamentals/performance
2223

2324
..
2425
Connect to MongoDB Atlas from AWS Lambda <https://www.mongodb.com/docs/atlas/manage-connections-aws-lambda/>

source/fundamentals/connections/connection-guide.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ options. In the example, we set two connection options:
7979

8080
.. _rust-client:
8181

82+
.. _rust-connect-to-mongodb-client:
83+
8284
MongoDB Client
8385
--------------
8486

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
.. _rust-performance:
2+
3+
==========================
4+
Performance Considerations
5+
==========================
6+
7+
.. contents:: On this page
8+
:local:
9+
:backlinks: none
10+
:depth: 2
11+
:class: singlecol
12+
13+
Overview
14+
--------
15+
16+
In this guide, you can learn how to optimize performance of the {+driver-short+}.
17+
To connect to MongoDB, you must create a ``Client`` instance. Your ``Client``
18+
instance automatically handles most aspects of connection, such as discovering server topology, monitoring
19+
your connection, and maintaining an internal connection pool.
20+
This guide describes best practices to configure and use your ``Client`` instance.
21+
22+
.. _rust-performance-client-lifecycle:
23+
24+
Client Lifecycle
25+
----------------
26+
27+
We recommend that you reuse your client across sessions and operations.
28+
You can use the same ``Client`` instance to perform multiple tasks, as the ``Client`` type is safe for concurrent use by multiple threads.
29+
Creating a new ``Client`` instance for each request results in slower performance.
30+
31+
The following code creates a method that accepts a pointer to an existing ``Client`` instance,
32+
which allows you to execute many requests by using same client:
33+
34+
.. literalinclude:: /includes/fundamentals/code-snippets/performance.rs
35+
:language: rust
36+
:dedent:
37+
:start-after: start-perf-client-faster
38+
:end-before: end-perf-client-faster
39+
40+
.. _rust-performance-parallelism:
41+
42+
Parallelism
43+
-----------
44+
45+
If you can run parallel data operations, you can optimize performance by running asynchronous, concurrent tasks.
46+
The following code uses the ``task`` module from the ``tokio`` crate to create separate, concurrent
47+
tasks for multiple data operations:
48+
49+
.. literalinclude:: /includes/fundamentals/code-snippets/performance-parallel.rs
50+
:language: rust
51+
:dedent:
52+
53+
.. _rust-performance-runtime:
54+
55+
Runtime
56+
-------
57+
58+
A ``Client`` instance is bound to the instance of the ``tokio`` or ``async-std`` runtime in which you created it.
59+
If you use a ``Client`` instance to perform operations on a different runtime, you might experience unexpected behavior or failures.
60+
61+
If you are using the ``test`` helper macro from the ``tokio`` or ``async_std`` crate to test your application,
62+
you might accidentally run operations on a different runtime than you intended.
63+
This is because these helper macros create a new runtime for each test.
64+
You can use one of the following strategies to avoid this issue:
65+
66+
- Attach the runtime to the ``Client`` instance without using the ``test`` helper macros.
67+
- Create a new ``Client`` instance for every ``async`` test.
68+
69+
This example follows the first strategy and creates a global runtime used only for testing.
70+
In the following code, the ``test_list_dbs()`` method uses a client that manually connects to this runtime
71+
to list databases in the deployment.
72+
73+
.. literalinclude:: /includes/fundamentals/code-snippets/performance-bundle-runtime.rs
74+
:language: rust
75+
:dedent:
76+
77+
Implementing the second strategy,
78+
the following code creates a new ``Client`` instance for each test run with ``tokio::test``,
79+
ensuring that there is no unintended interaction between runtimes:
80+
81+
.. literalinclude:: /includes/fundamentals/code-snippets/performance-new-client.rs
82+
:language: rust
83+
:dedent:
84+
85+
Additional Information
86+
----------------------
87+
88+
For more information about the concepts in this guide, see the following pages:
89+
90+
- :ref:`Connection Guide <rust-connect-to-mongodb-client>`
91+
92+
API Documentation
93+
-----------------
94+
95+
- `Client() <{+api+}/struct.Client.html>`__
96+
97+
.. TODO link to Async page when done
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use tokio::runtime::Runtime;
2+
use once_cell::sync::Lazy;
3+
4+
static CLIENT_RUNTIME: Lazy<(Client, Runtime)> = Lazy::new(|| {
5+
let rt = Runtime::new().unwrap();
6+
let client = rt.block_on(async {
7+
Client::with_uri_str("<connection string>").await.unwrap()
8+
});
9+
(client, rt)
10+
});
11+
12+
#[test]
13+
fn test_list_dbs() -> Result<(), Box<dyn Error>> {
14+
let (client, rt) = &*CLIENT_RUNTIME;
15+
rt.block_on(async {
16+
client.list_database_names(None, None).await
17+
})?;
18+
Ok(())
19+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#[tokio::test]
2+
async fn test_list_dbs() -> Result<(), Box<dyn Error>> {
3+
let client = Client::with_uri_str("<connection string>").await?;
4+
client.list_database_names(None, None).await?;
5+
Ok(())
6+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
let client = Client::with_uri_str("<connection string>").await?;
2+
let some_data = doc! { "title": "1984", "author": "George Orwell" };
3+
4+
for i in 0..5 {
5+
let client_ref = client.clone();
6+
let somedata_ref = some_data.clone();
7+
8+
task::spawn(async move {
9+
let collection = client_ref
10+
.database("items")
11+
.collection::<Document>(&format!("coll{}", i));
12+
13+
collection.insert_one(somedata_ref, None).await
14+
});
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
2+
// start-perf-client-slow
3+
async fn handle_request() -> Result<(), Box<dyn Error>> {
4+
let client = Client::with_uri_str("<connection string>").await?;
5+
// Do something with the client
6+
Ok(())
7+
}
8+
// end-perf-client-slow
9+
10+
// start-perf-client-faster
11+
async fn handle_request(client: &Client) -> Result<(), Box<dyn Error>> {
12+
// Do something with the client
13+
Ok(())
14+
}
15+
// end-perf-client-faster

0 commit comments

Comments
 (0)