From 6c1c9a49cfa7eb1c6f62751bf5e8c59eb7827bfb Mon Sep 17 00:00:00 2001 From: Nathan Stocks Date: Mon, 27 Jul 2026 20:09:47 -0600 Subject: [PATCH] Keep a data-frame transform's collect off the request worker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `tabular::transform` ends in an eager, frame-sized polars collect that ran on whatever runtime called it. Fourteen production call sites await it, several of them inside HTTP handlers, so a request worker sat through the whole collect and picked up nothing else while it ran. The collect now runs inside `spawn_blocking`, matching the closure `add_row` already uses. The signature is unchanged, so no call site moves. `transform_lazy`, a few lines below, documents the rule this was missing: eager polars work belongs inside `spawn_blocking`. Its own filter and take branches honor it, as do `add_col_lazy` and `add_row`; this tail was the one link without an offload. Only the tail moves — `transform_lazy` stays async because its vstack and sql branches await, and flattening those is a larger change. No benchmark accompanies this. The frame is already materialized, so the cost being removed is worker occupancy, not a crash risk. ENG-1432 --- crates/liboxen/src/core/df/tabular.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/liboxen/src/core/df/tabular.rs b/crates/liboxen/src/core/df/tabular.rs index 04e76df2d..d6346241d 100644 --- a/crates/liboxen/src/core/df/tabular.rs +++ b/crates/liboxen/src/core/df/tabular.rs @@ -435,7 +435,10 @@ fn unique_count_df(df: LazyFrame, columns: Vec) -> Result Result { let df = transform_lazy(df.lazy(), opts.clone()).await?; - Ok(transform_slice_lazy(df, &opts)?.collect()?) + task::spawn_blocking(move || -> Result { + Ok(transform_slice_lazy(df, &opts)?.collect()?) + }) + .await? } pub async fn transform_lazy(mut df: LazyFrame, opts: DFOpts) -> Result {