From ef745c0c5d3f4db643a9f4fac4503ddde8bb4cac Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Wed, 14 May 2025 04:51:01 +1000 Subject: [PATCH] chore: optimize workspace_latest_builds view query (#17789) Avoids two sequential scans of massive tables (`workspace_builds`, `provisioner_jobs`) and uses index scans instead. This new view largely replicates our already optimized query `GetWorkspaces` to fetch the latest build. The original query and the new query were compared against the dogfood database to ensure they return the exact same data in the exact same order (minus the new `workspaces.deleted = false` filter to improve performance even more). The performance is massively improved even without the `workspaces.deleted = false` filter, but it was added to improve it even more. Note: these query times are probably inflated due to high database load on our dogfood environment that this intends to partially resolve. Before: 2,139ms ([explain](https://explain.dalibo.com/plan/997e4fch241b46e6)) After: 33ms ([explain](https://explain.dalibo.com/plan/c888dc223870f181)) Co-authored-by: Cian Johnston --------- Signed-off-by: Danny Kopping Co-authored-by: Mathias Fredriksson Co-authored-by: Danny Kopping --- coderd/database/dump.sql | 77 ++++++++++------- ...kspace_latest_builds_optimization.down.sql | 58 +++++++++++++ ...orkspace_latest_builds_optimization.up.sql | 85 +++++++++++++++++++ 3 files changed, 188 insertions(+), 32 deletions(-) create mode 100644 coderd/database/migrations/000323_workspace_latest_builds_optimization.down.sql create mode 100644 coderd/database/migrations/000323_workspace_latest_builds_optimization.up.sql diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index fa5b4b821c..a03ea910f9 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -2024,18 +2024,52 @@ CREATE VIEW workspace_build_with_user AS COMMENT ON VIEW workspace_build_with_user IS 'Joins in the username + avatar url of the initiated by user.'; +CREATE TABLE workspaces ( + id uuid NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + owner_id uuid NOT NULL, + organization_id uuid NOT NULL, + template_id uuid NOT NULL, + deleted boolean DEFAULT false NOT NULL, + name character varying(64) NOT NULL, + autostart_schedule text, + ttl bigint, + last_used_at timestamp with time zone DEFAULT '0001-01-01 00:00:00+00'::timestamp with time zone NOT NULL, + dormant_at timestamp with time zone, + deleting_at timestamp with time zone, + automatic_updates automatic_updates DEFAULT 'never'::automatic_updates NOT NULL, + favorite boolean DEFAULT false NOT NULL, + next_start_at timestamp with time zone +); + +COMMENT ON COLUMN workspaces.favorite IS 'Favorite is true if the workspace owner has favorited the workspace.'; + CREATE VIEW workspace_latest_builds AS - SELECT DISTINCT ON (wb.workspace_id) wb.id, - wb.workspace_id, - wb.template_version_id, - wb.job_id, - wb.template_version_preset_id, - wb.transition, - wb.created_at, - pj.job_status - FROM (workspace_builds wb - JOIN provisioner_jobs pj ON ((wb.job_id = pj.id))) - ORDER BY wb.workspace_id, wb.build_number DESC; + SELECT latest_build.id, + latest_build.workspace_id, + latest_build.template_version_id, + latest_build.job_id, + latest_build.template_version_preset_id, + latest_build.transition, + latest_build.created_at, + latest_build.job_status + FROM (workspaces + LEFT JOIN LATERAL ( SELECT workspace_builds.id, + workspace_builds.workspace_id, + workspace_builds.template_version_id, + workspace_builds.job_id, + workspace_builds.template_version_preset_id, + workspace_builds.transition, + workspace_builds.created_at, + provisioner_jobs.job_status + FROM (workspace_builds + JOIN provisioner_jobs ON ((provisioner_jobs.id = workspace_builds.job_id))) + WHERE (workspace_builds.workspace_id = workspaces.id) + ORDER BY workspace_builds.build_number DESC + LIMIT 1) latest_build ON (true)) + WHERE (workspaces.deleted = false) + ORDER BY workspaces.id; CREATE TABLE workspace_modules ( id uuid NOT NULL, @@ -2072,27 +2106,6 @@ CREATE TABLE workspace_resources ( module_path text ); -CREATE TABLE workspaces ( - id uuid NOT NULL, - created_at timestamp with time zone NOT NULL, - updated_at timestamp with time zone NOT NULL, - owner_id uuid NOT NULL, - organization_id uuid NOT NULL, - template_id uuid NOT NULL, - deleted boolean DEFAULT false NOT NULL, - name character varying(64) NOT NULL, - autostart_schedule text, - ttl bigint, - last_used_at timestamp with time zone DEFAULT '0001-01-01 00:00:00+00'::timestamp with time zone NOT NULL, - dormant_at timestamp with time zone, - deleting_at timestamp with time zone, - automatic_updates automatic_updates DEFAULT 'never'::automatic_updates NOT NULL, - favorite boolean DEFAULT false NOT NULL, - next_start_at timestamp with time zone -); - -COMMENT ON COLUMN workspaces.favorite IS 'Favorite is true if the workspace owner has favorited the workspace.'; - CREATE VIEW workspace_prebuilds AS WITH all_prebuilds AS ( SELECT w.id, diff --git a/coderd/database/migrations/000323_workspace_latest_builds_optimization.down.sql b/coderd/database/migrations/000323_workspace_latest_builds_optimization.down.sql new file mode 100644 index 0000000000..9d9ae7aff4 --- /dev/null +++ b/coderd/database/migrations/000323_workspace_latest_builds_optimization.down.sql @@ -0,0 +1,58 @@ +DROP VIEW workspace_prebuilds; +DROP VIEW workspace_latest_builds; + +-- Revert to previous version from 000314_prebuilds.up.sql +CREATE VIEW workspace_latest_builds AS +SELECT DISTINCT ON (workspace_id) + wb.id, + wb.workspace_id, + wb.template_version_id, + wb.job_id, + wb.template_version_preset_id, + wb.transition, + wb.created_at, + pj.job_status +FROM workspace_builds wb + INNER JOIN provisioner_jobs pj ON wb.job_id = pj.id +ORDER BY wb.workspace_id, wb.build_number DESC; + +-- Recreate the dependent views +CREATE VIEW workspace_prebuilds AS + WITH all_prebuilds AS ( + SELECT w.id, + w.name, + w.template_id, + w.created_at + FROM workspaces w + WHERE (w.owner_id = 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid) + ), workspaces_with_latest_presets AS ( + SELECT DISTINCT ON (workspace_builds.workspace_id) workspace_builds.workspace_id, + workspace_builds.template_version_preset_id + FROM workspace_builds + WHERE (workspace_builds.template_version_preset_id IS NOT NULL) + ORDER BY workspace_builds.workspace_id, workspace_builds.build_number DESC + ), workspaces_with_agents_status AS ( + SELECT w.id AS workspace_id, + bool_and((wa.lifecycle_state = 'ready'::workspace_agent_lifecycle_state)) AS ready + FROM (((workspaces w + JOIN workspace_latest_builds wlb ON ((wlb.workspace_id = w.id))) + JOIN workspace_resources wr ON ((wr.job_id = wlb.job_id))) + JOIN workspace_agents wa ON ((wa.resource_id = wr.id))) + WHERE (w.owner_id = 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid) + GROUP BY w.id + ), current_presets AS ( + SELECT w.id AS prebuild_id, + wlp.template_version_preset_id + FROM (workspaces w + JOIN workspaces_with_latest_presets wlp ON ((wlp.workspace_id = w.id))) + WHERE (w.owner_id = 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid) + ) + SELECT p.id, + p.name, + p.template_id, + p.created_at, + COALESCE(a.ready, false) AS ready, + cp.template_version_preset_id AS current_preset_id + FROM ((all_prebuilds p + LEFT JOIN workspaces_with_agents_status a ON ((a.workspace_id = p.id))) + JOIN current_presets cp ON ((cp.prebuild_id = p.id))); diff --git a/coderd/database/migrations/000323_workspace_latest_builds_optimization.up.sql b/coderd/database/migrations/000323_workspace_latest_builds_optimization.up.sql new file mode 100644 index 0000000000..d65e09ef47 --- /dev/null +++ b/coderd/database/migrations/000323_workspace_latest_builds_optimization.up.sql @@ -0,0 +1,85 @@ +-- Drop the dependent views +DROP VIEW workspace_prebuilds; +-- Previously created in 000314_prebuilds.up.sql +DROP VIEW workspace_latest_builds; + +-- The previous version of this view had two sequential scans on two very large +-- tables. This version optimized it by using index scans (via a lateral join) +-- AND avoiding selecting builds from deleted workspaces. +CREATE VIEW workspace_latest_builds AS +SELECT + latest_build.id, + latest_build.workspace_id, + latest_build.template_version_id, + latest_build.job_id, + latest_build.template_version_preset_id, + latest_build.transition, + latest_build.created_at, + latest_build.job_status +FROM workspaces +LEFT JOIN LATERAL ( + SELECT + workspace_builds.id AS id, + workspace_builds.workspace_id AS workspace_id, + workspace_builds.template_version_id AS template_version_id, + workspace_builds.job_id AS job_id, + workspace_builds.template_version_preset_id AS template_version_preset_id, + workspace_builds.transition AS transition, + workspace_builds.created_at AS created_at, + provisioner_jobs.job_status AS job_status + FROM + workspace_builds + JOIN + provisioner_jobs + ON + provisioner_jobs.id = workspace_builds.job_id + WHERE + workspace_builds.workspace_id = workspaces.id + ORDER BY + build_number DESC + LIMIT + 1 +) latest_build ON TRUE +WHERE workspaces.deleted = false +ORDER BY workspaces.id ASC; + +-- Recreate the dependent views +CREATE VIEW workspace_prebuilds AS + WITH all_prebuilds AS ( + SELECT w.id, + w.name, + w.template_id, + w.created_at + FROM workspaces w + WHERE (w.owner_id = 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid) + ), workspaces_with_latest_presets AS ( + SELECT DISTINCT ON (workspace_builds.workspace_id) workspace_builds.workspace_id, + workspace_builds.template_version_preset_id + FROM workspace_builds + WHERE (workspace_builds.template_version_preset_id IS NOT NULL) + ORDER BY workspace_builds.workspace_id, workspace_builds.build_number DESC + ), workspaces_with_agents_status AS ( + SELECT w.id AS workspace_id, + bool_and((wa.lifecycle_state = 'ready'::workspace_agent_lifecycle_state)) AS ready + FROM (((workspaces w + JOIN workspace_latest_builds wlb ON ((wlb.workspace_id = w.id))) + JOIN workspace_resources wr ON ((wr.job_id = wlb.job_id))) + JOIN workspace_agents wa ON ((wa.resource_id = wr.id))) + WHERE (w.owner_id = 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid) + GROUP BY w.id + ), current_presets AS ( + SELECT w.id AS prebuild_id, + wlp.template_version_preset_id + FROM (workspaces w + JOIN workspaces_with_latest_presets wlp ON ((wlp.workspace_id = w.id))) + WHERE (w.owner_id = 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid) + ) + SELECT p.id, + p.name, + p.template_id, + p.created_at, + COALESCE(a.ready, false) AS ready, + cp.template_version_preset_id AS current_preset_id + FROM ((all_prebuilds p + LEFT JOIN workspaces_with_agents_status a ON ((a.workspace_id = p.id))) + JOIN current_presets cp ON ((cp.prebuild_id = p.id)));