func analyzeJobsPerformance()

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
}