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
|
# 018 — 요청·DB 결과·전송의 수명을 연결한 네이티브 경로
> 역사적 기록: 이 문서의 SQLite 구현은 사용자 요구와 맞지 않아
> [019의 SQL 없는 바이트 저장](019-byte-storage.md)으로 교체했다. 아래 결과와 원자료는
> 당시 구현의 기록이며 현재 구현의 의존성·검증 결과를 뜻하지 않는다.
2026-09-23. **기존 작성물의 저장·복원을 실제 TCP와 SQLite에 연결했다.** 한 실행기에서
이동한 배치를 저장하고 다른 실행기에서 복원할 수 있다. 새 네이티브 서버는 브라우저 없이
실행되며, 기존 CLI가 요청한다. 이번 결과는 실제 연결의 구현·정확성 확인이다. 속도·전체
메모리 우위나 새 알고리즘의 증거는 아니다.
알아낸 것은 연결의 책임 범위다. DB 결과를 응답 본문 배열로 다시 복사하지 않으려면,
전송이 끝날 때까지 원본의 수명도 이어져야 한다. 느린 수신자가 있으면 데이터뿐 아니라
그 결과를 유지하는 DB 자원도 살아 있다. 본문 길이·동시 연결·대기 시간을 함께 제한했다.
이 방식이 복사하는 방식보다 전체적으로 유리한지는 이번 검사로 결정하지 않는다.
## 실제 작성 흐름
기존 `.pure` 정의에서 도형을 움직인 뒤 CLI의 `connect`, `push 0`으로 저장한다.
빈 실행기를 열어 `connect`, `pull`하면 적용 코드·현재 값·초안이 복원된다. 서버를 재시작한
뒤에도 남는다. 다른 실행기가 먼저 갱신하면 이전 revision의 쓰기를 거절해 덮어쓰지 않는다.
실행 방법은 [구현 README](../../prototypes/connections/README.md)에 있다.
숫자 VM 자체에 DB나 네트워크를 넣지는 않았다. 네이티브 연결 경로는 C17로 작성했고 기존
Node/Wasm CLI를 재사용했다. C는 현재 SQLite·POSIX 연결을 직접 확인하기 위한 구현 선택이다.
언어 선택의 성능 우위를 주장하거나 기존 Zig 코어를 다시 작성한 것이 아니다.
## 연결에서 구현한 계약
| 경계 | 구현 | 실패·완료 |
|---|---|---|
| 요청 → 입력 구간 | Content-Length 확인 후 한도 안에서 할당하고 부분 수신 | 불완전한 본문은 DB에 제출하지 않고 회수 |
| 입력 구간 → DB | 같은 바이트 구간을 `SQLITE_STATIC`으로 바인드 | 모든 경로에서 statement를 먼저 해제한 뒤 입력을 회수 |
| DB 변경 → 응답 | revision 검사와 갱신을 트랜잭션으로 실행 | COMMIT 뒤에만 성공 응답; 실패·충돌은 기존 저장 내용 유지 |
| DB 결과 → 전송 | 같은 읽기 스냅샷에서 크기를 확인하고 결과 BLOB을 대여 | 별도 응답 본문 사본 없이 헤더·본문 구간을 순서대로 전송 |
| 전송 → 회수 | 부분 전송 위치와 소유자 해제 함수를 연결 | 완료·연결 오류·기한 종료에서 원본을 한 번 회수 |
SQLite의 바인딩 규칙에 따라 입력 포인터를 statement 종료까지 유지했다.
[공식 바인딩 수명 규칙](https://www.sqlite.org/c3ref/bind_blob.html).
조회 포인터도 변환·step·reset·finalize로 무효화될 수 있으므로 전송 중에는 해당 statement를
진행하거나 해제하지 않는다. [공식 결과 포인터 수명 규칙](https://www.sqlite.org/c3ref/column_blob.html).
본문 전송 완료는 `send`가 바이트를 받아들인 시점이다. 상대 앱의 처리 완료까지 보장하지 않는다.
DB 커밋과 응답 전달은 별개다. 응답을 읽지 않은 클라이언트가 끊어져도 이미 끝난 커밋은
남는다. 조건부 갱신으로 오래된 revision의 재시도를 거절하지만, 임의 RPC의 exactly-once나
분산 트랜잭션을 제공하지 않는다. [SQLite 트랜잭션 경계](https://www.sqlite.org/lang_transaction.html).
## 검증과 한계
Linux AArch64, GCC 14.2.0, SQLite 3.46.1에서 확인했다.
- 부분 송수신, 이진 본문, 버전 충돌, DB 잠금·오류, 불완전한 요청, 다른 쓰기 중의 읽기
스냅샷, 수신 종료, 자원 포화·기한 종료, 실제 작성물 복원·재시작 등 **15개 통합 시나리오**.
- 코어 검사: 전송 중 조기 해제 금지·중복 회수 방지, 저장 실패 후 내용 보존, revision 소진,
빈 본문과 읽기 예산 검사. 일반 빌드와 ASan·UBSan 빌드 모두 통과했다.
- 기존 CLI 회귀 검사 8개 통과. 복원 실패·누르고 있는 입력이 있을 때 로컬 상태와 원격
revision이 바뀌지 않는 것도 통합 검사에 포함했다.
- 검사 종료 후 연결과 보유 본문이 모두 0이고, 본문 접수 한도와 연결 슬롯 수를 넘지 않았다.
이는 지정한 자원의 수명·한도 검사이며 전체 RAM 상한이나 처리 성능 측정이 아니다.
SQLite 내부 할당·복사, 페이지 캐시·WAL, OS 버퍼, 클라이언트의 직렬화 비용은 남는다.
수신 헤더에 함께 들어온 본문 접두부도 한 번 복사한다. 장기 읽기는 WAL checkpoint 진행을
늦출 수 있어 디스크 비용도 포함한 판단이 필요하다. [SQLite WAL 읽기·checkpoint 규칙](https://www.sqlite.org/wal.html#concurrency).
DB 호출은 아직 동기식이므로 디스크 작업 중 다른 연결 처리가 지연될 수 있다. macOS 실행,
전원 장애·디스크 고장, 원격 배포, 인증·TLS, 범용 HTTP/RPC, 네이티브 UI는 검증 범위 밖이다.
동적 링크한 SQLite 자체를 sanitizer로 다시 빌드한 것은 아니다.
[원자료·재현](../../prototypes/connections/results/2026-09-23-native-connections/README.md),
[전송 수명](../../prototypes/connections/flow.c),
[DB 연결](../../prototypes/connections/store.c),
[호스트](../../prototypes/connections/server.c),
[기존 CLI 연결](../../prototypes/workbench/remote.mjs).
## 다음 결정
현재 경로와 바이트 구간의 수명은 네이티브 코드에 고정되어 있다. Pure 정의에서 이 연결을
작성하거나 실행 준비 단계에서 불필요한 연산·전달을 제거하는 기능은 아직 없다.
다음은 **이 저장 작업의 입력·검증·외부 효과·응답 관계를 실행 가능한 연결 정의로 분리하는 것**이다.
기존 문법·숫자 VM을 그대로 강제하지 않고, 외부 효과를 재계산하거나 롤백했다고 잘못 표현하지
않도록 범위를 정한다. 소비자의 요구와 수명에서 판단 가능한 전달 비용부터 다룬다.
새 DB·RPC 기능을 병렬로 늘리거나 종료한 공유/복사 벤치마크를 다시 실행하지 않는다.
|