-
Notifications
You must be signed in to change notification settings - Fork 943
feat: add provisioning timings to understand slow build times #14274
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
7e36010
Initial implementation
dannykopping 45b7fb4
More hacking, now including a job-wide view of timings
dannykopping 38c4197
API
dannykopping 803a9d4
Smol refactor
dannykopping 973ec6d
Capture dependency graph timings
dannykopping a070e07
Expand hash to include span category so multiple operations on the sa…
dannykopping 4a29b96
Tests
dannykopping 275bfca
lint/fmt
dannykopping 73bac3f
Moar tests
dannykopping 3d77c63
Improve coverage
dannykopping c0ae1ba
Remove stats API call, will follow up in another PR
dannykopping 28fa2f7
Fixing tests
dannykopping 68b16ff
Use max(end)-min(start) as stage timings, not local maximum
dannykopping 6f0b8f8
make fmt
dannykopping 724f139
Minor fix-ups
dannykopping c30a900
Pls god let this work
dannykopping 0d68e69
Move terraform test helpers into internal package
dannykopping 15282bb
Review comments
dannykopping 82ca13e
Merge branch 'main' of github.com:coder/coder into dk/provision-detai…
dannykopping 597ec85
More CI happiness
dannykopping 805c0f2
Restrict timings tests to non-Windows
dannykopping 46f3318
Give CI exactly what it wants FFS (see https://github.com/coder/coder…
dannykopping ebbaf31
@mtojek you legend :)
dannykopping eb5ec5c
Merge branch 'main' of https://github.com/coder/coder into dk/provisi…
dannykopping File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Moar tests
Signed-off-by: Danny Kopping <danny@coder.com>
- Loading branch information
commit 73bac3f4f020432f38a158ca81d9b593aa1844ad
There are no files selected for viewing
File renamed without changes.
File renamed without changes.
148 changes: 148 additions & 0 deletions
148
provisioner/terraform/testdata/timings-aggregation/fake-terraform.sh
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
package terraform | ||
dannykopping marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
import ( | ||
"bufio" | ||
"bytes" | ||
"slices" | ||
"testing" | ||
|
||
"github.com/cespare/xxhash" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"google.golang.org/protobuf/encoding/protojson" | ||
protobuf "google.golang.org/protobuf/proto" | ||
|
||
"github.com/coder/coder/v2/provisionersdk/proto" | ||
) | ||
|
||
func ParseTimingLines(t *testing.T, input []byte) []*proto.Timing { | ||
t.Helper() | ||
|
||
// Parse the input into *proto.Timing structs. | ||
var expected []*proto.Timing | ||
scanner := bufio.NewScanner(bytes.NewBuffer(input)) | ||
for scanner.Scan() { | ||
line := scanner.Bytes() | ||
|
||
var msg proto.Timing | ||
require.NoError(t, protojson.Unmarshal(line, &msg)) | ||
|
||
expected = append(expected, &msg) | ||
} | ||
require.NoError(t, scanner.Err()) | ||
StableSortTimings(t, expected) // To reduce flakiness. | ||
|
||
return expected | ||
} | ||
|
||
func TimingsAreEqual(t *testing.T, expected []*proto.Timing, actual []*proto.Timing) bool { | ||
t.Helper() | ||
|
||
// Shortcut check. | ||
if len(expected)+len(actual) == 0 { | ||
t.Logf("both timings are empty") | ||
return true | ||
} | ||
|
||
// Shortcut check. | ||
if len(expected) != len(actual) { | ||
t.Logf("timings lengths are not equal: %d != %d", len(expected), len(actual)) | ||
return false | ||
} | ||
|
||
// Compare each element; both are expected to be sorted in a stable manner. | ||
for i := 0; i < len(expected); i++ { | ||
ex := expected[i] | ||
ac := actual[i] | ||
if !protobuf.Equal(ex, ac) { | ||
t.Logf("timings are not equivalent: %q != %q", ex.String(), ac.String()) | ||
return false | ||
} | ||
} | ||
|
||
return true | ||
} | ||
|
||
func IngestAllSpans(t *testing.T, input []byte, aggregator *timingAggregator) { | ||
t.Helper() | ||
|
||
scanner := bufio.NewScanner(bytes.NewBuffer(input)) | ||
for scanner.Scan() { | ||
line := scanner.Bytes() | ||
log := parseTerraformLogLine(line) | ||
if log == nil { | ||
continue | ||
} | ||
|
||
ts, span, err := extractTimingSpan(log) | ||
if err != nil { | ||
// t.Logf("%s: failed span extraction on line: %q", err, line) | ||
continue | ||
} | ||
|
||
require.NotZerof(t, ts, "failed on line: %q", line) | ||
require.NotNilf(t, span, "failed on line: %q", line) | ||
|
||
aggregator.ingest(ts, span) | ||
} | ||
|
||
require.NoError(t, scanner.Err()) | ||
} | ||
|
||
func PrintTiming(t *testing.T, timing *proto.Timing) { | ||
t.Helper() | ||
|
||
marshaler := protojson.MarshalOptions{ | ||
Multiline: false, // Ensure it's set to false for single-line JSON | ||
Indent: "", // No indentation | ||
} | ||
|
||
out, err := marshaler.Marshal(timing) | ||
assert.NoError(t, err) | ||
t.Logf("%s", out) | ||
} | ||
|
||
func StableSortTimings(t *testing.T, timings []*proto.Timing) { | ||
t.Helper() | ||
|
||
slices.SortStableFunc(timings, func(a, b *proto.Timing) int { | ||
if a == nil || b == nil || a.Start == nil || b.Start == nil { | ||
return 0 | ||
} | ||
|
||
if a.Start.AsTime().Equal(b.Start.AsTime()) { | ||
// Special case: when start times are equal, we need to keep the ordering stable, so we hash both entries | ||
// and sort based on that (since end times could be equal too, in principle). | ||
ah := xxhash.Sum64String(a.String()) | ||
bh := xxhash.Sum64String(b.String()) | ||
|
||
if ah == bh { | ||
// WTF. | ||
t.Logf("identical timings detected!") | ||
PrintTiming(t, a) | ||
PrintTiming(t, b) | ||
return 0 | ||
} | ||
|
||
if ah < bh { | ||
return -1 | ||
} | ||
|
||
return 1 | ||
} | ||
|
||
if a.Start.AsTime().Before(b.Start.AsTime()) { | ||
return -1 | ||
} | ||
|
||
return 1 | ||
}) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.