From 981f61e2725b3331425849f00c8bfed9577786fb Mon Sep 17 00:00:00 2001 From: Danny Kopping Date: Thu, 23 Jan 2025 14:24:29 +0000 Subject: [PATCH] Incorporate in-progress jobs into state calculation Signed-off-by: Danny Kopping --- coderd/database/dump.sql | 16 ++++++++- .../migrations/000290_prebuilds.up.sql | 2 +- coderd/database/models.go | 16 ++++++++- coderd/database/queries.sql.go | 33 ++++++++++++------- coderd/database/queries/prebuilds.sql | 19 ++++++++--- coderd/prebuilds/controller.go | 12 +++++-- 6 files changed, 76 insertions(+), 22 deletions(-) diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 0860d6a45c..12cfd49b9f 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -1823,7 +1823,21 @@ CREATE TABLE workspace_modules ( ); CREATE VIEW workspace_prebuild_builds AS - SELECT workspace_builds.workspace_id + SELECT workspace_builds.id, + workspace_builds.created_at, + workspace_builds.updated_at, + workspace_builds.workspace_id, + workspace_builds.template_version_id, + workspace_builds.build_number, + workspace_builds.transition, + workspace_builds.initiator_id, + workspace_builds.provisioner_state, + workspace_builds.job_id, + workspace_builds.deadline, + workspace_builds.reason, + workspace_builds.daily_cost, + workspace_builds.max_deadline, + workspace_builds.template_version_preset_id FROM workspace_builds WHERE (workspace_builds.initiator_id = 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid); diff --git a/coderd/database/migrations/000290_prebuilds.up.sql b/coderd/database/migrations/000290_prebuilds.up.sql index 0a72566c69..b8631095fa 100644 --- a/coderd/database/migrations/000290_prebuilds.up.sql +++ b/coderd/database/migrations/000290_prebuilds.up.sql @@ -9,7 +9,7 @@ FROM workspaces WHERE owner_id = 'c42fdf75-3097-471c-8c33-fb52454d81c0'; CREATE VIEW workspace_prebuild_builds AS -SELECT workspace_id +SELECT * FROM workspace_builds WHERE initiator_id = 'c42fdf75-3097-471c-8c33-fb52454d81c0'; diff --git a/coderd/database/models.go b/coderd/database/models.go index 3b64a78f72..4bd085c75f 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -3407,7 +3407,21 @@ type WorkspacePrebuild struct { } type WorkspacePrebuildBuild struct { - WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"` + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"` + TemplateVersionID uuid.UUID `db:"template_version_id" json:"template_version_id"` + BuildNumber int32 `db:"build_number" json:"build_number"` + Transition WorkspaceTransition `db:"transition" json:"transition"` + InitiatorID uuid.UUID `db:"initiator_id" json:"initiator_id"` + ProvisionerState []byte `db:"provisioner_state" json:"provisioner_state"` + JobID uuid.UUID `db:"job_id" json:"job_id"` + Deadline time.Time `db:"deadline" json:"deadline"` + Reason BuildReason `db:"reason" json:"reason"` + DailyCost int32 `db:"daily_cost" json:"daily_cost"` + MaxDeadline time.Time `db:"max_deadline" json:"max_deadline"` + TemplateVersionPresetID uuid.NullUUID `db:"template_version_preset_id" json:"template_version_preset_id"` } type WorkspaceProxy struct { diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 5955713138..5efb8c9b37 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -5414,25 +5414,35 @@ WITH INNER JOIN template_version_presets tvp ON tvp.template_version_id = tv.id INNER JOIN template_version_preset_prebuilds tvpp ON tvpp.preset_id = tvp.id WHERE t.id = $1::uuid - GROUP BY t.id, tv.id, tvpp.id) + GROUP BY t.id, tv.id, tvpp.id), + prebuilds_in_progress AS (SELECT wpb.template_version_id, pj.id AS job_id, pj.type, pj.job_status + FROM workspace_prebuild_builds wpb + INNER JOIN workspace_latest_build wlb ON wpb.workspace_id = wlb.workspace_id + INNER JOIN provisioner_jobs pj ON wlb.job_id = pj.id + WHERE pj.job_status NOT IN + ('succeeded'::provisioner_job_status, 'canceled'::provisioner_job_status, + 'failed'::provisioner_job_status)) SELECT t.template_id, - COUNT(p.id) AS actual, -- running prebuilds for active version - MAX(CASE WHEN t.using_active_version THEN t.desired_instances ELSE 0 END) AS desired, -- we only care about the active version's desired instances - SUM(CASE WHEN t.using_active_version THEN 0 ELSE 1 END) AS extraneous, -- running prebuilds for inactive version + CAST(COUNT(p.id) AS INT) AS actual, -- running prebuilds for active version + CAST(MAX(CASE WHEN t.using_active_version THEN t.desired_instances ELSE 0 END) AS int) AS desired, -- we only care about the active version's desired instances + CAST(SUM(CASE WHEN t.using_active_version THEN 0 ELSE 1 END) AS INT) AS extraneous, -- running prebuilds for inactive version + CAST(COUNT(pip.template_version_id) AS INT) AS in_progress, t.deleted, t.deprecated FROM templates_with_prebuilds t LEFT JOIN running_prebuilds p ON p.template_version_id = t.template_version_id -GROUP BY t.template_id, p.id, t.deleted, t.deprecated + LEFT JOIN prebuilds_in_progress pip ON pip.template_version_id = t.template_version_id +GROUP BY t.template_id, p.id, t.deleted, t.deprecated, pip.template_version_id ` type GetTemplatePrebuildStateRow struct { - TemplateID uuid.UUID `db:"template_id" json:"template_id"` - Actual int64 `db:"actual" json:"actual"` - Desired interface{} `db:"desired" json:"desired"` - Extraneous int64 `db:"extraneous" json:"extraneous"` - Deleted bool `db:"deleted" json:"deleted"` - Deprecated bool `db:"deprecated" json:"deprecated"` + TemplateID uuid.UUID `db:"template_id" json:"template_id"` + Actual int32 `db:"actual" json:"actual"` + Desired int32 `db:"desired" json:"desired"` + Extraneous int32 `db:"extraneous" json:"extraneous"` + InProgress int32 `db:"in_progress" json:"in_progress"` + Deleted bool `db:"deleted" json:"deleted"` + Deprecated bool `db:"deprecated" json:"deprecated"` } func (q *sqlQuerier) GetTemplatePrebuildState(ctx context.Context, templateID uuid.UUID) ([]GetTemplatePrebuildStateRow, error) { @@ -5449,6 +5459,7 @@ func (q *sqlQuerier) GetTemplatePrebuildState(ctx context.Context, templateID uu &i.Actual, &i.Desired, &i.Extraneous, + &i.InProgress, &i.Deleted, &i.Deprecated, ); err != nil { diff --git a/coderd/database/queries/prebuilds.sql b/coderd/database/queries/prebuilds.sql index 9530057d0b..78bddc5116 100644 --- a/coderd/database/queries/prebuilds.sql +++ b/coderd/database/queries/prebuilds.sql @@ -17,13 +17,22 @@ WITH INNER JOIN template_version_presets tvp ON tvp.template_version_id = tv.id INNER JOIN template_version_preset_prebuilds tvpp ON tvpp.preset_id = tvp.id WHERE t.id = @template_id::uuid - GROUP BY t.id, tv.id, tvpp.id) + GROUP BY t.id, tv.id, tvpp.id), + prebuilds_in_progress AS (SELECT wpb.template_version_id, pj.id AS job_id, pj.type, pj.job_status + FROM workspace_prebuild_builds wpb + INNER JOIN workspace_latest_build wlb ON wpb.workspace_id = wlb.workspace_id + INNER JOIN provisioner_jobs pj ON wlb.job_id = pj.id + WHERE pj.job_status NOT IN + ('succeeded'::provisioner_job_status, 'canceled'::provisioner_job_status, + 'failed'::provisioner_job_status)) SELECT t.template_id, - COUNT(p.id) AS actual, -- running prebuilds for active version - MAX(CASE WHEN t.using_active_version THEN t.desired_instances ELSE 0 END) AS desired, -- we only care about the active version's desired instances - SUM(CASE WHEN t.using_active_version THEN 0 ELSE 1 END) AS extraneous, -- running prebuilds for inactive version + CAST(COUNT(p.id) AS INT) AS actual, -- running prebuilds for active version + CAST(MAX(CASE WHEN t.using_active_version THEN t.desired_instances ELSE 0 END) AS int) AS desired, -- we only care about the active version's desired instances + CAST(SUM(CASE WHEN t.using_active_version THEN 0 ELSE 1 END) AS INT) AS extraneous, -- running prebuilds for inactive version + CAST(COUNT(pip.template_version_id) AS INT) AS in_progress, t.deleted, t.deprecated FROM templates_with_prebuilds t LEFT JOIN running_prebuilds p ON p.template_version_id = t.template_version_id -GROUP BY t.template_id, p.id, t.deleted, t.deprecated; + LEFT JOIN prebuilds_in_progress pip ON pip.template_version_id = t.template_version_id +GROUP BY t.template_id, p.id, t.deleted, t.deprecated, pip.template_version_id; diff --git a/coderd/prebuilds/controller.go b/coderd/prebuilds/controller.go index 9a0e04f9a6..dccd87f341 100644 --- a/coderd/prebuilds/controller.go +++ b/coderd/prebuilds/controller.go @@ -3,6 +3,7 @@ package prebuilds import ( "context" "fmt" + "math" "time" "cdr.dev/slog" @@ -145,7 +146,7 @@ func (c Controller) reconcileTemplate(ctx context.Context, template database.Tem } for _, result := range results { - desired, actual, extraneous := result.Desired, result.Actual, result.Extraneous + desired, actual, extraneous, inProgress := result.Desired, result.Actual, result.Extraneous, result.InProgress // If the template has become deleted or deprecated since the last reconciliation, we need to ensure we // scale those prebuilds down to zero. @@ -153,9 +154,14 @@ func (c Controller) reconcileTemplate(ctx context.Context, template database.Tem desired = 0 } + toCreate := math.Max(0, float64(desired-(actual+inProgress))) + // TODO: we might need to get inProgress here by job type (i.e. create or destroy), then we wouldn't have this ambiguity + toDestroy := math.Max(0, float64(extraneous-inProgress)) + c.logger.Info(innerCtx, "template prebuild state retrieved", - slog.F("template_id", template.ID), - slog.F("desired", desired), slog.F("actual", actual), slog.F("extraneous", extraneous)) + slog.F("template_id", template.ID), slog.F("to_create", toCreate), slog.F("to_destroy", toDestroy), + slog.F("desired", desired), slog.F("actual", actual), + slog.F("extraneous", extraneous), slog.F("in_progress", inProgress)) } return nil