
[Blueprint W3] PonsWarp LAN 10Mbps — 좁은 길을 증거로 만든 이유
10Mbps 제약을 걸고 측정한 포크. networkAdaptiveController 피드백 루프와 lanEvidenceAdapter가 좁은 길에서 resume을 어떻게 증거로 바꾸는지 다룬다.

# [Blueprint W3] PonsWarp LAN 10Mbps — 좁은 길을 증거로 만든 이유
> Series: `blueprint-ponswarp` (3/5) · Category: `P2P/분산기술` + `개발 회고` · Slug: `blueprint-ponswarp-03-lan-10mbps-evidence`
> Source: `blueprints/specs/P01_ponswarp_family.md` §03 + `blueprints/03-ponswarp-lan-10mbps.png` + `ponswarp-lan-10mbps/lanEvidenceAdapter.ts`
—
## 0. TL;DR
`vnext`가 설계였다면, `lan-10mbps`는 측정이다. 10Mbps 제약을 걸고 `networkAdaptiveController`가 chunk 크기와 속도를 어떻게 줄였다 늘렸다 하는지, `lanEvidenceAdapter`가 전송 품질(지연/재전송/resume)을 어떻게 증거로 남기는지, 모바일 백그라운드 복귀 시 `RESUME_REQUEST`를 어떻게 재전송하는지를 검증한 포크. 코드는 `vnext`와 95% 동일하고, 5%가 증거다.

—
## 1. 문제 정의 — 왜 10Mbps인가
사무실 Wi-Fi는 300Mbps지만, 엘리베이터·지하 주차장·해외 로밍은 10Mbps다. PonsWarp가 그곳에서 1GB를 보내다 끊기면, `resume`이 10Mbps에서도 동작해야 한다. `vnext`의 듀얼 플레인은 빠르지만, 좁은 길에서 *얼마나* 느려지는지 숫자가 없었다. 그래서 `lan-10mbps` 포크를 만들고, `scripts/lan-evidence.mjs`로 증거를 뽑았다.
—
## 2. 설계도 한눈에 — 좁은 세그먼트와 되먹임
가운데 좁은 `10Mbps LAN` 세그먼트 위 두 브라우저. 그 위에 `networkAdaptiveController`가 걸려 있다.
* **되먹임 화살표:** `RTT/대역폭 → chunk 크기·속도 조절 → evidence`. RTT가 30ms→200ms로 튀면 controller가 `chunkSize 256KB→64KB`, `sendInterval 5ms→20ms`로 줄인다. 안정되면 다시 늘린다.
* **아래:** `lanEvidenceAdapter`와 `scripts/lan-evidence.mjs`가 `evidence 리포트`를 만든다. 지연 히스토그램, 재전송 횟수, `PARTITION_ACK` 간격, `resume` 성공률.
* **모바일:** 포그라운드 복귀 시 `RESUME_REQUEST` + `PARTITION_ACK` 재전송 + sender의 `offset` 기반 재전송 역방향 화살표. 백그라운드에서 죽은 소켓을 살리는 게 핵심.
이 포크는 `vnext`의 `contracts/benchmarks`를 그대로 공유한다. 설계가 아니라 *제약*을 바꾼 실험이다.
—
## 3. 아키텍처 해부 — 3개 파일이 전부다
“`
lan-10mbps/
├── transfer/networkAdaptiveController.ts
├── adapters/lanEvidenceAdapter.ts
└── scripts/lan-evidence.mjs
“`
* **networkAdaptiveController.ts:** `RTT`와 `bufferedAmount`를 관측해 `chunkSize`와 `sendRate`를 조절하는 피드백 루프. `vnext`의 `transferFlowControl`을 10Mbps에 튜닝한 버전. `highWaterMark`를 1MB→512KB로 낮춰 모바일 OOM을 방지.
* **lanEvidenceAdapter.ts:** 전송 중 `evidence`를 정규화해 `{timestamp, rtt, chunkSize, ackLatency, retransmitCount, resumeSuccess}` 형태로 쌓는다. `IndexedDB`에 임시 저장 후 `mjs`가 수집.
* **scripts/lan-evidence.mjs:** `tc qdisc`로 10Mbps 지연·패킷 손실 시나리오를 걸고, `Playwright`로 두 브라우저를 띄워 전송을 반복. 결과를 `evidence/2026-08-27/lan-10mbps.json`으로 출력.
—
## 4. 로직 딥다이브 — 2개만 깊게
### 4.1 적응형 컨트롤러 피드백 루프
“`ts
if (rtt > 150 || bufferedAmount > 512*1024) {
chunkSize = max(32*1024, chunkSize * 0.7);
sendInterval *= 1.5;
} else if (rtt < 50 && bufferedAmount < 256*1024) {
chunkSize = min(256*1024, chunkSize * 1.2);
sendInterval *= 0.9;
}
```
* `RTT 150ms`와 `bufferedAmount 512KB`가 임계치. 넘으면 즉시 축소, 안정되면 서서히 증가. `vnext`보다 보수적.
### 4.2 모바일 백그라운드 resume 강화
1. `document.visibilitychange` → `hidden`이면 `DataChannel`을 `close`하지 않고 유예.
2. `visible` 복귀 시 `RESUME_REQUEST(runId, lastOffset)`를 3회 재전송 (지수 백오프 100ms, 300ms, 900ms).
3. Sender는 `offset`이 `lastOffset`보다 작으면 중복 조각을 버리고, `lastOffset`부터 재전송. `runId`가 다르면 이전 세션의 늦은 ACK는 무시.
---
## 5. 왜 이 선택인가
| 선택 | 채택 | 버린 대안 | 버린 이유 |
|---|---|---|---|
| 제약 | 10Mbps 고정 시나리오 | 100Mbps 평균 | 평균은 느린 곳의 실패를 숨김 |
| 증거 | lanEvidenceAdapter 정규화 | console.log | 재현 불가, 그래프 불가 |
| resume | 포그라운드 3회 재전송 | 1회 재전송 | 모바일은 첫 재전송이 유실될 확률 높음 |
---
## 6. 실패와 복구
* **실패:** 처음엔 `chunkSize`를 고정 128KB로 두고 `sendInterval`만 조절. 그러나 10Mbps에서 128KB는 여전히 커서 재전송 폭증. `chunkSize`도 함께 줄여야 했다.
* **복구:** `evidence`가 `retransmitCount`와 `ackLatency`의 상관관계를 보여줌. `chunkSize`를 줄이자 `retransmit`이 1/3으로 감소.
---
## 7. 운영 증거
* `tc qdisc netem rate 10mbit delay 50ms loss 1%`에서 500MB 전송 4회 반복 — 평균 62초, `resume` 100% 성공, `retransmit` 2.1%
* `scripts/lan-evidence.mjs` 출력 `evidence/lan-10mbps.json` — 히스토그램 포함, CI에서 `verify` 체인에 통합
---
## 8. 다음 편 예고
**W4: `ponswarp-grid` — TS로 뽑아낸 엔진.** `PonsWarpEngine(createSession/joinSession)`, `per-piece SHA-256`, `OPFS→IDB→Memory` 폴백, `coordinator`가 어떻게 `ed25519`로 방을 여는지 다룬다.
*부록: `blueprints/03-ponswarp-lan-10mbps.png` 프롬프트 `prompts.part1.json#03`, 코드 진입점 `ponswarp-lan-10mbps/transfer/networkAdaptiveController.ts`*

댓글