From 8232c5b3bb00a656031620d1b30929e604d42717 Mon Sep 17 00:00:00 2001 From: Dennis Zhuang Date: Sat, 19 Sep 2026 15:54:49 +0800 Subject: [PATCH] perf(prom): release the remote write v1 decode buffer before writing remote_write_v1 kept the decoded builder alive until the handler returned, so the decompressed request payload stayed resident across the downstream write or pipeline await. With 8 concurrent large requests that is one extra copy of every payload held for the whole write. Rows and pipeline values own their data, so the builder can be dropped as soon as the conversion is done. Sustained-write A/B, 8 runs per side, 50M samples each: jemalloc allocated median drops 7.8% (155.0-162.7 MiB -> 141.4-158.6 MiB); samples per CPU second is unchanged (-0.6%, fully overlapping ranges). Signed-off-by: Dennis Zhuang --- src/servers/src/http/prom_store.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/servers/src/http/prom_store.rs b/src/servers/src/http/prom_store.rs index cb7b6307c4..b678a2e47e 100644 --- a/src/servers/src/http/prom_store.rs +++ b/src/servers/src/http/prom_store.rs @@ -175,12 +175,17 @@ async fn remote_write_v1( processor.set_pipeline(pipeline_handler, query_ctx.clone(), pipeline_def); } - let mut req = decode_remote_write_request(is_zstd, body, prom_validation_mode, &mut processor)?; + let mut decoded = + decode_remote_write_request(is_zstd, body, prom_validation_mode, &mut processor)?; + // Rows and pipeline values own their data; the decode buffer need not span downstream awaits. let req = if processor.use_pipeline { + drop(decoded); processor.exec_pipeline().await? } else { - req.as_insert_requests() + let req = decoded.as_insert_requests(); + drop(decoded); + req }; let batches = into_prom_write_batches(req, query_ctx);