Skip to content

Commit

Permalink
Add calculated endTime when serializing TaskExecutionSummary
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mehulkar committed Mar 24, 2023
1 parent 5b32049 commit bb3aae9
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 33 deletions.
8 changes: 4 additions & 4 deletions cli/integration_tests/basic_monorepo/run_summary.t
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
}
Expand Down
73 changes: 44 additions & 29 deletions cli/internal/runsummary/execution_summary.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package runsummary

import (
"encoding/json"
"fmt"
"os"
"sync"
Expand Down Expand Up @@ -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"`
Expand All @@ -99,23 +110,23 @@ 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,
}
}

// 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.
Expand All @@ -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)
Expand All @@ -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++
Expand All @@ -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
Expand Down

0 comments on commit bb3aae9

Please sign in to comment.