mirror of
https://github.com/coder/coder.git
synced 2025-07-13 21:36:50 +00:00
Decompose GetTemplatePrebuildState into separate queries, reimplement logic in Go
This is in service of testability Signed-off-by: Danny Kopping <danny@coder.com>
This commit is contained in:
@ -5442,148 +5442,155 @@ func (q *sqlQuerier) ClaimPrebuild(ctx context.Context, arg ClaimPrebuildParams)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getTemplatePrebuildState = `-- name: GetTemplatePrebuildState :many
|
||||
WITH
|
||||
-- All prebuilds currently running
|
||||
running_prebuilds AS (SELECT p.template_id,
|
||||
b.template_version_id,
|
||||
tvp_curr.id AS current_preset_id,
|
||||
tvp_desired.id AS desired_preset_id,
|
||||
COUNT(*) AS count,
|
||||
SUM(CASE
|
||||
WHEN p.lifecycle_state = 'ready'::workspace_agent_lifecycle_state THEN 1
|
||||
ELSE 0 END) AS eligible,
|
||||
STRING_AGG(p.id::text, ',') AS ids
|
||||
FROM workspace_prebuilds p
|
||||
INNER JOIN workspace_latest_build b ON b.workspace_id = p.id
|
||||
INNER JOIN provisioner_jobs pj ON b.job_id = pj.id
|
||||
INNER JOIN templates t ON p.template_id = t.id
|
||||
LEFT JOIN template_version_presets tvp_curr
|
||||
ON tvp_curr.id = b.template_version_preset_id
|
||||
LEFT JOIN template_version_presets tvp_desired
|
||||
ON tvp_desired.template_version_id = t.active_version_id
|
||||
WHERE (b.transition = 'start'::workspace_transition
|
||||
-- if a deletion job fails, the workspace will still be running
|
||||
OR pj.job_status IN ('failed'::provisioner_job_status, 'canceled'::provisioner_job_status,
|
||||
'unknown'::provisioner_job_status))
|
||||
AND (tvp_curr.name = tvp_desired.name
|
||||
OR tvp_desired.id IS NULL)
|
||||
GROUP BY p.template_id, b.template_version_id, tvp_curr.id,
|
||||
tvp_desired.id),
|
||||
-- All templates which have been configured for prebuilds (any version)
|
||||
templates_with_prebuilds AS (SELECT t.id AS template_id,
|
||||
tv.id AS template_version_id,
|
||||
tv.id = t.active_version_id AS using_active_version,
|
||||
tvpp.preset_id,
|
||||
tvp.name,
|
||||
MAX(tvpp.desired_instances) AS desired_instances,
|
||||
t.deleted,
|
||||
t.deprecated != '' AS deprecated
|
||||
FROM templates t
|
||||
INNER JOIN template_versions tv ON tv.template_id = t.id
|
||||
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.preset_id, tvp.name),
|
||||
-- Jobs relating to prebuilds current in-flight
|
||||
prebuilds_in_progress AS (SELECT wpb.template_version_id, wpb.transition, COUNT(wpb.transition) AS count
|
||||
FROM workspace_latest_build wlb
|
||||
INNER JOIN provisioner_jobs pj ON wlb.job_id = pj.id
|
||||
INNER JOIN workspace_prebuild_builds wpb ON wpb.id = wlb.id
|
||||
WHERE pj.job_status NOT IN
|
||||
('succeeded'::provisioner_job_status, 'canceled'::provisioner_job_status,
|
||||
'failed'::provisioner_job_status)
|
||||
GROUP BY wpb.template_version_id, wpb.transition)
|
||||
SELECT t.template_id,
|
||||
t.template_version_id,
|
||||
t.preset_id,
|
||||
t.using_active_version AS is_active,
|
||||
MAX(CASE
|
||||
WHEN p.template_version_id = t.template_version_id THEN p.ids
|
||||
ELSE '' END)::text AS running_prebuild_ids,
|
||||
COALESCE(MAX(CASE WHEN t.using_active_version THEN p.count ELSE 0 END),
|
||||
0)::int AS actual, -- running prebuilds for active version
|
||||
COALESCE(MAX(CASE WHEN t.using_active_version THEN p.eligible ELSE 0 END),
|
||||
0)::int AS eligible, -- prebuilds which can be claimed
|
||||
MAX(CASE WHEN t.using_active_version THEN t.desired_instances ELSE 0 END)::int AS desired, -- we only care about the active version's desired instances
|
||||
COALESCE(MAX(CASE
|
||||
WHEN p.template_version_id = t.template_version_id AND
|
||||
t.using_active_version = false
|
||||
THEN p.count
|
||||
ELSE 0 END),
|
||||
0)::int AS outdated, -- running prebuilds for inactive version
|
||||
COALESCE(GREATEST(
|
||||
(MAX(CASE WHEN t.using_active_version THEN p.count ELSE 0 END)::int
|
||||
-
|
||||
MAX(CASE WHEN t.using_active_version THEN t.desired_instances ELSE 0 END)),
|
||||
0),
|
||||
0) ::int AS extraneous, -- extra running prebuilds for active version
|
||||
COALESCE(MAX(CASE
|
||||
WHEN pip.transition = 'start'::workspace_transition THEN pip.count
|
||||
ELSE 0 END),
|
||||
0)::int AS starting,
|
||||
COALESCE(MAX(CASE
|
||||
WHEN pip.transition = 'stop'::workspace_transition THEN pip.count
|
||||
ELSE 0 END),
|
||||
0)::int AS stopping, -- not strictly needed, since prebuilds should never be left if a "stopped" state, but useful to know
|
||||
COALESCE(MAX(CASE
|
||||
WHEN pip.transition = 'delete'::workspace_transition THEN pip.count
|
||||
ELSE 0 END),
|
||||
0)::int AS deleting,
|
||||
t.deleted::bool AS template_deleted,
|
||||
t.deprecated::bool AS template_deprecated
|
||||
FROM templates_with_prebuilds t
|
||||
LEFT JOIN running_prebuilds p
|
||||
ON (p.template_version_id = t.template_version_id AND p.current_preset_id = t.preset_id)
|
||||
LEFT JOIN prebuilds_in_progress pip ON pip.template_version_id = t.template_version_id
|
||||
WHERE (t.using_active_version = TRUE
|
||||
OR p.count > 0)
|
||||
GROUP BY t.template_id, t.template_version_id, t.preset_id, t.using_active_version, t.deleted, t.deprecated
|
||||
const getPrebuildsInProgress = `-- name: GetPrebuildsInProgress :many
|
||||
SELECT wpb.template_version_id, wpb.transition, COUNT(wpb.transition) AS count
|
||||
FROM workspace_latest_build wlb
|
||||
INNER JOIN provisioner_jobs pj ON wlb.job_id = pj.id
|
||||
INNER JOIN workspace_prebuild_builds wpb ON wpb.id = wlb.id
|
||||
WHERE pj.job_status NOT IN
|
||||
('succeeded'::provisioner_job_status, 'canceled'::provisioner_job_status,
|
||||
'failed'::provisioner_job_status)
|
||||
GROUP BY wpb.template_version_id, wpb.transition
|
||||
`
|
||||
|
||||
type GetTemplatePrebuildStateRow struct {
|
||||
TemplateID uuid.UUID `db:"template_id" json:"template_id"`
|
||||
TemplateVersionID uuid.UUID `db:"template_version_id" json:"template_version_id"`
|
||||
PresetID uuid.UUID `db:"preset_id" json:"preset_id"`
|
||||
IsActive bool `db:"is_active" json:"is_active"`
|
||||
RunningPrebuildIds string `db:"running_prebuild_ids" json:"running_prebuild_ids"`
|
||||
Actual int32 `db:"actual" json:"actual"`
|
||||
Eligible int32 `db:"eligible" json:"eligible"`
|
||||
Desired int32 `db:"desired" json:"desired"`
|
||||
Outdated int32 `db:"outdated" json:"outdated"`
|
||||
Extraneous int32 `db:"extraneous" json:"extraneous"`
|
||||
Starting int32 `db:"starting" json:"starting"`
|
||||
Stopping int32 `db:"stopping" json:"stopping"`
|
||||
Deleting int32 `db:"deleting" json:"deleting"`
|
||||
TemplateDeleted bool `db:"template_deleted" json:"template_deleted"`
|
||||
TemplateDeprecated bool `db:"template_deprecated" json:"template_deprecated"`
|
||||
type GetPrebuildsInProgressRow struct {
|
||||
TemplateVersionID uuid.UUID `db:"template_version_id" json:"template_version_id"`
|
||||
Transition WorkspaceTransition `db:"transition" json:"transition"`
|
||||
Count int64 `db:"count" json:"count"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) GetTemplatePrebuildState(ctx context.Context, templateID uuid.UUID) ([]GetTemplatePrebuildStateRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getTemplatePrebuildState, templateID)
|
||||
func (q *sqlQuerier) GetPrebuildsInProgress(ctx context.Context) ([]GetPrebuildsInProgressRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getPrebuildsInProgress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []GetTemplatePrebuildStateRow
|
||||
var items []GetPrebuildsInProgressRow
|
||||
for rows.Next() {
|
||||
var i GetTemplatePrebuildStateRow
|
||||
var i GetPrebuildsInProgressRow
|
||||
if err := rows.Scan(&i.TemplateVersionID, &i.Transition, &i.Count); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getRunningPrebuilds = `-- name: GetRunningPrebuilds :many
|
||||
SELECT p.id AS workspace_id,
|
||||
p.template_id,
|
||||
b.template_version_id,
|
||||
tvp_curr.id AS current_preset_id,
|
||||
tvp_desired.id AS desired_preset_id,
|
||||
CASE
|
||||
WHEN p.lifecycle_state = 'ready'::workspace_agent_lifecycle_state THEN TRUE
|
||||
ELSE FALSE END AS eligible
|
||||
FROM workspace_prebuilds p
|
||||
INNER JOIN workspace_latest_build b ON b.workspace_id = p.id
|
||||
INNER JOIN provisioner_jobs pj ON b.job_id = pj.id
|
||||
INNER JOIN templates t ON p.template_id = t.id
|
||||
LEFT JOIN template_version_presets tvp_curr
|
||||
ON tvp_curr.id = b.template_version_preset_id
|
||||
LEFT JOIN template_version_presets tvp_desired
|
||||
ON tvp_desired.template_version_id = t.active_version_id
|
||||
WHERE (b.transition = 'start'::workspace_transition
|
||||
-- if a deletion job fails, the workspace will still be running
|
||||
OR pj.job_status IN ('failed'::provisioner_job_status, 'canceled'::provisioner_job_status,
|
||||
'unknown'::provisioner_job_status))
|
||||
AND (tvp_curr.name = tvp_desired.name
|
||||
OR tvp_desired.id IS NULL)
|
||||
`
|
||||
|
||||
type GetRunningPrebuildsRow struct {
|
||||
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
|
||||
TemplateID uuid.UUID `db:"template_id" json:"template_id"`
|
||||
TemplateVersionID uuid.UUID `db:"template_version_id" json:"template_version_id"`
|
||||
CurrentPresetID uuid.NullUUID `db:"current_preset_id" json:"current_preset_id"`
|
||||
DesiredPresetID uuid.NullUUID `db:"desired_preset_id" json:"desired_preset_id"`
|
||||
Eligible bool `db:"eligible" json:"eligible"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) GetRunningPrebuilds(ctx context.Context) ([]GetRunningPrebuildsRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getRunningPrebuilds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []GetRunningPrebuildsRow
|
||||
for rows.Next() {
|
||||
var i GetRunningPrebuildsRow
|
||||
if err := rows.Scan(
|
||||
&i.WorkspaceID,
|
||||
&i.TemplateID,
|
||||
&i.TemplateVersionID,
|
||||
&i.CurrentPresetID,
|
||||
&i.DesiredPresetID,
|
||||
&i.Eligible,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getTemplatePresetsWithPrebuilds = `-- name: GetTemplatePresetsWithPrebuilds :many
|
||||
SELECT t.id AS template_id,
|
||||
tv.id AS template_version_id,
|
||||
tv.id = t.active_version_id AS using_active_version,
|
||||
tvpp.preset_id,
|
||||
tvp.name,
|
||||
tvpp.desired_instances AS desired_instances,
|
||||
t.deleted,
|
||||
t.deprecated != '' AS deprecated
|
||||
FROM templates t
|
||||
INNER JOIN template_versions tv ON tv.template_id = t.id
|
||||
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 OR $1 IS NULL)
|
||||
`
|
||||
|
||||
type GetTemplatePresetsWithPrebuildsRow struct {
|
||||
TemplateID uuid.UUID `db:"template_id" json:"template_id"`
|
||||
TemplateVersionID uuid.UUID `db:"template_version_id" json:"template_version_id"`
|
||||
UsingActiveVersion bool `db:"using_active_version" json:"using_active_version"`
|
||||
PresetID uuid.UUID `db:"preset_id" json:"preset_id"`
|
||||
Name string `db:"name" json:"name"`
|
||||
DesiredInstances int32 `db:"desired_instances" json:"desired_instances"`
|
||||
Deleted bool `db:"deleted" json:"deleted"`
|
||||
Deprecated bool `db:"deprecated" json:"deprecated"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) GetTemplatePresetsWithPrebuilds(ctx context.Context, templateID uuid.NullUUID) ([]GetTemplatePresetsWithPrebuildsRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getTemplatePresetsWithPrebuilds, templateID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []GetTemplatePresetsWithPrebuildsRow
|
||||
for rows.Next() {
|
||||
var i GetTemplatePresetsWithPrebuildsRow
|
||||
if err := rows.Scan(
|
||||
&i.TemplateID,
|
||||
&i.TemplateVersionID,
|
||||
&i.UsingActiveVersion,
|
||||
&i.PresetID,
|
||||
&i.IsActive,
|
||||
&i.RunningPrebuildIds,
|
||||
&i.Actual,
|
||||
&i.Eligible,
|
||||
&i.Desired,
|
||||
&i.Outdated,
|
||||
&i.Extraneous,
|
||||
&i.Starting,
|
||||
&i.Stopping,
|
||||
&i.Deleting,
|
||||
&i.TemplateDeleted,
|
||||
&i.TemplateDeprecated,
|
||||
&i.Name,
|
||||
&i.DesiredInstances,
|
||||
&i.Deleted,
|
||||
&i.Deprecated,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
Reference in New Issue
Block a user