Skip to content

RUST-1001 Add warning about not polling futures to completion #439

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ This repository contains the officially supported MongoDB Rust driver, a client
- [Platforms](#platforms)
- [Atlas note](#atlas-note)
- [Windows DNS note](#windows-dns-note)
- [Warning about timeouts / cancellation](#warning-about-timeouts--cancellation)
- [Bug Reporting / Feature Requests](#bug-reporting--feature-requests)
- [Contributing](#contributing)
- [Running the tests](#running-the-tests)
Expand Down Expand Up @@ -244,6 +245,30 @@ let options = ClientOptions::parse_with_resolver_config(
let client = Client::with_options(options)?;
```

## Warning about timeouts / cancellation

In async Rust, it is common to implement cancellation and timeouts by dropping a future after a
certain period of time instead of polling it to completion. This is how
[`tokio::time::timeout`](https://docs.rs/tokio/1.10.1/tokio/time/fn.timeout.html) works, for
example. However, doing this with futures returned by the driver can leave the driver's internals in
an inconsistent state, which may lead to unpredictable or incorrect behavior (see RUST-937 for more
details). As such, it is **_highly_** recommended to poll all futures returned from the driver to
completion. In order to still use timeout mechanisms like `tokio::time::timeout` with the driver,
one option is to spawn tasks and time out on their
[`JoinHandle`](https://docs.rs/tokio/1.10.1/tokio/task/struct.JoinHandle.html) futures instead of on
the driver's futures directly. This will ensure the driver's futures will always be completely polled
while also allowing the application to continue in the event of a timeout.

e.g.
``` rust
let collection = client.database("ok").collection("ok");
let handle = tokio::task::spawn(async move {
collection.insert_one(doc! { "x": 1 }, None).await
});

tokio::time::timeout(Duration::from_secs(5), handle).await???;
```

## Bug Reporting / Feature Requests
To file a bug report or submit a feature request, please open a ticket on our [Jira project](https://jira.mongodb.org/browse/RUST):
- Create an account and login at [jira.mongodb.org](https://jira.mongodb.org)
Expand Down
36 changes: 36 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,42 @@
//! # Ok(())
//! # }
//! ```
//!
//! ## Warning about timeouts / cancellation
//!
//! In async Rust, it is common to implement cancellation and timeouts by dropping a future after a
//! certain period of time instead of polling it to completion. This is how
//! [`tokio::time::timeout`](https://docs.rs/tokio/1.10.1/tokio/time/fn.timeout.html) works, for
//! example. However, doing this with futures returned by the driver can leave the driver's internals in
//! an inconsistent state, which may lead to unpredictable or incorrect behavior (see RUST-937 for more
//! details). As such, it is **_highly_** recommended to poll all futures returned from the driver to
//! completion. In order to still use timeout mechanisms like `tokio::time::timeout` with the driver,
//! one option is to spawn tasks and time out on their
//! [`JoinHandle`](https://docs.rs/tokio/1.10.1/tokio/task/struct.JoinHandle.html) futures instead of on
//! the driver's futures directly. This will ensure the driver's futures will always be completely polled
//! while also allowing the application to continue in the event of a timeout.
//!
//! e.g.
//! ``` rust
//! # use std::time::Duration;
//! # use mongodb::{
//! # Client,
//! # bson::doc,
//! # };
//! #
//! # #[cfg(all(not(feature = "sync"), feature = "tokio-runtime"))]
//! # async fn foo() -> std::result::Result<(), Box<dyn std::error::Error>> {
//! #
//! # let client = Client::with_uri_str("mongodb://example.com").await?;
//! let collection = client.database("foo").collection("bar");
//! let handle = tokio::task::spawn(async move {
//! collection.insert_one(doc! { "x": 1 }, None).await
//! });
//!
//! tokio::time::timeout(Duration::from_secs(5), handle).await???;
//! # Ok(())
//! # }
//! ```

#![warn(missing_docs)]
#![warn(missing_crate_level_docs)]
Expand Down