Skip to content

Commit dbaabf1

Browse files
roachprod-microbench: post GitHub issues for performance regressions
Previously, performance regressions detected during the weekly microbenchmark comparison were only reported via Slack notifications. This made it difficult to track and ensure timely follow-up on regressions, as they were often discussed informally without formal issue tracking. This change extends the existing `--post-issues` flag to work with the compare command. When enabled, the system automatically creates GitHub issues for performance regressions that exceed 20% (the "red" regression threshold). Each issue includes: - Package name and list of regressed benchmarks - Regression percentages and formatted deltas - Link to the Google Sheet with detailed comparison data - Labels: O-microbench and C-performance for easy filtering The implementation reuses the same GitHub posting infrastructure and environment variables (GITHUB_BRANCH, GITHUB_SHA, GITHUB_BINARY) as the existing benchmark failure reporting. Issues are created per package to avoid spam, with up to 10 regressions listed in each issue summary. This change also renames `postBenchmarkIssue` to `postIssuesToGitHub` for consistency, as it now handles both execution failures and performance regressions. Epic: None Release note: None
1 parent 061d9a3 commit dbaabf1

File tree

5 files changed

+101
-3
lines changed

5 files changed

+101
-3
lines changed

build/teamcity/cockroach/nightlies/microbenchmark_weekly.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ if [ -d "$output_dir/experiment" ] && [ "$(ls -A "$output_dir/experiment")" ] \
207207
--sheet-desc="$sheet_description" \
208208
${influx_token:+--influx-token="$influx_token"} \
209209
${influx_host:+--influx-host="$influx_host"} \
210+
${TRIGGERED_BUILD:+--post-issues} \
210211
2>&1 | tee "$output_dir/sheets.txt"
211212
else
212213
echo "No microbenchmarks were run. Skipping comparison."

pkg/cmd/roachprod-microbench/compare.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/cockroachdb/cockroach/pkg/cmd/roachprod-microbench/google"
2121
"github.com/cockroachdb/cockroach/pkg/cmd/roachprod-microbench/model"
2222
"github.com/cockroachdb/cockroach/pkg/cmd/roachprod-microbench/util"
23+
"github.com/cockroachdb/cockroach/pkg/roachprod/logger"
2324
"github.com/cockroachdb/cockroach/pkg/util/timeutil"
2425
"github.com/cockroachdb/errors"
2526
"github.com/cockroachdb/errors/oserror"
@@ -37,6 +38,7 @@ type compareConfig struct {
3738
baselineDir string
3839
sheetDesc string
3940
threshold float64
41+
postIssues bool
4042
}
4143

4244
type slackConfig struct {
@@ -349,6 +351,52 @@ func (c *compare) compareUsingThreshold(comparisonResultsMap model.ComparisonRes
349351
return nil
350352
}
351353

354+
func (c *compare) postRegressionIssues(
355+
links map[string]string, comparisonResultsMap model.ComparisonResultsMap,
356+
) error {
357+
loggerCfg := logger.Config{Stdout: os.Stdout, Stderr: os.Stderr}
358+
l, err := loggerCfg.NewLogger("")
359+
if err != nil {
360+
return errors.Wrap(err, "failed to create logger for posting issues")
361+
}
362+
363+
for pkgName, comparisonResults := range comparisonResultsMap {
364+
var regressions []regressionInfo
365+
366+
for _, result := range comparisonResults {
367+
for _, detail := range result.Comparisons {
368+
comparison := detail.Comparison
369+
metric := result.Metric
370+
371+
// Check if this is a regression
372+
isRegression := (comparison.Delta < 0 && metric.Better > 0) ||
373+
(comparison.Delta > 0 && metric.Better < 0)
374+
375+
if isRegression && math.Abs(comparison.Delta) >= slackPercentageThreshold {
376+
regressions = append(regressions, regressionInfo{
377+
benchmarkName: detail.BenchmarkName,
378+
metricUnit: result.Metric.Unit,
379+
percentChange: math.Abs(comparison.Delta),
380+
formattedDelta: comparison.FormattedDelta,
381+
})
382+
}
383+
}
384+
}
385+
386+
if len(regressions) > 0 {
387+
sheetLink := links[pkgName]
388+
formatter, req := createRegressionPostRequest(pkgName, regressions, sheetLink, c.sheetDesc)
389+
err := postIssuesToGitHub(c.ctx, l, formatter, req)
390+
if err != nil {
391+
return errors.Wrapf(err, "failed to post regression issue for package %s", pkgName)
392+
}
393+
log.Printf("Posted regression issue for package: %s with %d regression(s)", pkgName, len(regressions))
394+
}
395+
}
396+
397+
return nil
398+
}
399+
352400
func (c *compare) pushToInfluxDB(comparisonResultsMap model.ComparisonResultsMap) error {
353401
client := influxdb2.NewClient(c.influxConfig.host, c.influxConfig.token)
354402
defer client.Close()

pkg/cmd/roachprod-microbench/executor.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ func (e *executor) executeBenchmarks() error {
460460
if e.postIssues && benchmarkResponse.key == e.postConfig.binary {
461461
artifactsDir := fmt.Sprintf("%s/%s", e.outputDir, benchmarkResponse.key)
462462
formatter, req := createBenchmarkPostRequest(artifactsDir, response, timeout)
463-
err = postBenchmarkIssue(context.Background(), e.log, formatter, req)
463+
err = postIssuesToGitHub(context.Background(), e.log, formatter, req)
464464
if err != nil {
465465
e.log.Errorf("Failed to post benchmark issue - %v", err)
466466
executorError = errors.CombineErrors(executorError, errors.Wrap(err, "failed to post benchmark issue"))

pkg/cmd/roachprod-microbench/github.go

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,50 @@ func createBenchmarkPostRequest(
4343
return formatter, req
4444
}
4545

46-
// postBenchmarkIssue posts a benchmark issue to github.
47-
func postBenchmarkIssue(
46+
// regressionInfo holds information about a single benchmark regression.
47+
type regressionInfo struct {
48+
benchmarkName string
49+
metricUnit string
50+
percentChange float64
51+
formattedDelta string
52+
}
53+
54+
// createRegressionPostRequest creates a post request for benchmark performance regressions.
55+
func createRegressionPostRequest(
56+
pkgName string, regressions []regressionInfo, sheetLink, description string,
57+
) (issues.IssueFormatter, issues.PostRequest) {
58+
// Build the regression summary message
59+
var sb strings.Builder
60+
sb.WriteString(fmt.Sprintf("Performance regressions detected in package %s\n\n", pkgName))
61+
sb.WriteString(fmt.Sprintf("Comparison: %s\n\n", description))
62+
sb.WriteString(fmt.Sprintf("Found %d benchmark(s) with regressions ≥20%%:\n\n", len(regressions)))
63+
64+
for i, reg := range regressions {
65+
if i >= 10 { // Limit to 10 regressions in the summary
66+
sb.WriteString(fmt.Sprintf("... and %d more regression(s)\n", len(regressions)-i))
67+
break
68+
}
69+
sb.WriteString(fmt.Sprintf("• %s (%s): %s (%.1f%%)\n",
70+
reg.benchmarkName, reg.metricUnit, reg.formattedDelta, reg.percentChange))
71+
}
72+
73+
sb.WriteString(fmt.Sprintf("\nDetailed comparison: %s\n", sheetLink))
74+
75+
// Create a failure object for the package with all regressions
76+
title := fmt.Sprintf("%s: performance regression", pkgName)
77+
f := githubpost.MicrobenchmarkFailure(
78+
pkgName,
79+
title,
80+
sb.String(),
81+
)
82+
83+
formatter, req := githubpost.DefaultFormatter(context.Background(), f)
84+
req.Labels = append(req.Labels, "O-microbench", "C-performance")
85+
return formatter, req
86+
}
87+
88+
// postIssuesToGitHub posts a benchmark issue to github.
89+
func postIssuesToGitHub(
4890
ctx context.Context, l *logger.Logger, formatter issues.IssueFormatter, req issues.PostRequest,
4991
) error {
5092
opts := issues.DefaultOptionsFromEnv()

pkg/cmd/roachprod-microbench/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,12 @@ func makeCompareCommand() *cobra.Command {
173173
return err
174174
}
175175
}
176+
if c.postIssues {
177+
err = c.postRegressionIssues(links, comparisonResult)
178+
if err != nil {
179+
return err
180+
}
181+
}
176182
}
177183

178184
if c.influxConfig.token != "" {
@@ -207,6 +213,7 @@ func makeCompareCommand() *cobra.Command {
207213
cmd.Flags().StringVar(&config.influxConfig.token, "influx-token", config.influxConfig.token, "pass an InfluxDB auth token to push the results to InfluxDB")
208214
cmd.Flags().StringToStringVar(&config.influxConfig.metadata, "influx-metadata", config.influxConfig.metadata, "pass metadata to add to the InfluxDB measurement")
209215
cmd.Flags().Float64Var(&config.threshold, "threshold", config.threshold, "threshold in percentage value for detecting perf regression ")
216+
cmd.Flags().BoolVar(&config.postIssues, "post-issues", config.postIssues, "post GitHub issues for performance regressions (requires env vars for github issues to be set)")
210217
return cmd
211218
}
212219

0 commit comments

Comments
 (0)