perf(actions): skip re-sending unchanged job summaries on the run vie… - #38617
Open
bircni wants to merge 1 commit into
Open
perf(actions): skip re-sending unchanged job summaries on the run vie…#38617bircni wants to merge 1 commit into
bircni wants to merge 1 commit into
Conversation
…w poll The run view polls roughly every second while a run is in progress, and every poll loaded and re-rendered each job's GITHUB_STEP_SUMMARY markdown, up to MaxJobNumPerRun jobs of up to 1 MiB each, even when nothing had changed. Compute a cheap DB-side fingerprint (row count plus the newest "updated") of the summaries in scope, send it with the response and accept the client's current one in the request. When they match, the response omits jobSummaries and the client keeps what it already rendered; when they differ, the full set is sent as before. COUNT is needed alongside MAX(updated) because "updated" has 1s granularity, so MAX alone would miss a deleted row or a step inserted in the same second as the previous one. An empty fingerprint never matches, since it means both "this run has no summaries" and "the client holds none yet". Assisted-by: Claude Code:claude-opus-4-8
Contributor
There was a problem hiding this comment.
Pull request overview
This PR reduces repeated DB load, markdown rendering work, and payload size in the Actions run view’s ~1s polling loop by introducing a lightweight server-side “job summaries fingerprint” and only re-sending/re-rendering GITHUB_STEP_SUMMARY content when that fingerprint changes.
Changes:
- Add
jobSummariesVersion(a summaries fingerprint) to the Actions run view request/response model. - Server computes and returns the fingerprint each poll, and conditionally omits
jobSummarieswhen the client already has the same version. - Client preserves the previously-rendered
jobSummarieswhen the version is unchanged and the server omits the field.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| web_src/js/modules/gitea-actions.ts | Extends the ActionsRun type with jobSummariesVersion. |
| web_src/js/components/ActionRunView.ts | Sends the client-held fingerprint; reuses prior summaries when version matches. |
| routers/web/repo/actions/view.go | Adds request/response fields; computes fingerprint and conditionally loads/renders summaries. |
| models/actions/run_job_summary.go | Adds DB-side fingerprint query helper for summaries. |
| models/actions/run_job_summary_test.go | Adds unit test coverage for fingerprint behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+205
to
+217
| var agg struct { | ||
| Count int64 `xorm:"count"` | ||
| MaxUpdated int64 `xorm:"max_updated"` | ||
| } | ||
| if _, err := sess. | ||
| Select("COUNT(*) AS count, COALESCE(MAX(updated), 0) AS max_updated"). | ||
| Get(&agg); err != nil { | ||
| return "", err | ||
| } | ||
| if agg.Count == 0 { | ||
| return "", nil | ||
| } | ||
| return fmt.Sprintf("%d-%d", agg.Count, agg.MaxUpdated), nil |
Comment on lines
+6
to
+13
| import ( | ||
| "testing" | ||
|
|
||
| "gitea.dev/models/unittest" | ||
|
|
||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
| ) |
Comment on lines
+42
to
+51
| // Job scoping: adding job B changes the all-jobs fingerprint but leaves job A's untouched. | ||
| vA := version(jobA) | ||
| require.NoError(t, UpsertActionRunJobSummary(ctx, repoID, runID, attemptID, jobB, 0, JobSummaryContentTypeMarkdown, []byte("b summary"))) | ||
| assert.NotEqual(t, v2, version(0)) | ||
| assert.Equal(t, vA, version(jobA)) | ||
|
|
||
| // Deleting a step changes the fingerprint. | ||
| beforeDelete := version(jobA) | ||
| require.NoError(t, DeleteActionRunJobSummary(ctx, repoID, runID, attemptID, jobA, 1)) | ||
| assert.NotEqual(t, beforeDelete, version(jobA)) |
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Split out of #38518, where the review asked to keep that branch to the correctness
fixes being backported to 1.27. The change is dropped there and continues here.
The run view polls roughly every second while a run is in progress. Every poll
loaded and re-rendered each job's
GITHUB_STEP_SUMMARYmarkdown — up toMaxJobNumPerRunjobs of up to 1 MiB each — even when nothing had changed, sothe cost scaled with both job count and the number of people watching the run.
GetActionRunJobSummariesVersionreturnsCOUNT(*)plus
MAX(updated)for the summaries in scope, with the same optionaljobIDscoping as
ListActionRunJobSummaries.COUNTis needed alongsideMAX(updated)becauseupdatedhas 1s granularity:MAXalone misses adeleted row, and a step inserted in the same second as the previous one.
jobSummariesVersionon the view request and response). When the client's matches, the response omits
jobSummariesand the content load and markdown render are skipped entirely;when it differs, the full set is sent as before.
summaries" and "the client holds none yet", so it is always treated as a miss.
There is no in-process cache: the fingerprint is computed and compared per
request, so no state is shared between requests. The rendered HTML is unchanged —
only how often it is recomputed and re-sent.