wodup 인입 비동기 잡
한국어 번역본
이 문서는 원문(영어)의 한국어 번역이다. 정본은 원문이며, 계약·게이트 판단이 갈리면 원문을 따른다. 원문: docs/data/wodup-import-async-jobs.md
wodup 인입은 public.wodup_import_batches를 인입 잡 큐로 사용한다.
인입은 일회성이다: 프로바이더 파일은 딱 한 번만 정본이고, 그 뒤로는 앱이 데이터의 주인이다. 재인입은 앱 기능이 아니라 오너가 실행하는 서비스 절차다 — 파이프라인 형상, 4대 불변식, 제거·재인입 런북은 import-pipeline.md을 본다.
흐름
- 브라우저가 원본 JSONL을 Supabase Storage에 업로드하고
wodup_import_batches행을 insert한다. 브라우저가 테이블에 직접 쓰는 것은 이 insert뿐이며, 이후의 모든 상태 변경은 상태 기계 RPC를 거친다. - 브라우저가 업로드 결과를
mark_wodup_import_batch_uploaded_v1(실패 시mark_wodup_import_batch_failed_v1)로 표시한다. 컬럼 직접 update는 회수됐다. wodup-start-import가batch_id를 받는다. 호출자 JWT는 anon 클라이언트로 검증하고 DB 작업은 service key로 수행하며, 검증된 user id는p_user_id인자로 흐른다.- Edge Function이
enqueue_wodup_import_batch(...)를 호출한다. - 배치 status가
queued가 된다. - Edge Function은 HTTP 202와 큐에 든 배치를 빠르게 반환한다.
- Edge Function이
EdgeRuntime.waitUntil(...)로wodup-process-import-jobs를 깨운다. start 함수는 무거운 정규화/인입 작업을 소유하지 않는다. - 워커가
start_wodup_import_batch로 배치를 클레임하고, 원본 파일을 내려받아 봉인된 배치 행과 크기/해시를 대조 검증하고, JSONL을 정규화하고,stage_wodup_import_batch로 스테이징하고,import_wodup_batch_to_canonical로 정본 기입한 뒤 같은 배치 행을 갱신한다. import_wodup_batch_to_canonical(...)은 스테이징된 세션 정체성이 이미 정본에 있으면 거부한다(22023/conflict_type: import_already_applied) — 이것이 일회성 게이트다. 또한 영향 범위의 통계 갱신 작업을user_exercise_stats_refresh_jobs에 넣는다.- UI는 status가
completed,completed_with_placeholders,failed가 될 때까지wodup_import_batches를 폴링한다.
인입된 세션은 앱에서 read-only다: v4 완료 세션 라이터가 source가 'barbelic'이 아닌 세션을 거부한다 (22023 / conflict_type: imported_session_read_only).
status 계약
wodup_import_batches.status가 잡 수명주기다.
uploading: 원본 파일 행을 만드는 중.uploaded: 원본 파일이 존재하고 큐에 넣을 수 있음.queued: 시작 요청이 접수됨. 백그라운드 작업이 돌아야 함.normalizing: 원본 파일을 검증·정규화하는 중.ready: 정규화 완료. 스테이징 행이 정본 기입을 기다림.importing: 스테이징 행을 앱 테이블로 실체화하는 중.completed: placeholder 없이 인입 완료.completed_with_placeholders: 미매핑 provider 키가 오너 스코프 external 종목으로 실체화된 채 인입 완료 (전역 placeholder 2단계는 폐지 — 매핑 실패는 BRIDexternal계보로 그 자리에서 한 번에 실체화된다).failed: 인입이 종료 에러에 도달함.
브라우저는 start 요청에서 긴 인입 작업을 기다리지 않는다. 잡을 시작하고 배치 행만 폴링한다. 워커는 start 함수, Supabase cron, 또는 미래의 전용 워커 러너가 깨울 수 있다.
오디언스
enqueue_wodup_import_batch · start_wodup_import_batch · stage_wodup_import_batch · import_wodup_batch_to_canonical은 service_role 전용이다. 호출자는 Edge Function뿐이며 브라우저는 부르지 않는다. 워커가 storage를 service_role(BYPASSRLS)로 읽으므로 소유권 가드는 배치 행 자체가 진다: wodup_import_batches_storage_ownership_check가 storage_path와 normalized_storage_path를 <user_id>/로 시작하도록 강제한다.
재시도 시맨틱
이미 queued인 배치에 wodup-start-import를 다시 불러도 안전하다. DB enqueue 함수는 행을 queued로 유지하고 last_operation_id만 갱신한다.
워커가 중복 기동돼도 start_wodup_import_batch(...)는 uploaded·queued 배치만 클레임할 수 있다. 이미 normalizing·importing인 배치는 첫 워커의 소유로 남는다.
이미 정본에 도달한 배치의 재실행은 일회성 게이트가 거부한다. 되돌아가는 길은 오너 런북뿐이다: 프로바이더 데이터 전량 제거(remove_import_data_v1) → 보존된 원본으로 배치 requeue(requeue_wodup_import_batch_v1) → 인입 재시작 — import-pipeline.md을 본다.
함수 경계
wodup-start-import: 요청 검증, 배치 소유권 확인, 배치 enqueue, 워커 기동.wodup-process-import-jobs: 큐에 든 배치를 클레임해 정규화·스테이징·정본 기입·최종 배치 status 갱신을 수행._shared/wodup-import-worker.ts: 재사용 가능한 워커 구현 — 미래의 워커 진입점이 인입 알고리즘을 복사하지 않게 한다.
통계 갱신
정본 기입은 브라우저 요청에서 통계를 직접 계산하지 않는다. 영향받는 유저/종목 범위를 user_exercise_stats_refresh_jobs로 보내 통계 작업이 잡 id로 관측·재시도 가능하게 유지한다. 그 큐의 워커 진입점은 stats-process-refresh-jobs Edge Function이며 process_user_exercise_stats_refresh_jobs(...)를 호출한다.