mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add provisioning timings to understand slow build times (#14274)
This commit is contained in:
@@ -2793,6 +2793,14 @@ func (q *querier) InsertProvisionerJobLogs(ctx context.Context, arg database.Ins
|
||||
return q.db.InsertProvisionerJobLogs(ctx, arg)
|
||||
}
|
||||
|
||||
// TODO: We need to create a ProvisionerJob resource type
|
||||
func (q *querier) InsertProvisionerJobTimings(ctx context.Context, arg database.InsertProvisionerJobTimingsParams) ([]database.ProvisionerJobTiming, error) {
|
||||
// if err := q.authorizeContext(ctx, policy.ActionCreate, rbac.ResourceSystem); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
return q.db.InsertProvisionerJobTimings(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) InsertProvisionerKey(ctx context.Context, arg database.InsertProvisionerKeyParams) (database.ProvisionerKey, error) {
|
||||
return insert(q.log, q.auth, rbac.ResourceProvisionerKeys.InOrg(arg.OrganizationID).WithID(arg.ID), q.db.InsertProvisionerKey)(ctx, arg)
|
||||
}
|
||||
|
||||
@@ -2470,6 +2470,13 @@ func (s *MethodTestSuite) TestSystemFunctions() {
|
||||
JobID: j.ID,
|
||||
}).Asserts( /*rbac.ResourceSystem, policy.ActionCreate*/ )
|
||||
}))
|
||||
s.Run("InsertProvisionerJobTimings", s.Subtest(func(db database.Store, check *expects) {
|
||||
// TODO: we need to create a ProvisionerJob resource
|
||||
j := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{})
|
||||
check.Args(database.InsertProvisionerJobTimingsParams{
|
||||
JobID: j.ID,
|
||||
}).Asserts( /*rbac.ResourceSystem, policy.ActionCreate*/ )
|
||||
}))
|
||||
s.Run("UpsertProvisionerDaemon", s.Subtest(func(db database.Store, check *expects) {
|
||||
org := dbgen.Organization(s.T(), db, database.Organization{})
|
||||
pd := rbac.ResourceProvisionerDaemon.InOrg(org.ID)
|
||||
|
||||
@@ -6652,6 +6652,15 @@ func (q *FakeQuerier) InsertProvisionerJobLogs(_ context.Context, arg database.I
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
func (*FakeQuerier) InsertProvisionerJobTimings(_ context.Context, arg database.InsertProvisionerJobTimingsParams) ([]database.ProvisionerJobTiming, error) {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) InsertProvisionerKey(_ context.Context, arg database.InsertProvisionerKeyParams) (database.ProvisionerKey, error) {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
|
||||
@@ -1712,6 +1712,13 @@ func (m metricsStore) InsertProvisionerJobLogs(ctx context.Context, arg database
|
||||
return logs, err
|
||||
}
|
||||
|
||||
func (m metricsStore) InsertProvisionerJobTimings(ctx context.Context, arg database.InsertProvisionerJobTimingsParams) ([]database.ProvisionerJobTiming, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.InsertProvisionerJobTimings(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("InsertProvisionerJobTimings").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) InsertProvisionerKey(ctx context.Context, arg database.InsertProvisionerKeyParams) (database.ProvisionerKey, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.InsertProvisionerKey(ctx, arg)
|
||||
|
||||
@@ -3604,6 +3604,21 @@ func (mr *MockStoreMockRecorder) InsertProvisionerJobLogs(arg0, arg1 any) *gomoc
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertProvisionerJobLogs", reflect.TypeOf((*MockStore)(nil).InsertProvisionerJobLogs), arg0, arg1)
|
||||
}
|
||||
|
||||
// InsertProvisionerJobTimings mocks base method.
|
||||
func (m *MockStore) InsertProvisionerJobTimings(arg0 context.Context, arg1 database.InsertProvisionerJobTimingsParams) ([]database.ProvisionerJobTiming, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "InsertProvisionerJobTimings", arg0, arg1)
|
||||
ret0, _ := ret[0].([]database.ProvisionerJobTiming)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// InsertProvisionerJobTimings indicates an expected call of InsertProvisionerJobTimings.
|
||||
func (mr *MockStoreMockRecorder) InsertProvisionerJobTimings(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertProvisionerJobTimings", reflect.TypeOf((*MockStore)(nil).InsertProvisionerJobTimings), arg0, arg1)
|
||||
}
|
||||
|
||||
// InsertProvisionerKey mocks base method.
|
||||
func (m *MockStore) InsertProvisionerKey(arg0 context.Context, arg1 database.InsertProvisionerKeyParams) (database.ProvisionerKey, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
@@ -9,6 +9,10 @@ func Now() time.Time {
|
||||
|
||||
// Time returns a time compatible with Postgres. Postgres only stores dates with
|
||||
// microsecond precision.
|
||||
// FIXME(dannyk): refactor all calls to Time() to expect the input time to be modified to UTC; there are currently a
|
||||
//
|
||||
// few calls whose behavior would change subtly.
|
||||
// See https://github.com/coder/coder/pull/14274#discussion_r1718427461
|
||||
func Time(t time.Time) time.Time {
|
||||
return t.Round(time.Microsecond)
|
||||
}
|
||||
|
||||
Generated
+89
@@ -136,6 +136,13 @@ CREATE TYPE provisioner_job_status AS ENUM (
|
||||
|
||||
COMMENT ON TYPE provisioner_job_status IS 'Computed status of a provisioner job. Jobs could be stuck in a hung state, these states do not guarantee any transition to another state.';
|
||||
|
||||
CREATE TYPE provisioner_job_timing_stage AS ENUM (
|
||||
'init',
|
||||
'plan',
|
||||
'graph',
|
||||
'apply'
|
||||
);
|
||||
|
||||
CREATE TYPE provisioner_job_type AS ENUM (
|
||||
'template_version_import',
|
||||
'workspace_build',
|
||||
@@ -849,6 +856,33 @@ CREATE SEQUENCE provisioner_job_logs_id_seq
|
||||
|
||||
ALTER SEQUENCE provisioner_job_logs_id_seq OWNED BY provisioner_job_logs.id;
|
||||
|
||||
CREATE VIEW provisioner_job_stats AS
|
||||
SELECT
|
||||
NULL::uuid AS job_id,
|
||||
NULL::provisioner_job_status AS job_status,
|
||||
NULL::uuid AS workspace_id,
|
||||
NULL::uuid AS worker_id,
|
||||
NULL::text AS error,
|
||||
NULL::text AS error_code,
|
||||
NULL::timestamp with time zone AS updated_at,
|
||||
NULL::double precision AS queued_secs,
|
||||
NULL::double precision AS completion_secs,
|
||||
NULL::double precision AS canceled_secs,
|
||||
NULL::double precision AS init_secs,
|
||||
NULL::double precision AS plan_secs,
|
||||
NULL::double precision AS graph_secs,
|
||||
NULL::double precision AS apply_secs;
|
||||
|
||||
CREATE TABLE provisioner_job_timings (
|
||||
job_id uuid NOT NULL,
|
||||
started_at timestamp with time zone NOT NULL,
|
||||
ended_at timestamp with time zone NOT NULL,
|
||||
stage provisioner_job_timing_stage NOT NULL,
|
||||
source text NOT NULL,
|
||||
action text NOT NULL,
|
||||
resource text NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE provisioner_jobs (
|
||||
id uuid NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
@@ -1915,6 +1949,58 @@ CREATE INDEX workspace_resources_job_id_idx ON workspace_resources USING btree (
|
||||
|
||||
CREATE UNIQUE INDEX workspaces_owner_id_lower_idx ON workspaces USING btree (owner_id, lower((name)::text)) WHERE (deleted = false);
|
||||
|
||||
CREATE OR REPLACE VIEW provisioner_job_stats AS
|
||||
SELECT pj.id AS job_id,
|
||||
pj.job_status,
|
||||
wb.workspace_id,
|
||||
pj.worker_id,
|
||||
pj.error,
|
||||
pj.error_code,
|
||||
pj.updated_at,
|
||||
GREATEST(date_part('epoch'::text, (pj.started_at - pj.created_at)), (0)::double precision) AS queued_secs,
|
||||
GREATEST(date_part('epoch'::text, (pj.completed_at - pj.started_at)), (0)::double precision) AS completion_secs,
|
||||
GREATEST(date_part('epoch'::text, (pj.canceled_at - pj.started_at)), (0)::double precision) AS canceled_secs,
|
||||
GREATEST(date_part('epoch'::text, (max(
|
||||
CASE
|
||||
WHEN (pjt.stage = 'init'::provisioner_job_timing_stage) THEN pjt.ended_at
|
||||
ELSE NULL::timestamp with time zone
|
||||
END) - min(
|
||||
CASE
|
||||
WHEN (pjt.stage = 'init'::provisioner_job_timing_stage) THEN pjt.started_at
|
||||
ELSE NULL::timestamp with time zone
|
||||
END))), (0)::double precision) AS init_secs,
|
||||
GREATEST(date_part('epoch'::text, (max(
|
||||
CASE
|
||||
WHEN (pjt.stage = 'plan'::provisioner_job_timing_stage) THEN pjt.ended_at
|
||||
ELSE NULL::timestamp with time zone
|
||||
END) - min(
|
||||
CASE
|
||||
WHEN (pjt.stage = 'plan'::provisioner_job_timing_stage) THEN pjt.started_at
|
||||
ELSE NULL::timestamp with time zone
|
||||
END))), (0)::double precision) AS plan_secs,
|
||||
GREATEST(date_part('epoch'::text, (max(
|
||||
CASE
|
||||
WHEN (pjt.stage = 'graph'::provisioner_job_timing_stage) THEN pjt.ended_at
|
||||
ELSE NULL::timestamp with time zone
|
||||
END) - min(
|
||||
CASE
|
||||
WHEN (pjt.stage = 'graph'::provisioner_job_timing_stage) THEN pjt.started_at
|
||||
ELSE NULL::timestamp with time zone
|
||||
END))), (0)::double precision) AS graph_secs,
|
||||
GREATEST(date_part('epoch'::text, (max(
|
||||
CASE
|
||||
WHEN (pjt.stage = 'apply'::provisioner_job_timing_stage) THEN pjt.ended_at
|
||||
ELSE NULL::timestamp with time zone
|
||||
END) - min(
|
||||
CASE
|
||||
WHEN (pjt.stage = 'apply'::provisioner_job_timing_stage) THEN pjt.started_at
|
||||
ELSE NULL::timestamp with time zone
|
||||
END))), (0)::double precision) AS apply_secs
|
||||
FROM ((provisioner_jobs pj
|
||||
JOIN workspace_builds wb ON ((wb.job_id = pj.id)))
|
||||
LEFT JOIN provisioner_job_timings pjt ON ((pjt.job_id = pj.id)))
|
||||
GROUP BY pj.id, wb.workspace_id;
|
||||
|
||||
CREATE TRIGGER inhibit_enqueue_if_disabled BEFORE INSERT ON notification_messages FOR EACH ROW EXECUTE FUNCTION inhibit_enqueue_if_disabled();
|
||||
|
||||
CREATE TRIGGER remove_organization_member_custom_role BEFORE DELETE ON custom_roles FOR EACH ROW EXECUTE FUNCTION remove_organization_member_role();
|
||||
@@ -2012,6 +2098,9 @@ ALTER TABLE ONLY provisioner_daemons
|
||||
ALTER TABLE ONLY provisioner_job_logs
|
||||
ADD CONSTRAINT provisioner_job_logs_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY provisioner_job_timings
|
||||
ADD CONSTRAINT provisioner_job_timings_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY provisioner_jobs
|
||||
ADD CONSTRAINT provisioner_jobs_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ const (
|
||||
ForeignKeyParameterSchemasJobID ForeignKeyConstraint = "parameter_schemas_job_id_fkey" // ALTER TABLE ONLY parameter_schemas ADD CONSTRAINT parameter_schemas_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
|
||||
ForeignKeyProvisionerDaemonsOrganizationID ForeignKeyConstraint = "provisioner_daemons_organization_id_fkey" // ALTER TABLE ONLY provisioner_daemons ADD CONSTRAINT provisioner_daemons_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
|
||||
ForeignKeyProvisionerJobLogsJobID ForeignKeyConstraint = "provisioner_job_logs_job_id_fkey" // ALTER TABLE ONLY provisioner_job_logs ADD CONSTRAINT provisioner_job_logs_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
|
||||
ForeignKeyProvisionerJobTimingsJobID ForeignKeyConstraint = "provisioner_job_timings_job_id_fkey" // ALTER TABLE ONLY provisioner_job_timings ADD CONSTRAINT provisioner_job_timings_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
|
||||
ForeignKeyProvisionerJobsOrganizationID ForeignKeyConstraint = "provisioner_jobs_organization_id_fkey" // ALTER TABLE ONLY provisioner_jobs ADD CONSTRAINT provisioner_jobs_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
|
||||
ForeignKeyProvisionerKeysOrganizationID ForeignKeyConstraint = "provisioner_keys_organization_id_fkey" // ALTER TABLE ONLY provisioner_keys ADD CONSTRAINT provisioner_keys_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
|
||||
ForeignKeyTailnetAgentsCoordinatorID ForeignKeyConstraint = "tailnet_agents_coordinator_id_fkey" // ALTER TABLE ONLY tailnet_agents ADD CONSTRAINT tailnet_agents_coordinator_id_fkey FOREIGN KEY (coordinator_id) REFERENCES tailnet_coordinators(id) ON DELETE CASCADE;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
DROP VIEW IF EXISTS provisioner_job_stats;
|
||||
|
||||
DROP TYPE IF EXISTS provisioner_job_timing_stage CASCADE;
|
||||
|
||||
DROP TABLE IF EXISTS provisioner_job_timings;
|
||||
@@ -0,0 +1,45 @@
|
||||
CREATE TYPE provisioner_job_timing_stage AS ENUM (
|
||||
'init',
|
||||
'plan',
|
||||
'graph',
|
||||
'apply'
|
||||
);
|
||||
|
||||
CREATE TABLE provisioner_job_timings
|
||||
(
|
||||
job_id uuid NOT NULL REFERENCES provisioner_jobs (id) ON DELETE CASCADE,
|
||||
started_at timestamp with time zone not null,
|
||||
ended_at timestamp with time zone not null,
|
||||
stage provisioner_job_timing_stage not null,
|
||||
source text not null,
|
||||
action text not null,
|
||||
resource text not null
|
||||
);
|
||||
|
||||
CREATE VIEW provisioner_job_stats AS
|
||||
SELECT pj.id AS job_id,
|
||||
pj.job_status,
|
||||
wb.workspace_id,
|
||||
pj.worker_id,
|
||||
pj.error,
|
||||
pj.error_code,
|
||||
pj.updated_at,
|
||||
GREATEST(EXTRACT(EPOCH FROM (pj.started_at - pj.created_at)), 0) AS queued_secs,
|
||||
GREATEST(EXTRACT(EPOCH FROM (pj.completed_at - pj.started_at)), 0) AS completion_secs,
|
||||
GREATEST(EXTRACT(EPOCH FROM (pj.canceled_at - pj.started_at)), 0) AS canceled_secs,
|
||||
GREATEST(EXTRACT(EPOCH FROM (
|
||||
MAX(CASE WHEN pjt.stage = 'init'::provisioner_job_timing_stage THEN pjt.ended_at END) -
|
||||
MIN(CASE WHEN pjt.stage = 'init'::provisioner_job_timing_stage THEN pjt.started_at END))), 0) AS init_secs,
|
||||
GREATEST(EXTRACT(EPOCH FROM (
|
||||
MAX(CASE WHEN pjt.stage = 'plan'::provisioner_job_timing_stage THEN pjt.ended_at END) -
|
||||
MIN(CASE WHEN pjt.stage = 'plan'::provisioner_job_timing_stage THEN pjt.started_at END))), 0) AS plan_secs,
|
||||
GREATEST(EXTRACT(EPOCH FROM (
|
||||
MAX(CASE WHEN pjt.stage = 'graph'::provisioner_job_timing_stage THEN pjt.ended_at END) -
|
||||
MIN(CASE WHEN pjt.stage = 'graph'::provisioner_job_timing_stage THEN pjt.started_at END))), 0) AS graph_secs,
|
||||
GREATEST(EXTRACT(EPOCH FROM (
|
||||
MAX(CASE WHEN pjt.stage = 'apply'::provisioner_job_timing_stage THEN pjt.ended_at END) -
|
||||
MIN(CASE WHEN pjt.stage = 'apply'::provisioner_job_timing_stage THEN pjt.started_at END))), 0) AS apply_secs
|
||||
FROM provisioner_jobs pj
|
||||
JOIN workspace_builds wb ON wb.job_id = pj.id
|
||||
LEFT JOIN provisioner_job_timings pjt ON pjt.job_id = pj.id
|
||||
GROUP BY pj.id, wb.workspace_id;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
INSERT INTO provisioner_job_timings (job_id, started_at, ended_at, stage, source, action, resource)
|
||||
VALUES
|
||||
-- Job 1 - init stage
|
||||
('424a58cb-61d6-4627-9907-613c396c4a38', NOW() - INTERVAL '1 hour 55 minutes', NOW() - INTERVAL '1 hour 50 minutes', 'init', 'source1', 'action1', 'resource1'),
|
||||
|
||||
-- Job 1 - plan stage
|
||||
('424a58cb-61d6-4627-9907-613c396c4a38', NOW() - INTERVAL '1 hour 50 minutes', NOW() - INTERVAL '1 hour 40 minutes', 'plan', 'source2', 'action2', 'resource2'),
|
||||
|
||||
-- Job 1 - graph stage
|
||||
('424a58cb-61d6-4627-9907-613c396c4a38', NOW() - INTERVAL '1 hour 40 minutes', NOW() - INTERVAL '1 hour 30 minutes', 'graph', 'source3', 'action3', 'resource3'),
|
||||
|
||||
-- Job 1 - apply stage
|
||||
('424a58cb-61d6-4627-9907-613c396c4a38', NOW() - INTERVAL '1 hour 30 minutes', NOW() - INTERVAL '1 hour 20 minutes', 'apply', 'source4', 'action4', 'resource4');
|
||||
@@ -1216,6 +1216,70 @@ func AllProvisionerJobStatusValues() []ProvisionerJobStatus {
|
||||
}
|
||||
}
|
||||
|
||||
type ProvisionerJobTimingStage string
|
||||
|
||||
const (
|
||||
ProvisionerJobTimingStageInit ProvisionerJobTimingStage = "init"
|
||||
ProvisionerJobTimingStagePlan ProvisionerJobTimingStage = "plan"
|
||||
ProvisionerJobTimingStageGraph ProvisionerJobTimingStage = "graph"
|
||||
ProvisionerJobTimingStageApply ProvisionerJobTimingStage = "apply"
|
||||
)
|
||||
|
||||
func (e *ProvisionerJobTimingStage) Scan(src interface{}) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = ProvisionerJobTimingStage(s)
|
||||
case string:
|
||||
*e = ProvisionerJobTimingStage(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for ProvisionerJobTimingStage: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullProvisionerJobTimingStage struct {
|
||||
ProvisionerJobTimingStage ProvisionerJobTimingStage `json:"provisioner_job_timing_stage"`
|
||||
Valid bool `json:"valid"` // Valid is true if ProvisionerJobTimingStage is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullProvisionerJobTimingStage) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
ns.ProvisionerJobTimingStage, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.ProvisionerJobTimingStage.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullProvisionerJobTimingStage) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.ProvisionerJobTimingStage), nil
|
||||
}
|
||||
|
||||
func (e ProvisionerJobTimingStage) Valid() bool {
|
||||
switch e {
|
||||
case ProvisionerJobTimingStageInit,
|
||||
ProvisionerJobTimingStagePlan,
|
||||
ProvisionerJobTimingStageGraph,
|
||||
ProvisionerJobTimingStageApply:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func AllProvisionerJobTimingStageValues() []ProvisionerJobTimingStage {
|
||||
return []ProvisionerJobTimingStage{
|
||||
ProvisionerJobTimingStageInit,
|
||||
ProvisionerJobTimingStagePlan,
|
||||
ProvisionerJobTimingStageGraph,
|
||||
ProvisionerJobTimingStageApply,
|
||||
}
|
||||
}
|
||||
|
||||
type ProvisionerJobType string
|
||||
|
||||
const (
|
||||
@@ -2282,6 +2346,33 @@ type ProvisionerJobLog struct {
|
||||
ID int64 `db:"id" json:"id"`
|
||||
}
|
||||
|
||||
type ProvisionerJobStat struct {
|
||||
JobID uuid.UUID `db:"job_id" json:"job_id"`
|
||||
JobStatus ProvisionerJobStatus `db:"job_status" json:"job_status"`
|
||||
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
|
||||
WorkerID uuid.NullUUID `db:"worker_id" json:"worker_id"`
|
||||
Error sql.NullString `db:"error" json:"error"`
|
||||
ErrorCode sql.NullString `db:"error_code" json:"error_code"`
|
||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||
QueuedSecs float64 `db:"queued_secs" json:"queued_secs"`
|
||||
CompletionSecs float64 `db:"completion_secs" json:"completion_secs"`
|
||||
CanceledSecs float64 `db:"canceled_secs" json:"canceled_secs"`
|
||||
InitSecs float64 `db:"init_secs" json:"init_secs"`
|
||||
PlanSecs float64 `db:"plan_secs" json:"plan_secs"`
|
||||
GraphSecs float64 `db:"graph_secs" json:"graph_secs"`
|
||||
ApplySecs float64 `db:"apply_secs" json:"apply_secs"`
|
||||
}
|
||||
|
||||
type ProvisionerJobTiming struct {
|
||||
JobID uuid.UUID `db:"job_id" json:"job_id"`
|
||||
StartedAt time.Time `db:"started_at" json:"started_at"`
|
||||
EndedAt time.Time `db:"ended_at" json:"ended_at"`
|
||||
Stage ProvisionerJobTimingStage `db:"stage" json:"stage"`
|
||||
Source string `db:"source" json:"source"`
|
||||
Action string `db:"action" json:"action"`
|
||||
Resource string `db:"resource" json:"resource"`
|
||||
}
|
||||
|
||||
type ProvisionerKey struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
|
||||
@@ -357,6 +357,7 @@ type sqlcQuerier interface {
|
||||
InsertOrganizationMember(ctx context.Context, arg InsertOrganizationMemberParams) (OrganizationMember, error)
|
||||
InsertProvisionerJob(ctx context.Context, arg InsertProvisionerJobParams) (ProvisionerJob, error)
|
||||
InsertProvisionerJobLogs(ctx context.Context, arg InsertProvisionerJobLogsParams) ([]ProvisionerJobLog, error)
|
||||
InsertProvisionerJobTimings(ctx context.Context, arg InsertProvisionerJobTimingsParams) ([]ProvisionerJobTiming, error)
|
||||
InsertProvisionerKey(ctx context.Context, arg InsertProvisionerKeyParams) (ProvisionerKey, error)
|
||||
InsertReplica(ctx context.Context, arg InsertReplicaParams) (Replica, error)
|
||||
InsertTemplate(ctx context.Context, arg InsertTemplateParams) error
|
||||
|
||||
@@ -5545,6 +5545,68 @@ func (q *sqlQuerier) InsertProvisionerJob(ctx context.Context, arg InsertProvisi
|
||||
return i, err
|
||||
}
|
||||
|
||||
const insertProvisionerJobTimings = `-- name: InsertProvisionerJobTimings :many
|
||||
INSERT INTO provisioner_job_timings (job_id, started_at, ended_at, stage, source, action, resource)
|
||||
SELECT
|
||||
$1::uuid AS provisioner_job_id,
|
||||
unnest($2::timestamptz[]),
|
||||
unnest($3::timestamptz[]),
|
||||
unnest($4::provisioner_job_timing_stage[]),
|
||||
unnest($5::text[]),
|
||||
unnest($6::text[]),
|
||||
unnest($7::text[])
|
||||
RETURNING job_id, started_at, ended_at, stage, source, action, resource
|
||||
`
|
||||
|
||||
type InsertProvisionerJobTimingsParams struct {
|
||||
JobID uuid.UUID `db:"job_id" json:"job_id"`
|
||||
StartedAt []time.Time `db:"started_at" json:"started_at"`
|
||||
EndedAt []time.Time `db:"ended_at" json:"ended_at"`
|
||||
Stage []ProvisionerJobTimingStage `db:"stage" json:"stage"`
|
||||
Source []string `db:"source" json:"source"`
|
||||
Action []string `db:"action" json:"action"`
|
||||
Resource []string `db:"resource" json:"resource"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) InsertProvisionerJobTimings(ctx context.Context, arg InsertProvisionerJobTimingsParams) ([]ProvisionerJobTiming, error) {
|
||||
rows, err := q.db.QueryContext(ctx, insertProvisionerJobTimings,
|
||||
arg.JobID,
|
||||
pq.Array(arg.StartedAt),
|
||||
pq.Array(arg.EndedAt),
|
||||
pq.Array(arg.Stage),
|
||||
pq.Array(arg.Source),
|
||||
pq.Array(arg.Action),
|
||||
pq.Array(arg.Resource),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []ProvisionerJobTiming
|
||||
for rows.Next() {
|
||||
var i ProvisionerJobTiming
|
||||
if err := rows.Scan(
|
||||
&i.JobID,
|
||||
&i.StartedAt,
|
||||
&i.EndedAt,
|
||||
&i.Stage,
|
||||
&i.Source,
|
||||
&i.Action,
|
||||
&i.Resource,
|
||||
); 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 updateProvisionerJobByID = `-- name: UpdateProvisionerJobByID :exec
|
||||
UPDATE
|
||||
provisioner_jobs
|
||||
|
||||
@@ -144,3 +144,15 @@ WHERE
|
||||
updated_at < $1
|
||||
AND started_at IS NOT NULL
|
||||
AND completed_at IS NULL;
|
||||
|
||||
-- name: InsertProvisionerJobTimings :many
|
||||
INSERT INTO provisioner_job_timings (job_id, started_at, ended_at, stage, source, action, resource)
|
||||
SELECT
|
||||
@job_id::uuid AS provisioner_job_id,
|
||||
unnest(@started_at::timestamptz[]),
|
||||
unnest(@ended_at::timestamptz[]),
|
||||
unnest(@stage::provisioner_job_timing_stage[]),
|
||||
unnest(@source::text[]),
|
||||
unnest(@action::text[]),
|
||||
unnest(@resource::text[])
|
||||
RETURNING *;
|
||||
|
||||
@@ -73,6 +73,9 @@ sql:
|
||||
- column: "notification_messages.payload"
|
||||
go_type:
|
||||
type: "[]byte"
|
||||
- column: "provisioner_job_stats.*_secs"
|
||||
go_type:
|
||||
type: "float64"
|
||||
rename:
|
||||
group_member: GroupMemberTable
|
||||
group_members_expanded: GroupMember
|
||||
|
||||
Reference in New Issue
Block a user