add prebuild metrics and observability

Signed-off-by: Danny Kopping <danny@coder.com>
This commit is contained in:
Sas Swart
2025-02-25 09:07:48 +00:00
committed by Danny Kopping
parent a87e12750e
commit 4e1e745912
13 changed files with 319 additions and 5 deletions

View File

@ -1977,6 +1977,13 @@ func (q *querier) GetParameterSchemasByJobID(ctx context.Context, jobID uuid.UUI
return q.db.GetParameterSchemasByJobID(ctx, jobID)
}
func (q *querier) GetPrebuildMetrics(ctx context.Context) ([]database.GetPrebuildMetricsRow, error) {
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceTemplate); err != nil {
return nil, err
}
return q.db.GetPrebuildMetrics(ctx)
}
func (q *querier) GetPrebuildsInProgress(ctx context.Context) ([]database.GetPrebuildsInProgressRow, error) {
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceTemplate); err != nil {
return nil, err

View File

@ -3784,6 +3784,10 @@ func (q *FakeQuerier) GetParameterSchemasByJobID(_ context.Context, jobID uuid.U
return parameters, nil
}
func (q *FakeQuerier) GetPrebuildMetrics(ctx context.Context) ([]database.GetPrebuildMetricsRow, error) {
panic("not implemented")
}
func (q *FakeQuerier) GetPrebuildsInProgress(ctx context.Context) ([]database.GetPrebuildsInProgressRow, error) {
panic("not implemented")
}

View File

@ -987,6 +987,13 @@ func (m queryMetricsStore) GetParameterSchemasByJobID(ctx context.Context, jobID
return schemas, err
}
func (m queryMetricsStore) GetPrebuildMetrics(ctx context.Context) ([]database.GetPrebuildMetricsRow, error) {
start := time.Now()
r0, r1 := m.s.GetPrebuildMetrics(ctx)
m.queryLatencies.WithLabelValues("GetPrebuildMetrics").Observe(time.Since(start).Seconds())
return r0, r1
}
func (m queryMetricsStore) GetPrebuildsInProgress(ctx context.Context) ([]database.GetPrebuildsInProgressRow, error) {
start := time.Now()
r0, r1 := m.s.GetPrebuildsInProgress(ctx)

View File

@ -2031,6 +2031,21 @@ func (mr *MockStoreMockRecorder) GetParameterSchemasByJobID(ctx, jobID any) *gom
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetParameterSchemasByJobID", reflect.TypeOf((*MockStore)(nil).GetParameterSchemasByJobID), ctx, jobID)
}
// GetPrebuildMetrics mocks base method.
func (m *MockStore) GetPrebuildMetrics(ctx context.Context) ([]database.GetPrebuildMetricsRow, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetPrebuildMetrics", ctx)
ret0, _ := ret[0].([]database.GetPrebuildMetricsRow)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetPrebuildMetrics indicates an expected call of GetPrebuildMetrics.
func (mr *MockStoreMockRecorder) GetPrebuildMetrics(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPrebuildMetrics", reflect.TypeOf((*MockStore)(nil).GetPrebuildMetrics), ctx)
}
// GetPrebuildsInProgress mocks base method.
func (m *MockStore) GetPrebuildsInProgress(ctx context.Context) ([]database.GetPrebuildsInProgressRow, error) {
m.ctrl.T.Helper()

View File

@ -204,6 +204,7 @@ type sqlcQuerier interface {
GetOrganizations(ctx context.Context, arg GetOrganizationsParams) ([]Organization, error)
GetOrganizationsByUserID(ctx context.Context, userID uuid.UUID) ([]Organization, error)
GetParameterSchemasByJobID(ctx context.Context, jobID uuid.UUID) ([]ParameterSchema, error)
GetPrebuildMetrics(ctx context.Context) ([]GetPrebuildMetricsRow, error)
GetPrebuildsInProgress(ctx context.Context) ([]GetPrebuildsInProgressRow, error)
GetPresetByWorkspaceBuildID(ctx context.Context, workspaceBuildID uuid.UUID) (TemplateVersionPreset, error)
GetPresetParametersByTemplateVersionID(ctx context.Context, templateVersionID uuid.UUID) ([]TemplateVersionPresetParameter, error)

View File

@ -5442,6 +5442,91 @@ func (q *sqlQuerier) ClaimPrebuild(ctx context.Context, arg ClaimPrebuildParams)
return i, err
}
const getPrebuildMetrics = `-- name: GetPrebuildMetrics :many
SELECT
t.name as template_name,
tvp.name as preset_name,
COUNT(*) FILTER ( -- created
-- TODO (sasswart): double check which job statuses should be included here
WHERE
pj.initiator_id = 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid
AND pj.job_status = 'succeeded'::provisioner_job_status
) as created,
COUNT(*) FILTER ( -- failed
-- TODO (sasswart): should we count cancelled here?
WHERE pj.initiator_id = 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid
AND pj.job_status = 'failed'::provisioner_job_status
) as failed,
COUNT(*) FILTER ( -- assigned
WHERE pj.initiator_id = 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid
AND NOT w.owner_id = 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid
) as assigned,
COUNT(*) FILTER ( -- exhausted
-- TODO (sasswart): write a filter to count this
-- we should be able to count:
-- - workspace builds
-- - that have a preset id
-- - and that preset has prebuilds enabled
-- - and the job for the prebuild was initiated by a user other than the prebuilds user
WHERE
wb.template_version_preset_id IS NOT NULL
AND w.owner_id != 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid
AND wb.initiator_id != 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid
) as exhausted,
COUNT(*) FILTER ( -- used_preset
WHERE wb.template_version_preset_id IS NOT NULL
) as used_preset
FROM workspace_builds wb
INNER JOIN provisioner_jobs pj ON wb.job_id = pj.id
LEFT JOIN workspaces w ON wb.workspace_id = w.id
LEFT JOIN template_version_presets tvp ON wb.template_version_preset_id = tvp.id
LEFT JOIN template_versions tv ON tv.id = wb.template_version_id
LEFT JOIN templates t ON t.id = tv.template_id
WHERE pj.initiator_id = 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid
GROUP BY t.name, tvp.name
`
type GetPrebuildMetricsRow struct {
TemplateName sql.NullString `db:"template_name" json:"template_name"`
PresetName sql.NullString `db:"preset_name" json:"preset_name"`
Created int64 `db:"created" json:"created"`
Failed int64 `db:"failed" json:"failed"`
Assigned int64 `db:"assigned" json:"assigned"`
Exhausted int64 `db:"exhausted" json:"exhausted"`
UsedPreset int64 `db:"used_preset" json:"used_preset"`
}
func (q *sqlQuerier) GetPrebuildMetrics(ctx context.Context) ([]GetPrebuildMetricsRow, error) {
rows, err := q.db.QueryContext(ctx, getPrebuildMetrics)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetPrebuildMetricsRow
for rows.Next() {
var i GetPrebuildMetricsRow
if err := rows.Scan(
&i.TemplateName,
&i.PresetName,
&i.Created,
&i.Failed,
&i.Assigned,
&i.Exhausted,
&i.UsedPreset,
); 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 getPrebuildsInProgress = `-- name: GetPrebuildsInProgress :many
SELECT t.id AS template_id, wpb.template_version_id, wpb.transition, COUNT(wpb.transition) AS count
FROM workspace_latest_build wlb

View File

@ -71,3 +71,46 @@ RETURNING w.id, w.name;
INSERT INTO template_version_preset_prebuilds (id, preset_id, desired_instances, invalidate_after_secs)
VALUES (@id::uuid, @preset_id::uuid, @desired_instances::int, @invalidate_after_secs::int)
RETURNING *;
-- name: GetPrebuildMetrics :many
SELECT
t.name as template_name,
tvp.name as preset_name,
COUNT(*) FILTER ( -- created
-- TODO (sasswart): double check which job statuses should be included here
WHERE
pj.initiator_id = 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid
AND pj.job_status = 'succeeded'::provisioner_job_status
) as created,
COUNT(*) FILTER ( -- failed
-- TODO (sasswart): should we count cancelled here?
WHERE pj.initiator_id = 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid
AND pj.job_status = 'failed'::provisioner_job_status
) as failed,
COUNT(*) FILTER ( -- assigned
WHERE pj.initiator_id = 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid
AND NOT w.owner_id = 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid
) as assigned,
COUNT(*) FILTER ( -- exhausted
-- TODO (sasswart): write a filter to count this
-- we should be able to count:
-- - workspace builds
-- - that have a preset id
-- - and that preset has prebuilds enabled
-- - and the job for the prebuild was initiated by a user other than the prebuilds user
WHERE
wb.template_version_preset_id IS NOT NULL
AND w.owner_id != 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid
AND wb.initiator_id != 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid
) as exhausted,
COUNT(*) FILTER ( -- used_preset
WHERE wb.template_version_preset_id IS NOT NULL
) as used_preset
FROM workspace_builds wb
INNER JOIN provisioner_jobs pj ON wb.job_id = pj.id
LEFT JOIN workspaces w ON wb.workspace_id = w.id
LEFT JOIN template_version_presets tvp ON wb.template_version_preset_id = tvp.id
LEFT JOIN template_versions tv ON tv.id = wb.template_version_id
LEFT JOIN templates t ON t.id = tv.template_id
WHERE pj.initiator_id = 'c42fdf75-3097-471c-8c33-fb52454d81c0'::uuid
GROUP BY t.name, tvp.name;