Skip to content

Optimize prow loader with bulk writer, COPY protocol, and GCS improvements#3768

Merged
openshift-merge-bot[bot] merged 2 commits into
openshift:mainfrom
mstaeble:prow-loader-bulk-writer
Jul 15, 2026
Merged

Optimize prow loader with bulk writer, COPY protocol, and GCS improvements#3768
openshift-merge-bot[bot] merged 2 commits into
openshift:mainfrom
mstaeble:prow-loader-bulk-writer

Conversation

@mstaeble

@mstaeble mstaeble commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Restructures the prow loader from 50 concurrent goroutines each doing their own DB work into a producer/consumer pipeline: 50 GCS fetch workers feed results through a channel to a single bulk writer that batches and commits atomically using PostgreSQL COPY protocol and temp tables.

  • Bulk writer pipeline: 50 GCS fetch workers -> channel -> single writer batching 100 job runs per transaction. ProwJobRun, annotations, pull requests, and tests all commit atomically per batch.
  • Anti-join pre-filtering: Identifies new job run IDs via temp table + LEFT JOIN before GCS fetching, eliminating redundant work on steady-state loads.
  • Server-side GCS filtering: Replaces client-side regex with MatchGlob for all FindAllMatches callers.
  • Pre-compiled regexes: Release config regexes compiled once at construction.
  • Batched ProwJob upserts: 100 per batch instead of per-row.
  • BQ query filter: Excludes pending/triggered jobs at the query level.
  • Removed locks and caches: Per-row mutexes and in-memory caches no longer needed.

Benchmarks (staging, release 4.19, 1h lookback)

Scenario Main Bulk-Writer Speedup
Cold load 108s 21s 5.1x
48h steady-state ~55s 43s 1.3x

Parity test confirmed: identical run IDs, test counts, and annotations between main and bulk-writer for the same BQ window.

Test plan

  • make lint passes
  • make test passes
  • make e2e passes
  • Parity test against staging DB (identical row counts and per-run test counts)
  • Cold load benchmark (48h, 10,895 jobs, 11m25s)
  • Steady-state benchmark (48h, 72 new jobs, 43s)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved discovery of job-run artifacts (events, interval files, JUnit reports, and cluster data) across storage paths.
    • More accurate behavior when events.json is missing.
    • Excluded non-final job states (pending/triggered) from imported results.
    • Strengthened suite import and test extraction for skipped, flaky, and nested suites.
  • Performance
    • Faster job-run ingestion via parallel fetching and batched persistence.
  • Tests
    • Added coverage for test-case extraction scenarios and suite handling.

@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Pipeline controller notification
This repo is configured to use the pipeline controller. Second-stage tests will be triggered either automatically or after lgtm label is added, depending on the repository configuration. The pipeline controller will automatically detect which contexts are required and will utilize /test Prow commands to trigger the second stage.

For optional jobs, comment /test ? to see a list of all defined jobs. To trigger manually all jobs from second stage use /pipeline required command.

This repository is configured in: automatic mode

@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jul 14, 2026
@openshift-ci

openshift-ci Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@mstaeble

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 6312d141-76bc-4a2f-95f8-09df8d3bf293

📥 Commits

Reviewing files that changed from the base of the PR and between 73b9cf4 and 724ab0a.

📒 Files selected for processing (2)
  • pkg/dataloader/prowloader/extract_test_cases_test.go
  • pkg/dataloader/prowloader/prow.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/dataloader/prowloader/extract_test_cases_test.go
  • pkg/dataloader/prowloader/prow.go

Walkthrough

The PR replaces regex-based GCS file discovery with glob queries, excludes pending and triggered BigQuery jobs, and restructures Prow ingestion into preprocessing, concurrent job-run fetching, and batched database persistence with expanded test coverage.

Changes

Prow ingestion and GCS discovery

Layer / File(s) Summary
Glob-based GCS discovery
pkg/dataloader/prowloader/gcs/gcs_jobrun.go, pkg/api/jobrunevents/..., pkg/api/jobrunintervals/..., pkg/variantregistry/ocp.go
GCS matching now uses context-aware glob queries that return flat object paths; event, interval, JUnit, and cluster-data callers process all matching paths.
Job preprocessing and eligibility
pkg/dataloader/prowloader/prow.go, pkg/dataloader/prowloader/bigqueryjobs.go, pkg/db/suites.go
Release patterns are compiled at initialization, non-terminal jobs are excluded, job definitions are bulk-upserted, existing runs are filtered, and suite importability uses set membership plus patterns.
Run assembly and test extraction
pkg/dataloader/prowloader/prow.go, pkg/dataloader/prowloader/extract_test_cases_test.go
Job-run results, annotations, pull requests, associations, and tests are assembled as typed rows; JUnit extraction preserves failure, skip, child-suite, and flake behavior.
Batch persistence
pkg/dataloader/prowloader/prow.go
Accumulated rows are copied into temporary tables and persisted with set-based transactional inserts and upserts.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant BigQuery
  participant ProwLoader
  participant GCSJobRun
  participant PostgreSQL
  BigQuery->>ProwLoader: Load eligible non-pending, non-triggered jobs
  ProwLoader->>PostgreSQL: Bulk upsert ProwJob definitions
  ProwLoader->>GCSJobRun: Fetch JUnit and job-run artifacts
  GCSJobRun-->>ProwLoader: Return jobRunResult rows
  ProwLoader->>PostgreSQL: Write job-run batches in a transaction
Loading

Suggested labels: approved, lgtm

Suggested reviewers: stbenjam, xueqzhan

🚥 Pre-merge checks | ✅ 18 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.27% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Test Coverage For New Features ⚠️ Warning Only extractTestCases and a pattern test exist; jobrunevents/jobrunintervals/gcs have no *_test.go files, and the prow batch/GCS helpers lack direct tests. Add unit tests for JobRunEvents/Intervals, GCSJobRun FindAllMatches/GetGCSJunitPaths/GetCombinedJUnitTestSuites, IsSuiteImportable, and the prow batch pipeline helpers.
Single Responsibility And Clear Naming ⚠️ Warning ProwLoader has ~21 fields and Load() spans syncing, fetching, partitions, prefetching, preprocessing, concurrent ingest, batch writes, and metrics. Split the loader into smaller services (discovery, GCS fetch, persistence, metrics/partitions) and keep the orchestrator thin.
✅ Passed checks (18 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: prow loader optimization with bulk writing, COPY usage, and GCS-related improvements.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Go Error Handling ✅ Passed Touched code wraps errors with context, avoids panic, and guards nil clients/refs; the lone _ = row.Scan(...) is explicitly justified.
Sql Injection Prevention ✅ Passed All added DB/BigQuery queries use placeholders or static SQL; temp-table names are fixed/validated, and no user input is interpolated into query values.
Excessive Css In React Should Use Styles ✅ Passed No React components or inline CSS were changed; the PR only touches Go backend files, so the styling rule is not applicable.
Feature Documentation ✅ Passed No docs/features doc covers the prow-loader/GCS changes, and no feature docs were modified; the only feature doc is unrelated job-analysis symptoms.
Stable And Deterministic Test Names ✅ Passed The new test file uses only static table-driven names; no Ginkgo titles or generated identifiers appear in the changed code.
Test Structure And Quality ✅ Passed The only new test is a plain table-driven Go unit test, not Ginkgo; it has no cluster ops, Eventually calls, or resource cleanup concerns.
Microshift Test Compatibility ✅ Passed No new Ginkgo/e2e specs were added; the only new test is a plain Go unit test with no MicroShift-unsupported APIs or guards needed.
Single Node Openshift (Sno) Test Compatibility ✅ Passed No new Ginkgo e2e tests were added; the only new test is a plain testing unit test, and changed files contain no SNO-sensitive It/Describe/Context/When blocks.
Topology-Aware Scheduling Compatibility ✅ Passed No manifests/controllers were changed, and the touched Go files add no topology, affinity, selector, or PDB scheduling constraints.
Ote Binary Stdout Contract ✅ Passed Touched files add no main/init/TestMain/BeforeSuite stdout writes; searches found no fmt.Print/log.SetOutput/klog in changed code.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No new Ginkgo e2e tests were added; the only new test is a plain Go unit test and no IPv4/external-network assumptions were found.
No-Weak-Crypto ✅ Passed Touched files add no weak-crypto APIs or secret comparisons; diff scan of added lines found none.
Container-Privileges ✅ Passed PR only changes Go code; no container/K8s manifests or privilege settings (privileged, hostPID/Network/IPC, SYS_ADMIN, allowPrivilegeEscalation) were modified.
No-Sensitive-Data-In-Logs ✅ Passed Touched logs only emit job/run IDs, GCS paths, PR metadata, and variant metadata; no passwords, tokens, PII, session IDs, or customer data found.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/dataloader/prowloader/prow.go (1)

163-172: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Silent cache-population failure: loadProwJobCache drops the GORM query error entirely.

dbc.DB.Model(&models.ProwJob{}).Find(&allJobs)'s returned *gorm.DB (and its .Error) is never captured. Previously this ran once at startup; now preprocessProwJobs re-runs it every Load() cycle and pl.prowJobCache becomes the sole source every one of the 50 concurrent fetchJobRunResult workers relies on (dbProwJob, ok := pl.prowJobCache[pj.Spec.Job]). If this query transiently fails, the cache silently becomes empty, every job run is silently skipped with only a per-job warning log, and pl.errors never reflects the true root cause.

As per path instructions, **/*.go: "do not ignore returned errors with _ without clear justification". Based on learnings, one-off context propagation isn't required here — just capture and propagate the existing error.

🐛 Proposed fix
-func loadProwJobCache(dbc *db.DB) map[string]*models.ProwJob {
+func loadProwJobCache(dbc *db.DB) (map[string]*models.ProwJob, error) {
 	prowJobCache := map[string]*models.ProwJob{}
 	var allJobs []*models.ProwJob
-	dbc.DB.Model(&models.ProwJob{}).Find(&allJobs)
+	if err := dbc.DB.Model(&models.ProwJob{}).Find(&allJobs).Error; err != nil {
+		return nil, fmt.Errorf("loading prow job cache: %w", err)
+	}
 	for _, j := range allJobs {
 		prowJobCache[j.Name] = j
 	}
 	log.Infof("job cache created with %d entries from database", len(prowJobCache))
-	return prowJobCache
+	return prowJobCache, nil
 }
-	pl.prowJobCache = loadProwJobCache(pl.dbc)
-	return entries, nil
+	cache, err := loadProwJobCache(pl.dbc)
+	if err != nil {
+		return nil, fmt.Errorf("reloading prow job cache: %w", err)
+	}
+	pl.prowJobCache = cache
+	return entries, nil

Also applies to: 709-711

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/dataloader/prowloader/prow.go` around lines 163 - 172, Update
loadProwJobCache to capture the *gorm.DB result from the Find query and
propagate its Error instead of returning only a cache map. Adjust callers such
as preprocessProwJobs to handle the returned error and record it in the existing
pl.errors flow, while preserving successful cache population and logging
behavior.

Source: Path instructions

🧹 Nitpick comments (4)
pkg/db/suites.go (1)

88-106: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Consider a sets.Set[string] for the explicit-list membership check.

IsSuiteImportable linearly scans testSuites (~40 entries) on every call; this runs at least twice per job run across a 50-way-concurrent batch loader. A package-level sets.Set[string] built once from testSuites would make this O(1) and aligns with the "use sets.Set for unique-string collections" coding guideline already applied elsewhere in this PR (e.g. releaseSet in prow.go).

♻️ Proposed fix
+var testSuiteSet = sets.New(testSuites...)
+
 func IsSuiteImportable(name string) bool {
-	// Check explicit list
-	for _, s := range testSuites {
-		if s == name {
-			return true
-		}
-	}
+	if testSuiteSet.Has(name) {
+		return true
+	}
 
 	// Check patterns
 	for _, re := range testSuitePatterns {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/db/suites.go` around lines 88 - 106, Update IsSuiteImportable to use a
package-level sets.Set[string] initialized once from testSuites for explicit
suite membership checks, replacing the per-call linear scan; retain the existing
testSuitePatterns matching and return behavior unchanged.

Source: Coding guidelines

pkg/dataloader/prowloader/prow.go (3)

293-310: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Per-job-run Infof on a 50-way-concurrent hot path may add meaningful log volume/overhead at scale.

log.Infof("%d of %d job runs processed", ...) fires once per job run across up to 50 concurrent workers. For a cold load processing tens of thousands of runs, this is a very high volume of INFO logs competing for the shared logger/stdout, working against this PR's stated latency goals. Consider throttling (e.g., log every N runs or on a timer) instead of per-item.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/dataloader/prowloader/prow.go` around lines 293 - 310, Throttle the
progress logging in the worker loop around jobsImportedCount so it does not emit
an Infof for every job run. Log progress only at a suitable interval or count
threshold while preserving the existing counter updates and final processing
behavior.

996-1013: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Redundant BuildID parsing across preprocessProwJobs and fetchJobRunResult.

strconv.ParseUint(pj.Status.BuildID, 0, 64) is computed in preprocessProwJobs (line 655) to build candidate.id/candidateIDs, but that value is discarded — entries only carries *prow.ProwJob. fetchJobRunResult then re-parses the same string for every entry. As per path instructions, pkg/**/*.go: "Avoid calling the same utility function multiple times with identical arguments in the same code path." Threading the parsed ID through entries avoids the duplicate parse.

Also applies to: 655-661

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/dataloader/prowloader/prow.go` around lines 996 - 1013, Thread the parsed
BuildID from preprocessProwJobs into each entry instead of discarding
candidate.id, preserving it alongside the *prow.ProwJob. Update
fetchJobRunResult and its callers to accept and use the parsed uint64 value,
removing the redundant strconv.ParseUint call while retaining invalid-ID
handling in preprocessProwJobs.

Source: Path instructions


300-300: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer structured fields for job/build IDs here, per coding guideline.

log.WithError(err).Warningf("couldn't fetch job %s/%s, continuing", pj.Spec.Job, pj.Status.BuildID) formats the job name and build ID into the message string. As per coding guidelines: "Prefer structured logging where it makes sense, particularly for names and IDs; often a log.WithField() call is preferred over formatting values into a string."

♻️ Proposed fix
-				fetchErrsCh <- err
-				log.WithError(err).Warningf("couldn't fetch job %s/%s, continuing", pj.Spec.Job, pj.Status.BuildID)
+				fetchErrsCh <- err
+				log.WithError(err).WithField("job", pj.Spec.Job).WithField("buildID", pj.Status.BuildID).
+					Warning("couldn't fetch job, continuing")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/dataloader/prowloader/prow.go` at line 300, Update the warning log in the
job-fetch error path to attach pj.Spec.Job and pj.Status.BuildID as structured
fields via the existing log entry, while keeping the error and warning message
intact without formatting those identifiers into it.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/dataloader/prowloader/prow.go`:
- Around line 1645-1647: Replace the raw status value 12 in the failure-counting
logic with the named sippyprocessingv1.TestStatusFailure constant, matching the
existing constant usage in extractTestCases and preserving the current failures
increment behavior.
- Around line 1334-1344: Update the prow_pull_requests upsert in the surrounding
transaction to refresh author and title on conflicts, reusing the incoming
EXCLUDED values so previously blank fields can be backfilled while preserving
the existing merged_at and updated_at behavior. Confirm this matches the former
findOrAddPullRequests backfill semantics.
- Around line 1358-1396: Update the test upsert in the tests-existence SQL
within the surrounding loader flow so a name conflict with a soft-deleted row
clears deleted_at and restores the test instead of doing nothing. Preserve the
existing behavior for active tests and ensure the subsequent INNER JOIN tests
... deleted_at IS NULL can include reappearing tests; leave suite handling
unchanged.
- Line 1148: Convert the Labels value in the relevant prow job result
construction to a native []string before it reaches db.CopyToTempTable and pgx
CopyFrom. Follow the existing overall_result conversion pattern, while leaving
the labelsCache lookup and surrounding fields unchanged.

---

Outside diff comments:
In `@pkg/dataloader/prowloader/prow.go`:
- Around line 163-172: Update loadProwJobCache to capture the *gorm.DB result
from the Find query and propagate its Error instead of returning only a cache
map. Adjust callers such as preprocessProwJobs to handle the returned error and
record it in the existing pl.errors flow, while preserving successful cache
population and logging behavior.

---

Nitpick comments:
In `@pkg/dataloader/prowloader/prow.go`:
- Around line 293-310: Throttle the progress logging in the worker loop around
jobsImportedCount so it does not emit an Infof for every job run. Log progress
only at a suitable interval or count threshold while preserving the existing
counter updates and final processing behavior.
- Around line 996-1013: Thread the parsed BuildID from preprocessProwJobs into
each entry instead of discarding candidate.id, preserving it alongside the
*prow.ProwJob. Update fetchJobRunResult and its callers to accept and use the
parsed uint64 value, removing the redundant strconv.ParseUint call while
retaining invalid-ID handling in preprocessProwJobs.
- Line 300: Update the warning log in the job-fetch error path to attach
pj.Spec.Job and pj.Status.BuildID as structured fields via the existing log
entry, while keeping the error and warning message intact without formatting
those identifiers into it.

In `@pkg/db/suites.go`:
- Around line 88-106: Update IsSuiteImportable to use a package-level
sets.Set[string] initialized once from testSuites for explicit suite membership
checks, replacing the per-call linear scan; retain the existing
testSuitePatterns matching and return behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: e64f9dcc-1da5-48be-a9bb-7e10d19c3ab2

📥 Commits

Reviewing files that changed from the base of the PR and between fa7e1d0 and e59882a.

📒 Files selected for processing (8)
  • pkg/api/jobrunevents/job_run_events.go
  • pkg/api/jobrunintervals/job_run_intervals.go
  • pkg/dataloader/prowloader/bigqueryjobs.go
  • pkg/dataloader/prowloader/extract_test_cases_test.go
  • pkg/dataloader/prowloader/gcs/gcs_jobrun.go
  • pkg/dataloader/prowloader/prow.go
  • pkg/db/suites.go
  • pkg/variantregistry/ocp.go

Comment thread pkg/dataloader/prowloader/prow.go Outdated
Comment thread pkg/dataloader/prowloader/prow.go
Comment thread pkg/dataloader/prowloader/prow.go
Comment thread pkg/dataloader/prowloader/prow.go Outdated
@mstaeble mstaeble force-pushed the prow-loader-bulk-writer branch from e59882a to ed02f66 Compare July 14, 2026 03:05
@openshift-ci openshift-ci Bot added the ready-for-human-review Indicates a PR has been reviewed by automated tools and is ready for human review label Jul 14, 2026
@mstaeble mstaeble marked this pull request as ready for review July 14, 2026 04:19
@mstaeble mstaeble changed the title [WIP] Optimize prow loader with bulk writer, COPY protocol, and GCS improvements Optimize prow loader with bulk writer, COPY protocol, and GCS improvements Jul 14, 2026
@openshift-ci openshift-ci Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jul 14, 2026
@openshift-ci openshift-ci Bot requested review from stbenjam and xueqzhan July 14, 2026 04:19
@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Scheduling required tests:
/test e2e

defaultJunitFileRegEx *regexp.Regexp
intervalFilesRegex *regexp.Regexp
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are your thoughts on keeping the globs defined as constants in a common group?

**junit*xml is used in multiple places and keeping the artifact list we pull in grouped together could make it a little easier to maintain / read.

Also noting that the . is getting dropped for the junit, not necessarily problematic but was it intentional?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestions on both counts. Extracted the globs as named constants (GlobJunitXML, GlobEventsJSON, GlobIntervalsJSON, GlobTimelinesJSON, GlobClusterData) in gcs_jobrun.go and updated all callers. Also added the literal . before extensions in all globs (*junit.xml, *e2e-events.json, etc.). The missing dot was unintentional — the original regex (junit.*xml) didn't enforce it either, but it should have.

Comment thread pkg/dataloader/prowloader/prow.go Outdated
}

runCleanup, err := db.CopyToTempTable(ctx, conn, "tmp_prow_job_runs", runs, runCols)
defer runCleanup()

@neisw neisw Jul 14, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code-review:pr prefers

if err != nil { return err} ; defer runCleanup()

multiple places below

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Restructured all CopyToTempTable call sites to check the error before deferring cleanup:

if cleanup, err := db.CopyToTempTable(...); err != nil {
     return err
} else {
    defer cleanup()
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well the linter doesn't like that, so instead it will be the following.

cleanup, err := ...
if err != nil {
    return err
}
defer cleanup()

Comment thread pkg/dataloader/prowloader/prow.go Outdated
{Name: "id", Type: "bigint NOT NULL", Value: func(id *uint) any { return *id }},
},
)
defer cleanup()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code-review:pr prefers

if err != nil { return err} ; defer cleanup()

But concern is that db.CopyToTempTable might return nil cleanup on error which doesn't appear to be the case. Feel free to ignore.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same pattern applied here as well.

…ments

Restructure the prow loader from 50 concurrent goroutines each doing their
own DB work into a producer/consumer pipeline: 50 GCS fetch workers feed
results through a channel to a single bulk writer that batches and commits
atomically using PostgreSQL COPY protocol and temp tables.

Key changes:
- Replace per-row GORM operations with COPY + temp table + single transaction
  per batch (100 job runs). ProwJobRun, annotations, pull requests, and tests
  all commit atomically.
- Pre-filter already-loaded job runs via anti-join against prow_job_runs before
  GCS fetching, eliminating redundant work on steady-state loads.
- Replace client-side regex filtering of GCS objects with server-side MatchGlob.
- Pre-compile release config regexes at construction time.
- Batch ProwJob upserts (100 per batch) instead of per-row.
- Filter pending/triggered jobs in BQ query.
- Remove per-row locks and caches that are no longer needed.

Benchmarks (staging, release 4.19, 1h lookback):
- Cold load: 5.1x faster (108s -> 21s)
- 48h steady-state: 43s (13,604 candidates, 72 new jobs)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mstaeble mstaeble force-pushed the prow-loader-bulk-writer branch from ed02f66 to 73b9cf4 Compare July 15, 2026 17:25

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/dataloader/prowloader/prow.go`:
- Around line 658-664: Update both build-ID parsing sites in the
candidate-loading flow, including the block around candidate creation and the
corresponding logic near the second occurrence, to parse decimal values with a
63-bit limit rather than base-0 unsigned 64-bit parsing. Continue skipping
candidates whenever parsing fails or the value exceeds PostgreSQL bigint’s
positive range, and preserve the existing candidate append behavior for valid
IDs.
- Around line 749-753: Update the candidate-ID existence check around the query
used by the insert flow to treat tombstoned prow_job_runs records as existing,
preventing them from reaching the plain INSERT path and causing batch rollback.
Apply the same correction to the corresponding logic near the second referenced
location, and preserve the intended deleted-versus-restored behavior
consistently for dependent records.
- Line 1618: Update the test identity map in the surrounding test-processing
logic to use a comparable struct containing separate suite and test fields,
preventing collisions from dots in either name. Keep the legacy
synthetic-conversion key separate from this structured identity, and add a
regression test covering distinct dotted suite/test names such as (“a.b”, “c”)
and (“a”, “b.c”).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 1f55240a-1ee2-4e27-887d-0127af25a360

📥 Commits

Reviewing files that changed from the base of the PR and between ed02f66 and 73b9cf4.

📒 Files selected for processing (8)
  • pkg/api/jobrunevents/job_run_events.go
  • pkg/api/jobrunintervals/job_run_intervals.go
  • pkg/dataloader/prowloader/bigqueryjobs.go
  • pkg/dataloader/prowloader/extract_test_cases_test.go
  • pkg/dataloader/prowloader/gcs/gcs_jobrun.go
  • pkg/dataloader/prowloader/prow.go
  • pkg/db/suites.go
  • pkg/variantregistry/ocp.go
🚧 Files skipped from review as they are similar to previous changes (6)
  • pkg/dataloader/prowloader/extract_test_cases_test.go
  • pkg/api/jobrunintervals/job_run_intervals.go
  • pkg/api/jobrunevents/job_run_events.go
  • pkg/dataloader/prowloader/gcs/gcs_jobrun.go
  • pkg/db/suites.go
  • pkg/variantregistry/ocp.go

Comment thread pkg/dataloader/prowloader/prow.go Outdated
Comment thread pkg/dataloader/prowloader/prow.go
Comment thread pkg/dataloader/prowloader/prow.go Outdated
…for test dedup

- ParseUint: use base 10 and 63-bit limit to match PostgreSQL bigint range
- Anti-join: remove deleted_at filter so soft-deleted runs are not re-inserted
- Replace dot-delimited string key with testCaseKey struct to prevent collisions
- Add regression test for dotted suite/test name combinations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Scheduling required tests:
/test e2e

@openshift-ci

openshift-ci Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

@mstaeble: all tests passed!

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@neisw

neisw commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

/lgtm

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Jul 15, 2026
@openshift-ci

openshift-ci Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: mstaeble, neisw

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jul 15, 2026
@openshift-merge-bot openshift-merge-bot Bot merged commit 9802b5e into openshift:main Jul 15, 2026
10 checks passed
@mstaeble mstaeble deleted the prow-loader-bulk-writer branch July 15, 2026 20:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm Indicates that a PR is ready to be merged. ready-for-human-review Indicates a PR has been reviewed by automated tools and is ready for human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants