Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions coderd/database/dbgen/dbgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -1615,6 +1615,7 @@ func provisionerJobTiming(t testing.TB, db database.Store, seed database.Provisi
StartedAt: []time.Time{takeFirst(seed.StartedAt, dbtime.Now())},
EndedAt: []time.Time{takeFirst(seed.EndedAt, dbtime.Now())},
Stage: []database.ProvisionerJobTimingStage{takeFirst(seed.Stage, database.ProvisionerJobTimingStageInit)},
StageSeq: []int32{seed.StageSeq},
Source: []string{takeFirst(seed.Source, "source")},
Action: []string{takeFirst(seed.Action, "action")},
Resource: []string{takeFirst(seed.Resource, "resource")},
Expand Down
5 changes: 4 additions & 1 deletion coderd/database/dump.sql

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ALTER TABLE ONLY
provisioner_job_timings
DROP COLUMN IF EXISTS stage_seq;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ALTER TABLE ONLY
provisioner_job_timings
ADD COLUMN stage_seq integer NOT NULL DEFAULT 0;

COMMENT ON COLUMN
provisioner_job_timings.stage_seq IS
'Distinguish repeated runs of the same stage within a single build job.';
2 changes: 2 additions & 0 deletions coderd/database/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 9 additions & 4 deletions coderd/database/queries.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions coderd/database/queries/provisionerjobs.sql
Original file line number Diff line number Diff line change
Expand Up @@ -329,15 +329,16 @@ ORDER BY random()
LIMIT @max_jobs;

-- name: InsertProvisionerJobTimings :many
INSERT INTO provisioner_job_timings (job_id, started_at, ended_at, stage, source, action, resource)
INSERT INTO provisioner_job_timings (job_id, started_at, ended_at, stage, source, action, resource, stage_seq)
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[])
unnest(@resource::text[]),
unnest(@stage_seq::integer[])
RETURNING *;

-- name: GetProvisionerJobTimingsByJobID :many
Expand Down
1 change: 1 addition & 0 deletions coderd/provisionerdserver/provisionerdserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -2182,6 +2182,7 @@ func (s *server) completeWorkspaceBuildJob(ctx context.Context, job database.Pro
}

params.Stage = append(params.Stage, stg)
params.StageSeq = append(params.StageSeq, t.StageSeq)
params.Source = append(params.Source, t.Source)
params.Resource = append(params.Resource, t.Resource)
params.Action = append(params.Action, t.Action)
Expand Down
15 changes: 8 additions & 7 deletions coderd/workspacebuilds.go
Original file line number Diff line number Diff line change
Expand Up @@ -1295,13 +1295,14 @@ func (api *API) buildTimings(ctx context.Context, build database.WorkspaceBuild)
}

res.ProvisionerTimings = append(res.ProvisionerTimings, codersdk.ProvisionerTiming{
JobID: t.JobID,
Stage: codersdk.TimingStage(t.Stage),
Source: t.Source,
Action: t.Action,
Resource: t.Resource,
StartedAt: t.StartedAt,
EndedAt: t.EndedAt,
JobID: t.JobID,
Stage: codersdk.TimingStage(t.Stage),
StageSequence: t.StageSeq,
Source: t.Source,
Action: t.Action,
Resource: t.Resource,
StartedAt: t.StartedAt,
EndedAt: t.EndedAt,
})
}
for _, t := range agentScriptTimings {
Expand Down
15 changes: 8 additions & 7 deletions codersdk/workspacebuilds.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,13 +231,14 @@ const (
)

type ProvisionerTiming struct {
JobID uuid.UUID `json:"job_id" format:"uuid"`
StartedAt time.Time `json:"started_at" format:"date-time"`
EndedAt time.Time `json:"ended_at" format:"date-time"`
Stage TimingStage `json:"stage"`
Source string `json:"source"`
Action string `json:"action"`
Resource string `json:"resource"`
JobID uuid.UUID `json:"job_id" format:"uuid"`
StartedAt time.Time `json:"started_at" format:"date-time"`
EndedAt time.Time `json:"ended_at" format:"date-time"`
Stage TimingStage `json:"stage"`
StageSequence int32 `json:"stage_sequence"`
Source string `json:"source"`
Action string `json:"action"`
Resource string `json:"resource"`
}

type AgentScriptTiming struct {
Expand Down
18 changes: 9 additions & 9 deletions provisioner/terraform/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,29 +325,25 @@ func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr l
<-doneErr
}()

endStage := e.timings.startStage(database.ProvisionerJobTimingStagePlan)
endStage := e.timings.startStage(database.ProvisionerJobTimingStagePlan, 0)
err := e.execWriteOutput(ctx, killCtx, args, env, outWriter, errWriter)
endStage(err)
if err != nil {
return nil, xerrors.Errorf("terraform plan: %w", err)
}

// Capture the duration of the call to `terraform graph`.
graphTimings := newTimingAggregator(database.ProvisionerJobTimingStageGraph)
graphTimings.ingest(createGraphTimingsEvent(timingGraphStart))

endGraph := e.timings.startStage(database.ProvisionerJobTimingStageGraph, 0)
state, plan, err := e.planResources(ctx, killCtx, planfilePath)
endGraph(err)
if err != nil {
graphTimings.ingest(createGraphTimingsEvent(timingGraphErrored))
return nil, xerrors.Errorf("plan resources: %w", err)
}
planJSON, err := json.Marshal(plan)
if err != nil {
return nil, xerrors.Errorf("marshal plan: %w", err)
}

graphTimings.ingest(createGraphTimingsEvent(timingGraphComplete))

var moduleFiles []byte
// Skipping modules archiving is useful if the caller does not need it, eg during
// a workspace build. This removes some added costs of sending the modules
Expand Down Expand Up @@ -390,7 +386,7 @@ func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr l
Parameters: state.Parameters,
Resources: state.Resources,
ExternalAuthProviders: state.ExternalAuthProviders,
Timings: append(e.timings.aggregate(), graphTimings.aggregate()...),
Timings: e.timings.aggregate(),
Presets: state.Presets,
Plan: planJSON,
ResourceReplacements: resReps,
Expand Down Expand Up @@ -599,18 +595,22 @@ func (e *executor) apply(
}()

// `terraform apply`
endStage := e.timings.startStage(database.ProvisionerJobTimingStageApply)
endStage := e.timings.startStage(database.ProvisionerJobTimingStageApply, 0)
err := e.execWriteOutput(ctx, killCtx, args, env, outWriter, errWriter)
endStage(err)
if err != nil {
return nil, xerrors.Errorf("terraform apply: %w", err)
}

// `terraform show` & `terraform graph`
// The sequence number is `1` as `graph` has already been called during `plan`. (the 0th stage)
endGraph := e.timings.startStage(database.ProvisionerJobTimingStageGraph, 1)
state, err := e.stateResources(ctx, killCtx)
endGraph(err)
if err != nil {
return nil, err
}

statefilePath := e.files.StateFilePath()
stateContent, err := os.ReadFile(statefilePath)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions provisioner/terraform/provision.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ func (s *server) Plan(

// The JSON output of `terraform init` doesn't include discrete fields for capturing timings of each plugin,
// so we capture the whole init process.
initTimings := newTimingAggregator(database.ProvisionerJobTimingStageInit)
endStage := initTimings.startStage(database.ProvisionerJobTimingStageInit)
initTimings := newTimingAggregator(database.ProvisionerJobTimingStageInit, 0)
endStage := initTimings.startStage(database.ProvisionerJobTimingStageInit, 0)

err = e.init(ctx, killCtx, sess)
endStage(err)
Expand Down
2 changes: 1 addition & 1 deletion provisioner/terraform/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,6 @@ func (s *server) executor(files tfpath.Layouter, stage database.ProvisionerJobTi
cliConfigPath: s.cliConfigPath,
files: files,
logger: s.logger.Named("executor"),
timings: newTimingAggregator(stage),
timings: newTimingAggregator(stage, 0),
}
}
22 changes: 16 additions & 6 deletions provisioner/terraform/timings.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ const (
)

type timingAggregator struct {
stage database.ProvisionerJobTimingStage
stage database.ProvisionerJobTimingStage
stageSequence int32

// Protects the stateLookup map.
lookupMu sync.Mutex
Expand All @@ -88,16 +89,18 @@ type timingSpan struct {
messageCode initMessageCode
start, end time.Time
stage database.ProvisionerJobTimingStage
stageSeq int32
action, provider, resource string
state proto.TimingState
}

// newTimingAggregator creates a new aggregator which measures the duration of resource init/plan/apply actions; stage
// represents the stage of provisioning the timings are occurring within.
func newTimingAggregator(stage database.ProvisionerJobTimingStage) *timingAggregator {
func newTimingAggregator(stage database.ProvisionerJobTimingStage, seq int32) *timingAggregator {
return &timingAggregator{
stage: stage,
stateLookup: make(map[uint64]*timingSpan),
stage: stage,
stageSequence: seq,
stateLookup: make(map[uint64]*timingSpan),
}
}

Expand All @@ -109,7 +112,12 @@ func (t *timingAggregator) ingest(ts time.Time, s *timingSpan) {
return
}

s.stage = t.stage
// Only set the stage if it hasn't already been set on the span.
// Explicitly set stage takes precedence.
if s.stage == "" {
s.stage = t.stage
s.stageSeq = t.stageSequence
}
ts = dbtime.Time(ts.UTC())

switch s.kind {
Expand Down Expand Up @@ -184,13 +192,14 @@ func (t *timingAggregator) aggregate() []*proto.Timing {
// should be called to mark the end of the stage. This is used to measure a
// stage's total duration across all it's discrete events and unmeasured
// overhead/events.
func (t *timingAggregator) startStage(stage database.ProvisionerJobTimingStage) (end func(err error)) {
func (t *timingAggregator) startStage(stage database.ProvisionerJobTimingStage, seq int32) (end func(err error)) {
ts := timingSpan{
kind: timingStageStart,
stage: stage,
resource: "coder_stage_" + string(stage),
action: "terraform",
provider: "coder",
stageSeq: seq,
}
endTs := ts
t.ingest(dbtime.Now(), &ts)
Expand Down Expand Up @@ -279,6 +288,7 @@ func (e *timingSpan) toProto() *proto.Timing {
Source: e.provider,
Resource: e.resource,
State: e.state,
StageSeq: e.stageSeq,
}
}

Expand Down
2 changes: 1 addition & 1 deletion provisioner/terraform/timings_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
require.Truef(t, stage.Valid(), "%q is not a valid stage name; acceptable values: %v",
file.Name, database.AllProvisionerJobTimingStageValues())

agg := newTimingAggregator(stage)
agg := newTimingAggregator(stage, 0)
ingestAllSpans(t, file.Data, agg)
actualTimings = append(actualTimings, agg.aggregate()...)
}
Expand Down Expand Up @@ -164,7 +164,7 @@
stateLookup: make(map[uint64]*timingSpan),
}

end := agg.startStage(database.ProvisionerJobTimingStageApply)

Check failure on line 167 in provisioner/terraform/timings_internal_test.go

View workflow job for this annotation

GitHub Actions / gen

not enough arguments in call to agg.startStage

Check failure on line 167 in provisioner/terraform/timings_internal_test.go

View workflow job for this annotation

GitHub Actions / test-go-pg-17

not enough arguments in call to agg.startStage

Check failure on line 167 in provisioner/terraform/timings_internal_test.go

View workflow job for this annotation

GitHub Actions / test-go-race-pg

not enough arguments in call to agg.startStage

Check failure on line 167 in provisioner/terraform/timings_internal_test.go

View workflow job for this annotation

GitHub Actions / lint

not enough arguments in call to agg.startStage

Check failure on line 167 in provisioner/terraform/timings_internal_test.go

View workflow job for this annotation

GitHub Actions / lint

not enough arguments in call to agg.startStage

Check failure on line 167 in provisioner/terraform/timings_internal_test.go

View workflow job for this annotation

GitHub Actions / test-go-pg (ubuntu-latest)

not enough arguments in call to agg.startStage

Check failure on line 167 in provisioner/terraform/timings_internal_test.go

View workflow job for this annotation

GitHub Actions / test-go-pg (ubuntu-latest)

not enough arguments in call to agg.startStage
end(nil)

evts := agg.aggregate()
Expand Down
2 changes: 1 addition & 1 deletion provisionerd/proto/provisionerd_drpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion provisionerd/proto/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,12 @@ import "github.com/coder/coder/v2/apiversion"
// - Added new field `template_version_id` to `provisioner.Metadata`
// - Added new field `exp_reuse_terraform_workspace` to `provisioner.Job.WorkspaceBuild`
// - Added fields `template_version_id`, `template_id`, and `exp_reuse_terraform_workspace` to `provisioner.Config`
//
// API v1.13:
// - Added new field `stage_seq` to `Timing` to distinguish repeated stages
const (
CurrentMajor = 1
CurrentMinor = 12
CurrentMinor = 13
)

// CurrentVersion is the current provisionerd API version.
Expand Down
Loading
Loading