diff options
Diffstat (limited to 'docs/research')
| -rw-r--r-- | docs/research/085-cycle-sized-files.md | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/docs/research/085-cycle-sized-files.md b/docs/research/085-cycle-sized-files.md new file mode 100644 index 0000000..dc32b6b --- /dev/null +++ b/docs/research/085-cycle-sized-files.md @@ -0,0 +1,62 @@ +# 085 — 압축한 파일에 한 주기의 0을: 커밋이 새 블록을 받지 않게 + +2026-09-27. [084](084-parallel-syncs.md)에서 쓰기의 꼬리 지연을 만드는 것이 새 블록(꼬리를 늘리며 쓰는 0과 그 크기·블록의 +저널 커밋)이라고 확인한 뒤 고친 것. 트리 저장소(`store/store-tree.c`)의 파일 형식은 그대로다. + +착수 기준: + +- 바꿀 결정: 커밋이 필요할 때마다 꼬리를 256 KiB씩 늘리게 둘지, 압축이 새 파일을 만들 때 다음 압축까지 쓸 만큼의 0을 한 번에 + 써 둘지. 공간을 제자리에서 다시 쓰는 저장소(두 파일을 번갈아 쓰거나 고리처럼 돌려 쓰기)는 읽는 쪽이 모두 잠금을 쥐어야 + 하고(지금은 다른 프로세스도 잠금 없이 옛 파일을 매핑해 읽는다) 둘째 파일만큼 디스크를 더 쓴다. 그보다 싼 길이 있는지 본다. +- 최소 확인: 저장소만의 커밋(HTTP 없이)과 앱의 내구성 POST에서, 커밋 속도와 지연, ext4 저널 커밋 수(`/proc/fs/jbd2`)를 전과 + 번갈아 잰다. 압축 사이에 파일이 커지지 않고, 살아 있는 데이터가 줄면 파일도 준다는 것을 검사로 지킨다. +- 중단 기준: 처리량이나 지연 중 하나라도 잡음 밖으로 나빠지면 되돌린다. + +## 한 것 + +압축한 파일을 설치할 때 0을 `cycle_end`까지 써 둔다. 다음 압축은 끝이 `4 × live + slack`을 넘을 때 일어나므로, 거기에 커밋 +하나가 앞에 요구하는 여유(`TAIL_MIN`)를 더한 곳까지다. 다만 대신하는 파일이 닿았던 크기를 넘지 않는다. 그래서 저장소는 전보다 +공간을 더 쓰지 않고, 살아 있는 데이터가 줄면 파일도 따라 준다. 그 주기의 커밋은 이미 있는 블록만 덮어쓴다. 동기화가 데이터만 +내보내고 저널 커밋을 하지 않는다. + +새 검사 `cycles`(`store/test-tree-store.c`)는 두 가지를 확인한다. 4 KiB 값 32개를 1,600번 덮어쓰면, 값이 다 생긴 뒤 두 번째 +압축부터 압축 사이에 파일 크기가 한 번도 바뀌지 않는다(압축 스물 번 남짓). 같은 키를 64 B로 바꾸면 압축 세 번 안에 파일이 +가장 컸을 때의 절반 아래로 준다. 기존 검사(고장, 단계마다 죽는 프로세스, 찢긴 헤드, 레코드, 압축, 손상)도 ASan·UBSan 빌드에서 +그대로 통과한다. + +## 장치가 말한 것 + +Raspberry Pi 5, SD 카드의 ext4. + +**저장소만.** 문서 64개 중 하나의 앞에 16 B를 넣는 커밋 4,000번, 변형마다 네 라운드의 중앙값, 순서는 라운드마다 돌린다 +([journal.py](../../research/experiments/013-parallel-syncs/journal.py)): + +| | 커밋/s | p90 | p99 | 저널 커밋 | +|---|---:|---:|---:|---:| +| 전(커밋이 꼬리를 늘림) | 317.5 | 4.07 ms | 29.5 ms | 109 | +| 지금(압축한 파일에 한 주기의 0) | **385.5** | 3.98 ms | 26.9 ms | **45** | +| 상한(압축도 꼬리 늘리기도 없음) | 466.0 | 3.92 ms | 22.6 ms | 16.5 | + +([원시 결과](../../research/experiments/013-parallel-syncs/2026-09-27-journal-ext4-linux-aarch64.jsonl).) 바꾸기 전에 같은 방식으로 +잰 첫 비교에서는 366 → 447 커밋/s, p99 45.5 → 23.4 ms, 저널 커밋 113.5 → 46이었다 +([원시 결과](../../research/experiments/013-parallel-syncs/2026-09-27-journal-before-085-ext4-linux-aarch64.jsonl)). 커밋은 ++21–22%, 저널 커밋은 −59%다. 상한까지 남은 것은 압축 자체의 비용(새 파일, 이름 바꾸기, 디렉터리 동기화)이다. + +**앱.** 점수판의 내구성 POST 64연결, 4초씩 여덟 라운드([variants.py](../../research/experiments/013-parallel-syncs/variants.py), +[원시 결과](../../research/experiments/013-parallel-syncs/2026-09-27-store-variants-085-ext4-linux-aarch64.jsonl)): + +| | posts/s | p99 | +|---|---:|---:| +| 전 | 21,725 | 14.8 ms | +| 지금 | **22,881** | 17.9 ms | +| 상한 | 23,486 | 14.3 ms | + +처리량은 +5.3%다. p99는 이 실행에서 3 ms 나빴지만, 앞선 여섯 라운드 실행에서는 반대였다(23.0 → 15.9 ms). 두 실행을 합친 +라운드 열네 개의 중앙값은 15.0 ms와 15.5 ms로 같다. 이 SD 카드에서는 라운드마다 p99가 12–26 ms로 흔들려 이 차이를 가려내지 +못한다. 알아 둘 것이 하나 있다. 0을 쓰는 일이 주기에 걸쳐 다섯 번쯤 나뉘던 것이 압축 한 번에 모인다. 그래서 멈춤은 드물어지고 +한 번은 길어진다(점수판에서 약 1.1 MiB, 10 ms 안팎). 기기가 하는 일은 저널 커밋이 줄어든 만큼 적다. + +## 다음 + +- 압축을 없애는 것(상한): 쓸 공간을 제자리에서 다시 쓰는 저장소. 읽는 쪽이 매핑하는 동안 공유 잠금(`flock`)을 쥐고, 쓰는 쪽은 + 배타 잠금을 얻을 때만 옛 파일을 다시 쓴다. 디스크는 둘째 파일만큼 더 든다. 작은 저장소(한 주기가 수십 MiB 아래)부터 고려한다. |
