From 93fcd6b6b5ea7ae598a7445924971f65974ed05b Mon Sep 17 00:00:00 2001 From: houseme Date: Tue, 4 Aug 2026 21:10:29 +0800 Subject: [PATCH] fix(observability): fallback mimalloc requested memory stats (#5695) Co-authored-by: heihutu --- rustfs/src/memory_observability.rs | 46 ++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/rustfs/src/memory_observability.rs b/rustfs/src/memory_observability.rs index 8a5e2aef7..a5e930985 100644 --- a/rustfs/src/memory_observability.rs +++ b/rustfs/src/memory_observability.rs @@ -259,16 +259,32 @@ fn mimalloc_stat_current(value: &Value, metric: &str) -> Option { mimalloc_stat_field(value, metric, "current") } +#[cfg(any(test, not(target_os = "windows")))] +fn mimalloc_stat_sum(value: &Value, metrics: &[&str], field: &str) -> Option { + metrics + .iter() + .map(|metric| mimalloc_stat_field(value, metric, field)) + .try_fold(0_u64, |sum, value| value.map(|value| sum.saturating_add(value))) + .filter(|value| *value > 0) +} + #[cfg(any(test, not(target_os = "windows")))] fn parse_mimalloc_stats_json(stats_json: &str) -> Option { let value = serde_json::from_str::(stats_json).ok()?; + let malloc_metrics = ["malloc_normal", "malloc_huge"]; let observation = AllocatorMemoryObservation { reserved_bytes: mimalloc_stat_current(&value, "reserved"), committed_bytes: mimalloc_stat_current(&value, "committed"), page_committed_bytes: mimalloc_stat_current(&value, "page_committed"), - malloc_requested_bytes: mimalloc_stat_current(&value, "malloc_requested"), - malloc_requested_peak_bytes: mimalloc_stat_field(&value, "malloc_requested", "peak"), - malloc_requested_total_bytes: mimalloc_stat_field(&value, "malloc_requested", "total"), + malloc_requested_bytes: mimalloc_stat_current(&value, "malloc_requested") + .filter(|value| *value > 0) + .or_else(|| mimalloc_stat_sum(&value, &malloc_metrics, "current")), + malloc_requested_peak_bytes: mimalloc_stat_field(&value, "malloc_requested", "peak") + .filter(|value| *value > 0) + .or_else(|| mimalloc_stat_sum(&value, &malloc_metrics, "peak")), + malloc_requested_total_bytes: mimalloc_stat_field(&value, "malloc_requested", "total") + .filter(|value| *value > 0) + .or_else(|| mimalloc_stat_sum(&value, &malloc_metrics, "total")), heap_count: mimalloc_stat_current(&value, "heaps").or_else(|| mimalloc_stat_current(&value, "heap_count")), }; @@ -504,6 +520,30 @@ mod tests { assert_eq!(parsed.heap_count, Some(8)); } + #[test] + fn parse_mimalloc_stats_json_falls_back_to_allocated_bytes_when_requested_is_zero() { + let parsed = parse_mimalloc_stats_json( + r#"{ + "stat_version": 1, + "mimalloc_version": 300, + "reserved": { "total": 1048576, "peak": 1048576, "current": 1048576 }, + "committed": { "total": 524288, "peak": 524288, "current": 524288 }, + "malloc_normal": { "total": 7340032, "peak": 262144, "current": 196608 }, + "malloc_huge": { "total": 3145728, "peak": 131072, "current": 65536 }, + "malloc_requested": { "total": 0, "peak": 0, "current": 0 }, + "heaps": { "total": 1, "peak": 1, "current": 1 } + }"#, + ) + .expect("mimalloc v3 stats should parse"); + + assert_eq!(parsed.reserved_bytes, Some(1_048_576)); + assert_eq!(parsed.committed_bytes, Some(524_288)); + assert_eq!(parsed.malloc_requested_bytes, Some(262_144)); + assert_eq!(parsed.malloc_requested_peak_bytes, Some(393_216)); + assert_eq!(parsed.malloc_requested_total_bytes, Some(10_485_760)); + assert_eq!(parsed.heap_count, Some(1)); + } + #[test] fn parse_mimalloc_stats_json_rejects_unrecognized_payload() { assert_eq!(parse_mimalloc_stats_json(r#"{ "allocator": "unknown" }"#), None);