feat: claim prebuilds based on workspace parameters instead of preset id (#19279)

Closes https://github.com/coder/coder/issues/18356.

This change finds and selects a matching preset if one was not chosen
during workspace creation. This solidifies the relationship between
presets and parameters.

When a workspace is created without in explicitly chosen preset, it will
now still be eligible to claim a prebuilt workspace if one is available.
This commit is contained in:
Sas Swart
2025-08-20 11:02:53 +02:00
committed by GitHub
parent 5e84d257b7
commit f9a6adc704
15 changed files with 736 additions and 37 deletions
+41
View File
@@ -7252,6 +7252,47 @@ func (q *sqlQuerier) CountInProgressPrebuilds(ctx context.Context) ([]CountInPro
return items, nil
}
const findMatchingPresetID = `-- name: FindMatchingPresetID :one
WITH provided_params AS (
SELECT
unnest($1::text[]) AS name,
unnest($2::text[]) AS value
),
preset_matches AS (
SELECT
tvp.id AS template_version_preset_id,
COALESCE(COUNT(tvpp.name), 0) AS total_preset_params,
COALESCE(COUNT(pp.name), 0) AS matching_params
FROM template_version_presets tvp
LEFT JOIN template_version_preset_parameters tvpp ON tvpp.template_version_preset_id = tvp.id
LEFT JOIN provided_params pp ON pp.name = tvpp.name AND pp.value = tvpp.value
WHERE tvp.template_version_id = $3
GROUP BY tvp.id
)
SELECT pm.template_version_preset_id
FROM preset_matches pm
WHERE pm.total_preset_params = pm.matching_params -- All preset parameters must match
ORDER BY pm.total_preset_params DESC -- Return the preset with the most parameters
LIMIT 1
`
type FindMatchingPresetIDParams struct {
ParameterNames []string `db:"parameter_names" json:"parameter_names"`
ParameterValues []string `db:"parameter_values" json:"parameter_values"`
TemplateVersionID uuid.UUID `db:"template_version_id" json:"template_version_id"`
}
// FindMatchingPresetID finds a preset ID that is the largest exact subset of the provided parameters.
// It returns the preset ID if a match is found, or NULL if no match is found.
// The query finds presets where all preset parameters are present in the provided parameters,
// and returns the preset with the most parameters (largest subset).
func (q *sqlQuerier) FindMatchingPresetID(ctx context.Context, arg FindMatchingPresetIDParams) (uuid.UUID, error) {
row := q.db.QueryRowContext(ctx, findMatchingPresetID, pq.Array(arg.ParameterNames), pq.Array(arg.ParameterValues), arg.TemplateVersionID)
var template_version_preset_id uuid.UUID
err := row.Scan(&template_version_preset_id)
return template_version_preset_id, err
}
const getPrebuildMetrics = `-- name: GetPrebuildMetrics :many
SELECT
t.name as template_name,