diff --git a/core/lib/src/config/config.rs b/core/lib/src/config/config.rs index 73c6a107..806cbf2a 100644 --- a/core/lib/src/config/config.rs +++ b/core/lib/src/config/config.rs @@ -73,6 +73,8 @@ pub struct Config { /// _**Note:** Rocket only reads this value from sources in the [default /// provider](Config::figment())._ pub workers: usize, + /// Limit on threads to start for blocking tasks. **(default: `512`)** + pub max_blocking: usize, /// How, if at all, to identify the server via the `Server` header. /// **(default: `"Rocket"`)** pub ident: Ident, @@ -170,6 +172,7 @@ impl Config { address: Ipv4Addr::new(127, 0, 0, 1).into(), port: 8000, workers: num_cpus::get(), + max_blocking: 512, ident: Ident::default(), limits: Limits::default(), temp_dir: std::env::temp_dir().into(), @@ -358,6 +361,7 @@ impl Config { launch_info_!("address: {}", bold(&self.address)); launch_info_!("port: {}", bold(&self.port)); launch_info_!("workers: {}", bold(self.workers)); + launch_info_!("max blocking threads: {}", bold(self.max_blocking)); launch_info_!("ident: {}", bold(&self.ident)); launch_info_!("limits: {}", bold(&self.limits)); launch_info_!("temp dir: {}", bold(&self.temp_dir.relative().display())); @@ -451,6 +455,9 @@ impl Config { /// The stringy parameter name for setting/extracting [`Config::workers`]. pub const WORKERS: &'static str = "workers"; + /// The stringy parameter name for setting/extracting [`Config::max_blocking`]. + pub const MAX_BLOCKING: &'static str = "max_blocking"; + /// The stringy parameter name for setting/extracting [`Config::keep_alive`]. pub const KEEP_ALIVE: &'static str = "keep_alive"; diff --git a/core/lib/src/lib.rs b/core/lib/src/lib.rs index de46729e..93744801 100644 --- a/core/lib/src/lib.rs +++ b/core/lib/src/lib.rs @@ -233,12 +233,13 @@ pub use async_trait::async_trait; /// WARNING: This is unstable! Do not use this method outside of Rocket! #[doc(hidden)] -pub fn async_run(fut: F, workers: usize, force_end: bool, name: &str) -> R +pub fn async_run(fut: F, workers: usize, sync: usize, force_end: bool, name: &str) -> R where F: std::future::Future { let runtime = tokio::runtime::Builder::new_multi_thread() .thread_name(name) .worker_threads(workers) + .max_blocking_threads(sync) .enable_all() .build() .expect("create tokio runtime"); @@ -254,7 +255,7 @@ pub fn async_run(fut: F, workers: usize, force_end: bool, name: &str) -> R /// WARNING: This is unstable! Do not use this method outside of Rocket! #[doc(hidden)] pub fn async_test(fut: impl std::future::Future) -> R { - async_run(fut, 1, true, "rocket-worker-test-thread") + async_run(fut, 1, 32, true, "rocket-worker-test-thread") } /// WARNING: This is unstable! Do not use this method outside of Rocket! @@ -263,8 +264,8 @@ pub fn async_main(fut: impl std::future::Future + Send) -> R { // FIXME: These config values won't reflect swaps of `Rocket` in attach // fairings with different config values, or values from non-Rocket configs. // See tokio-rs/tokio#3329 for a necessary solution in `tokio`. - let config = Config::from(Config::figment()); - async_run(fut, config.workers, config.shutdown.force, "rocket-worker-thread") + let c = Config::from(Config::figment()); + async_run(fut, c.workers, c.max_blocking, c.shutdown.force, "rocket-worker-thread") } /// Executes a `future` to completion on a new tokio-based Rocket async runtime. diff --git a/site/guide/9-configuration.md b/site/guide/9-configuration.md index 78c59cab..d863b358 100644 --- a/site/guide/9-configuration.md +++ b/site/guide/9-configuration.md @@ -17,24 +17,25 @@ is configured with. This means that no matter which configuration provider Rocket is asked to use, it must be able to read the following configuration values: -| key | kind | description | debug/release default | -|----------------|-------------------|-------------------------------------------------|-------------------------| -| `address` | `IpAddr` | IP address to serve on | `127.0.0.1` | -| `port` | `u16` | Port to serve on. | `8000` | -| `workers`* | `usize` | Number of threads to use for executing futures. | cpu core count | -| `ident` | `string`, `false` | If and how to identify via the `Server` header. | `"Rocket"` | -| `keep_alive` | `u32` | Keep-alive timeout seconds; disabled when `0`. | `5` | -| `log_level` | [`LogLevel`] | Max level to log. (off/normal/debug/critical) | `normal`/`critical` | -| `cli_colors` | `bool` | Whether to use colors and emoji when logging. | `true` | -| `secret_key` | [`SecretKey`] | Secret key for signing and encrypting values. | `None` | -| `tls` | [`TlsConfig`] | TLS configuration, if any. | `None` | -| `limits` | [`Limits`] | Streaming read size limits. | [`Limits::default()`] | -| `limits.$name` | `&str`/`uint` | Read limit for `$name`. | form = "32KiB" | -| `ctrlc` | `bool` | Whether `ctrl-c` initiates a server shutdown. | `true` | -| `shutdown`* | [`Shutdown`] | Graceful shutdown configuration. | [`Shutdown::default()`] | +| key | kind | description | debug/release default | +|-----------------|-------------------|-------------------------------------------------|-------------------------| +| `address` | `IpAddr` | IP address to serve on | `127.0.0.1` | +| `port` | `u16` | Port to serve on. | `8000` | +| `workers`* | `usize` | Number of threads to use for executing futures. | cpu core count | +| `max_blocking`* | `usize` | Limit on threads to start for blocking tasks. | `512` | +| `ident` | `string`, `false` | If and how to identify via the `Server` header. | `"Rocket"` | +| `keep_alive` | `u32` | Keep-alive timeout seconds; disabled when `0`. | `5` | +| `log_level` | [`LogLevel`] | Max level to log. (off/normal/debug/critical) | `normal`/`critical` | +| `cli_colors` | `bool` | Whether to use colors and emoji when logging. | `true` | +| `secret_key` | [`SecretKey`] | Secret key for signing and encrypting values. | `None` | +| `tls` | [`TlsConfig`] | TLS configuration, if any. | `None` | +| `limits` | [`Limits`] | Streaming read size limits. | [`Limits::default()`] | +| `limits.$name` | `&str`/`uint` | Read limit for `$name`. | form = "32KiB" | +| `ctrlc` | `bool` | Whether `ctrl-c` initiates a server shutdown. | `true` | +| `shutdown`* | [`Shutdown`] | Graceful shutdown configuration. | [`Shutdown::default()`] | -* Note: the `workers` and `shutdown.force` configuration parameters are -only read from the [default provider](#default-provider). +* Note: the `workers`, `max_blocking`, and `shutdown.force` configuration +parameters are only read from the [default provider](#default-provider). ### Profiles @@ -138,6 +139,7 @@ sensible. address = "127.0.0.1" port = 8000 workers = 16 +max_blocking = 512 keep_alive = 5 ident = "Rocket" log_level = "normal" @@ -358,6 +360,21 @@ than those provided by [`Config::figment()`]. In other words, only the values set by the `ROCKET_WORKERS` environment variable or in the `workers` property of `Rocket.toml` will be considered - all other `workers` values are ignored. +The `max_blocking` parameter sets an upper limit on the number of threads the +underlying `async` runtime will spawn to execute potentially blocking, +synchronous tasks via [`spawn_blocking`] or equivalent. Similar to the `workers` +parameter, `max_blocking` cannot be reconfigured or be configured from sources +other than those provided by [`Config::figment()`]. Unlike `workers`, threads +corresponding to `max_blocking` are not always active and will exit if idling. +In general, the default value of `512` should not be changed unless physical or +virtual resources are scarce. Rocket only executes work on blocking threads when +required such as when performing file system I/O via [`TempFile`] or wrapping +synchronous work via [`rocket_sync_db_pools`]. + +[`spawn_blocking`]: @tokio/task/fn.spawn_blocking.html +[`TempFile`]: @api/rocket/fs/enum.TempFile.html +[`rocket_sync_db_pools`]: @api/rocket_sync_db_pools/index.html + ## Extracting Values Your application can extract any configuration that implements [`Deserialize`]