Skip to content

TRT-2848: Refresh summary tables incrementally during prow load - #3852

Open
mstaeble wants to merge 1 commit into
openshift:mainfrom
mstaeble:worktree-incremental-summary-refresh
Open

TRT-2848: Refresh summary tables incrementally during prow load#3852
mstaeble wants to merge 1 commit into
openshift:mainfrom
mstaeble:worktree-incremental-summary-refresh

Conversation

@mstaeble

@mstaeble mstaeble commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Refreshes test_daily_totals and test_cumulative_summaries incrementally as part of each prow load batch, replacing the separate full-refresh step that re-scanned the entire prow_job_run_tests table
  • Extracts batch writing logic from prow.go into a dedicated pgwriter package with focused, single-responsibility functions for each SQL step
  • Parallelizes carry-forward across releases (4 workers via errgroup.Group) to reduce carry-forward time from ~3 minutes to ~50 seconds
  • Pins currentDate at load start and threads it through all SQL to prevent midnight-crossing corruption
  • Adds 32 integration tests covering job runs, annotations, PRs, test results, daily/cumulative summaries, carry-forward, timestamp tracking, and edge cases

Staging validation

Tested against the staging database with multiple lookback windows:

  • 5-minute lookback: ~69 prow jobs, completed in under 2 minutes including summary upserts
  • 1-hour lookback: 17-23 jobs with 30-43K tests, completed in ~1 minute steady-state
  • 12-hour lookback (cold start simulation): 364 candidate jobs, completed successfully with correct daily totals and cumulative summaries
  • Carry-forward parallelization: reduced from ~3 minutes (sequential) to ~50 seconds (4 workers) on staging

Test plan

  • make lint passes (0 issues)
  • go test ./pkg/... passes
  • make integration passes (150 tests)
  • Verified against staging with multiple lookback windows (5m, 1h, 12h)
  • Compare daily totals and cumulative summaries against full-refresh baseline

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Improved ingestion and persistence of job runs, annotations, pull requests, test results, and test outputs.
    • Daily totals and cumulative summaries now update consistently across batches and missed dates.
    • Historical summaries automatically carry forward to fill eligible gaps.
    • Pull-request metadata is preserved and restored when records are reprocessed.
  • Bug Fixes

    • Improved handling of duplicate records, soft-deleted tests and suites, empty batches, and invalid future-dated results.
    • Refresh operations now focus on materialized views while summary updates occur during data loading.

@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-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Jul 30, 2026
@openshift-ci-robot

openshift-ci-robot commented Jul 30, 2026

Copy link
Copy Markdown

@mstaeble: This pull request references TRT-2848 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "5.0.0" version, but no target version was set.

Details

In response to this:

Summary

  • Extracts batch writing logic from prow.go into a dedicated pgwriter package with focused, single-responsibility functions for each SQL step (job runs, annotations, PRs, test results, daily totals, cumulative summaries)
  • Adds incremental upsert of test_daily_totals and test_cumulative_summaries within the prow loader's write transaction, so summary tables stay current without a separate full-refresh pass
  • Parallelizes carry-forward across releases (4 workers via errgroup.Group) to reduce carry-forward time from ~3 minutes to ~50 seconds
  • Pins currentDate at load start and threads it through all SQL to prevent midnight-crossing corruption
  • Adds 32 integration tests covering job runs, annotations, PRs, test results, daily/cumulative summaries, carry-forward, timestamp tracking, and edge cases

Test plan

  • make lint passes (0 issues)
  • go test ./pkg/... passes
  • make integration passes (150 tests)
  • Verify against staging with --prow-load-since 1h lookback
  • Compare daily totals and cumulative summaries against full-refresh baseline

🤖 Generated with Claude Code

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 openshift-eng/jira-lifecycle-plugin repository.

@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 30, 2026
@openshift-ci

openshift-ci Bot commented Jul 30, 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

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@mstaeble, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 33 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro Plus

Run ID: 6afd578b-bebb-4f09-a590-9884f8d421d8

📥 Commits

Reviewing files that changed from the base of the PR and between f52bdb3 and 3ba7641.

📒 Files selected for processing (5)
  • pkg/dataloader/prowloader/accumulate_test.go
  • pkg/dataloader/prowloader/pgwriter/pgwriter.go
  • pkg/dataloader/prowloader/prow.go
  • pkg/sippyserver/server.go
  • test/integration/pgwriter_test.go

Walkthrough

Prow ingestion now uses shared pgwriter row types and batch persistence. The writer stores job-run entities, test results, daily totals, and cumulative summaries, while carry-forward runs during loading. RefreshData no longer refreshes summary tables, and integration tests cover persistence and rollup behavior.

Changes

Prow persistence and summary pipeline

Layer / File(s) Summary
Writer contracts and entity persistence
pkg/dataloader/prowloader/pgwriter/pgwriter.go, test/integration/pgwriter_test.go
Adds pgwriter batch row types and transactional persistence for runs, annotations, pull requests, associations, tests, and outputs, with integration coverage.
Daily and cumulative summary maintenance
pkg/dataloader/prowloader/pgwriter/pgwriter.go, test/integration/pgwriter_test.go
Builds batch deltas, updates daily and cumulative summaries, and carries summaries forward across missing dates and releases.
ProwLoader pgwriter integration
pkg/dataloader/prowloader/prow.go, pkg/dataloader/prowloader/accumulate_test.go
Refactors loader results and extracted rows to pgwriter types, delegates batching to pgwriter.Write, initializes the current date, and invokes carry-forward during loading.
RefreshData responsibility change
pkg/sippyserver/server.go
Removes daily and cumulative summary refreshes and retains materialized-view refresh behavior.
Writer validation and rollback coverage
test/integration/pgwriter_test.go
Adds coverage for empty and invalid batches, duplicate identifiers, future-dated results, timestamps, aggregation, and rollback behavior.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • openshift/sippy#3833: Adds the summary timestamp fields consumed by this pgwriter aggregation and carry-forward logic.
  • openshift/sippy#3845: Also changes the Prow test-result ingestion path used by the new pgwriter pipeline.

Suggested labels: lgtm

Suggested reviewers: dgoodwin, petr-muller

🚥 Pre-merge checks | ✅ 20 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Ipv6 And Disconnected Network Test Compatibility ⚠️ Warning The new integration tests depend on intutil.NewTestDB, whose harness pulls docker.io/library/postgres:16 from a public registry. Mirror the Postgres image in an internal registry or preseed it in CI; otherwise these tests will fail in disconnected environments.
✅ Passed checks (20 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: incremental summary-table refresh during Prow load.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 No panics or ignored _ errors; new pgwriter helpers wrap DB failures with %w, and the few return err paths propagate already-wrapped helper errors.
Sql Injection Prevention ✅ Passed New pgwriter SQL uses placeholders for release/date values; no user-controlled values are concatenated into queries.
Excessive Css In React Should Use Styles ✅ Passed PR only changes Go backend/tests; no React components or inline style objects were added, so the CSS guideline is not applicable.
Test Coverage For New Features ✅ Passed PASS: new batch writer and carry-forward APIs are covered by integration tests, and accumulateAndWrite has dedicated unit tests with edge cases.
Single Responsibility And Clear Naming ✅ Passed PASS: the PR extracts a focused pgwriter package, uses clear row/entity names, and keeps ProwLoader as orchestration over smaller helpers.
Feature Documentation ✅ Passed No docs/features page covers the new pgwriter/summary-refresh flow; the only feature doc is about job-analysis symptoms/labels, and no docs file changed.
Stable And Deterministic Test Names ✅ Passed All test names are static literals; no Ginkgo titles or dynamic title construction. The only t.Run uses fixed table-defined names.
Test Structure And Quality ✅ Passed PASS: These are plain Go tests, not Ginkgo; each case is focused, DB fixtures clean up via t.Cleanup, no waits/timeouts are needed, and assertion style matches repo patterns.
Microshift Test Compatibility ✅ Passed No new Ginkgo e2e tests were added; the new tests are plain Go integration/unit tests and don’t reference MicroShift-unsupported OpenShift APIs.
Single Node Openshift (Sno) Test Compatibility ✅ Passed No new Ginkgo e2e tests were added; the new pgwriter tests are standard Go integration tests and make no node-topology assumptions.
Topology-Aware Scheduling Compatibility ✅ Passed The PR only changes prow data loading, pgwriter SQL, sippyserver refresh logic, and tests; no manifests/controllers/scheduling constraints were added.
Ote Binary Stdout Contract ✅ Passed Touched code has no fmt.Print/os.Stdout or log redirection changes in process-level entrypoints; it uses logrus/stderr logging and test helpers only.
No-Weak-Crypto ✅ Passed No MD5/SHA1/DES/RC4/3DES/Blowfish/ECB, custom crypto, or secret-comparison code appears in the touched files or diff hunks.
Container-Privileges ✅ Passed PASS: The PR only changes Go/test files; no manifest diffs show privileged:true, hostPID/Network/IPC, allowPrivilegeEscalation:true, or SYS_ADMIN.
No-Sensitive-Data-In-Logs ✅ Passed New logs only emit counts, releases, and durations; no tokens, passwords, PII, or hostnames were added in changed files.
✨ 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.

@mstaeble mstaeble changed the title TRT-2848: Optimize incremental cumulative summary upsert TRT-2848: Refresh summary tables incrementally during prow load Jul 30, 2026
@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jul 30, 2026

@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: 5

🧹 Nitpick comments (3)
test/integration/pgwriter_test.go (2)

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

Derive these status values from the canonical test-status constants.

Hardcoded 1/12/13 will silently drift if the junit status enum changes. Prefer referencing the existing status constants used by the loader (e.g. the v1 test status type) rather than redeclaring literals here.

#!/bin/bash
# Locate canonical test status constants for 1 (success), 12 (failure), 13 (flake)
rg -nP --type=go -C2 '=\s*(1|12|13)\b' -g '**/apis/**' | rg -i -C2 'status|flake|fail'
ast-grep run --pattern 'const ($$$)' --lang go pkg/apis | head -80
🤖 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 `@test/integration/pgwriter_test.go` around lines 18 - 22, Replace the literal
values in the statusSuccess, statusFailure, and statusFlake declarations with
the canonical test-status constants from the existing v1 status type used by the
loader, preserving the current success, failure, and flake mappings.

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

No coverage for carry-forward across multiple releases.

Every carryForward call passes a single release, so the new parallel per-release path is never exercised with >1 release. Adding []string{"4.18", "4.17"} to one case would cover the concurrent path and its error aggregation.

🤖 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 `@test/integration/pgwriter_test.go` around lines 603 - 641, The carryForward
tests only exercise a single release and miss the multi-release parallel path.
Update an existing carryForward test, such as TestCarryForwardIsReleaseScoped,
to pass both “4.18” and “4.17” together, and assert each release’s expected
result so concurrent processing and error aggregation are covered.
pkg/sippyserver/server.go (1)

150-151: 🩺 Stability & Availability | 🔵 Trivial

Dropping sippy_cumulative_summary_refresh_millis breaks any dashboard/alert on it.

The incremental path in pgwriter has no equivalent timing metric, so summary-maintenance latency becomes unobservable. Consider emitting a comparable histogram from the writer/carry-forward path and retiring the old metric in dashboards.

🤖 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/sippyserver/server.go` around lines 150 - 151, Preserve observability for
summary-maintenance latency when removing
sippy_cumulative_summary_refresh_millis by adding a comparable timing histogram
to the pgwriter incremental/carry-forward path. Instrument the relevant writer
flow, using the existing metric naming and labeling conventions where
applicable, and ensure dashboards and alerts are migrated to the replacement
metric rather than left referencing the retired one.
🤖 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/pgwriter/pgwriter.go`:
- Around line 589-612: Update carryForwardRelease and the related handling
around findLatestDateWithData so a release with cumulative data only older than
the 30-day lookback is treated as a skipped carry-forward: log the
stale/no-recent-data condition and return nil, rather than propagating an error
that aborts Load. Preserve error propagation for genuine database failures and
keep normal carry-forward behavior unchanged when a recent date is found.
- Around line 472-491: Update ensureDailyTotalRows to convert the civil.Date day
argument into a PostgreSQL-compatible bind value before passing it to tx.Exec,
using the existing date-binding convention such as a UTC date string or
time.Time. Use the converted value consistently for both date placeholders while
preserving the current insert and error-handling behavior.
- Around line 398-417: Extend the cumulative-summary day-loop upper bound in the
release-date processing flow to the maximum of tomorrow and the latest batch
date from releaseDates. Preserve the existing minimum-date grouping and
ensureCumulativeSummaryRows/updateCumulativeSummaries calls, so future-dated
batches are included rather than dropped.

In `@test/integration/pgwriter_test.go`:
- Around line 222-224: Replace the vacuous soft-delete assertions with validity
checks: in test/integration/pgwriter_test.go lines 222-224 for the models.Test
row and lines 1039-1041 for the models.Suite row, use require.True on
softDeleted.DeletedAt.Valid with an appropriate failure message. Preserve the
existing unscoped queries.
- Around line 696-699: In the CarryForwardCumulativeSummaries test, replace
assert.Error with require.Error before calling err.Error() in the subsequent
assertion, so a nil error stops the test cleanly before dereferencing it.

---

Nitpick comments:
In `@pkg/sippyserver/server.go`:
- Around line 150-151: Preserve observability for summary-maintenance latency
when removing sippy_cumulative_summary_refresh_millis by adding a comparable
timing histogram to the pgwriter incremental/carry-forward path. Instrument the
relevant writer flow, using the existing metric naming and labeling conventions
where applicable, and ensure dashboards and alerts are migrated to the
replacement metric rather than left referencing the retired one.

In `@test/integration/pgwriter_test.go`:
- Around line 18-22: Replace the literal values in the statusSuccess,
statusFailure, and statusFlake declarations with the canonical test-status
constants from the existing v1 status type used by the loader, preserving the
current success, failure, and flake mappings.
- Around line 603-641: The carryForward tests only exercise a single release and
miss the multi-release parallel path. Update an existing carryForward test, such
as TestCarryForwardIsReleaseScoped, to pass both “4.18” and “4.17” together, and
assert each release’s expected result so concurrent processing and error
aggregation are covered.
🪄 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: Pro Plus

Run ID: b2e681e0-a6cb-4e7b-8ad1-c02fe7166add

📥 Commits

Reviewing files that changed from the base of the PR and between 317ffc9 and 2c33833.

📒 Files selected for processing (6)
  • pkg/dataloader/prowloader/accumulate_test.go
  • pkg/dataloader/prowloader/pgwriter/pgwriter.go
  • pkg/dataloader/prowloader/prow.go
  • pkg/sippyserver/server.go
  • test/integration/pgwriter_test.go
  • test/integration/util/schema.go

Comment thread pkg/dataloader/prowloader/pgwriter/pgwriter.go
Comment thread pkg/dataloader/prowloader/pgwriter/pgwriter.go
Comment thread pkg/dataloader/prowloader/pgwriter/pgwriter.go
Comment thread test/integration/pgwriter_test.go Outdated
Comment thread test/integration/pgwriter_test.go
@mstaeble
mstaeble force-pushed the worktree-incremental-summary-refresh branch 2 times, most recently from 33eeaf7 to f52bdb3 Compare July 30, 2026 15:36
@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 30, 2026
@mstaeble
mstaeble marked this pull request as ready for review July 30, 2026 16:33
@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 30, 2026
@openshift-ci
openshift-ci Bot requested review from neisw and smg247 July 30, 2026 16:33

@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.

🧹 Nitpick comments (7)
pkg/dataloader/prowloader/pgwriter/pgwriter.go (4)

457-478: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer defer rows.Close() over manual close on each path.

Two explicit rows.Close() calls are easy to miss when a new early return is added. defer rows.Close() right after the error check is the idiomatic form and remains correct with the rows.Err() check.

🤖 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/pgwriter/pgwriter.go` around lines 457 - 478,
Update queryReleaseDates to defer rows.Close() immediately after the successful
tx.Query check, then remove both explicit rows.Close() calls while preserving
the existing scan-error and rows.Err() handling.

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

Hardcoded status codes will silently drift from the Go constants.

1, 12, 13 are sippyprocessingv1.TestStatusSuccess/Failure/Flake. Nothing ties the SQL to those constants, so a renumbering miscounts summaries with no compile error. Since these are internal constants (no injection concern), bind them as parameters or at minimum add a comment naming each.

♻️ Sketch
-			COUNT(*) FILTER (WHERE tmp.status = 1) AS successes,
-			COUNT(*) FILTER (WHERE tmp.status = 12) AS failures,
-			COUNT(*) FILTER (WHERE tmp.status = 13) AS flakes,
+			COUNT(*) FILTER (WHERE tmp.status = $1) AS successes,
+			COUNT(*) FILTER (WHERE tmp.status = $2) AS failures,
+			COUNT(*) FILTER (WHERE tmp.status = $3) AS flakes,

with int(sippyprocessingv1.TestStatusSuccess), int(sippyprocessingv1.TestStatusFailure), int(sippyprocessingv1.TestStatusFlake) passed to Exec (the min/max_*_ts filters need the same treatment).

🤖 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/pgwriter/pgwriter.go` around lines 437 - 439,
Update the summary SQL in the pgwriter query to replace hardcoded status values
with parameters bound from sippyprocessingv1.TestStatusSuccess,
TestStatusFailure, and TestStatusFlake, and apply the same parameterization to
the min/max timestamp filters. Ensure the corresponding Exec arguments and
placeholder ordering remain aligned.

407-425: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Two day-at-a-time Exec loops over test_cumulative_summaries. Both the in-batch cumulative upsert and the carry-forward catch-up iterate one day at a time, issuing a separate statement per day per release. The shared fix is to drive each from a single set-based statement over generate_series(<start>, <end>, '1 day'), which cuts round trips and makes multi-day work atomic.

  • pkg/dataloader/prowloader/pgwriter/pgwriter.go#L407-L425: replace the for day := minDate; !day.After(tomorrow) loop's per-day ensureCumulativeSummaryRows/updateCumulativeSummaries calls with day-set-joined statements so a back-dated batch doesn't multiply round trips inside the write transaction.
  • pkg/dataloader/prowloader/pgwriter/pgwriter.go#L622-L640: replace the per-day carry-forward INSERT with one generate_series-driven insert (or wrap the loop in a single transaction) so a multi-day catch-up can't leave the table partially advanced.
🤖 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/pgwriter/pgwriter.go` around lines 407 - 425,
Replace the per-day loop at
pkg/dataloader/prowloader/pgwriter/pgwriter.go:407-425 with set-based statements
using generate_series from each release’s minimum date through tomorrow,
preserving the ensureCumulativeSummaryRows and updateCumulativeSummaries
behavior in the joined day set. Also replace the carry-forward per-day INSERT at
pkg/dataloader/prowloader/pgwriter/pgwriter.go:622-640 with one
generate_series-driven statement, or make the loop a single transaction, so both
multi-day paths avoid per-day round trips and remain atomic.

480-499: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

NOT EXISTS + INSERT is a check-then-act; ON CONFLICT DO NOTHING is safer and self-documenting.

Both ensure*Rows helpers rely on NOT EXISTS to avoid duplicates. That's fine under today's single-writer design (Write is driven serially from one goroutine), but it silently degrades to duplicate rows or a constraint error if a second loader ever writes concurrently. ON CONFLICT (…) DO NOTHING on the natural key is both race-safe and cheaper.

Separately, ensureCumulativeSummaryRows needs the GROUP BY (because batch_date <= $1 spans days) while ensureDailyTotalRows doesn't (batch_deltas is already unique per release+date). A one-line "why" comment on the cumulative variant would save the next reader that deduction.

Also applies to: 525-545

🤖 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/pgwriter/pgwriter.go` around lines 480 - 499,
Update ensureDailyTotalRows and ensureCumulativeSummaryRows to use INSERT ... ON
CONFLICT DO NOTHING on each table’s natural key instead of NOT EXISTS checks,
preserving the existing inserted values and aggregation behavior. Add a brief
comment in ensureCumulativeSummaryRows explaining that GROUP BY is required
because batch_date <= the requested date spans multiple days; keep
ensureDailyTotalRows ungrouped because its source is already unique per release
and date.
test/integration/pgwriter_test.go (2)

721-742: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Test name covers only half of what it asserts.

TestCarryForwardErrorsWhenNoDataWithinLookback first asserts the no-op path when no data exists at all (line 725), then the error path beyond the 30-day window. Splitting these into two tests (e.g. TestCarryForwardIsNoOpWhenNoDataExists + the current name) makes a failure immediately attributable.

🤖 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 `@test/integration/pgwriter_test.go` around lines 721 - 742, Split
TestCarryForwardErrorsWhenNoDataWithinLookback into two focused tests: move the
initial no-data assertion into a new TestCarryForwardIsNoOpWhenNoDataExists, and
keep the seeded old-data scenario and far-future lookback error assertion in
TestCarryForwardErrorsWhenNoDataWithinLookback.

830-833: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Assert the job-run and test rows rolled back too, not just daily totals.

insertJobRuns/insertTestResults run before the future-date guard fires, so they're the rows most at risk if the rollback ever regressed. Checking only test_daily_totals (which would be empty even without a rollback, since the guard precedes the summary writes) under-tests the invariant.

💚 Proposed additional assertions
 	var dtCount int64
 	require.NoError(t, dbc.DB.Model(&models.TestDailyTotal{}).Count(&dtCount).Error)
 	assert.Equal(t, int64(0), dtCount, "transaction should have rolled back, no daily totals written")
+
+	var runCount int64
+	require.NoError(t, dbc.DB.Model(&models.ProwJobRun{}).Count(&runCount).Error)
+	assert.Equal(t, int64(0), runCount, "transaction should have rolled back, no job runs written")
+
+	var jrtCount int64
+	require.NoError(t, dbc.DB.Model(&models.ProwJobRunTest{}).Count(&jrtCount).Error)
+	assert.Equal(t, int64(0), jrtCount, "transaction should have rolled back, no test results written")
🤖 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 `@test/integration/pgwriter_test.go` around lines 830 - 833, Extend the
rollback assertions in this integration test to also count rows from the models
written by insertJobRuns and insertTestResults, and assert both counts are zero
after the future-date guard failure. Keep the existing daily-total assertion and
use the corresponding model symbols to verify all transaction writes were rolled
back.
pkg/dataloader/prowloader/accumulate_test.go (1)

36-51: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

calls is captured and copied but never asserted in the table test.

Every case's writerFunc deep-copies each batch into calls, yet the subtest only asserts on pl.errors. Either assert the expected batch count/sizes (which is what the copies were evidently written for, and would pin the 100-row flush threshold) or drop the calls plumbing from this table to cut the noise. TestAccumulateAndWriteJobRuns_PerBatchErrors does assert Len(calls, 4), so the pattern is clearly intended.

Also applies to: 124-125, 149-149

🤖 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/accumulate_test.go` around lines 36 - 51, Update
the table-driven test around its writerFunc and subtest assertions to validate
the captured calls, including expected batch count and sizes that confirm the
100-row flush threshold; apply the same assertion update to the additional cases
noted in the comment. Reuse the existing calls capture and match the pattern
from TestAccumulateAndWriteJobRuns_PerBatchErrors rather than removing the
plumbing.

Source: Path instructions

🤖 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.

Nitpick comments:
In `@pkg/dataloader/prowloader/accumulate_test.go`:
- Around line 36-51: Update the table-driven test around its writerFunc and
subtest assertions to validate the captured calls, including expected batch
count and sizes that confirm the 100-row flush threshold; apply the same
assertion update to the additional cases noted in the comment. Reuse the
existing calls capture and match the pattern from
TestAccumulateAndWriteJobRuns_PerBatchErrors rather than removing the plumbing.

In `@pkg/dataloader/prowloader/pgwriter/pgwriter.go`:
- Around line 457-478: Update queryReleaseDates to defer rows.Close()
immediately after the successful tx.Query check, then remove both explicit
rows.Close() calls while preserving the existing scan-error and rows.Err()
handling.
- Around line 437-439: Update the summary SQL in the pgwriter query to replace
hardcoded status values with parameters bound from
sippyprocessingv1.TestStatusSuccess, TestStatusFailure, and TestStatusFlake, and
apply the same parameterization to the min/max timestamp filters. Ensure the
corresponding Exec arguments and placeholder ordering remain aligned.
- Around line 407-425: Replace the per-day loop at
pkg/dataloader/prowloader/pgwriter/pgwriter.go:407-425 with set-based statements
using generate_series from each release’s minimum date through tomorrow,
preserving the ensureCumulativeSummaryRows and updateCumulativeSummaries
behavior in the joined day set. Also replace the carry-forward per-day INSERT at
pkg/dataloader/prowloader/pgwriter/pgwriter.go:622-640 with one
generate_series-driven statement, or make the loop a single transaction, so both
multi-day paths avoid per-day round trips and remain atomic.
- Around line 480-499: Update ensureDailyTotalRows and
ensureCumulativeSummaryRows to use INSERT ... ON CONFLICT DO NOTHING on each
table’s natural key instead of NOT EXISTS checks, preserving the existing
inserted values and aggregation behavior. Add a brief comment in
ensureCumulativeSummaryRows explaining that GROUP BY is required because
batch_date <= the requested date spans multiple days; keep ensureDailyTotalRows
ungrouped because its source is already unique per release and date.

In `@test/integration/pgwriter_test.go`:
- Around line 721-742: Split TestCarryForwardErrorsWhenNoDataWithinLookback into
two focused tests: move the initial no-data assertion into a new
TestCarryForwardIsNoOpWhenNoDataExists, and keep the seeded old-data scenario
and far-future lookback error assertion in
TestCarryForwardErrorsWhenNoDataWithinLookback.
- Around line 830-833: Extend the rollback assertions in this integration test
to also count rows from the models written by insertJobRuns and
insertTestResults, and assert both counts are zero after the future-date guard
failure. Keep the existing daily-total assertion and use the corresponding model
symbols to verify all transaction writes were rolled back.

ℹ️ Review info
⚙️ Run configuration

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

Review profile: CHILL

Plan: Pro Plus

Run ID: 8c4e232a-ab67-4f99-98ba-eb856ab613e4

📥 Commits

Reviewing files that changed from the base of the PR and between 2c33833 and f52bdb3.

📒 Files selected for processing (5)
  • pkg/dataloader/prowloader/accumulate_test.go
  • pkg/dataloader/prowloader/pgwriter/pgwriter.go
  • pkg/dataloader/prowloader/prow.go
  • pkg/sippyserver/server.go
  • test/integration/pgwriter_test.go

@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Scheduling required tests:
/test e2e

Replace the single generate_series cascade with a per-(release, date)
loop and a shared batch_deltas temp table. This eliminates duplicate
aggregation and avoids the row explosion from generate_series, cutting
the cumulative summary upsert from ~330us/test to ~86us/test against a
production-sized database.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mstaeble
mstaeble force-pushed the worktree-incremental-summary-refresh branch from f52bdb3 to 3ba7641 Compare July 30, 2026 16:59
@openshift-merge-bot

Copy link
Copy Markdown
Contributor

Scheduling required tests:
/test e2e

@openshift-ci

openshift-ci Bot commented Jul 30, 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.

@petr-muller petr-muller left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I have reviewed the code and had some convos with Claude Code about and found no problems, but I do not feel too confident about knowing this part of Sippy that well, so LGTM+hold for the case you want someone better with DBs than me to look as well. Unhold as needed

/hold

@openshift-ci openshift-ci Bot added the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Jul 31, 2026
@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Jul 31, 2026
@openshift-ci

openshift-ci Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: mstaeble, petr-muller

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:
  • OWNERS [mstaeble,petr-muller]

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

ghCommenter: ghCommenter,
promPusher: promPusher,
loadSince: loadSince,
currentDate: civil.DateOf(time.Now().UTC()),

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.

This could be ok for now but what are the implications when we get to loading historical data? There is more to it than just this so it is really just an observation for now, nothing to take action on

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.

Or maybe this works as any historical updates will need to be brought all the way forward to today.

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.

When we get to loading historical data, we will want a more deliberate command for that. Thinking aloud, we'll want to load the oldest data first, loading a few days at a time. Then we'll want to separately backfill the summary tables. The commands already exist for that summary backfill that go deliberately day by day.

Comment thread pkg/sippyserver/server.go
// (test_daily_totals, test_cumulative_summaries) are updated
// incrementally by the prow loader; use "sippy backfill" to repair them.
func RefreshData(dbc *db.DB, cacheClient cache.Cache, opts RefreshOptions) error {
log.Infof("Refreshing data")

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.

e2e seems to be passing so seed-data may not be an issue. Without new data coming in running the refresh command will just be refreshing the materialized views so all may be fine, posting the review from ai-helpers in this area just to confirm there are no real concerns raised here:

[High] LSP: RefreshData() behavioral change -- server.go: Removing dailysummary.Refresh and cumulativesummary.Refresh from RefreshData() silently changes the function's contract for all
callers, not just the prow loader:

  • cmd/sippy/load.go -- calls RefreshData after prow load. Since the prow loader now handles summaries incrementally, this caller gets equivalent behavior. OK.
  • cmd/sippy/refresh.go -- explicit "refresh" command. After this PR, sippy refresh will not update daily totals or cumulative summaries. This is a breaking change for operators.
  • cmd/sippy/seed_data.go -- seeds data then refreshes. Summary tables will not be rebuilt after seeding.

The PR description mentions "use sippy backfill to repair them," but the refresh and seed-data commands' help text and behavior are not updated to reflect this change. Recommendation:
Either (a) have those commands also invoke backfill/carry-forward for summary tables, or (b) explicitly document the behavioral change in command help text and the PR description.

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.

The change to "sippy refresh" is deliberate. The backfill command is the tool to use to re-calculate summary tables.

There is probably a gap in the seed_data command. I had not considered that.

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.

For the seed data path, it has been converted to use the BackfillData function already. It should be functioning properly.

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. do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. 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.

4 participants