diff --git a/README.md b/README.md index 34b6260b8..4939b380f 100644 --- a/README.md +++ b/README.md @@ -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) @@ -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) diff --git a/src/lib.rs b/src/lib.rs index ff529a924..c05548603 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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> { +//! # +//! # 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)]