Skip to content

CI: GitHub App API rate limit 소진으로 게이트 잡이 저장소 전체에서 실패한다 #1465

Closed

Description

@Sadturtleman

작업 유형

maintenance — CI·빌드·의존성·저장소 운영 유지보수

주 담당 모듈

platform — Android 앱·CI·빌드·릴리스·저장소 운영

📜 Overview

GitHub App installation 의 API rate limit 이 소진돼, gh api 를 쓰는 게이트 잡들이 저장소 전체에서 실패하고 있다. 코드 문제가 아니라 지금 열린 모든 PR 이 같은 자리에서 빨갛다.

gh: API rate limit exceeded for installation. ... (HTTP 403)
##[error]Process completed with exit code 1

실측 — 작성자·워크플로를 가리지 않는다

PR 작성자 실패 잡 job id
#1377 Sadturtleman guard (merge-order-guard) 확인 시각 02:28
#1377 Sadturtleman Repository Quality 02:28
#1265 Sadturtleman Classify CodeQL impact · Repository Quality 02:2x
#1447 1hyok Repository Quality / Classify CodeQL impact 99038176045
#1440 1hyok 위와 동일 99038184198

가장 이른 관측은 01:22 UTC, 가장 늦은 관측은 02:34 UTC 다. 그 사이 재실행(gh run rerun --failed)을 세 번 걸었고 전부 같은 403 으로 다시 죽었다 — 재실행은 남은 quota 를 더 쓸 뿐이라 해법이 아니다.

곁들여 관측된 것 둘

  1. mergeability 판정도 같이 망가졌다. fix(home): 주간 요약 그리드에 실제 기록 수를 연결한다 (#562) #1055 · fix(home): 시안에 없는 기록 카테고리 카드를 걷는다 (#700) #1197 이 CONFLICTING / DIRTY 로 표시되는데, 두 브랜치 모두 origin/develop 을 조상으로 포함한다(단일 merge-base, git merge-base --is-ancestor = yes). 충돌이 존재할 수 없는 상태라 표시가 사실과 다르다.
  2. Android Managed Device 잡이 timeout(exit 124)으로 죽는다 (feat(mindrecord): 작성 실패를 텔레메트리에 남긴다 — 쓰기 승격 3종 (#964) #1278, job 99038245936 — Managed Device XML 결과가 없습니다). rate limit 과 같은 원인인지 부하 때문인지는 분리해서 봐야 한다.

왜 지금 터졌나 (가설)

오늘 develop 에 30건 넘게 병합되면서 PR 당 워크플로가 동시에 돌았다. 이 저장소의 게이트들은 잡마다 gh api 를 여러 번 부른다 — merge-order-guard 는 closing 이슈마다 blocked_by 를, review-debt-guard 는 열린 PR 전수를, Classify pull request impact 는 PR 메타를 조회한다. PR 수 × 잡 수 × 잡당 호출수가 곱해져 installation 한도(시간당 15,000)를 넘긴 것으로 보인다.

✅ 수용 기준

  • 게이트 스크립트의 gh api 호출 수를 잡 단위로 줄인다 — 같은 PR 메타를 잡마다 다시 부르지 않고 한 번 조회해 job output 으로 넘긴다
  • rate limit(HTTP 403 + rate limit exceeded) 을 일반 실패와 구분한다. 지금은 「가드가 사실을 확인하고 거절함」과 구분되지 않아, 빨간 체크를 보고도 원인을 로그까지 들어가야 안다
  • 한도 소진 시 재시도 대기(x-ratelimit-reset 까지 backoff) 또는 명시적 neutral 종료로 바꾼다 — 지금은 즉시 exit 1 이라 재실행이 quota 를 더 태운다
  • gh api rate_limit 을 주기적으로 찍어 남은 quota 를 관측 가능하게 한다

📍 Note

  • 이 이슈는 관측 보고다. .github/ 는 내 담당 area 가 아니라 조치는 담당자 몫으로 남긴다.
  • 당장 급한 PR 은 한도가 회복될 때까지(installation 한도는 시간당 리셋) 기다리는 것 외에 방법이 없어 보인다. 재실행을 반복하면 회복이 더 늦어진다.

Activity

  1. github-actions commented on Aug 29, 2026

    @github-actions
    Contributor

    이 이슈는 필수 메타데이터를 판정할 수 없어 자동으로 닫았습니다.

    • 작업 유형이 없거나 허용된 값이 아닙니다.
    • 주 담당 모듈이 없거나 허용된 값이 아닙니다.

    이슈 등록 양식에 맞게 본문을 보완한 뒤 다시 열어 주세요.

  2. Sadturtleman commented on Aug 29, 2026

    @Sadturtleman
    CollaboratorAuthor

    곁들여 — GMD 잡이 결과 없이 timeout 으로 죽는 일이 반복됩니다

    rate limit 과 별개 증상이라 같이 적어 둡니다. 오늘 제 PR 세 곳에서 같은 형태로 나왔습니다.

    ##[error]Managed Device XML 결과가 없습니다: app/build/outputs/androidTest-results/managedDevice
    ##[error]Process completed with exit code 124
    
    PR 잡 job
    #1278 Pixel 2 API 30 androidTest 99038245936
    #1278 Pixel 2 API 34 accessibility smoke 99062855467
    #1265 Pixel 2 API 30 androidTest 06:43 회차

    124 는 timeout 이고 XML 이 아예 안 나옵니다 — 테스트가 실패한 것이 아니라 실행이 끝나기 전에 잘린 것입니다. 같은 커밋을 로컬 pixel2Api30 에서 돌리면 통과합니다(#1265 는 2/2, #1055 는 41/41).

    재실행하면 대체로 통과하지만, 승인된 PR 이 인프라 사정으로 매번 한두 번씩 더 돌아야 머지되는 상태입니다. 한도(timeout-minutes)를 늘릴 자리인지, 에뮬레이터 부팅이 오래 걸리는 것인지 한 번 봐 주시면 좋겠습니다 — 지금은 재실행이 유일한 대처입니다.

  3. added
    maintenanceCI·빌드·의존성·저장소 운영 유지보수
    area:platformCI·빌드·릴리스·저장소 운영 영역
    on Aug 29, 2026
  4. added
    pr-open이 이슈를 구현하는 PR 이 열려 있다 — base 가 기본 브랜치가 아니어도 드러낸다
    on Aug 30, 2026
  5. 1hyok commented on Aug 30, 2026

    @1hyok
    Collaborator

    현행성 재판정 — 수용 기준 4개 항목별

    본문이 쓰인 뒤 #1526 이 머지되면서 일부가 이미 착륙했습니다. 지금 기준으로 다시 세어 봤습니다.

    # 수용 기준 판정 근거
    1 잡당 gh api 호출 수 감축 남아 있었음 → PR #1598 #1526 의 커밋 메시지가 «이 PR 범위 밖» 이라고 명시하고 넘겼습니다. merge-order-guard.yml 이 base 이름을 pulls/<n>(REST)로 물은 뒤 같은 PR 을 GraphQL 로 다시 물었고, 닫는 이슈마다 issues/<n> 을 더 불렀습니다
    2 한도 소진과 일반 실패 구분 부분 이행 → PR #1598 로 완결 ensure-api-quota.mjs classify 가 덮지만 merge-order-guard.yml · review-debt-guard.yml 두 곳에만 배선돼 있었습니다. 정작 위 표에서 함께 빨개진 Repository Quality · Classify CodeQL impact 는 무방비였습니다
    3 backoff 또는 명시적 종료 부분 이행 → PR #1598 로 완결 같습니다. ensure 가 리셋까지 대기하거나(evaluateQuota 의 wait) «한도 소진» 을 명시하고 끝냅니다(exhausted). 배선된 곳이 2/4 였습니다
    4 gh api rate_limit 주기 관측 남아 있었음 → PR #1598 게이트 잡이 도는 순간에만 찍혔습니다. PR 이 돌지 않는 시간대의 소진은 여전히 보이지 않았고, 그게 이 이슈에서 «가장 이른 관측이 01:22 UTC» 였던 이유입니다

    gh api 를 부르는 워크플로 전수 (origin/develop 기준)

    워크플로 gh api 줄 수 보호 (재판정 전) 보호 (PR #1598 뒤)
    merge-order-guard.yml 8 → 6 ✅ ✅
    review-debt-guard.yml 7 ✅ ✅
    codeql.yml 3 ❌ ✅
    repository-quality.yml 3 ❌ ✅
    release-aab-preflight.yml 3 ❌ ❌ (주 2회 스케줄)
    review-request-all.yml 3 ❌ ❌ (게이트 아님)
    android-managed-device.yml 1 ❌ ❌ (dispatch 경로만)
    release-distribution.yml 1 ❌ ❌ (릴리스 전용)
    screenshot-baseline-generate.yml 1 ❌ ❌ (dispatch 전용)
    unit-test.yml 1 ❌ ❌ (dispatch·merge_group 경로만)

    gh api 를 부르는 워크플로 10개 중 PR 이벤트마다 도는 게이트는 4개이고, 재판정 시점에 그중
    2개만 보호되고 있었습니다. PR #1598 이 나머지 2개를 덮어 4/4 가 됩니다. 나머지 6개는 PR
    이벤트 경로에서 돌지 않거나(스케줄·dispatch·릴리스) 게이트가 아니어서 곱셈에 들어가지 않습니다.
    (집계에서 정책 테스트 *.test.mjs 안의 gh api 문자열은 뺐습니다 — 실행되는 호출이 아닙니다.)

    본문에서 낡은 것

    • 📍 Note 의 «.github/ 는 내 담당 area 가 아니라 조치는 담당자 몫으로 남긴다» — 작성 시점
      기준입니다. 이 이슈는 지금 area:platform / @1hyok 로 어사인돼 있어 제 몫이 맞고, 그렇게
      진행했습니다.
    • «재실행 외에 방법이 없어 보인다» — ensure 가 리셋까지 기다리거나 «한도 소진» 을 명시하고
      끝내므로, 사람이 재실행을 걸어 quota 를 더 태우는 경로는 이제 게이트 4곳에서 닫힙니다.

    곁들여 관측된 둘

    수용 기준에 들어 있지 않아 이 PR 의 범위가 아닙니다.

    1. mergeability 가 CONFLICTING/DIRTY 로 표시된 것 — 한도 소진의 증상으로 봅니다. GitHub 이
      한도 아래에서 mergeability 를 재계산하지 못하면 마지막 값이 그대로 남습니다.
    2. Managed Device exit 124 — 한도와 별개 축이라 따로 진단해야 합니다. 잡 로그만으로는 OOM 과
      adb timeout 이 갈리지 않습니다.

    줄일 것이 없다고 판정한 두 자리

    • Repository Quality 와 Classify CodeQL impact 의 pulls/<n>/files 중복. 같은 데이터를
      두 번 받지만 서로 다른 workflow run 이라 job output 으로 넘길 수 없습니다. 캐시·아티팩트로
      우회하면 두 run 이 동시에 돌아 경합하고, git diff 로 바꾸면 truncation 대조와 rename 처리를
      잃습니다 — CodeQL 분석 범위를 정하는 자리라 그 위험이 이득보다 큽니다. PR 이벤트당 1회짜리
      중복이라 그대로 둡니다.
    • review-debt-guard 의 열린 PR 순회. 본문의 가설은 이 루프가 PR 마다 여러 번씩 태운다는
      것이었는데, 코드를 전수로 읽어 보니 대부분의 PR 은 reviews 한 번을 받고 «아무도 판정하지
      않음» 으로 continue 합니다. 커밋·커밋별 파일·코멘트 2종까지 가는 무거운 경로는 최신 판정이
      변경요청이면서 같은 리뷰어에게 재요청까지 걸린 소수뿐이라, 열린 PR 30건 기준 호출은 30여 회이지
      150회가 아닙니다. 사람의 PR 을 닫는 가드라 이득 없는 재작성은 위험만 늘립니다.

    PR #1598 이 머지되면 이 이슈는 Closes 로 함께 닫힙니다.

  6. added a commit that references this issue on Aug 30, 2026
  7. removed
    pr-open이 이슈를 구현하는 PR 이 열려 있다 — base 가 기본 브랜치가 아니어도 드러낸다
    on Aug 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:platformCI·빌드·릴리스·저장소 운영 영역maintenanceCI·빌드·의존성·저장소 운영 유지보수

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions