aboutsummaryrefslogtreecommitdiffstats
path: root/docs/design
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-09-26 22:56:47 +0100
committereunhhu <[email protected]>2026-09-26 22:56:47 +0100
commita51232af741cb952d7deb72f238c583919ababc0 (patch)
tree43b250cb0d76460fb447461e878cce0a65aefc41 /docs/design
parentea631f980a029141cd0b4835f35b27cd487ff3ea (diff)
downloadpure-a51232af741cb952d7deb72f238c583919ababc0.tar.gz
pure-a51232af741cb952d7deb72f238c583919ababc0.zip
Show views from the last save's snapshot on connection threads (080)
The owner publishes a snapshot of the saved states after every save, before it hands back the answers; connection threads take the newest one each round and show views from it with a reader app of their own. A read made after a write's answer sees the write, nothing unsaved or undone is ever shown, and reads no longer wait while the owner applies events or syncs. Apps with states always get an owner thread, even with one connection thread. A round's inputs go to the owner first; its views are shown and then answered together (answering each one as it was shown made one thread's 20-row page 8% slower). On the Pi 5, with 8 connections posting durable scores and 56 reading the total, reads went from 10-16k/s at p99 13-29 ms to 107-149k/s at p99 0.6-0.7 ms, and posts stayed in range; axum and drogon, which sync under their lock, read 1.1-1.3k/s at p99 111-156 ms. A snapshot of a changed 1,024-item list costs 1.2 us (65,536 items: 87 us) against a 3-4 ms save.
Diffstat (limited to 'docs/design')
-rw-r--r--docs/design/008-module-format.md27
1 files changed, 18 insertions, 9 deletions
diff --git a/docs/design/008-module-format.md b/docs/design/008-module-format.md
index be658dd..b77ec9c 100644
--- a/docs/design/008-module-format.md
+++ b/docs/design/008-module-format.md
@@ -291,16 +291,22 @@ text·리스트가 남을 때만 위치를 유지한다.
ingress, 요청에서 빌린 PUT 응답). 효과는 비동기 인터페이스(`pure_runtime_take`/`complete`)로 소유 스레드에서 처리한다.
- **이벤트 한 바퀴가 묶음 하나다.** 준비된 요청을 모두 지연 저장 모드의 앱에 차례로 적용하고, 저장 한 번 **뒤에**
응답한다. 저장이 실패하면 묶음의 읽기까지 모두 실패한다. 같은 문서 키가 다시 오거나 문서 수가 한 저장의 한도를
- 넘으려 하면 묶음을 먼저 끝낸다. 보기와 앱 상태의 이벤트는 어느 묶음에나 들어간다. 그래서 묶음 안의 모든 요청은
- 자기 묶음의 저장으로 저장되거나 그것과 함께 실패하고, 보기는 저장될 상태만 내보낸다. 한 연결의 요청은 앞 요청에
- 답한 뒤에 읽으므로 쓴 것을 곧 읽는다.
+ 넘으려 하면 묶음을 먼저 끝낸다. 앱 상태의 이벤트는 어느 묶음에나 들어간다. 그래서 묶음 안의 모든 요청은
+ 자기 묶음의 저장으로 저장되거나 그것과 함께 실패한다. 보기는 저장된 상태만 내보낸다(아래 스냅숏). 한 연결의
+ 요청은 앞 요청에 답한 뒤에 읽으므로 쓴 것을 곧 읽는다.
- **저장소 핸들.** 문서와 상태를 저장소 핸들(`pure_store_data`)로 열어 파일 매핑 하나를 공유하고, 묶음마다 한 번만
저장소가 바뀌었는지 본다.
- **연결 스레드와 소유자.** `--threads N`은 스레드마다 같은 포트의 리스너(`SO_REUSEPORT`), epoll, 런타임 코어를 둔다.
키별 문서만 가진 모듈은 스레드마다 앱과 저장소 핸들을 두고 저장소 말고는 아무것도 나누지 않는다. 상태를 가진
- 모듈은 상태의 소유자가 하나여야 하므로, 스레드 하나가 더 앱을 소유한다. 연결 스레드는 요청을 받고 해석하고 답을
- 쓰는 일(시스템 호출이 대부분이다)을 나눠 맡고, 준비된 요청을 소유자에게 넘긴다. 소유자는 모든 스레드의 요청을 한
- 묶음으로 적용하고 저장 한 번 뒤에 각 스레드에 돌려주며(eventfd로 깨운다), 스레드는 돌려받은 것을 완료하고 답한다.
+ 모듈은 상태의 소유자가 하나여야 하므로, 연결 스레드가 하나여도 따로 소유자 스레드가 앱을 돌린다. 연결 스레드는
+ 요청을 받고 해석하고 답을 쓰는 일(시스템 호출이 대부분이다)을 나눠 맡고, 입력과 문서 요청을 소유자에게 넘긴다.
+ 소유자는 모든 스레드의 요청을 한 묶음으로 적용하고 저장 한 번 뒤에 각 스레드에 돌려주며(eventfd로 깨운다),
+ 스레드는 돌려받은 것을 완료하고 답한다.
+- **보기는 스냅숏에서.** 소유자는 저장이 성공할 때마다, 답을 돌려주기 전에 저장된 상태의 스냅숏을 게시한다.
+ 연결 스레드는 라운드마다(돌려받은 답을 처리한 뒤) 최신 스냅숏을 잡아 자기 읽기용 앱으로 그 라운드의 보기를 모두
+ 평가하고 답을 모아 보낸다. 입력은 그보다 먼저 소유자에게 넘겨, 소유자가 그동안 적용하고 저장한다. 그래서 쓰기의
+ 답을 받은 뒤의 읽기는 그 쓰기를 보고, 저장되지 않았거나 실패로 되돌린 상태는 아무도 보지 않으며, 소유자가
+ 동기화하는 동안에도 읽기는 기다리지 않는다. 스냅숏을 만들지 못하면 보기도 소유자가 답한다.
작업 슬롯의 라우트와 입력은 완료될 때까지 그 스레드의 런타임 코어가 붙잡고, 빌려준 저장소 매핑의 참조 수는
원자적이다. 저장한 묶음 뒤에는 새 요청이 100 µs 동안 끊길 때까지(최대 1 ms) 모은다. 답을 받은 클라이언트의 다음
요청이 한 저장에 들어가게 하는 것으로, 한 스레드에서는 답하는 시간이 그 구실을 한다(묶음당 이벤트 30 → 50, p50은
@@ -316,9 +322,12 @@ text·리스트가 남을 때만 위치를 유지한다.
상태 코드·ETag·오류·본문이 같고, 동시 편집이 커밋을 나누어 쓰며, `kill -9` 뒤에도 확인 응답한 편집이 모두 남는다.
앱 라우트는 키 있는 리스트 앱에 무작위 POST(쿼리 필드와 본문을 가진 메시지, text, 키)와 GET(수, text, 표의 쪽)을
800번 보내 상태 코드·오류·본문·JSON 행이 파이썬 모델과 같고, 멈췄다 다시 띄워도, 여섯 클라이언트가 보내는 중에
-`kill -9`로 죽여도 확인 응답한 이벤트(약 4,200개)가 모두 남는다. 한 스레드와 소유자 모드 모두 확인한다. 같은 서비스를
-axum·drogon으로 손수 쓴 것과의 비교는 [연구 077](../research/077-apps-as-http-services.md): 내구성 있는 쓰기 약 76배,
-읽기는 drogon과 같고 axum보다 빠르며(20행 JSON 145k/s 대 95k/s), CPU와 메모리가 가장 적다.
+`kill -9`로 죽여도 확인 응답한 이벤트(약 4,200개)가 모두 남는다. 그동안 다른 연결의 읽기는 먼저 확인 응답된 쓰기를
+모두 보고, 합계가 뒤로 가지 않으며, 다시 띄운 서버의 합계는 읽기가 본 어떤 합계 이상이다. 연결 스레드 하나와 셋에서
+모두 확인한다. 같은 서비스를 axum·drogon으로 손수 쓴 것과의 비교는 [연구 077](../research/077-apps-as-http-services.md):
+내구성 있는 쓰기 약 76배, 읽기는 drogon과 같고 axum보다 빠르며(20행 JSON 145k/s 대 95k/s), CPU와 메모리가 가장 적다.
+쓰기와 읽기를 함께 보내면 읽기가 149k/s(p99 0.6 ms)로, 잠금을 쥔 채 동기화하는 axum·drogon(1.1–1.3k/s, p99
+111–156 ms)과 동기화하지 않는 메모리 판(74–83k/s)보다 많다([연구 080](../research/080-snapshot-reads.md)).
아직 모듈 앱을 런타임 코어의 효과 단위로 세분하지는 않았다(`runtime.c`의 `definition`은 앱 전체다). 호스트가 직접 호출한다.