Skip to content

Discord webhook 429 응답에 따라 분할 메시지 전송을 재시도한다 #67

Description

@opficdev

배경

DevLog_iOS run #29677641610에서 Discord webhook request for pair feat/#734 ↔ test/#701-rn fragment 1 failed with status 429 오류가 발생함.

#50에서 구성한 17개 분할 메시지 중 9개만 Discord에 전달되고, 10번째 메시지가 429를 반환한 뒤 나머지 7개는 전송되지 않음.

#51에서 전체 report가 GitHub Actions Summary에는 보존되지만, Discord에는 일부 report만 남는 부분 전송 상태가 발생함.

현재 Watcher는 Discord 응답 제한 정보에 따른 대기나 재시도 없이 첫 실패를 즉시 상위로 전달함. 재시도를 모두 소진한 뒤에는 현재와 동일하게 Watcher job 실패를 유지해야 함.

작업 범위

  • src/reportChannels/reportChannel.ts의 Discord 전송 경계에서 429 전용 재시도 추가
  • 실패한 동일 fragment를 재시도한 뒤 성공한 경우에만 다음 fragment 전송
  • 429 응답의 숫자형 Retry-After 헤더를 우선 사용하고, 사용할 수 없으면 Discord JSON retry_after 값 사용
  • 성공 응답에서 X-RateLimit-Remaining: 0이 확인되면 X-RateLimit-Reset-After만큼 기다린 뒤 다음 fragment 전송
  • Discord 제한값을 고정된 요청 횟수로 가정하지 않고 응답의 상대 대기시간 사용
  • fragment마다 최대 3회 재시도
  • Discord 전송 전체 60초 대기 예산 적용 (429 재시도 대기와 X-RateLimit-Reset-After 기반 bucket reset 대기 합산)
  • Retry-After, retry_after, X-RateLimit-Reset-After로 계산한 다음 대기시간이 남은 전체 예산을 초과하거나 유효한 대기시간을 얻을 수 없으면 일찍 재요청하지 않고 즉시 실패
  • 재시도를 모두 소진하면 기존과 같이 ReportChannelResult 실패를 반환하고 Watcher job 실패 유지
  • 최종 오류에 기존 pair label, fragment number, HTTP status와 재시도 소진 여부 포함
  • 오류에서 Discord webhook URL과 응답 본문을 노출하지 않는 기존 보호 유지
  • README.md의 report channel 설명과 문제 해결 항목에 429 대기·재시도·최종 실패 동작 추가

제외 범위

  • Discord report Markdown 생성과 구조 변경
  • src/reportChannels/discordMessageSplitter.ts의 분할 규칙 변경
  • 429 외 HTTP 상태의 재시도
  • ReportChannelResult와 공개 함수 시그니처 변경
  • reusable workflow input, secret 이름과 권한 변경
  • GitHub Actions Summary와 stdout fallback 정책 변경
  • AI 예측, deterministic merge risk와 report 내용 변경

완료 기준

  • 429이 발생한 동일 fragment가 성공하거나 재시도를 소진할 때까지 다음 fragment를 전송하지 않음
  • 성공 응답에서 bucket 소진이 확인되면 다음 fragment 전송 전 reset 시간을 준수함
  • 429 외 실패는 기존처럼 즉시 실패함
  • fragment별 재시도 횟수 한도와 Discord 전송 전체 60초 대기 예산을 준수함
  • 429 재시도 대기와 성공 응답 뒤 bucket reset 대기를 동일한 전체 예산에 합산함
  • 재시도 소진 뒤 Discord 부분 전송을 성공으로 처리하지 않고 Watcher job 실패를 유지함
  • GitHub Actions Summary에는 Discord 결과와 관계없이 전체 report가 보존됨
  • 기존 fragment 순서와 실패 위치 정보가 유지됨
  • webhook URL과 Discord 응답 본문이 오류에 노출되지 않음
  • 공개 report channel 및 reusable workflow 계약이 변경되지 않음
  • README에 실제 동작이 반영됨

검증

  • Retry-After 숫자 헤더 우선 적용 테스트
  • JSON retry_after fallback 테스트
  • 성공 응답의 bucket 소진 후 다음 fragment 대기 테스트
  • 동일 fragment 재시도와 다음 fragment 순서 테스트
  • fragment별 최대 3회 재시도 테스트
  • Retry-After, retry_after, X-RateLimit-Reset-After 대기 합산 기준 60초 전체 예산 초과 테스트
  • 누락되거나 유효하지 않은 대기시간 처리 테스트
  • 500429 외 상태의 즉시 실패 회귀 테스트
  • 최종 오류의 pair label, fragment number, status와 재시도 소진 정보 테스트
  • webhook URL과 응답 본문 비노출 테스트
  • GitHub Actions Summary와 stdout 보존 테스트
  • npm run build 통과
  • npm test 통과

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions