Repository navigation
fix(diskio): cap unpacking budget by available Linux memory #5189
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
base: main
Are you sure you want to change the base?
Changes from all commits
8a93a37
bbb2b55
6021c2c
cbecb2b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -489,20 +489,34 @@ fn effective_thread_count(ram_budget: usize, thread_count: IoThreadCount) -> usi | |
|
|
||
| pub(super) fn unpack_ram(io_chunk_size: usize, budget: Option<usize>) -> usize { | ||
| const RAM_ALLOWANCE_FOR_RUSTUP_AND_BUFFERS: usize = 200 * 1024 * 1024; | ||
| let minimum_ram = io_chunk_size * 2; | ||
| let minimum_ram = io_chunk_size.saturating_mul(2); | ||
|
|
||
| let default_max_unpack_ram = match effective_limits::memory_limit() { | ||
| Ok(effective) | ||
| if effective as usize > minimum_ram + RAM_ALLOWANCE_FOR_RUSTUP_AND_BUFFERS => | ||
| { | ||
| effective as usize - RAM_ALLOWANCE_FOR_RUSTUP_AND_BUFFERS | ||
| let memory_limit = effective_limits::memory_limit(); | ||
| let available_memory = cfg_select! { | ||
| target_os = "linux" => linux_available_memory(Path::new("/proc/meminfo")), | ||
| _ => None, | ||
| }; | ||
|
|
||
| // Physical memory and process limits don't account for other applications. | ||
| // Honor both sources when present, or use whichever is available. | ||
| let effective = match (memory_limit, available_memory) { | ||
| (Ok(effective), Some(available)) => Ord::min(effective, available), | ||
| (Ok(effective), None) => effective, | ||
| (Err(e), Some(available)) => { | ||
| error!("{:#}", anyhow!(e).context("can't determine memory limit")); | ||
| available | ||
| } | ||
| Ok(_) => minimum_ram, | ||
| Err(e) => { | ||
| (Err(e), None) => { | ||
| error!("{:#}", anyhow!(e).context("can't determine memory limit")); | ||
| minimum_ram | ||
| 0 | ||
| } | ||
| }; | ||
| let default_max_unpack_ram = Ord::max( | ||
| usize::try_from(effective) | ||
| .unwrap_or(usize::MAX) | ||
| .saturating_sub(RAM_ALLOWANCE_FOR_RUSTUP_AND_BUFFERS), | ||
| minimum_ram, | ||
| ); | ||
|
|
||
| let unpack_ram = match budget { | ||
| Some(budget) => { | ||
|
|
@@ -537,6 +551,22 @@ pub(super) fn unpack_ram(io_chunk_size: usize, budget: Option<usize>) -> usize { | |
| } | ||
| } | ||
|
|
||
| #[cfg(target_os = "linux")] | ||
| fn linux_available_memory(path: &Path) -> Option<u64> { | ||
| let meminfo = std::fs::read_to_string(path).ok()?; | ||
| let available = meminfo | ||
| .lines() | ||
| .find_map(|line| line.strip_prefix("MemAvailable:"))?; | ||
| let mut fields = available.split_whitespace(); | ||
| let kib = fields.next()?.parse::<u64>().ok()?; | ||
| if fields.next()? != "kB" || fields.next().is_some() { | ||
| return None; | ||
| } | ||
| // MemAvailable includes reclaimable caches, unlike MemFree. Older kernels | ||
| // may omit it; in that case retain the effective-limits fallback. | ||
| kib.checked_mul(1024) | ||
| } | ||
|
Comment on lines
+554
to
+568
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if that is the best way to do this because this info has already been fetched in the underlying Before all that I'd like to know if @rbtcollins has any relevant context to share here regarding the original rationale behind the decision of using |
||
|
|
||
| static RAM_NOTICE_SHOWN: OnceLock<()> = OnceLock::new(); | ||
|
|
||
| /// The Threaded executor uses substantially more memory than its ram_budget | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.