diff options
Diffstat (limited to 'docs/research')
| -rw-r--r-- | docs/research/079-lent-views.md | 52 |
1 files changed, 52 insertions, 0 deletions
diff --git a/docs/research/079-lent-views.md b/docs/research/079-lent-views.md new file mode 100644 index 0000000..1cb8453 --- /dev/null +++ b/docs/research/079-lent-views.md @@ -0,0 +1,52 @@ +# 079 — 보기도 빌려준다: text 상태를 저장된 익스텐트 그대로 + +2026-09-26. [로드맵](../roadmap.md) 단기 1 "Vectored loan 완성". + +착수 기준: + +- 바꿀 결정: 앱의 보기를 HTTP로 내보낼 때 값을 버퍼에 복사해 보낼지, 저장소 매핑의 익스텐트를 그대로 `writev`에 넘길지. +- 남은 질문: 문서 GET은 이미 빌려준다([075](075-http-host-against-axum.md)). 앱 보기(`GET /views/NAME`)는 text 상태를 + 그대로 보여 줄 때도 매번 복사했다. 빌려주면 얼마나 달라지고, 빌려준 것은 언제나 돌아오는가(답이 끝날 때, 읽지 않고 + 떠날 때, 다른 스레드가 답할 때, 그사이 저장소가 압축될 때)? +- 최소 확인: 같은 1 MiB 보기를 복사하는 빌드와 빌려주는 빌드로 번갈아 재고, 빌려준 동안과 뒤의 저장소 매핑 수와 예산을 + 확인한다. +- 중단 기준: 한 번이라도 다른 버전이 섞여 나가거나, 답이 끝난 뒤 매핑이나 예산이 남으면 되돌린다. + +## 한 것 + +- `pure_module_text_state`: 보기가 text 상태 그대로(`view page = note`, 명령 하나 `TEXT_LOAD`)인지 알려 준다. +- `pure-serve`는 그런 보기의 text draft 현재 버전을 저장소 매핑이 모두 담고 있으면 그 익스텐트를 답의 본문으로 빌려준다. + 문서 GET과 같은 경로다. 답은 매핑 참조를 쥐고(예산에 계상), 소켓에 다 들어가고 티켓이 풀릴 때 놓는다. 방금 고쳐 + 아직 저장소에 없는 조각이 있으면 전처럼 복사한다. 계산한 text(보간, 조건식)는 복사한다. +- 익스텐트 배열은 필요한 만큼 늘린다(세는 패스가 없다). 대여 답도 보기의 `Content-Type`을 유지한다. + +## 장치가 말한 것 + +Raspberry Pi 5, 서버는 CPU 0–2, 부하는 CPU 3. 1 MiB 메모를 `GET /views/page`로 8연결이 5초간 읽는다. 복사 빌드와 대여 +빌드를 번갈아 두 번씩 쟀다. + +| | 복사 | 대여 | +|---|---:|---:| +| 연결 스레드 1: 답/s | 776–817 | **1,875–1,901** | +| p50 | 9.2 ms | **3.5 ms** | +| 최대 RSS | 10.3 MiB | **5.7 MiB** | +| 연결 스레드 3 + 소유자: 답/s | 590–617 | **1,167–1,194** | +| 최대 RSS | 11.9–12.4 MiB | **6.2 MiB** | + +같은 크기의 문서 GET은 axum 1,372/s, drogon 708/s였다([075](075-http-host-against-axum.md)). 큰 답에서는 스레드 하나가 더 +빠르다. 답 하나에 1 MiB를 받는 부하 생성기(코어 하나)와 스레드 사이의 인계가 몫을 차지하는 것으로 보인다. + +## 회수 확인 + +aup `make check`의 `server_lent_views`(스레드 1, 연결 스레드 3 + 소유자): + +- 한 연결에 1 MiB 보기 32개를 이어 보내면 커널 버퍼가 받지 못한 답이 빌린 채 기다린다. 그동안 메모를 다섯 번 바꿔도 + 32개 답은 각각 한 버전 전체로 나온다(첫 답은 바꾸기 전 버전). +- 기다리는 동안 저장소 파일의 매핑은 2개다(빌려준 옛 매핑 + 현재). 1 MiB 쓰기가 이어지면 저장소가 압축해 파일을 바꾸는데, + 빌려준 옛 매핑은 `(deleted)`인 옛 파일의 페이지를 쥐고 있다. 답을 다 읽으면 1개로 돌아온다. +- 읽지 않고 연결을 닫아도(답의 티켓이 끝남) 1개로 돌아오고, 서버를 멈출 때 `retained_payload_bytes`는 0이다. 연결 + 스레드 3개일 때는 소유자가 빌려준 매핑을 연결 스레드가 놓는다(참조 수가 원자적이다). + +검사를 만들며 두 가지를 배웠다. 수신 버퍼를 4 KiB로 줄인 독자는 루프백에서도 지연 ACK와 맞물려 초당 40 KB쯤으로 기어간다. +그러면 서버의 쓰기가 10초 넘게 진전이 없어 연결 기한이 연결을 닫는다(의도된 동작). 그래서 답이 서버 쪽에 걸리게 하려면 +수신 버퍼를 줄이는 대신, 커널이 흡수하는 양(수 MB)보다 훨씬 많이 이어 보내야 한다. |
