Skip to content

[Sub] 영상 워커 서비스 기본 경로 구현#124

Open
chowon442 wants to merge 3 commits into
devfrom
feat/86-worker-service
Open

[Sub] 영상 워커 서비스 기본 경로 구현#124
chowon442 wants to merge 3 commits into
devfrom
feat/86-worker-service

Conversation

@chowon442

@chowon442 chowon442 commented Jun 4, 2026

Copy link
Copy Markdown
Member

📌 관련 이슈

🏷️ PR 타입

  • ✨ 기능 추가 (Feature)
  • 🐛 버그 수정 (Bug Fix)
  • ♻️ 리팩토링 (Refactoring)
  • 📝 문서 수정 (Documentation)
  • 🎨 스타일 변경 (Style)
  • ✅ 테스트 추가 (Test)

📝 작업 내용

  • features/video/worker/에 Cloud Tasks HTTP push용 /jobs/run handler와 VideoWorkerRunner를 추가했습니다.
  • video_jobs 저장소 계약에 lease acquire / heartbeat / release 경로를 추가해 live lease 거부, stale lease 탈취, self-fence를 검증할 수 있게 했습니다.
  • worker runner가 stage orchestrator와 heartbeat/cancel poll을 병렬로 실행하고, stage/segment boundary마다 user-facing progress를 기록하도록 연결했습니다.
  • cancel poll 및 stage 경계 cancel_requested 확인을 추가해 running job이 canceled terminal 상태로 남도록 했습니다.
  • Task 2.04 문서를 review로 갱신하고 구현/검증 완료 항목을 체크했습니다.

📸 스크린샷

uv run ruff check .
# All checks passed!

uv run ruff format --check .
# 175 files already formatted

uv run pytest \
  tests/features/video/test_worker_runner.py \
  tests/app/test_video_worker_http_handler.py \
  tests/features/video/test_video_job_client.py \
  tests/features/video/test_pipeline_skeleton.py \
  tests/app/test_video_jobs.py
# 36 passed, 1 warning

전체 테스트 참고:

uv run pytest
# 453 passed, 22 skipped, 14 failed, 2 warnings

실패 14건은 이번 변경 범위 밖의 기존 OCR 테스트 기대값 불일치입니다. tests/ocr_node/test_math_postprocessor.py는 수학 기호 LaTeX 변환 기대값과 현재 출력이 다르고, tests/ocr_node/test_models.pyOCROptions가 현재 flash/gpt4o-mini만 허용하지만 테스트는 sonnet/opus를 기대합니다.

✅ 체크리스트

  • 코드 리뷰를 받을 준비가 완료되었습니다
  • 테스트를 작성하고 모두 통과했습니다
  • 문서를 업데이트했습니다 (필요한 경우)
  • 코드 스타일 가이드를 준수했습니다
  • 셀프 리뷰를 완료했습니다

📎 기타 참고사항

  • Cloud Run gen2 서비스 구성과 instance concurrency=1 확인은 실제 배포 전 인프라 체크로 남아 있습니다.
  • sandbox 격리, worker image packaging, cancel refund terminal 정책은 후속 task 범위입니다.

Summary by CodeRabbit

  • 새로운 기능

    • 비디오 작업 비동기 워커 서비스 및 /jobs/run HTTP 실행 엔드포인트 추가
    • 세그먼트 단위 진행률 이벤트 및 표시(세그먼트 인덱스/총량) 지원
    • 워커 인스턴스 리스(lease) 관리, 하트비트, 취소 폴링 및 실행 제한 설정 추가
    • 워커 생성 시 인스턴스 ID/인증 토큰 구성 및 기본 결정 로직 추가
  • 테스트

    • 워커 HTTP 핸들러, 팩토리, 런너 동작을 검증하는 테스트 추가
  • 문서

    • 작업 체크리스트 및 운영 주의사항 업데이트

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Too many files changed? Review this PR in Change Stack to see how the pieces fit before you dive in.

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d62156d5-3a79-4984-9302-b3aeaabf1b0a

📥 Commits

Reviewing files that changed from the base of the PR and between e576a79 and 9e8be51.

📒 Files selected for processing (4)
  • src/proovy_agent/features/video/jobs/repository.py
  • src/proovy_agent/features/video/worker/http_handler.py
  • src/proovy_agent/features/video/worker/runner.py
  • tests/features/video/test_worker_runner.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/features/video/test_worker_runner.py
  • src/proovy_agent/features/video/worker/http_handler.py
  • src/proovy_agent/features/video/worker/runner.py

📝 Walkthrough

Walkthrough

이 PR은 Cloud Tasks HTTP push로 비디오 잡을 실행하는 워커 스택을 추가합니다. Settings 필드 및 앱 라우팅을 업데이트하고, SegmentProgress 이벤트를 도입하며, VideoJobRepository에 lease 수명주기와 lease-검증형 진행/종료 메서드를 추가하고, VideoWorkerRunner 및 /jobs/run HTTP 핸들러와 관련 테스트를 구현합니다.

Changes

워커 서비스 통합

Layer / File(s) Summary
설정, 라우터 등록, 모듈 진입점
src/proovy_agent/common/config.py, src/proovy_agent/app/main.py, src/proovy_agent/features/video/worker/__init__.py
Settings에 6개 워커 환경변수를 추가하고 video_worker 라우터를 FastAPI 앱에 포함하며 worker 패키지 진입점에서 주요 심볼을 재노출합니다.
세그먼트 진행 이벤트 시스템
src/proovy_agent/features/video/pipeline/stage_context.py, src/proovy_agent/features/video/pipeline/__init__.py, src/proovy_agent/features/video/pipeline/stages/render.py, src/proovy_agent/features/video/pipeline/stages/tts.py
SegmentProgressEvent와 핸들러 타입을 추가하고 StageContext에 segment_progress_events/segment_progress_handler 및 emit_segment_progress를 추가했으며, render/tts 단계에서 세그먼트 인덱스/총량을 포함한 진행 이벤트를 발행하도록 변경했습니다.
리스 기반 저장소 계약 및 구현
src/proovy_agent/features/video/jobs/repository.py
VideoJobRepository 프로토콜에 acquire_lease/heartbeat_lease/release_lease와 lease-검증 update_progress_for_lease/finalize_for_lease를 추가하고, InMemory/Postgres 구현에서 원자적 acquire, stale 탈취, heartbeat/release 제약, cancel 처리 변경을 구현했습니다.
워커 런너 핵심 구현
src/proovy_agent/features/video/worker/runner.py
VideoWorkerRunner를 추가해 lease 획득 후 파이프라인을 단계별로 실행하고 stage/segment 진행을 lease-guarded로 갱신하며, heartbeat와 cancel 폴링을 병렬 운영하고 lease 상실/취소/예외별 분기 처리를 구현했습니다.
팩토리와 HTTP 핸들러 및 인증
src/proovy_agent/features/video/worker/factory.py, src/proovy_agent/features/video/worker/http_handler.py
create_video_worker_runner가 Postgres 저장소 생성과 instance_id 결정을 담당하며, /jobs/run 핸들러는 Bearer 또는 X-Proovy-Worker-Token 헤더 기반 인증(hmac.compare_digest) 후 runner.run 호출 및 결과 매핑을 수행합니다.
테스트: 핸들러·팩토리·런너
tests/app/test_video_worker_http_handler.py, tests/features/video/test_worker_factory.py, tests/features/video/test_worker_runner.py
HTTP 핸들러의 정상/재시도 가능한 실패/인증/설정 누락 동작을 검증하고, 팩토리의 DB 필수 검증 및 Postgres 저장소 선택을 확인하며, 런너의 lease/heartbeat/cancel/segment progress 흐름을 포괄적으로 검증합니다.
작업 메타데이터 및 체크리스트
tasks/phase-2-video-async-infra/04-worker-service.md
작업 상태를 review로 변경하고 완료일을 설정했으며 사전 준비 및 구현 체크리스트를 갱신했습니다.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested Labels

준원

Suggested Reviewers

  • gaeunee2
  • haein45

🐰 워커가 리스를 잡고 나르는 길,
하트비트는 초침처럼 규칙을 재며,
세그먼트 하나마다 작은 발자국 남기고,
취소가 오면 경계에서 조용히 멈출래,
파이프라인 끝에선 artifact 대신 평온을 남기네.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 34.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목 '[Sub] 영상 워커 서비스 기본 경로 구현'은 주요 변경사항인 영상 워커 서비스 구현을 명확하게 요약하고 있습니다.
Description check ✅ Passed PR 설명은 템플릿의 주요 섹션(관련 이슈, PR 타입, 작업 내용, 체크리스트)을 모두 포함하고 있으며, 작업 내용이 구체적으로 기술되어 있습니다.
Linked Issues check ✅ Passed PR의 코드 변경사항이 #86의 모든 주요 요구사항(HTTP 핸들러, runner, lease 메커니즘, progress write, cancel poll, heartbeat)을 구현하고 있습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 #86의 범위 내에 있으며, 워커 서비스 구현과 관련된 설정, 저장소, 파이프라인, HTTP 핸들러, 테스트 등으로 구성되어 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/86-worker-service

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 and usage tips.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요약

워커 runner·lease·cancel poll·progress 연동은 설계(§3.4/§3.6)와 단위 테스트 방향이 잘 맞습니다. 다만 /jobs/run HTTP 경계 인증 부재, lease 획득 실패 시 200 ack, progress write의 lease 미검증은 배포 전에 손보는 편이 안전합니다.


[문제 1]

  • 심각도: Critical
  • 범주: 보안
  • 근거: http_handler.pyPOST /jobs/run에 Cloud Tasks OIDC(또는 동등한 invoker 검증)가 없고, main.py에서 API 앱과 동일 라우터로 노출됩니다. 설계(video-generation-design.md §3.4)는 “OIDC 토큰 (Cloud Tasks → 워커)”를 전제합니다. ingress가 /jobs/*까지 열려 있으면 job_id만 알면 임의 워커 실행·리소스 소모가 가능합니다.
  • 수정안: FastAPI dependency로 OIDC JWT audience/issuer 검증을 추가하거나, 워커 전용 서비스/ingress에서 /jobs/run만 제한합니다. 최소한 배포 전 체크리스트에 “퍼블릭 API와 워커 라우트 분리”를 명시하세요.

[문제 2]

  • 심각도: High
  • 범주: 정확성
  • 근거: acquire_leaseNone이면 상태와 무관하게 SKIPPED + HTTP 200을 반환합니다. 다른 인스턴스가 살아 있는 lease를 쥔 RUNNING 잡에 Cloud Tasks 재배달이 오면, 워커는 작업 없이 2xx로 ack하고 큐 재시도가 끝납니다. 선행 워커가 비정상 종료했는데 lease가 아직 stale이 아니면(기본 480s), 잡이 RUNNING에 오래 남을 수 있습니다(lazy sweep 전까지).
  • 수정안: get(job_id) 후 분기하세요. TERMINAL 또는 이미 처리 완료 → 200 SKIPPED. RUNNING + live lease(다른 holder, stale 아님) → 503 등 재시도 유도. QUEUED 등 재시도 가능 상태는 별도 정책 검토.

[문제 3]

  • 심각도: High
  • 범주: 정확성
  • 근거: PostgresVideoJobRepository.update_progress / InMemoryVideoJobRepository.update_progress의 UPDATE가 lease_holder_instance_id를 검사하지 않습니다. self-fence·lease 탈취 후에도 이전 워커의 in-flight update_progress가 새 holder의 stage/progress를 덮을 수 있습니다.
  • 수정안: 워커 경로용 update_progresslease_holder_instance_id = :instance_id 조건을 추가하거나, repository에 instance_id를 받는 variant를 두고 _WorkerProgressWriter에서 전달하세요.

[문제 4]

  • 심각도: Medium
  • 범주: 정확성
  • 근거: 파이프라인 성공 직후 heartbeat_lease 실패 시 SELF_FENCED로 200 반환하고 finalize/artifact_object_key 저장을 건너뜁니다. 의도된 self-fence이지만, 후속 워커·lazy detection까지 중복 실행·지연 복구 비용이 큽니다.
  • 수정안: (추가 확인 필요) lease 소유가 확인된 경우에만 finalize하거나, finalize와 release를 단일 트랜잭션으로 묶고 실패 시 503으로 Cloud Tasks 재시도를 허용하는 방안을 검토하세요.

[문제 5]

  • 심각도: Medium
  • 범주: 유지보수성
  • 근거: create_video_worker_runnerdebug=True이고 database_url이 비어 있으면 InMemoryVideoJobRepository를 씁니다. 워커 프로세스에 DEBUG가 잘못 켜지면 인스턴스 간 상태가 공유되지 않습니다.
  • 수정안: 워커 팩토리에서는 in-memory fallback을 막고, database_url 필수로 fail-fast 하세요.

리스크(짧게): 환불은 task 2.07 범위로 보이며 이번 PR에서 누락된 것은 문서와 일치합니다. sync_cancel_event의 job당 get() 폴링은 segment/stage마다 DB 부하가 될 수 있어(성능) 후속 최적화 여지가 있습니다.

Open in Web View Automation 

Sent by Cursor Automation: Chowon Reviewer

return runner


@router.post("/run", response_model=RunVideoJobResponse)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical · 보안] POST /jobs/run에 OIDC/invoker 검증이 없습니다. 설계(§3.4)는 Cloud Tasks → 워커 OIDC를 전제하고, main.py에서 API와 같은 앱에 라우터가 붙어 있습니다.

수정안: OIDC JWT 검증 dependency 추가, 또는 워커 전용 서비스·ingress로 /jobs/run만 격리하세요.

skipped = await self._repository.get(job_id)
return VideoWorkerRunResult(
job_id=job_id,
status=VideoWorkerRunStatus.SKIPPED,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[High · 정확성] acquire_lease 실패 시 항상 SKIPPED + HTTP 200입니다. 다른 워커가 live lease를 쥔 RUNNING 잡에 Cloud Tasks 재배달이 오면 2xx ack 후 큐 재시도가 멈춰, 선행 워커 비정상 종료 + lease 미만료 시 잡이 RUNNING에 묶일 수 있습니다.

수정안: terminal/이미 완료 → 200 SKIPPED; RUNNING + live foreign lease → 503 등 재시도 유도.


async def update_progress(
self,
job_id: str,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[High · 정확성] update_progress UPDATE에 lease_holder_instance_id 조건이 없습니다. lease 탈취/self-fence 이후 이전 워커의 in-flight progress write가 새 holder 상태를 덮을 수 있습니다.

수정안: WHERE ... AND lease_holder_instance_id = %(instance_id)s (또는 워커 전용 API)로 holder 일치 시에만 갱신하세요.

attempt_id=active_attempt_id,
)

if await self._repository.heartbeat_lease(job_id, instance_id=self._instance_id) is None:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Medium · 정확성] 파이프라인 성공 직후 heartbeat_lease 실패 시 finalize/artifact 저장 없이 SELF_FENCED + 200입니다. 의도된 fence일 수 있으나 중복 실행·복구 지연 비용이 큽니다.

수정안: (추가 확인) finalize+release를 트랜잭션으로 묶거나, 이 경로에서 503 재시도 정책 검토.

@chowon442 chowon442 self-assigned this Jun 4, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/proovy_agent/features/video/jobs/repository.py`:
- Around line 247-250: The lease liveness check in acquire_lease relies on
progress_updated_at but request_cancel also updates that same field, which can
prevent stale-lease takeover; change request_cancel so it does not modify
progress_updated_at (either stop updating it or introduce and update a separate
cancel_requested_at/timestamp field) and update any code that reads/writes
progress_updated_at accordingly (check functions acquire_lease and
request_cancel and the related logic around the second occurrence noted in the
diff) to ensure lease liveness and cancel signaling are separated.

In `@src/proovy_agent/features/video/worker/http_handler.py`:
- Around line 47-59: Update the _get_video_worker_runner function signature to
declare a return type of VideoWorkerRunner and add the corresponding import for
VideoWorkerRunner at the top of http_handler.py; specifically, change def
_get_video_worker_runner(request: Request): to def
_get_video_worker_runner(request: Request) -> VideoWorkerRunner: and import
VideoWorkerRunner (used by create_video_worker_runner) so type checkers see the
correct return type.

In `@src/proovy_agent/features/video/worker/runner.py`:
- Around line 127-130: The completed branch in the event handling currently only
calls await self.sync_cancel_event() but doesn't act on it, so a cancel right at
the final stage can be ignored; modify the logic inside the runner (the
event.status handling where self.sync_cancel_event() is called) so that when
event.status == "completed" and await self.sync_cancel_event() is True you
immediately raise asyncio.CancelledError (mirroring the started branch
behavior), ensuring cancelled requests are honored at the final stage.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4a118afc-b7dc-4519-b00c-5fb22d92c37e

📥 Commits

Reviewing files that changed from the base of the PR and between 9428d19 and e576a79.

📒 Files selected for processing (15)
  • src/proovy_agent/app/main.py
  • src/proovy_agent/common/config.py
  • src/proovy_agent/features/video/jobs/repository.py
  • src/proovy_agent/features/video/pipeline/__init__.py
  • src/proovy_agent/features/video/pipeline/stage_context.py
  • src/proovy_agent/features/video/pipeline/stages/render.py
  • src/proovy_agent/features/video/pipeline/stages/tts.py
  • src/proovy_agent/features/video/worker/__init__.py
  • src/proovy_agent/features/video/worker/factory.py
  • src/proovy_agent/features/video/worker/http_handler.py
  • src/proovy_agent/features/video/worker/runner.py
  • tasks/phase-2-video-async-infra/04-worker-service.md
  • tests/app/test_video_worker_http_handler.py
  • tests/features/video/test_worker_factory.py
  • tests/features/video/test_worker_runner.py

Comment thread src/proovy_agent/features/video/jobs/repository.py
Comment thread src/proovy_agent/features/video/worker/http_handler.py Outdated
Comment thread src/proovy_agent/features/video/worker/runner.py
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.

[Sub] 영상 워커 서비스 — http_handler + runner + progress + cancel poll (worker/) [2.04]

1 participant