Skip to content

perf: fetch merged PRs via GraphQL (different rate-limit bucket)#5

Merged
costajohnt merged 1 commit into
mainfrom
perf/graphql-merged-prs
May 6, 2026
Merged

perf: fetch merged PRs via GraphQL (different rate-limit bucket)#5
costajohnt merged 1 commit into
mainfrom
perf/graphql-merged-prs

Conversation

@costajohnt
Copy link
Copy Markdown
Owner

Summary

Closes the rate-limit risk introduced by #1's pagination fix.

The merged-PR pagination was hitting GitHub's 30/min REST search bucket: up to 10 search calls per cold-start × 4 widget endpoints = 40 search calls in a cold-start cluster. Each cold-start storm reliably tripped 429s in production.

This switches the merged-PR fetch to GraphQL search, which counts against the 5000-points-per-hour GraphQL bucket instead of the search bucket. At ~1 point per page, 4 widgets × 10 pages = 40 points/storm — plenty of headroom.

Bonus: `repository.stargazerCount` comes inline in the GraphQL response, so the card/recent/top-repos transforms no longer need separate `getStarsForRepos` roundtrips. Eliminates ~36 extra core-API calls on cold start.

Diffs

  • github-data.ts: `paginateMergedPRsGraphQL` replaces `paginateSearch`. Open/closed-unmerged counts still use REST search (1 call each, within budget).
  • ContributionData: adds `repoStars: Record<string, number>`, populated inline by the GraphQL fetch.
  • api/card, api/recent, api/top-repos: filter by `data.repoStars` instead of round-tripping `getStarsForRepos`.

Test plan

  • `pnpm test` — 114/114 passing (test mocks rewritten to graphql shape, helper `gqlPage` keeps fixtures readable)
  • `pnpm run typecheck` — clean
  • After deploy, hit all 4 endpoints with `cache=no` simultaneously — should NOT trigger 429s anymore (was the failure mode that started this PR)
  • Verify card still serves 114 / 36 with the right top repos

The merged-PR pagination was the rate-limit bottleneck — up to 10 search
calls per cold-start, multiplied by 4 widget endpoints = 40 search calls
in a cold-start cluster, blowing through GitHub's 30/min REST search bucket.

GraphQL search runs against the 5000/hour points bucket instead. At ~1 point
per page, 4 widgets × 10 pages = 40 points. Plenty of headroom. Star counts
also come back inline via repository.stargazerCount, so the card/recent/
top-repos transforms no longer need separate getStarsForRepos roundtrips.

Open and closed-unmerged counts still use REST search (1 call each, well
within the 30/min budget).

Touches:
- github-data.ts: paginateMergedPRsGraphQL replaces paginateSearch
- ContributionData adds repoStars: Record<string, number> populated inline
- card/recent/top-repos endpoints filter by data.repoStars instead of
  fetching stars separately
@costajohnt costajohnt merged commit 51d3fca into main May 6, 2026
4 checks passed
@costajohnt costajohnt deleted the perf/graphql-merged-prs branch May 6, 2026 23:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant