[WarpInfra] 희소한 조각부터, 여러 피어에게 동시에
여러 Node가 한 파일을 나눠 받을 때, 스케줄러가 "어느 조각을 먼저 요청할지" 고르는 기준이 결과를 가른다. 같은 그리드라도 스케줄러에 따라 완료 시간이 몇 배씩 벌어질 수 있다. 조각 분할과 무결성 검증이 완벽해도, 어떤 조각을 누구에게 언제 요청할지 정하는 정책이 서투르면 그리드는 그저 느린 서버 여러 대가 된다. WarpInfra는 rarest-first를 택하고, Have fanout으로 조각 보유 정보를 퍼뜨리고, multi-peer piece race로 지연을 줄였다. 이 세 가지가 각각 어떤 질문에 대한 답인지, 그리고 왜 그 조합이었는지를 본다.
세 가지 메커니즘, 세 가지 질문
| 메커니즘 | 질문 | 답 |
|---|---|---|
| Rarest-first | 어떤 조각부터 요청하나? | 가장 적은 피어가 가진 조각부터 |
| Have fanout | 누가 뭘 가졌는지 어떻게 아나? | Have 메시지를 동적으로 퍼뜨린다 |
| Multi-peer piece race | 한 조각을 한 피어에게만 물어보나? | 여러 피어에 동시 요청, 먼저 온 것 사용 |
세 가지가 독립적으로 보이지만 실제로는 하나의 사슬이다. rarest-first가 작동하려면 조각별 보유 현황을 알아야 하고, 그 정보를 공급하는 게 Have fanout이고, 정보가 완벽하지 않을 때 생기는 단일 피어 대기의 빈틈을 메는 게 piece race다. 하나만 빼도 나머지의 효율이 떨어진다 — Have 정보가 없으면 rarest-first는 근거 없는 추측이 되고, race가 없으면 Have 정보의 시차가 곧 대기 시간이 된다.
이 사슬을 거꾸로 읽으면 설계 순서가 나온다. 스케줄러의 성능 문제는 결국 "정보의 신선도"와 "단일 의존의 위험"이라는 두 변수의 함수다. Have fanout이 신선도를 담당하고, race가 단일 의존의 위험을 담당한다. 스케줄러 알고리즘 자체(rarest-first)는 그 두 변수가 건강할 때만 빛난다. 알고리즘 논쟁을 하기 전에 정보 흐름과 실패 격리부터 설계해야 하는 이유가 이것이다.
rarest-first가 중요한 이유 — 마지막 조각 문제
rarest-first의 가치를 가장 선명하게 보여주는 건 시작이 아니라 끝이다. 순차 스케줄(0부터 N까지 순서대로)을 상상해 보자. 모든 수신 노드가 같은 순서로 조각을 요구하면, 초반에는 첫 조각이 온 피어가 대세를 이루고, 마지막에는 "가장 뒤 조각을 가진 피어"가 병목이 된다. 그리드 전체의 완료 시간은 마지막 조각의 도착 시간이니, 순차 스케줄의 그리드는 본질적으로 "가장 느린 한 피어"의 속도로 완료된다. 열 대가 나눠 받아도 완료 시간은 한 대 속도 — 분산의 약속이 자기 스케줄러에게 배신당하는 구조다.
rarest-first는 이 문제를 정반대로 풀었다. 가장 적은 피어가 가진 조각부터 요구한다. 희소한 조각이 먼저 복제되기 때문에, 시간이 흐를수록 조각별 보유 분포가 평탄해진다. 평탄한 분포에서는 어느 조각이든 여러 피어에게서 받을 수 있으니, 완료 직전의 병목이 구조적으로 생기기 어렵다. "마지막 조각을 기다리는" 시간을 줄이는 것이 곧 그리드 전체 완료 시간을 줄이는 것이다.
임의(random) 선택은 이 사이 어딘가에 있다. 순차보다는 낫지만 보급률을 제어하지 못한다. 운 나쁘면 특정 조각이 계속 희소한 상태로 남는다. 확률 게임에서 마지막 조각 문제는 사라지지 않고, 다만 출현 빈도가 낮아질 뿐이다. 그리고 문제는 빈도가 아니라 꼬리다 — 백 번 중 한 번 나오는 마지막 조각 병목이, 시스템의 완료 시간 보장을 무너뜨린다. 분산 시스템 설계에서 기대값보다 꼬리를 먼저 보는 습관이 여기서도 유효하다.
이 판단은 새로운 발명이 아니라 BitTorrent 세계에서 수년간 검증된 지혜의 차용이다. "검증된 지혜의 차용"은 니치 편의 부품 철학과도 일치한다. 스케줄러를 새로 발명하는 대신 검증된 정책을 우리의 Have/piece 구조에 맞게 조립한 것이다.
Have fanout — 스케줄러의 연료 공급
rarest-first 판단의 입력은 조각별 보유 현황이다. 이 정보가 늦거나 부분적이면 판단도 늦거나 틀린다. Have fanout은 피어가 새 조각을 완성할 때마다 그 사실을 그리드에 퍼뜨리는 메커니즘이다. "동적"이라는 수식이 붙은 이유는, 정적 목록 배포 방식과 달리 조각이 생길 때마다 정보가 흐르기 때문이다.
fanout의 비용도 있다. Have 메시지 자체가 대역폭과 처리를 쓴다. 조각이 수만 개인 대용량에서 Have 메시지가 조각 전송을 압도하면 본말전도다. 그래서 fanout 설계의 핵심은 "얼마나 자주, 누구에게"이고, 이는 메타 채널의 부하 상한과 직결된다. 다행히 Have 정보는 바이트가 아니라 메타다 — 플레인 분리 원칙(S1-02)에 따라 이 트래픽은 coordinator와 별개의 피어 경로로 흐르고, 바이트 대역폭과 경쟁하지 않는다.
정보의 시차 문제도 있다. Have 메시지가 도착하기 전까지 스케줄러는 그 조각의 존재를 모른다. 시차가 스케줄러의 눈을 흐리게 하는 정도는 감수하되, race가 그 흐림의 대가를 청구하지 않게 하는 게 두 메커니즘의 역할 분담이다. 완벽한 정보를 기다리는 설계는 대기 비용을 치르는 설계고, 불완전한 정보로 빠르게 판단하되 빈틈을 안전망으로 덮는 설계가 실무에서 이긴다.
구현 커밋
| 커밋 | 내용 |
|---|---|
258740e | dynamic rarest on Have fanout, WarpInfra GUI 바인딩 |
3f2d9a4 | endgame multi-peer piece race, WarpInfra UI 패널 |
문서 근거: docs/reports/warpinfra-have-and-bench-2026-07-18.md, warpinfra-phase-a-work-report-2026-07-17.md.
이 문서들의 날짜를 보면 Have와 벤치가 같은 시점(2026-07-18)에 다뤄졌다는 걸 알 수 있다. Have fanout이라는 정보 메커니즘과 그것을 측정하는 벤치가 세트로 기록됐다는 뜻이다. 정보 메커니즘은 "동작한다"만으로는 부족하고 "정보가 얼마나 빨리 퍼지는가"가 성능의 상한을 정하니, 구현과 측정이 같은 워크리포트에 묶인 건 자연스러운 짝이다.
두 커밋의 순서가 의미 있는데, Have fanout이 먼저고 piece race가 나중이다. race는 "endgame"이라는 수식과 함께 들어왔다 — 전송 말미의 잔여 조각을 여러 피어에게 동시에 요청해 먼저 도착한 것을 쓰는 방식이다. race를 상시 켜두면 중복 요청이 대역폭을 낭비하니, 가장 병목이 되는 국면에만 켜는 것이 합리적 배치다. Have 정보가 좋아질수록 race가 필요한 국면은 줄어들고, race는 안전망으로 남는다.
GUI 바인딩과 UI 패널이 커밋 제목에 함께 언급된 것도 건너뛰지 말자. 스케줄러 같은 내부 엔진을 고칠 때 관찰 인터페이스를 같이 만들지 않으면, 그 엔진은 동작은 해도 설명할 수 없는 존재가 된다. "지금 어떤 조각을 누구에게 요청 중인가"를 UI에서 볼 수 있어야 rarest-first가 실제로 rarest-first인지 눈으로 확인할 수 있다. 내부 정책과 외부 관찰이 같은 커밋으로 들어간 것은 관찰 가능성을 사후 추가 기능이 아니라 구현의 일부로 취급한다는 규율이다.
채택/기각 표
| 스케줄러 | 채택? | 이유 |
|---|---|---|
| 순차(0..N) 요청 | ❌ | 마지막 조각 병목 |
| 임의(random) 선택 | ❌ | 보급률 제어 불가 |
| Rarest-first + race | ✅ | 평탄화 + 지연 견고성 |
이 조합이 만드는 전체 그림
세 메커니즘을 합치면 그리드의 조각 유통은 이렇게 그려진다. 어떤 피어가 조각을 완성하면 Have가 퍼지고(S2- Have fanout), 모든 수신 노드는 최신 보유 지도를 근거로 가장 희소한 조각을 경쟁적으로 요청하며(rarest-first), 지연이나 정보 시차로 멈춘 요청이 있으면 endgame race가 그 자리를 메운다(multi-peer race). 각 층은 아래 층의 실패를 덮는다.
이 그림에서 의도적으로 빠진 것이 coordinator다. 이 모든 판단이 피어 측에서 일어난다. 중앙이 "이 조각은 이 피어에게"를 지시하지 않는다 — 플레인 분리 원칙의 스케줄링 버전이다. 중앙 스케줄러가 있으면 전역 최적이 가능할 수도 있지만, 중앙이 죽으면 스케줄링도 죽고, 중앙의 판단 부하가 노드 수에 비례해 늘어난다. 분산 스케줄링은 전역 최적을 포기하는 대신 장애 격리와 확장성을 가져온다. 데이터 그리드에서 완료 시간의 꼬리보다 장애 격리가 중요하다고 판단한 것이 이 배치다.
검증 한계 (솔직히)
현재 벤치 증거는 로컬 루프백/소규모 수준이다. multi-peer race가 실제 WAN 다중 머신에서 이득인지는 RC1 게이트의 20GB 실측 전까지 "미측정"으로 남긴다. 이 구분이 중요한 이유는, 루프백 환경에서는 지연 분산이 사실상 없어서 race의 이득이 구조적으로 과소평가되기 때문이다. 반대로 말하면 LAN에서 race의 이득이 작게 나와도 WAN에서는 클 수 있다 — 그래서 "효과 없다"도 "효과 있다"도 아직 말할 수 없다. 증거가 만들어질 자리는 20GB multi-host 실측이고, 이 편은 그때 다시 검증할 가설의 기록이다.
루프백 벤치가 그래도 남겨주는 것도 있다. 정확성 — race로 중복 수신된 조각이 최종 해시 검증에서 문제를 일으키지 않는가, Have 정보의 지연이 잘못된 스케줄링으로 이어져도 최종 상태가 올바른가 — 같은 올바름 속성은 규모와 무관하게 검증된다. 성능 주장은 미측정으로 남기고 올바름 주장은 지금 증명한다. 이 순서가 바뀌면(성능 데모를 먼저 하고 올바름을 나중에) 전송 제품에서 가장 위험한 형태의 출시가 된다. 빠른데 가끔 틀리는 전송은 느린 것보다 나쁘다.