in metrics/metrics/common.go [97:144]
func analyzeJobsPerformance(jobs []*buildkite.Job) (*jobsPerformance, error) {
events := make([]event, 0)
result := &jobsPerformance{passed: true}
for _, job := range jobs {
if job.State == nil || *job.State != "passed" {
result.passed = false
}
if !isFinishedWorkerTask(job) {
continue
}
if result.firstJobRunnableAt == nil || job.RunnableAt.Time.Before(result.firstJobRunnableAt.Time) {
result.firstJobRunnableAt = job.RunnableAt
}
// Job lifecycle: scheduled -> created -> runnable -> started -> finished
// wait time = started - runnable
// run time = finished - started
// total time = finished - runnable
// Scheduled and created are affected by "wait" steps, so we don't look at them here.
duration := getDifferenceSeconds(job.RunnableAt, job.FinishedAt)
if duration > result.longestRunningTaskRunSeconds {
result.longestRunningTaskName = *job.Name
result.longestRunningTaskRunSeconds = duration
}
}
sortFunc := func(i, j int) bool { return events[i].Time.Before(events[j].Time) }
sort.Slice(events, sortFunc)
runningTasks := 0
prevTime := result.firstJobRunnableAt
for _, evt := range events {
elapsed := getDifferenceSeconds(prevTime, evt.Timestamp)
if runningTasks == 0 {
result.totalWaitSeconds += elapsed
} else {
result.totalWaitSeconds += elapsed
}
runningTasks += evt.runDelta
prevTime = evt.Timestamp
}
if runningTasks > 0 {
return nil, fmt.Errorf("There are %d unfinished jobs", runningTasks)
}
return result, nil
}