Skip to content

fix: live_scores 수집이 강제 종료돼도 실시간 피드가 얼지 않게 한다 - #169

Open
choigod1023 wants to merge 1 commit into
mainfrom
codex/fix-live-scores-resilience
Open

fix: live_scores 수집이 강제 종료돼도 실시간 피드가 얼지 않게 한다#169
choigod1023 wants to merge 1 commit into
mainfrom
codex/fix-live-scores-resilience

Conversation

@choigod1023

Copy link
Copy Markdown
Owner

문제

배포 사이트에서 진행 중인 경기가 라이브 점수 없이 "결과 확인 중" 으로만 표시됐다.

원인은 fly.io 수집기 장애다. deploy/supervisor.pyrun_livesrc/live_scores.py180초에 강제 종료하는데, 이 스크립트는

  • 네이버 4리그 × 5일 조회
  • 진행 중 야구 경기별 타석·주자 상세 조회
  • NAMED 5일 조회

를 전부 순차로 받은 뒤 맨 끝에 파일을 한 번만 썼다. 상류 응답이 느려 180초를 넘기면 persist_artifact에 도달하기 전에 죽어 live_scores.json이 마지막 성공 시점에 그대로 얼어붙는다.

프론트(web/src/lib/match-status.js)는 피드가 10분 넘게 낡으면 진행 경기를 pending("결과 확인 중")으로 강등하고 점수를 감춘다. 이 게이트 자체는 낡은 값을 라이브인 척 보여주지 않으려는 의도된 설계라서 건드리지 않았다.

실측 (2026-09-04 02:00 UTC 무렵)

로그:  실시간 점수 예외: TimeoutExpired: 'src/live_scores.py' timed out after 180 seconds  (약 3분마다 반복)
헬스:  live_stored_at 2026-09-04T01:02:17Z  (약 1시간 정지) / odds_stored_at 01:50 (정상)

원천 API는 외부에서 정상·빠름(네이버 0.1초, NAMED 약 2초)이라, fly 머신의 CPU 경합으로 스크립트가 180초 안에 못 끝나는 것으로 보인다.

변경

수집기가 스스로 시간을 지키고 받은 데까지 반드시 저장하게 한다. deploy/supervisor.py는 건드리지 않는다.

  • FETCH_BUDGET_SECONDS(150초) 마감. 네이버·NAMED 반복의 매 날짜 경계에서 남은 시간을 확인해 초과 시 수집 중단.
  • 네이버 단계가 예산의 절반을 넘겨 걸리면 그 시점 결과를 먼저 저장. 평상시(수 초)에는 마지막에 한 번만 저장(불필요한 쓰기 없음).
  • 부분 저장에도 generated_at을 새로 찍어 프론트가 최신으로 취급하게 하고, 진단용 partial: true를 남긴다.
  • 요청당 타임아웃 축소: 네이버 20→10초, NAMED 20→10초, 타석 상세 12→8초.
  • 조회 일수 5→4일(-2..+1). -3일은 이미 종료돼 merge_recent_gamesprevious로 보존하므로 매회 다시 받을 이유가 없다.
  • 타석 상세 조회는 한 회 실행에서 SITUATION_LIMIT(12경기)까지만.
  • 문서 생성 로직을 build_document로 분리해 부분 저장과 최종 저장이 공유.

테스트

  • 예산 소진 시 저장 발생·NAMED 요청 생략·partial 플래그 확인
  • build_document의 진행 수 집계, partial 노출 조건, 45일 내 종료 이력 보존
  • python -m pytest -q 전체 333건 통과
  • 실 API 스모크: main() 약 3.7초 완료, n_games=460, 정상 generated_at

후속 (이 PR 범위 밖)

  • 경기별 observed_at 추가 → 프론트가 경기 단위로 신선도 판단, merge_recent_games가 보존한 옛 기록이 눈에 드러남.
  • 부분 저장 뒤 프로세스가 죽으면 4일보다 오래된 NAMED 종료 경기가 다음 전체 성공 전까지 빠질 수 있음. 드문 실패 경로라 이번엔 감수.
  • fly 머신 CPU 경합 자체(다른 looper 동반 재시작)는 별도 조사 필요.

🤖 Generated with Claude Code

supervisor(run_live)는 src/live_scores.py를 180초에 강제 종료한다. 이 스크립트는
네이버 4리그 × 5일 + 야구 타석 상세 + NAMED 5일을 전부 순차로 받은 뒤 맨 끝에
파일을 한 번만 썼다. 그래서 상류 응답이 느려 180초를 넘기면 파일이 한 줄도
새로 안 써졌고, live_scores.json이 마지막 성공 시점에 그대로 얼어붙었다.

프론트(web/src/lib/match-status.js)는 피드가 10분 넘게 낡으면 진행 중 경기를
"결과 확인 중"으로 강등하고 라이브 점수를 감춘다. 실측: 배포 사이트에서
피드가 약 1시간 멈춰 모든 진행 경기가 "결과 확인 중"으로 표시됐다.

수집기가 스스로 시간을 지키고 받은 데까지 반드시 저장하도록 고친다.

- FETCH_BUDGET_SECONDS(150초) 마감을 두고, 네이버·NAMED 반복의 매 날짜
  경계에서 남은 시간을 확인해 초과 시 수집을 멈춘다.
- 네이버 단계가 예산의 절반을 넘겨 걸리면 그 시점 결과를 먼저 저장한다.
  평상시(수 초)에는 마지막에 한 번만 저장한다.
- 부분 저장에도 generated_at을 새로 찍어 프론트가 최신으로 취급하게 하고,
  진단용으로 partial=true 를 남긴다.
- 요청당 타임아웃 축소: 네이버 20→10초, NAMED 20→10초, 타석 상세 12→8초.
- 조회 일수 5→4일(-2..+1). -3일은 이미 종료돼 merge_recent_games가 previous로
  보존하므로 매회 다시 받을 이유가 없다.
- 타석 상세 조회는 한 회 실행에서 SITUATION_LIMIT(12경기)까지만.

문서 생성 로직을 build_document로 분리해 부분 저장과 최종 저장이 공유한다.
테스트: 예산 소진 시 저장·NAMED 생략, partial 플래그, 종료 이력 보존.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: 57c9f4ba-e375-4965-a2ae-9e73581f349b


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.

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