Hugging Face의 Xet Team은 Git LFS를 한 번에 끊지 않고 Hub를 옮겼습니다.
배포 직후 약 6%의 Hub 다운로드가 새 인프라를 탔고, 6개월 동안 500,000개 저장소와 20 PB가 이동했습니다.
실무에서는 호환성 경로, 배경 이전, 배치 기준, 피크 처리량을 따로 점검해야 합니다.
huggingface.co의 「Migrating the Hub from Git LFS to Xet」는 Hub의 저장소 체계를 Git LFS에서 Xet로 옮긴 과정을 설명합니다. 이 글은 그중 무엇이 실제로 바뀌었는지, 어떤 수치가 나왔는지, 그리고 운영팀이 무엇을 배워야 하는지를 읽는 관점으로 정리합니다.
Hugging Face는 왜 하드 컷오버를 택하지 않았나
Hugging Face의 Xet Team은 올해 1월 새 저장소 백엔드를 배포했습니다. 그 직후 약 6%의 Hub 다운로드가 새 인프라를 통해 흘러갔습니다. 이 출발점은 작아 보이지만, 이후 확장의 방향을 보여줍니다.
원문은 6개월 동안 500,000개 저장소와 20 PB가 Xet로 이동했다고 밝힙니다. 또한 5월에는 새 사용자와 조직의 기본값이 되었습니다. 즉, 이 전환은 한 번에 자르는 방식이 아니라 점진적 확장 방식이었습니다.
이 글의 핵심은 기술 명칭보다 운영 철학에 있습니다. 저장소를 멈추지 않고, 기존 흐름을 깨지 않고, 새 흐름을 뒤에서 키웠습니다. 그래서 Git LFS와 Xet가 일정 기간 공존할 수 있었습니다.
500,000개 저장소와 20 PB가 보여주는 전환 규모
아래 표는 원문에서 확인된 값만 정리한 것입니다. 각 숫자가 무엇을 재는지 함께 적었습니다.
| 항목 | 원문 인용 | 무엇을 재는가 |
|---|---|---|
| 배포 직후 전환 비율 | shortly after shifted ~6% of Hub downloads through the infrastructure |
새 인프라가 초기에 흡수한 다운로드 비율입니다. |
| 6개월 누적 이동 규모 | In 6 months, 500,000 repositories holding 20 PB joined the move to Xet |
전환에 합류한 저장소 수와 총 데이터 규모입니다. |
| 배경 이전 상태 | is keeping 500,000 repos in sync with Xet storage all without missing a beat |
현재도 동기화를 유지 중인 저장소 수입니다. |
| 배치 기준 | Batches the files into jobs based on size or number of files; either 1000 files or 500MB, whichever comes first |
이전 작업 한 번에 묶는 파일 수와 용량 한도입니다. |
| bartowski 사례 | With nearly 500 TB across 2,000 repos, bartowski's migration uncovered a few weak links |
대규모 실험에서 드러난 저장소 수와 데이터 규모입니다. |
| RichardErkhov 사례 | RichardErkhov (1.7PB and 25,000 repos) |
개별 대규모 전환 사례의 저장소 수와 용량입니다. |
| mradermacher 사례 | mradermacher (6.1PB and 42,000 repos |
더 큰 전환 사례의 저장소 수와 용량입니다. |
| CAS 처리량 | mradermacher and RichardErkhov migrations: CAS peaked around ~300 Gb/s, while still serving ~40 Gb/s of everyday load. |
대규모 이전 시의 최대 처리량과 평상시 처리량입니다. |
이 표에서 눈에 띄는 점은 숫자가 한 축으로만 커지지 않는다는 점입니다. 저장소 수, 총 용량, 평상시 처리량, 피크 처리량이 함께 움직입니다. 운영 전환은 단일 수치가 아니라 여러 축의 동시 관리라는 사실이 드러납니다.
브리지와 배경 이전이 호환성을 지킨 방식
보통 저장소 체계 전환은 세 가지 부담을 만듭니다. 서비스를 멈춰야 하고, 클라이언트를 바꿔야 하고, 이전 작업을 예약해야 합니다. 원문은 이 세 가지를 그대로 따르지 않았다고 보여줍니다.
첫째, 저장소를 한 번에 끊는 하드 컷오버가 없었습니다. 둘째, Xet가 켜진 저장소는 Xet 파일과 LFS 파일을 함께 담을 수 있었습니다. 셋째, LFS에서 Xet로의 저장소 이전은 잠금 없이 배경에서 실행할 수 있었습니다.
이 구조가 중요한 이유는 사용자 경험이 바뀌는 지점을 줄였기 때문입니다. Xet를 아는 클라이언트인 hf-xet와 huggingface_hub 연동은 전체 Xet 경로를 탑니다. 업로드에서는 content defined chunking으로 파일을 조각 내고, 다운로드에서는 복원 정보를 요청합니다.
반대로 오래된 huggingface_hub와 huggingface.js는 다른 경로를 탑니다. 이 경우 Git LFS Bridge가 단일 presigned URL을 만들어 LFS처럼 보이게 합니다. 겉으로는 같은 다운로드처럼 보여도, 뒤에서는 파일을 다시 조립해 전달합니다.
즉, 호환성은 기능을 덜어내서 지킨 것이 아닙니다. 경로를 둘로 나눠서 지켰습니다. 새 클라이언트는 Xet 스택을 직접 타고, 옛 클라이언트는 브리지를 거칩니다.
이 분기는 전환 중에도 사용자 흐름을 덜 흔들게 합니다.

내일 점검할 운영 항목은 무엇인가
원문을 실무 관점으로 바꾸면 점검 항목은 분명합니다. 첫째, 저장소를 멈추지 않고도 이전할 수 있는지 봐야 합니다. 둘째, 잠금 대신 배경 작업으로 흘릴 수 있는지 봐야 합니다.
셋째, 구버전 클라이언트가 남아도 동작을 유지할 경로가 있는지 봐야 합니다.
이 글은 배경 이전이 웹훅으로 시작된다고 설명합니다. 이벤트는 분산 큐로 들어가고, orchestrator가 처리합니다. orchestrator는 저장소에 Xet를 켜고, LFS 리비전을 조회하고, 작업을 묶습니다.
그다음 migration worker pods가 LFS 파일을 내려받아 xet-core로 CAS에 올립니다.
여기서 배치 기준은 실무적으로 매우 중요합니다.
원문은 작업을 1000 files 또는 500MB 단위로 묶는다고 밝힙니다.
이 기준은 너무 잘게 쪼개도 비효율이고, 너무 크게 묶어도 병목이 생긴다는 뜻입니다.
또 하나의 교훈은 임시 디스크와 마운트 포인트입니다. bartowski 사례에서는 글로벌 중복 제거용 shard 파일이 /tmp에 먼저 써진 뒤 캐시로 옮겨졌습니다. 그런데 워커 pod에서 /tmp와 Xet 캐시가 서로 다른 마운트 포인트에 있었습니다.
그 이동이 실패했고, 디스크가 차서 No space left on device 오류가 이어졌습니다.
대규모 이전에서는 처리량만 보아서는 안 됩니다. CAS는 mradermacher와 RichardErkhov 이전 중 약 300 Gb/s까지 올랐습니다. 동시에 평상시에는 약 40 Gb/s를 계속 처리했습니다.
따라서 평시와 피크를 분리한 관찰이 필요합니다.

자주 묻는 질문
Git LFS에서 Xet로 옮길 때 서비스를 멈춰야 합니까?
아니요, 원문은 하드 컷오버가 없었다고 설명합니다. 배경 이전이 잠금 없이 돌아갔기 때문에 다운로드와 업로드를 끊지 않고 전환할 수 있었습니다.
오래된 huggingface_hub나 huggingface.js도 Xet 저장소를 쓸 수 있습니까?
네, 쓸 수 있습니다. 오래된 클라이언트는 Git LFS Bridge를 통해 Xet 저장소에 접근합니다. 다만 Xet-aware 클라이언트와는 다른 경로를 탑니다.
운영에서 가장 먼저 확인할 지점은 무엇입니까?
배치 기준과 디스크 경로를 먼저 확인해야 합니다.
원문은 1000 files 또는 500MB 배치를 쓰고, /tmp와 캐시 마운트 포인트 차이로 장애가 날 수 있음을 보여줍니다.
대규모 이전에서는 경로 설계가 처리량만큼 중요합니다.
