Repository navigation
π fix(dashboard): the metrics collector reads each repo's GitHub metadata at most once an hour instead of every five minutes - #17175
Conversation
β¦ost hourly
collectOutreach (stars/forks) and collectIssuesDisabled (has_issues) read
GET /repos/{owner}/{repo} for the primary repo and every watched repo on
every five-minute collect: 189 calls an hour on the 20-repo projectbluefin
hive, 177 charged. Hold each successful read for an hour and share it
between both uses; a failed read is retried on the next collect. The
advisory create path keeps its fresh ProbeIssuesDisabled check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BQYnPGFJx81c8JXR3o1sg1
Signed-off-by: Danathar <Danathar@users.noreply.github.com>
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
Open PR overlap checkBase branch: Conflicts are checked with every PR merged onto the current tip of No conflicting open PRs or same-file overlaps were found. |
There was a problem hiding this comment.
Read metrics_collector.go, metrics_issues_disabled.go and github/advisory.go at the v6 merge-base alongside the diff. This looks correct to me.
What I checked:
repoMetadata(metrics_collector.go:748) only stores a successful, non-nil read and releases the mutex before the network call, so a failed read is retried on the next collect and a slow GitHub call doesn't hold the lock. The key is lower-casedowner/name, matching howcollectIssuesDisabledalready de-dupes viaseen[full].IssuesDisabledFor(advisory.go:80) is a pure extraction of the old body ofProbeIssuesDisabled(v6advisory.go:75-78): samehas_issues/forklogic, nil for nil metadata.ProbeIssuesDisabledstill exists for the advisory create path, so that fresh check is unchanged.- Fail-open behaviour in
collectIssuesDisabledis preserved: on error the previous verdict for that repo is carried forward exactly as before. mc.clock()(metrics_collector.go:907) is the existing injectable time source, so the hour window is testable and the new test drives it.collectOutreachalready calledgh.GetRepoonmc.client()without the inner-client nil guardProbeIssuesDisabledhad, and it runs on the same tick, so routing the banner throughGetRepoadds no new exposure.
One observation, not a defect: since the primary repo is also in gh.Repositories(), the banner and the outreach card now share one read per hour for it, which is what the body describes.
β hive: agent=reviewer backend=copilot model=claude-fable-5.1 copilot=1.0.88
|
Hive status β updated in place; edits do not notify. Full history is in the Activity Monitor. Reviewed by Reviewer notesRead this against the v6 tree; it looks correct to me, and it is a smaller change than the +225 suggests (most of it is the test). What I checked:
No findings. |
Summary
The dashboard metrics collector re-read every watched repo's metadata (
GET /repos/{owner}/{repo}) on every five-minute collect. Each 20-repo pass cost about 21 calls, and 177 of the 189 calls in the last hour were charged. All it uses from those reads is the outreach card's stars and forks and the Issues-disabled banner'shas_issues/fork. Those change a few times a day. Each repo's metadata is now read at most once an hour, and one read of the primary repo serves both uses.The report behind this PR guessed that the reads seldom got a
304because repo metadata changes often. That part is wrong: the metadata barely changed. The missing304s came from the ETag cache losing its entries, which #17172 fixes. This PR is still worth having on its own: it cuts about 170 requests an hour whether or not they would be free, and it removes the 20 charged reads that every App token refresh forces (about every 45 minutes).What went wrong
Live reading from the projectbluefin hive (
hosted-projectbluefin-knuckle, builddd6b372,fleet_statussnapshot 2026-11-08T01:45:47Z, one-hour accounting window):304hive:metrics_collectorGET /repos/{owner}/{repo}restAccountingWindow), not 39 minutes as the report said.agent metrics collectedevery ~5 minutes, 12 times an hour. One pass reads the primary repo incollectOutreach, then all 20 watched repos again incollectIssuesDisabled(ProbeIssuesDisabled). That is 21 reads per pass, or 252 an hour.stars: 0for three collects while the budget was critical.Why so few
304s. I re-read the 20 repos' metadata conditionally, with my own token and ETags (read-only, public data). 20 of 20 were unchanged after 10 minutes, and 18 of 20 after 15 minutes. With a working cache, about 97% of these reads would be free. Live, 6% were. The ETag cache was evicting them (#17172).What else reads repo metadata. I left these on their own cadence on purpose:
ProbeIssuesDisabledimmediately before it creates. That is a correctness check, so it stays fresh.contribute_standby_dispatch.go,forge/github_forge.go,main_eval_cycle.goandrfcnotify_wire.goread on demand for their own purposes and are not on the collector's loop.The banner is informational only, and a failed read keeps its last verdict, so an hour of staleness can't cause wrong behaviour. At worst, the banner appears or clears up to an hour late after someone flips a repo's Issues setting.
What changes
MetricsCollector.repoMetadataholds each repo's successfulGET /repos/{owner}/{repo}answer forrepoMetadataRefreshInterval(one hour). The key is the repo name, lower-cased.collectOutreach(stars and forks) andcollectIssuesDisabled(has_issues/fork) both read through it, so the primary repo is read once for both.github.IssuesDisabledForclassifies metadata that has already been read.ProbeIssuesDisableduses it too, so both name the same cause and remedy.Files
src/pkg/dashboard/metrics_collector.go:repoMetadata, the held metadata, and stars/forks read through it.src/pkg/dashboard/metrics_issues_disabled.go: the banner check reads throughrepoMetadata.src/pkg/github/advisory.go:IssuesDisabledFor.src/docs/fleet-health.md: the banner clears within the hour of the fix.changelog.d/fixed-metrics-repo-metadata-hourly.mdTests
TestMetricsCollector_RepoMetadataReadHourlyruns twelve collects five minutes apart and asserts that each repo is read once, that stars, forks and the banner verdict come from the held read, and that after an hour each repo is read again and a changedhas_issuesshows. On v6 tip it fails: the primary repo was read 24 times and each other repo 12 times in 55 minutes. It passes with the fix.TestMetricsCollector_RepoMetadataFailureRetriesNextCollectchecks that a failed read is retried on the next collect while the previous verdict stays up. It passes on tip and with the fix; it guards the fail-open behaviour.TestCollectIssuesDisabled_*tests still pass.Full packages run:
./pkg/dashboard/,./pkg/github/. Also run:go vet,check-docs-links.py,check-docs-citations.py.How to verify live
After deploying,
top_consumersshould showhive:metrics_collector GET /repos/{owner}/{repo}at about 20 requests an hour (one per watched repo) instead of 189.Refs #17172
π€ Generated with Claude Code
https://claude.ai/code/session_01BQYnPGFJx81c8JXR3o1sg1