From bb3aae9139fd370bd0173c7eaf4d681a2ed3144e Mon Sep 17 00:00:00 2001 From: Mehul Kar Date: Thu, 23 Mar 2023 16:16:38 -0700 Subject: [PATCH] Add calculated endTime when serializing TaskExecutionSummary Adds a custom MarshalJSON method to get TaskExection to look the way we want it to look. This also allows us to make the TaskExecutionSummary struct itself a bit tighter with private fields, etc. This commit also: - renames some fields for readability - removes the Label field from TaskExecutionSummary as it isn't needed. I cross checked with `--profile` flag to make sure it wasn't somehow used by the chrometracing profile that is generated. --- .../basic_monorepo/run_summary.t | 8 +- cli/internal/runsummary/execution_summary.go | 73 +++++++++++-------- 2 files changed, 48 insertions(+), 33 deletions(-) diff --git a/cli/integration_tests/basic_monorepo/run_summary.t b/cli/integration_tests/basic_monorepo/run_summary.t index b30e40f8d0ab8..25cff222bc5d2 100644 --- a/cli/integration_tests/basic_monorepo/run_summary.t +++ b/cli/integration_tests/basic_monorepo/run_summary.t @@ -17,8 +17,8 @@ Setup $ cat $(/bin/ls .turbo/runs/*.json | head -n1) | jq '.tasks | map(select(.taskId == "my-app#build")) | .[0].execution' { - "start": "[0-9-:\.TZ]+", (re) - "duration": [0-9]+, (re) + "startTime": [0-9]+, (re) + "endTime": [0-9]+, (re) "status": "built", "error": null } @@ -29,8 +29,8 @@ Setup $ cat $(/bin/ls .turbo/runs/*.json | head -n1) | jq '.tasks | map(select(.taskId == "util#build")) | .[0].execution' { - "start": "[0-9-:\.TZ]+", (re) - "duration": [0-9]+, (re) + "startTime": [0-9]+, (re) + "endTime": [0-9]+, (re) "status": "built", "error": null } diff --git a/cli/internal/runsummary/execution_summary.go b/cli/internal/runsummary/execution_summary.go index 588c19b4d4825..bad07c3082cd2 100644 --- a/cli/internal/runsummary/execution_summary.go +++ b/cli/internal/runsummary/execution_summary.go @@ -1,6 +1,7 @@ package runsummary import ( + "encoding/json" "fmt" "os" "sync" @@ -59,25 +60,35 @@ func (en executionEventName) toString() string { // TaskExecutionSummary contains data about the state of a single task in a turbo run. // Some fields are updated over time as the task prepares to execute and finishes execution. type TaskExecutionSummary struct { - StartAt time.Time `json:"start"` - - Duration time.Duration `json:"duration"` - - // Target which has just changed - Label string `json:"-"` + startAt time.Time // set once + status executionEventName // current status, updated during execution + err error // only populated for failure statuses + duration time.Duration // updated during the task execution +} - // Its current status - Status string `json:"status"` +// MarshalJSON munges the TaskExecutionSummary into a format we want +// We'll use an anonmyous, private struct for this, so it's not confusingly duplicated +func (ts *TaskExecutionSummary) MarshalJSON() ([]byte, error) { + serializable := struct { + Start int64 `json:"startTime"` + End int64 `json:"endTime"` + Status string `json:"status"` + Err error `json:"error"` + }{ + Start: ts.startAt.UnixMilli(), + End: ts.startAt.Add(ts.duration).UnixMilli(), + Status: ts.status.toString(), + Err: ts.err, + } - // Error, only populated for failure statuses - Err error `json:"error"` + return json.Marshal(&serializable) } // executionSummary is the state of the entire `turbo run`. Individual task state in `Tasks` field type executionSummary struct { // mu guards reads/writes to the `state` field mu sync.Mutex `json:"-"` - state map[string]*TaskExecutionSummary `json:"-"` // key is a taskID + tasks map[string]*TaskExecutionSummary `json:"-"` // key is a taskID Success int `json:"success"` Failure int `json:"failed"` Cached int `json:"cached"` @@ -99,7 +110,7 @@ func newExecutionSummary(start time.Time, tracingProfile string) *executionSumma Failure: 0, Cached: 0, Attempted: 0, - state: make(map[string]*TaskExecutionSummary), + tasks: make(map[string]*TaskExecutionSummary), startedAt: start, profileFilename: tracingProfile, } @@ -107,15 +118,15 @@ func newExecutionSummary(start time.Time, tracingProfile string) *executionSumma // Run starts the Execution of a single task. It returns a function that can // be used to update the state of a given taskID with the executionEventName enum -func (es *executionSummary) run(label string) (func(outcome executionEventName, err error), *TaskExecutionSummary) { +func (es *executionSummary) run(taskID string) (func(outcome executionEventName, err error), *TaskExecutionSummary) { start := time.Now() taskExecutionSummary := es.add(&executionEvent{ Time: start, - Label: label, + Label: taskID, Status: targetBuilding, }) - tracer := chrometracing.Event(label) + tracer := chrometracing.Event(taskID) // This function can be called with an enum and an optional error to update // the state of a given taskID. @@ -125,11 +136,11 @@ func (es *executionSummary) run(label string) (func(outcome executionEventName, result := &executionEvent{ Time: now, Duration: now.Sub(start), - Label: label, + Label: taskID, Status: outcome, } if err != nil { - result.Err = fmt.Errorf("running %v failed: %w", label, err) + result.Err = fmt.Errorf("running %v failed: %w", taskID, err) } // Ignore the return value here es.add(result) @@ -141,19 +152,23 @@ func (es *executionSummary) run(label string) (func(outcome executionEventName, func (es *executionSummary) add(event *executionEvent) *TaskExecutionSummary { es.mu.Lock() defer es.mu.Unlock() - if s, ok := es.state[event.Label]; ok { - s.Status = event.Status.toString() - s.Err = event.Err - s.Duration = event.Duration + + var taskExecSummary *TaskExecutionSummary + if ts, ok := es.tasks[event.Label]; ok { + // If we already know about this task, we'll update it with the new event + taskExecSummary = ts } else { - es.state[event.Label] = &TaskExecutionSummary{ - StartAt: event.Time, - Label: event.Label, - Status: event.Status.toString(), - Err: event.Err, - Duration: event.Duration, - } + // If we don't know about it yet, init and add it into the parent struct + // (event.Status should always be `targetBuilding` here.) + taskExecSummary = &TaskExecutionSummary{startAt: event.Time} + es.tasks[event.Label] = taskExecSummary } + + // Update the Status, Duration, and Err fields + taskExecSummary.status = event.Status + taskExecSummary.err = event.Err + taskExecSummary.duration = event.Duration + switch { case event.Status == TargetBuildFailed: es.Failure++ @@ -166,7 +181,7 @@ func (es *executionSummary) add(event *executionEvent) *TaskExecutionSummary { es.Attempted++ } - return es.state[event.Label] + return es.tasks[event.Label] } // writeChromeTracing writes to a profile name if the `--profile` flag was passed to turbo run