From c7481402a0654f919faeb633d8c07ba17607d2f5 Mon Sep 17 00:00:00 2001 From: Christian Schwarz Date: Thu, 29 Aug 2024 14:02:27 +0200 Subject: [PATCH] pageserver: default to 4MiB stack size and add env var to control it (#8862) # Motivation In https://github.com/neondatabase/neon/pull/8832 I get tokio runtime worker stack overflow errors in debug builds. In a similar vein, I had tokio runtimer worker stack overflow when trying to eliminate `async_trait` (https://github.com/neondatabase/neon/pull/8296). The 2MiB default is kind of arbitrary - so this PR bumps it to 4MiB. It also adds an env var to control it. # Risk Assessment With our 4 runtimes, the worst case stack memory usage is `4 (runtimes) * ($num_cpus (executor threads) + 512 (blocking pool threads)) * 4MiB`. On i3en.3xlarge, that's `8384 MiB`. On im4gn.2xlarge, that's `8320 MiB`. Before this change, it was half that. Looking at production metrics, we _do_ have the headroom to accomodate this worst case case. # Alternatives The problems only occur with debug builds, so technically we could only raise the stack size for debug builds. However, it would be another configuration where `debug != release`. # Future Work If we ever enable single runtime mode in prod (=> https://github.com/neondatabase/neon/issues/7312 ) then the worst case will drop to 25% of its current value. Eliminating the use of `tokio::spawn_blocking` / `tokio::fs` in favor of `tokio-epoll-uring` (=> https://github.com/neondatabase/neon/issues/7370 ) would reduce the worst case to `4 (runtimes) * $num_cpus (executor threads) * 4 MiB`. --- pageserver/src/task_mgr.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pageserver/src/task_mgr.rs b/pageserver/src/task_mgr.rs index ed9e001fd2..6a4e90dd55 100644 --- a/pageserver/src/task_mgr.rs +++ b/pageserver/src/task_mgr.rs @@ -146,6 +146,12 @@ impl FromStr for TokioRuntimeMode { } } +static TOKIO_THREAD_STACK_SIZE: Lazy = Lazy::new(|| { + env::var("NEON_PAGESERVER_TOKIO_THREAD_STACK_SIZE") + // the default 2MiB are insufficent, especially in debug mode + .unwrap_or_else(|| NonZeroUsize::new(4 * 1024 * 1024).unwrap()) +}); + static ONE_RUNTIME: Lazy> = Lazy::new(|| { let thread_name = "pageserver-tokio"; let Some(mode) = env::var("NEON_PAGESERVER_USE_ONE_RUNTIME") else { @@ -164,6 +170,7 @@ static ONE_RUNTIME: Lazy> = Lazy::new(|| { tokio::runtime::Builder::new_current_thread() .thread_name(thread_name) .enable_all() + .thread_stack_size(TOKIO_THREAD_STACK_SIZE.get()) .build() .expect("failed to create one single runtime") } @@ -173,6 +180,7 @@ static ONE_RUNTIME: Lazy> = Lazy::new(|| { .thread_name(thread_name) .enable_all() .worker_threads(num_workers.get()) + .thread_stack_size(TOKIO_THREAD_STACK_SIZE.get()) .build() .expect("failed to create one multi-threaded runtime") } @@ -199,6 +207,7 @@ macro_rules! pageserver_runtime { .thread_name($name) .worker_threads(TOKIO_WORKER_THREADS.get()) .enable_all() + .thread_stack_size(TOKIO_THREAD_STACK_SIZE.get()) .build() .expect(std::concat!("Failed to create runtime ", $name)) });