1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
# 033 — 연속 읽기를 유지하며 큰 문서의 쓰기량 줄이기
2026-09-24. 이전 [031](031-relocatable-document-preview.md)에서 확인한 문제는
1 MiB 문서의 1 B 교체에도 현재 저장소가 새 파일에 1,048,600 B를 기록한다는 점이다.
이 결과는 Pure의 기본 저장소를 곧바로 교체한 것이 아니라, 같은 앱 인터페이스에
붙인 **선택형 저장 경로**를 실제 CLI·HTTP·TLS·GTK 작업으로 검증한 중간 결과다.
이 보고서는 페이지 전용 바이너리와 결과 파일의 당시 상태를 기록한다.
`store-paged.c`는 이후 [034](034-hybrid-document-storage.md)의 인라인·페이지
혼합형으로 확장돼, 현재 소스를 새로 빌드하면 이 보고서의 페이지 전용
바이너리와 동일하지 않다.
## 선택한 표현과 유지한 계약
[`store-paged.c`](../../prototypes/connections/store-paged.c)는 현재 Linux AArch64 호스트의
16 KiB 페이지를 사용한다. 작은 불변 루트 파일에는 문서 길이·버전·페이지 참조를
기록하고, 데이터 파일에는 새 페이지를 덧붙인다. 길이가 같은 교체에서는 영향을
받은 페이지만 기록한 다음 새 루트를 `fsync`·`rename`·디렉터리 `fsync` 순서로 공개한다.
읽기는 필요한 페이지들을 하나의 예약된 가상 주소 범위에 매핑하므로 기존
`PureSpan` 소비자는 여전히 연속 바이트를 본다. `MAP_FIXED` 매핑은 자신이 예약한
범위 안에서만 사용한다. 이 호출의 정렬·교체 규칙은 [mmap(2)](https://man7.org/linux/man-pages/man2/mmap.2.html)에,
파일과 디렉터리 동기화의 구분은 [fsync(2)](https://man7.org/linux/man-pages/man2/fsync.2.html)에,
이름 교체의 원자성은 [rename(2)](https://man7.org/linux/man-pages/man2/rename.2.html)에
기술되어 있다.
읽기 시작 시 공유 잠금으로 루트와 데이터 파일을 함께 선택하고 매핑이 끝나면 잠금을
놓는다. 오래된 매핑은 새 루트가 공개되고 이전 데이터 파일이 제거돼도 이전 바이트를
유지한다. 쓰기는 기존과 같은 `.writer.lock`의 배타 잠금을 사용하므로 버전 충돌·동시
작성 규칙을 유지한다. 큰 문서의 세그먼트가 살아 있는 데이터의 약 2배를 넘거나
작은 문서의 세그먼트가 최소 8페이지 상한에 이르면 새 세대를 작성하고,
공개가 내구화된 뒤 낡은 세대와 크래시 후 남은 세그먼트·임시 루트를 수거한다.
삽입·삭제처럼 뒤쪽 바이트 위치가 바뀌는 작업은 아직 새 세대 전체를 기록한다.
## 같은 실제 작업의 측정
[`check_write_cost.py`](../../prototypes/connections/check_write_cost.py)는 동일한
`editor.pure` CLI로 문서를 만든 뒤 1 B를 교체하고 `strace`에서 **객체 파일로 성공한
사용자 공간 `write` 요청의 바이트 수**를 합산한다. 저장 장치의 물리 쓰기량, 저널,
메타데이터·디렉터리 동기화 비용 또는 실행 시간을 뜻하지 않는다.
| 작업 | 기존 불변 파일 | 선택형 페이지 저장소 | 판단 |
|---|---:|---:|---|
| 1 MiB 문서 마지막 1 B 교체 | 1,048,600 B / 258회 | 16,944 B / 3회 | 약 **61.9배 적은 요청 바이트** |
| 1 B 문서의 1 B 교체 | 25 B / 2회 | 16,440 B / 3회 | 약 **657.6배 많은 요청 바이트** |
[원본 측정 결과](../../prototypes/connections/results/2026-09-24-paged-storage/write-cost.json).
큰 문서의 페이지 저장소 데이터 파일은 최초 1 MiB 작성 후 이 교체에서 16 KiB가
추가됐고, 루트는 560 B였다. 작은 문서에서는 전체 페이지 기록이 압도적으로
비싸다. **이 이유로 페이지 저장소는 기본값이 아니다.** 다음 설계는 작은 값의
인라인 표현과 큰 값의 페이지 표현을 같은 의미론 아래에서 선택해야 한다.
선택형 CLI의 링크된 `text`는 39,960 B로 기존 CLI의 35,807 B보다 4,153 B 늘었으나,
기존과 같은 40,960 B 상한을 통과했다. 기본 실행 코어는 바꾸지 않았다.
[코드·의존성 결과](../../prototypes/connections/results/2026-09-24-paged-storage/paged-profile.json).
1 MiB 새 문서 편집의 선택형 CLI 피크 RSS는 2,080 KiB이며 현재 4 MiB 회귀 상한을
통과했다. 이 수치는 대규모 동시 작업이나 파일시스템 캐시를 포함하지 않는다.
[메모리 결과](../../prototypes/connections/results/2026-09-24-paged-storage/memory.json).
## 앱과 실패 경로 검증
기존 문서 앱의 [CLI·HTTP 검사](../../prototypes/connections/results/2026-09-24-paged-storage/app-release.json),
[ASan·UBSan 검사](../../prototypes/connections/results/2026-09-24-paged-storage/app-asan.json),
[페이지 저장소 단위 검사](../../prototypes/connections/results/2026-09-24-paged-storage/unit.json)와
[계측 단위 검사](../../prototypes/connections/results/2026-09-24-paged-storage/unit-asan.json)를
통과했다. 단위 검사는 범위 읽기, 오래된 매핑, 같은 길이 교체, 세대 재작성·수거,
공개 전 크래시, 충돌, 공개 후 내구화 불확실성 및 길이 변경을 섞은 결정적 128회
편집을 확인한다.
[GTK 문서·정의 편집](../../prototypes/connections/results/2026-09-24-paged-storage/gtk-flow.json)과
[인증 TLS 문서 피어](../../prototypes/connections/results/2026-09-24-paged-storage/peer.json)도
같은 저장 경로로 1 MiB 문서와 기존 충돌·복원 작업을 통과했다. GTK 1 MiB 문서 창은
44,656 KiB RSS여서 이 저장 변경이 UI 전체 메모리 문제를 해결했다는 뜻은 아니다.
페이지 저장 형식은 `PUREP001`이며 기존 `PUREB001`과 호환되지 않는다. 패키지는
형식을 확인하고 별도 저장 디렉터리를 요구한다. [선택형 CLI 묶음](../../prototypes/connections/results/2026-09-24-paged-storage/pure-document-paged-micro-linux-aarch64.tar.gz)
(25,456 B)과 [전체 호스트 묶음](../../prototypes/connections/results/2026-09-24-paged-storage/pure-document-paged-authoring-linux-aarch64.tar.gz)
(111,765 B)을 각각 다른 위치에서 풀어
[CLI](../../prototypes/connections/results/2026-09-24-paged-storage/package-micro.json)와
[CLI·HTTP·TLS·GTK](../../prototypes/connections/results/2026-09-24-paged-storage/package-full.json)를
재검증했다. 각 묶음은 같은 입력에서 두 번 생성해 해시 일치를 확인했다.
## 공개 전 남은 조건
이 형식은 16 KiB OS 페이지를 요구하며 macOS 빌드, 기존 저장소 마이그레이션,
작은 값의 효율적인 인라인 표현이 없다. 읽기에서 페이지 참조가 흩어질수록 매핑
호출이 늘어나는 비용과 큰 동시 작업의 최악 지연도 아직 검증하지 않았다.
쓰기 syscall 바이트 감소가 실제 장치 쓰기량·처리량 개선을 보장하지 않는다.
따라서 현재 기본 배포는 기존 저장소를 유지한다. 선택형 경로의 다음 검증은
작은 값과 큰 값의 혼합 작업, 오래 살아 있는 읽기, 크래시 뒤 저장 공간 수거 및
실제 앱 지연을 같은 계약 아래에서 확인하는 것이다.
페이지 전용 결과의 바이너리 해시와 풀어서 실행 가능한 묶음은 위 결과 디렉터리에
보존했다. 현재 소스의 후속 구현은 `make -C prototypes/connections check-hybrid`로
검증한다. 페이지 전용 소스 빌드 명령은 현재 트리에 더 이상 제공하지 않는다.
|