aboutsummaryrefslogtreecommitdiffstats
path: root/docs/research/085-cycle-sized-files.md
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-09-27 14:46:31 +0100
committereunhhu <[email protected]>2026-09-27 14:46:31 +0100
commitaea3f640ea59d91f7ca9b193acfcc3e26740318b (patch)
tree5329bdb7ea61881c4f73d5f646e2ff1511d95a40 /docs/research/085-cycle-sized-files.md
parent87480ce170895f841c40930ce24a7a026646ae14 (diff)
downloadpure-aea3f640ea59d91f7ca9b193acfcc3e26740318b.tar.gz
pure-aea3f640ea59d91f7ca9b193acfcc3e26740318b.zip
Give a compacted file the zeros of its whole cycle (085)
Commits grew the tree store's tail 256 KiB at a time, each growth a sync that also committed the ext4 journal for the new size and blocks. A file installed by compaction now carries written zeros up to where the next compaction will come (4 x live + slack, plus a commit's margin), capped at the size of the file it replaces, so a store never takes more space than before and shrinks when its live data does. The cycle's commits then only overwrite blocks the file has. Store commits: 317.5 -> 385.5 a second, ext4 journal commits 109 -> 45 for 4,000 commits; durable app posts +5.3%, their p99 the same within this SD card's noise. The new cycles test keeps sizes still between compactions once the live data is whole, and shows the file shrinking with it.
Diffstat (limited to 'docs/research/085-cycle-sized-files.md')
-rw-r--r--docs/research/085-cycle-sized-files.md62
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 아래)부터 고려한다.