-
Notifications
You must be signed in to change notification settings - Fork 130
/
critical_path.go
76 lines (65 loc) · 2.96 KB
/
critical_path.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package metrics
import (
"fmt"
"github.com/bazelbuild/continuous-integration/metrics/clients"
"github.com/bazelbuild/continuous-integration/metrics/data"
)
type CriticalPath struct {
client clients.BuildkiteClient
pipelines []*data.PipelineID
columns []Column
lastNBuilds int
debug bool
}
func (cp *CriticalPath) Name() string {
return "critical_path"
}
func (cp *CriticalPath) Columns() []Column {
return cp.columns
}
func (*CriticalPath) Type() MetricType {
return BuildBasedMetric
}
func (*CriticalPath) RelevantDelta() int {
return 200 // builds
}
func (cp *CriticalPath) Collect() (data.DataSet, error) {
result := data.CreateDataSet(GetColumnNames(cp.columns))
for _, pipeline := range cp.pipelines {
builds, err := cp.client.GetMostRecentBuilds(pipeline, cp.lastNBuilds)
if err != nil {
return nil, fmt.Errorf("Cannot collect critical path statistics for pipeline %s: %v", pipeline, err)
}
for _, build := range builds {
if build.FinishedAt == nil {
continue
}
// TODO(fweikert): remove
if cp.debug {
fmt.Printf("%v -> %v -> %v -> %v (%d)\n", *build.ScheduledAt, *build.CreatedAt, *build.StartedAt, *build.FinishedAt, *build.Number)
for _, job := range build.Jobs {
if job.Name != nil {
fmt.Printf("%v -> %v -> %v -> %v -> %v (%s)\n", *job.ScheduledAt, *job.CreatedAt, *job.RunnableAt, *job.StartedAt, *job.FinishedAt, *job.Name)
}
}
}
// Caveat: The Buildkite API only returns the latest invocation of each job within a given build,
// which causes a problem when a failing build is retried via the web UI. In this case the time
// between the original run and the retry will be counted as wait time by this code.
performance, err := analyzeJobsPerformance(build.Jobs)
if err != nil {
return nil, fmt.Errorf("Could not calculate job performance for build %d: %v", *build.Number, err)
}
err = result.AddRow(pipeline.Org, pipeline.Slug, *build.Number, performance.totalWaitSeconds, performance.totalRunSeconds, performance.longestRunningTaskName, performance.longestRunningTaskRunSeconds, *build.State)
if err != nil {
return nil, fmt.Errorf("Failed to add result for build %d: %v", *build.Number, err)
}
}
}
return result, nil
}
// CREATE TABLE critical_path (org VARCHAR(255), pipeline VARCHAR(255), build INT, wait_time_seconds FLOAT, run_time_seconds FLOAT, longest_task_name VARCHAR(255), longest_task_time_seconds FLOAT, result VARCHAR(255), PRIMARY KEY(org, pipeline, build));
func CreateCriticalPath(client clients.BuildkiteClient, lastNBuilds int, pipelines ...*data.PipelineID) *CriticalPath {
columns := []Column{Column{"org", true}, Column{"pipeline", true}, Column{"build", true}, Column{"wait_time_seconds", false}, Column{"run_time_seconds", false}, Column{"longest_task_name", false}, Column{"longest_task_time_seconds", false}, Column{"result", false}}
return &CriticalPath{client: client, pipelines: pipelines, columns: columns, lastNBuilds: lastNBuilds, debug: false}
}