From 6aec4488ebe8f377b4ea9bb4948649831cba0dbc Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Fri, 16 Oct 2026 17:52:28 +0200 Subject: [PATCH] fix: give inline script runs their own job dir (#11822) * fix: give inline script runs their own job dir Co-Authored-By: Claude Opus 5.5 * fix: remove the inline job dir when the run is dropped Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Claude Opus 5.5 --- backend/windmill-api/src/jobs.rs | 2 -- backend/windmill-common/src/jobs.rs | 2 -- backend/windmill-worker/src/worker.rs | 33 +++++++++++++++++++++++++-- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs index cb8d47df4f..c567a593d9 100644 --- a/backend/windmill-api/src/jobs.rs +++ b/backend/windmill-api/src/jobs.rs @@ -9282,7 +9282,6 @@ async fn run_inline_preview_script( end_user_email, viewer_data_access, lang: preview.language, - job_dir: "".to_string(), worker_name: "".to_string(), worker_dir: "".to_string(), client: AuthedClient { @@ -9433,7 +9432,6 @@ async fn run_inline_script_inner( permissioned_as: username_to_permissioned_as(&authed.username), permissioned_as_email: authed.email.clone(), end_user_email, - job_dir: "".to_string(), worker_name: "".to_string(), worker_dir: "".to_string(), client: AuthedClient { diff --git a/backend/windmill-common/src/jobs.rs b/backend/windmill-common/src/jobs.rs index cd65603d4f..d7190ff45f 100644 --- a/backend/windmill-common/src/jobs.rs +++ b/backend/windmill-common/src/jobs.rs @@ -483,7 +483,6 @@ pub struct RunInlinePreviewScriptFnParams { pub worker_name: String, pub conn: crate::worker::Connection, pub client: AuthedClient, - pub job_dir: String, pub worker_dir: String, pub killpill_rx: tokio::sync::broadcast::Receiver<()>, } @@ -507,7 +506,6 @@ pub struct RunInlineScriptFnParams { pub worker_name: String, pub conn: crate::worker::Connection, pub client: AuthedClient, - pub job_dir: String, pub worker_dir: String, pub killpill_rx: tokio::sync::broadcast::Receiver<()>, pub user_db: Option<(crate::db::UserDB, crate::db::Authed)>, diff --git a/backend/windmill-worker/src/worker.rs b/backend/windmill-worker/src/worker.rs index a55f06a191..7ad7558c98 100644 --- a/backend/windmill-worker/src/worker.rs +++ b/backend/windmill-worker/src/worker.rs @@ -7245,6 +7245,33 @@ pub async fn parse_sig_of_lang( }) } +/// Inline runs happen outside any worker's job loop, so they get no job dir from it. Executors +/// write there (DuckDB's spill and result files), so each run needs its own: an empty one +/// resolves to `/`, shared by every concurrent run and unwritable to a non-root server. +/// Removed on drop: no job loop owns it, and the run's future is dropped mid-await when the +/// HTTP client disconnects. +struct InlineJobDir(String); + +impl Drop for InlineJobDir { + fn drop(&mut self) { + let dir = std::mem::take(&mut self.0); + match tokio::runtime::Handle::try_current() { + Ok(handle) => { + handle.spawn_blocking(move || std::fs::remove_dir_all(dir)); + } + Err(_) => { + let _ = std::fs::remove_dir_all(dir); + } + } + } +} + +async fn create_inline_job_dir() -> error::Result { + let job_dir = format!("{}/inline/{}", *WINDMILL_DIR, Uuid::new_v4()); + tokio::fs::create_dir_all(&job_dir).await?; + Ok(InlineJobDir(job_dir)) +} + pub fn init_worker_internal_server_inline_utils( killpill_rx: tokio::sync::broadcast::Receiver<()>, base_internal_url: String, @@ -7275,13 +7302,14 @@ pub fn init_worker_internal_server_inline_utils( let mut occupancy_metrics = OccupancyMetrics::new(Instant::now()); let mut has_stream: bool = false; let mut killpill_rx = params.killpill_rx; + let job_dir = create_inline_job_dir().await?; run_language_executor( &job, ¶ms.conn, ¶ms.client, None, - ¶ms.job_dir, + &job_dir.0, ¶ms.worker_dir, &mut mem_peak, &mut canceled_by, @@ -7336,13 +7364,14 @@ pub fn init_worker_internal_server_inline_utils( let mut occupancy_metrics = OccupancyMetrics::new(Instant::now()); let mut has_stream: bool = false; let mut killpill_rx = params.killpill_rx; + let job_dir = create_inline_job_dir().await?; run_language_executor( &job, ¶ms.conn, ¶ms.client, None, - ¶ms.job_dir, + &job_dir.0, ¶ms.worker_dir, &mut mem_peak, &mut canceled_by,