본문으로 건너뛰기

ADR-0041: 학습 데이터셋 export 다운로드 — part 단위 API 스트리밍 프록시

  • 상태: 승인
  • 날짜: 2026-08-06
  • 관련: ADR-0027(데이터셋 레이어), ADR-0039(스토리지 쿼터), 이슈 #2989 M1, 설계 docs/DESIGN_DATASET_DOWNLOAD.md

맥락

데이터셋 export 는 완료 시 export_uri(S3 URI)만 반환했다. prod SeaweedFS 는 ClusterIP 전용이라 외부 사용자는 산출물에 접근할 수 없었고(S3 자격 배포 문제), presigned URL 도 같은 이유로 동작하지 않는다(에픽 #2989 결정). 산출물은 단일 객체가 아니라 part-0001.jsonl+ (50k 행/part) prefix 이며, export_uri 는 슬래시로 끝나는 prefix 다.

결정

  1. 다운로드 = JWT 인가·감사를 통과하는 API 스트리밍 프록시 (GET .../exports/{eid}/download) — blob 프록시(S3 비노출)와 동형. S3 외부 노출·presigned 비채택 (에픽 축과 정합).
  2. part 단위 서빙 (?part=N + X-Export-Part-Count 헤더) — 서버 이어붙임 스트리밍은 Content-Length/Range 계산에 전 part HEAD 가 필요해 정합이 깨진다. 이어붙임은 클라이언트(CLI) 책임: part 를 순차로 받아 단일 파일로 저장.
  3. Range 는 part 내 단일 바이트 범위만 (bytes=a-b/a-/-suffix) — 206 + Content-Range. 형식 오류·다중 범위·불만족 범위는 전부 416 (조용한 전체 200 폴백은 클라이언트 이어받기를 오염시키므로 금지). parse_byte_range 는 M4 video 뷰어(blob Range)가 재사용할 유틸.
  4. 행 0건 completed export (part 0개 — run_export 의 if buf 가드 실측): 빈 200 + X-Export-Part-Count: 0. part 부재 + row_count>0 은 502 (GC/불일치).
  5. S3 접근은 svc 레벨 sync 헬퍼(list/head/stream)로 감싼다 — boto3 직접 호출은 테스트에서 스텁 불가(CI 30s 타임아웃). 스트리밍은 iter_chunks(256KB) 로 전체 메모리 적재 금지.
  6. 파일명(<name>-v<N>[-partNNNN].jsonl)은 사용자 입력이므로 mlflow 프록시의 Content-Disposition 인젝션 방어(제어문자=통째 대체 + RFC5987)를 동형 적용.

비채택 대안

  • presigned URL — SeaweedFS ClusterIP 전용, 외부에서 원리적으로 불가.
  • 서버측 part 이어붙임 단일 스트림 — Range/Content-Length 정합 붕괴, 중간 part 소실 시 부분 성공이 은폐됨.
  • export 를 단일 객체로 재설계 — 대형 export 의 메모리/재시도 특성 악화, 기존 산출물과 비호환.

결과

  • CLI gend dataset download (재사용 기본·--fresh·--with-blobs) 가 이 API 를 소비 — S3 자격 배포 없이 HF-CLI 급 pull 완성 (M1).
  • M2(업로드 CLI)·M3(청크 업로드)·M4(video Range 뷰어)가 같은 유틸/패턴 위에 선다.