diff --git a/backend/migrations/20260225100000_asset_covering_index.down.sql b/backend/migrations/20260225100000_asset_covering_index.down.sql new file mode 100644 index 0000000000..12f1b56303 --- /dev/null +++ b/backend/migrations/20260225100000_asset_covering_index.down.sql @@ -0,0 +1,5 @@ +DROP INDEX IF EXISTS idx_asset_ws_path_kind_recent; + +-- Restore the dropped indexes +CREATE INDEX IF NOT EXISTS idx_asset_workspace_created_id ON asset (workspace_id, created_at DESC, id DESC); +CREATE INDEX IF NOT EXISTS idx_asset_kind_path ON asset (workspace_id, kind, path); diff --git a/backend/migrations/20260225100000_asset_covering_index.up.sql b/backend/migrations/20260225100000_asset_covering_index.up.sql new file mode 100644 index 0000000000..cfa388de87 --- /dev/null +++ b/backend/migrations/20260225100000_asset_covering_index.up.sql @@ -0,0 +1,11 @@ +-- Covering index for the list_assets CTE: GROUP BY (path, kind) + MAX(created_at, id) + ORDER BY +-- Includes usage_kind and usage_path to allow full index-only scan (avoiding heap lookups for filter conditions) +CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_asset_ws_path_kind_recent + ON asset (workspace_id, path, kind, created_at DESC, id DESC) + INCLUDE (usage_kind, usage_path); + +-- Drop indexes now subsumed by idx_asset_ws_path_kind_recent: +-- idx_asset_workspace_created_id (workspace_id, created_at DESC, id DESC) - only used by list_assets CTE +-- idx_asset_kind_path (workspace_id, kind, path) - only used by list_assets CTE/outer join, covered by new index + PK +DROP INDEX CONCURRENTLY IF EXISTS idx_asset_workspace_created_id; +DROP INDEX CONCURRENTLY IF EXISTS idx_asset_kind_path; diff --git a/backend/windmill-api-assets/src/lib.rs b/backend/windmill-api-assets/src/lib.rs index 48817cfdd1..d1dfde41e0 100644 --- a/backend/windmill-api-assets/src/lib.rs +++ b/backend/windmill-api-assets/src/lib.rs @@ -144,7 +144,7 @@ async fn list_assets( format!( r#"FROM asset LEFT JOIN v2_job job_cte ON asset.usage_kind = 'job' - AND asset.usage_path = job_cte.id::text + AND job_cte.id = CASE WHEN asset.usage_kind = 'job' THEN asset.usage_path::uuid END AND job_cte.workspace_id = $1"# ) } else { @@ -209,7 +209,7 @@ async fn list_assets( ) = resource.path AND resource.workspace_id = $1 LEFT JOIN v2_job job ON asset.usage_kind = 'job' - AND asset.usage_path = job.id::text + AND job.id = CASE WHEN asset.usage_kind = 'job' THEN asset.usage_path::uuid END AND job.workspace_id = $1 WHERE asset.workspace_id = $1 AND (asset.kind <> 'resource' OR resource.path IS NOT NULL)