diff --git a/backend/.sqlx/query-0e621bba5913482b8235d7d8442b8f0e9012c265e150afd4aa41972bf7334ba2.json b/backend/.sqlx/query-0e621bba5913482b8235d7d8442b8f0e9012c265e150afd4aa41972bf7334ba2.json new file mode 100644 index 0000000000..5dd30c1492 --- /dev/null +++ b/backend/.sqlx/query-0e621bba5913482b8235d7d8442b8f0e9012c265e150afd4aa41972bf7334ba2.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO deployment_metadata (workspace_id, path, flow_version, job_id)\n VALUES ($1, $2, $3, $4)\n ON CONFLICT (workspace_id, path, flow_version) WHERE flow_version IS NOT NULL\n DO UPDATE SET job_id = EXCLUDED.job_id", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Int8", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "0e621bba5913482b8235d7d8442b8f0e9012c265e150afd4aa41972bf7334ba2" +} diff --git a/backend/.sqlx/query-1de29cdd474cbd61e15b63d111e1c42aefee683e14cc738a809ecca17370e6ee.json b/backend/.sqlx/query-1de29cdd474cbd61e15b63d111e1c42aefee683e14cc738a809ecca17370e6ee.json new file mode 100644 index 0000000000..56c538d440 --- /dev/null +++ b/backend/.sqlx/query-1de29cdd474cbd61e15b63d111e1c42aefee683e14cc738a809ecca17370e6ee.json @@ -0,0 +1,29 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT f.lock_error_logs, dm.job_id\n FROM flow f\n LEFT JOIN deployment_metadata dm ON f.versions[array_upper(f.versions, 1)] = dm.flow_version\n AND f.workspace_id = dm.workspace_id AND f.path = dm.path\n WHERE f.path = $1 AND f.workspace_id = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "lock_error_logs", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "job_id", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + true, + true + ] + }, + "hash": "1de29cdd474cbd61e15b63d111e1c42aefee683e14cc738a809ecca17370e6ee" +} diff --git a/backend/.sqlx/query-513ed713afdbafb587026d1536c47a9bbaa6967e36777454746b8817d68219a5.json b/backend/.sqlx/query-513ed713afdbafb587026d1536c47a9bbaa6967e36777454746b8817d68219a5.json new file mode 100644 index 0000000000..31187af03a --- /dev/null +++ b/backend/.sqlx/query-513ed713afdbafb587026d1536c47a9bbaa6967e36777454746b8817d68219a5.json @@ -0,0 +1,35 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT s.lock, s.lock_error_logs, dm.job_id\n FROM script s\n LEFT JOIN deployment_metadata dm ON s.hash = dm.script_hash AND s.workspace_id = dm.workspace_id\n WHERE s.hash = $1 AND s.workspace_id = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "lock", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "lock_error_logs", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "job_id", + "type_info": "Uuid" + } + ], + "parameters": { + "Left": [ + "Int8", + "Text" + ] + }, + "nullable": [ + true, + true, + true + ] + }, + "hash": "513ed713afdbafb587026d1536c47a9bbaa6967e36777454746b8817d68219a5" +} diff --git a/backend/.sqlx/query-7abde47077c38ccf005ce7180a383f97076b2cbe2f617f7af39550a0db157b2b.json b/backend/.sqlx/query-7abde47077c38ccf005ce7180a383f97076b2cbe2f617f7af39550a0db157b2b.json new file mode 100644 index 0000000000..571a374b96 --- /dev/null +++ b/backend/.sqlx/query-7abde47077c38ccf005ce7180a383f97076b2cbe2f617f7af39550a0db157b2b.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO deployment_metadata (workspace_id, path, script_hash, job_id)\n VALUES ($1, $2, $3, $4)\n ON CONFLICT (workspace_id, script_hash) WHERE script_hash IS NOT NULL\n DO UPDATE SET job_id = EXCLUDED.job_id", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Varchar", + "Varchar", + "Int8", + "Uuid" + ] + }, + "nullable": [] + }, + "hash": "7abde47077c38ccf005ce7180a383f97076b2cbe2f617f7af39550a0db157b2b" +} diff --git a/backend/.sqlx/query-97bf27f210572499b42ce04f19f116cc87ed06c49dcca04360250ddfd89d7ab3.json b/backend/.sqlx/query-97bf27f210572499b42ce04f19f116cc87ed06c49dcca04360250ddfd89d7ab3.json deleted file mode 100644 index 739184045f..0000000000 --- a/backend/.sqlx/query-97bf27f210572499b42ce04f19f116cc87ed06c49dcca04360250ddfd89d7ab3.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT lock_error_logs FROM flow WHERE path = $1 AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "lock_error_logs", - "type_info": "Text" - } - ], - "parameters": { - "Left": [ - "Text", - "Text" - ] - }, - "nullable": [ - true - ] - }, - "hash": "97bf27f210572499b42ce04f19f116cc87ed06c49dcca04360250ddfd89d7ab3" -} diff --git a/backend/.sqlx/query-e3ac59fcf6193007a21c808a275d3d84fd76e44f9240f4292c5c1208096b8563.json b/backend/.sqlx/query-e3ac59fcf6193007a21c808a275d3d84fd76e44f9240f4292c5c1208096b8563.json deleted file mode 100644 index e8cba862fe..0000000000 --- a/backend/.sqlx/query-e3ac59fcf6193007a21c808a275d3d84fd76e44f9240f4292c5c1208096b8563.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT lock, lock_error_logs FROM script WHERE hash = $1 AND workspace_id = $2", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "lock", - "type_info": "Text" - }, - { - "ordinal": 1, - "name": "lock_error_logs", - "type_info": "Text" - } - ], - "parameters": { - "Left": [ - "Int8", - "Text" - ] - }, - "nullable": [ - true, - true - ] - }, - "hash": "e3ac59fcf6193007a21c808a275d3d84fd76e44f9240f4292c5c1208096b8563" -} diff --git a/backend/migrations/20251204080000_add_job_id_to_deployment_metadata.down.sql b/backend/migrations/20251204080000_add_job_id_to_deployment_metadata.down.sql new file mode 100644 index 0000000000..0b04137182 --- /dev/null +++ b/backend/migrations/20251204080000_add_job_id_to_deployment_metadata.down.sql @@ -0,0 +1,2 @@ +-- Remove job_id column from deployment_metadata table +ALTER TABLE deployment_metadata DROP COLUMN IF EXISTS job_id; diff --git a/backend/migrations/20251204080000_add_job_id_to_deployment_metadata.up.sql b/backend/migrations/20251204080000_add_job_id_to_deployment_metadata.up.sql new file mode 100644 index 0000000000..8c54c702cd --- /dev/null +++ b/backend/migrations/20251204080000_add_job_id_to_deployment_metadata.up.sql @@ -0,0 +1,2 @@ +-- Add job_id column to deployment_metadata table to track the current deployment job +ALTER TABLE deployment_metadata ADD COLUMN IF NOT EXISTS job_id UUID; diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 639b270bd0..131e8da69f 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -5950,6 +5950,9 @@ paths: type: string lock_error_logs: type: string + job_id: + type: string + format: uuid /w/{workspace}/jobs/list_selected_job_groups: # We use post because sending a huge array as a query param can produce @@ -6836,6 +6839,9 @@ paths: properties: lock_error_logs: type: string + job_id: + type: string + format: uuid /w/{workspace}/flows/get_triggers_count/{path}: get: diff --git a/backend/windmill-api/src/flows.rs b/backend/windmill-api/src/flows.rs index 88d4f08fe9..7495dbd75b 100644 --- a/backend/windmill-api/src/flows.rs +++ b/backend/windmill-api/src/flows.rs @@ -577,6 +577,20 @@ async fn create_flow( .execute(&mut *new_tx) .await?; + // Store the job_id in deployment_metadata for this flow deployment + sqlx::query!( + "INSERT INTO deployment_metadata (workspace_id, path, flow_version, job_id) + VALUES ($1, $2, $3, $4) + ON CONFLICT (workspace_id, path, flow_version) WHERE flow_version IS NOT NULL + DO UPDATE SET job_id = EXCLUDED.job_id", + w_id, + nf.path, + version, + dependency_job_uuid + ) + .execute(&mut *new_tx) + .await?; + new_tx.commit().await?; webhook.send_message( w_id.clone(), @@ -1115,6 +1129,25 @@ async fn update_flow( )) })?; + // Store the job_id in deployment_metadata for this flow deployment + sqlx::query!( + "INSERT INTO deployment_metadata (workspace_id, path, flow_version, job_id) + VALUES ($1, $2, $3, $4) + ON CONFLICT (workspace_id, path, flow_version) WHERE flow_version IS NOT NULL + DO UPDATE SET job_id = EXCLUDED.job_id", + w_id, + nf.path, + version, + dependency_job_uuid + ) + .execute(&mut *new_tx) + .await + .map_err(|e| { + error::Error::internal_err(format!( + "Error updating deployment_metadata with job_id: {e:#}" + )) + })?; + if let Some(old_dep_job) = old_dep_job { sqlx::query!( "UPDATE v2_job_queue SET @@ -1154,9 +1187,10 @@ async fn list_tokens( list_tokens_internal(&db, &w_id, &path, true).await } -#[derive(FromRow, Serialize)] +#[derive(Serialize)] struct DeploymentStatus { lock_error_logs: Option, + job_id: Option, } async fn get_deployment_status( Extension(db): Extension, @@ -1164,9 +1198,12 @@ async fn get_deployment_status( ) -> JsonResult { let path = path.to_path(); let mut tx = db.begin().await?; - let status_o: Option = sqlx::query_as!( - DeploymentStatus, - "SELECT lock_error_logs FROM flow WHERE path = $1 AND workspace_id = $2", + let status_o = sqlx::query!( + "SELECT f.lock_error_logs, dm.job_id + FROM flow f + LEFT JOIN deployment_metadata dm ON f.versions[array_upper(f.versions, 1)] = dm.flow_version + AND f.workspace_id = dm.workspace_id AND f.path = dm.path + WHERE f.path = $1 AND f.workspace_id = $2", path, w_id, ) @@ -1175,8 +1212,13 @@ async fn get_deployment_status( let status = not_found_if_none(status_o, "DeploymentStatus", path)?; + let deployment_status = DeploymentStatus { + lock_error_logs: status.lock_error_logs, + job_id: status.job_id, + }; + tx.commit().await?; - Ok(Json(status)) + Ok(Json(deployment_status)) } async fn get_flow_by_path( diff --git a/backend/windmill-api/src/scripts.rs b/backend/windmill-api/src/scripts.rs index 7cbcf8cc03..f71d6e00ed 100644 --- a/backend/windmill-api/src/scripts.rs +++ b/backend/windmill-api/src/scripts.rs @@ -998,14 +998,14 @@ async fn create_script_internal<'c>( } let tx = PushIsolationLevel::Transaction(tx); - let (_, new_tx) = windmill_queue::push( + let (job_id, mut new_tx) = windmill_queue::push( &db, tx, &w_id, JobPayload::Dependencies { hash, language: ns.language, - path: ns.path, + path: ns.path.clone(), dedicated_worker: ns.dedicated_worker, }, windmill_queue::PushArgs::from(&args), @@ -1034,6 +1034,21 @@ async fn create_script_internal<'c>( None, ) .await?; + + // Store the job_id in deployment_metadata for this script deployment + sqlx::query!( + "INSERT INTO deployment_metadata (workspace_id, path, script_hash, job_id) + VALUES ($1, $2, $3, $4) + ON CONFLICT (workspace_id, script_hash) WHERE script_hash IS NOT NULL + DO UPDATE SET job_id = EXCLUDED.job_id", + w_id, + ns.path, + hash.0, + job_id + ) + .execute(&mut *new_tx) + .await?; + Ok((hash, new_tx, None)) } else { if codebase.is_none() { @@ -1750,19 +1765,22 @@ async fn raw_script_by_hash( Ok(r.script.content) } -#[derive(FromRow, Serialize)] +#[derive(Serialize)] struct DeploymentStatus { lock: Option, lock_error_logs: Option, + job_id: Option, } async fn get_deployment_status( Extension(db): Extension, Path((w_id, hash)): Path<(String, ScriptHash)>, ) -> JsonResult { let mut tx = db.begin().await?; - let status_o: Option = sqlx::query_as!( - DeploymentStatus, - "SELECT lock, lock_error_logs FROM script WHERE hash = $1 AND workspace_id = $2", + let status_o = sqlx::query!( + "SELECT s.lock, s.lock_error_logs, dm.job_id + FROM script s + LEFT JOIN deployment_metadata dm ON s.hash = dm.script_hash AND s.workspace_id = dm.workspace_id + WHERE s.hash = $1 AND s.workspace_id = $2", hash.0, w_id, ) @@ -1771,8 +1789,14 @@ async fn get_deployment_status( let status = not_found_if_none(status_o, "DeploymentStatus", hash.to_string())?; + let deployment_status = DeploymentStatus { + lock: status.lock, + lock_error_logs: status.lock_error_logs, + job_id: status.job_id, + }; + tx.commit().await?; - Ok(Json(status)) + Ok(Json(deployment_status)) } pub async fn require_is_writer(authed: &ApiAuthed, path: &str, w_id: &str, db: DB) -> Result<()> { diff --git a/backend/windmill-queue/src/jobs.rs b/backend/windmill-queue/src/jobs.rs index 25d10784bb..059bfc69b8 100644 --- a/backend/windmill-queue/src/jobs.rs +++ b/backend/windmill-queue/src/jobs.rs @@ -3040,7 +3040,7 @@ pub async fn concurrency_key( .await .map(|x| { if x.is_none() { - tracing::info!("No concurrency key found for job {id}, defaulting to empty string"); + tracing::info!("No concurrency key found for job {id}"); } return x; }) diff --git a/frontend/src/routes/(root)/(logged)/flows/get/[...path]/+page.svelte b/frontend/src/routes/(root)/(logged)/flows/get/[...path]/+page.svelte index 55c0b4f775..9a243495c8 100644 --- a/frontend/src/routes/(root)/(logged)/flows/get/[...path]/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/flows/get/[...path]/+page.svelte @@ -75,6 +75,7 @@ let inputSelected: 'saved' | 'history' | undefined = $state(undefined) let jsonView = $state(false) let deploymentInProgress = $state(false) + let deploymentJobId: string | undefined = $state(undefined) let intervalId: number | undefined = undefined @@ -166,8 +167,11 @@ }) if (status.lock_error_logs == undefined || status.lock_error_logs != '') { deploymentInProgress = false + deploymentJobId = undefined flow.lock_error_logs = status.lock_error_logs clearInterval(intervalId) + } else if (status.job_id) { + deploymentJobId = status.job_id } } } @@ -541,6 +545,13 @@ Deployment in progress + {#if deploymentJobId} + view job + {/if} {/if} {#if flow.lock_error_logs && flow.lock_error_logs != ''} diff --git a/frontend/src/routes/(root)/(logged)/scripts/get/[...hash]/+page.svelte b/frontend/src/routes/(root)/(logged)/scripts/get/[...hash]/+page.svelte index 0dbe481e9f..40a0a51d1d 100644 --- a/frontend/src/routes/(root)/(logged)/scripts/get/[...hash]/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/scripts/get/[...hash]/+page.svelte @@ -89,6 +89,7 @@ let topHash: string | undefined = $state() let can_write = $state(false) let deploymentInProgress = $state(false) + let deploymentJobId: string | undefined = $state(undefined) let intervalId: number let shareModal: ShareModal | undefined = $state() let runForm: RunForm | undefined = $state() @@ -156,9 +157,12 @@ }) if (status.lock != undefined || status.lock_error_logs != undefined) { deploymentInProgress = false + deploymentJobId = undefined script.lock = status.lock script.lock_error_logs = status.lock_error_logs clearInterval(intervalId) + } else if (status.job_id) { + deploymentJobId = status.job_id } } } @@ -689,6 +693,13 @@ Deployment in progress + {#if deploymentJobId} + view job + {/if} {/if}