Skip to content

fix(scheduler): retry rate-limited comment fetches with backoff - #1459

Merged
seonghobae merged 2 commits into
mainfrom
fix/pr-review-fix-scheduler-rate-limit-backoff
Aug 31, 2026
Merged

fix(scheduler): retry rate-limited comment fetches with backoff#1459
seonghobae merged 2 commits into
mainfrom
fix/pr-review-fix-scheduler-rate-limit-backoff

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Investigating the user's report that many .github PRs show "This branch has conflicts that must be resolved," I surveyed all open PRs across .github, noema, and contextual-orchestrator: 40 of .github's 81 open PRs are mergeable_state: dirty. This repo already has a purpose-built, safe, automated conflict-resolution mechanism for exactly this (scripts/ci/pr_review_fix_scheduler.py.github/workflows/pr-review-autofix.yml, gated so the resulting head is fully re-reviewed and re-checked before it can merge), dispatched hourly by github-hourly-review-repair.yml. So rather than hand-resolving 40 conflicts (duplicative, risky, and not scalable — the same mechanism also needs to keep working for future conflicts), I root-caused why that mechanism has stopped working.

Root cause: the most recent hourly run inspected 50 candidate PRs and dispatched zero autofixes ("autofix_dispatches": 0), with essentially every candidate PR's decision reading "error": "API rate limit exceeded for installation ID 141441800" (the shared OpenCode GitHub App installation, contended by many concurrent org-wide scheduled workflows). Two compounding causes in pr_review_fix_scheduler.py:

  1. issue_comments() fetched a PR's entire issue-comment history with the default 30-per-page pagination, even though recent_fix_marker_exists() only ever needs the most recent marker comment.
  2. process_queue()'s concurrent comment-prefetch (up to 10 simultaneous gh api --paginate calls) silently swallowed a failed fetch (except Exception: pass) and then had inspect_pr() immediately retry the same doomed call sequentially with zero backoff — doubling the wasted request volume for every already-failing PR.

Developer experience

  • issue_comments() now requests per_page=100 (cutting page count for long comment threads by up to 3x) and retries a detected rate-limit error with a short linear backoff (up to 2 attempts) before propagating.
  • process_queue() now caps prefetch concurrency at 4 workers instead of 10, and a PR whose comment fetch still fails after retries is deferred to the next scheduled pass ("wait") instead of being silently swallowed and then redundantly re-fetched and reported as a scary "error".
  • This is a single shared script, so the fix applies identically to every one of the ~19 product-specific hourly review-repair callers (contextual-orchestrator-hourly-review-repair.yml, etc.), not just .github's own.

User experience

The hourly conflict/review-feedback autofix loop should start actually dispatching repairs again instead of erroring out on nearly every candidate PR, gradually clearing the current 40-PR dirty backlog (bounded by the existing max_dispatches: 1-per-hour setting, left unchanged in this PR — a separate, more speculative lever I didn't want to mix into this root-cause fix).

Test plan

  • New regression tests: is_rate_limit_error signature matching, issue_comments retry-then-succeed / exhaust-retries-then-raise / no-retry-for-non-rate-limit-errors, process_queue deferring a PR whose comment fetch failed (both the single-PR sequential path and the multi-PR concurrent path), proving only the failing PR is deferred while others proceed normally.
  • coverage run -m pytest tests -q (full suite) — 1936 passed, 1 skipped, 21 subtests passed
  • coverage report --show-missing — 100%
  • interrogate — 100%

Generated by Claude Code

Summary by CodeRabbit

  • 개선 사항
    • 시간별 PR 리뷰 복구 작업의 API 호출 안정성이 향상되었습니다.
    • 일시적인 rate limit 오류에 대해 자동 재시도와 지연 처리가 적용됩니다.
    • 댓글 조회 페이지당 처리량이 늘어나고, 동시 요청 수가 조정되었습니다.
    • 댓글 조회에 실패한 PR은 오류로 처리되지 않고 다음 실행으로 자동 연기됩니다.

Root-caused why github-hourly-review-repair.yml's most recent run
inspected 50 PRs and dispatched zero autofixes (autofix_dispatches: 0),
which is why 40 of .github's 81 open PRs were stuck reporting "This
branch has conflicts that must be resolved" with no automatic repair ever
reaching them: every candidate PR's decision read "error: API rate limit
exceeded for installation ID ...".

Two compounding causes in pr_review_fix_scheduler.py:

1. issue_comments() fetched a PR's entire issue-comment history with the
   default 30-per-page pagination, even though recent_fix_marker_exists()
   only ever needs the most recent marker.
2. process_queue()'s concurrent comment-prefetch (up to 10 simultaneous
   `gh api --paginate` calls against the same shared, org-wide-contended
   OpenCode app installation) silently swallowed a failed fetch and then
   had inspect_pr() immediately retry the same doomed call sequentially
   with zero backoff, doubling the wasted request volume for every
   already-failing PR.

issue_comments() now requests per_page=100 (cutting page count for long
comment threads by up to 3x) and retries a detected rate-limit error with
a short linear backoff (up to 2 attempts) before propagating.
process_queue() now caps prefetch concurrency at 4 workers instead of 10,
and a PR whose comment fetch still fails after retries is deferred to the
next scheduled pass ("wait") instead of being silently swallowed and then
redundantly re-fetched and reported as a scary "error".

This is a single shared script, so the fix applies identically to every
one of the ~19 product-specific hourly review-repair callers, not just
.github's own.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BV96rXhqoR3tYZ9AeAVur4
@coderabbitai

coderabbitai Bot commented Aug 30, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 58ffefcf-da4c-4580-8bf2-e41cb3c5c6ad

📥 Commits

Reviewing files that changed from the base of the PR and between 38e7ddd and b64c3a4.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • scripts/ci/pr_review_fix_scheduler.py
  • tests/test_pr_review_fix_scheduler.py

📝 Walkthrough

Walkthrough

시간별 PR 리뷰 복구 스케줄러가 댓글 조회에 per_page=100과 rate-limit 재시도를 적용합니다. 댓글 조회 실패 PR은 "wait" 상태로 연기합니다. 병렬 조회 작업자 수를 4개로 제한합니다.

Changes

PR 리뷰 복구 스케줄러

Layer / File(s) Summary
댓글 조회 재시도와 페이지 제한
scripts/ci/pr_review_fix_scheduler.py, tests/test_pr_review_fix_scheduler.py
rate-limit 오류 판별, 최대 재시도, 선형 backoff, per_page=100 조회를 추가했습니다. 재시도 소진과 비 rate-limit 오류 동작을 테스트합니다.
큐 조회 실패 연기와 동시성 제한
scripts/ci/pr_review_fix_scheduler.py, tests/test_pr_review_fix_scheduler.py, CHANGELOG.md
댓글 조회 실패를 기록하고 해당 PR을 "wait" 상태로 연기합니다. 병렬 작업자 수를 10개에서 4개로 줄였습니다. 순차 및 병렬 처리 동작을 테스트하고 변경 로그를 갱신했습니다.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Scheduler
  participant issue_comments
  participant GitHubAPI as GitHub API
  participant inspect_pr

  Scheduler->>issue_comments: PR 댓글 조회(per_page=100)
  issue_comments->>GitHubAPI: 댓글 API 요청
  GitHubAPI-->>issue_comments: 댓글 또는 rate-limit 오류
  issue_comments->>GitHubAPI: 필요 시 backoff 후 재시도
  issue_comments-->>Scheduler: 댓글 결과 또는 조회 오류
  alt 조회 성공
    Scheduler->>inspect_pr: PR 검사
  else 조회 실패
    Scheduler->>Scheduler: "wait" 상태로 다음 실행에 연기
  end
Loading
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/pr-review-fix-scheduler-rate-limit-backoff

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.

Copy link
Copy Markdown
Contributor Author

Standing down on the required opencode-review check failure ("No APPROVED or CHANGES_REQUESTED from opencode-agent on the current head") — not this PR's diff, and not a flake: this is a pre-existing, org-wide outage, not something this PR's own change touches or could have caused.

Evidence: a broad sample of recent OpenCode Review Dispatch runs across many unrelated PRs and repositories org-wide shows near-universal conclusion: failure over the last several hours. On this PR's current head there is no Strix check or status at all (not running, not failed — absent), so strix_evidence_state() correctly reports "missing" and OpenCode correctly never gets dispatched, by design, until Strix produces authoritative current-head evidence. See the parallel note I left on #1456 for the fuller trail — this traces to the already-tracked live sidecar-preflight outage and a related self-target Strix status-publish token gap (docs/product-technical-gap-baseline.md's 2026-08-30 entries), the latter already fixed in principle by PR #1441 (itself blocked by the same merge-conflict backlog this PR fixes).

Ironically apt: this PR fixes the mechanism that should eventually help clear that same conflict backlog, including #1441. Will keep this PR watched rather than guess at a fix for the deeper Strix-dispatch gap without further diagnosis.


Generated by Claude Code

@seonghobae
seonghobae marked this pull request as ready for review August 31, 2026 00:46

@devin-ai-integration devin-ai-integration 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.

Devin Review found 1 potential issue.

Devin Review

Comment on lines +110 to +111
"-f",
"per_page=100",

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.

🔴 Comment retrieval becomes a write request

-f per_page=100 changes the request to POST without a comment body. Every candidate is deferred, so hourly repairs stop dispatching.

Suggested change
"-f",
"per_page=100",
"-X",
"GET",
"-f",
"per_page=100",
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

…eduler-rate-limit-backoff

# Conflicts:
#	CHANGELOG.md
@seonghobae
seonghobae merged commit 883edda into main Aug 31, 2026
39 of 48 checks passed
@seonghobae
seonghobae deleted the fix/pr-review-fix-scheduler-rate-limit-backoff branch August 31, 2026 00:59
seonghobae added a commit that referenced this pull request Aug 31, 2026
Devin Review found both immediately after each PR merged (bypass-merged
past the org-wide opencode-review outage); both are real defects the local
test suites' mocks couldn't catch since they never exercised gh's actual
HTTP-method-defaulting or GitHub's actual statuses-history shape.

1. issue_comments() (#1459) added -f per_page=100 to its gh api call with
   no explicit -X GET. gh api defaults to POST once any -f/-F field is
   present unless -X overrides it, so every comment fetch became a
   malformed POST against the comment-creation endpoint -- failing every
   call outright, the opposite of this fix's purpose. Now pins -X GET.

2. rest_pr_node() (#1456) fetched classic statuses from the plural
   commits/{sha}/statuses endpoint, which returns full history with no
   dedup -- a stale success could outlive a later real failure for
   strix_evidence_state(). Switched to the singular, combined
   commits/{sha}/status endpoint, which already reports only the most
   recent status per context.

Co-authored-by: Claude <noreply@anthropic.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.

2 participants