Skip to content

fix(github): stop re-collecting closed and unmerged pull requests - #9244

Open
dvecy wants to merge 1 commit into
apache:mainfrom
dvecy:fix/github-pr-unfinished-details
Open

dvecy wants to merge 1 commit into
apache:mainfrom
dvecy:fix/github-pr-unfinished-details

Conversation

@dvecy

@dvecy dvecy commented Oct 20, 2026

Copy link
Copy Markdown

Summary

Collect Pull Requests has a second phase that re-fetches pull requests one by one to refresh
the ones that are not finished yet. It picked them with:

state != 'closed' OR merged = false

A pull request that was closed without being merged never becomes merged, so it keeps matching
for ever: one GET /pulls/{number} for it on every single run, for as long as the repo exists.
#9240 reports ~15k of those on one instance, the same payload stored again and again, and a run
finally dying on a 422 from GitHub.

Why that condition was there

/pulls (the list endpoint) does not return merged, commits, additions, deletions,
review_comments or merged_by. So after a full sync every closed pull request is stored
without them, and OR merged = false (44f2db2) was added to fetch each one once more and fill
them in. That repair is still needed — it just never stopped.

Fix

Select on commits = 0 instead of merged = false. Every pull request has at least one commit,
so commits = 0 means "details were never collected", and it stops being true as soon as they
are. Exactly one case changes: a closed, never merged pull request whose details are already in
the database is no longer re-fetched. Open pull requests, merged ones, and the repair pass after
a full sync behave as before. No schema change, no migration, no config change.

The query moved into UnfinishedPullRequestsClauses so the test runs the collector's own query
instead of a copy of it.

One thing to be aware of

A closed pull request that is reopened later is no longer picked up by an incremental run. That
is already the case for merged ones, github_graphql does the same (it only refreshes
state = 'OPEN'), and a full sync reconciles it. Catching reopens incrementally would need the
list phase to page by sort=updated — happy to file a follow-up issue if you want that tracked.

Tests

New TestGithubUnfinishedPullRequests seeds one pull request per case and asserts the exact set
the query returns. On main it fails with [1 2 4 5 6] instead of [1 2 4 5]; with the fix it
passes.

Locally against PostgreSQL 16: go test ./plugins/github/... all packages ok, go vet clean,
golangci-lint run ./plugins/github/... 0 issues, gofmt -l plugins/github/ empty. The
condition is plain portable SQL, so MySQL and PostgreSQL behave the same.

Does this close any open issues?

Closes #9240

The "Collect Pull Requests" detail collector re-collected every pull
request matching `state != 'closed' OR merged = false`. `merged` stays
false forever on a pull request that was closed without being merged, so
those were re-collected on every incremental run, one request each, for as
long as the repo lived.

The condition was widened to `OR merged = false` so that `merged` gets
filled in after a full sync: `repos/{owner}/{repo}/pulls` does not return
`merged`, `commits`, `additions`, `deletions`, `review_comments` or
`merged_by`, so a pull request that was only ever seen in a list page is
stored without them. That repair is still needed, but it has to end once
the details are in.

Select on `commits = 0` instead. Every pull request has at least one
commit, so it marks exactly the records whose details were never
collected, and it stops matching as soon as they are. Open pull requests
are re-collected as before.

The clauses move into `UnfinishedPullRequestsClauses` so the regression
test runs the same query as the collector.

Closes apache#9240

Signed-off-by: Sadat Nazarli <118483002+dvecy@users.noreply.github.com>
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.

[Bug][GitHub] Closed (unmarked) Pull Requests are re-fetched on every run

1 participant