aboutsummaryrefslogtreecommitdiffstats
path: root/docs/design
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-09-27 07:30:51 +0100
committereunhhu <[email protected]>2026-09-27 07:30:51 +0100
commiteb37aad3ae7f9df1d6c0b09108e3a2d69d0d4d82 (patch)
tree4ae7ca2e9ab8c0319c5215040b22c20b5832a909 /docs/design
parent194d243165c2b78d64e0263f39f7a63dbe4a1310 (diff)
downloadpure-eb37aad3ae7f9df1d6c0b09108e3a2d69d0d4d82.tar.gz
pure-eb37aad3ae7f9df1d6c0b09108e3a2d69d0d4d82.zip
Read JSON bodies in place, groups included (082)
pure-serve no longer copies a JSON body's strings into the message's payload. The ingress is the prefix, a table of each field's start and size, and the body as it came; the connection thread scans the body once, puts numbers into the prefix and records where each string is. Only a string with escapes is unescaped, over itself (it never grows). A group of fields comes as an object inside the body and, for a fixed field, as its path in the query (?to.zip=7). A group where a field belongs, a value where a group does, an array or true/false/null is json_type; an unknown member, inside a group too, json_field. The string scan and UTF-8 checks now pass over eight bytes at a time while none needs a look (pure_utf8_step, so every text check gains). On the Pi, posting {"name":"k","text":"x..."} to a stateless app went from 652-655 to 3,737-3,953 requests/s with 256 KiB of text (server CPU 1.52 to 0.25 ms a request, peak RSS 5.2 to 3.2-3.4 MiB) and from 29.1k to 76.3-76.7k/s with 4 KiB; the load generator gains a json method and benchmarks/http/echo.aup is the app.
Diffstat (limited to 'docs/design')
-rw-r--r--docs/design/008-module-format.md15
1 files changed, 10 insertions, 5 deletions
diff --git a/docs/design/008-module-format.md b/docs/design/008-module-format.md
index 0f0a6d8..5156e0a 100644
--- a/docs/design/008-module-format.md
+++ b/docs/design/008-module-format.md
@@ -266,16 +266,21 @@ text·리스트가 남을 때만 위치를 유지한다.
|---|---|
| `GET /views/이름` | 보기의 값. 수는 가장 짧은 왕복 10진수, text는 UTF-8 그대로(`text/plain`). text 상태를 그대로 보이는 보기(`view page = note`)는 저장된 익스텐트를 빌려준다([079](../research/079-lent-views.md)) |
| `GET /views/표?from=N&count=M` | 표의 행: `{"rows":전체,"from":N,"items":[{열: 값}…]}`(`application/json`). 요청한 행의 칸만 계산한다. 값이 없는 칸은 `null` |
-| `POST /inputs/이름` | 이벤트. text 입력은 본문이 text, 키 입력은 누름(down 뒤 up). 메시지는 고정 필드(u32, 수)를 쿼리(`?points=3`)로, 가변 필드 하나를 본문으로 받거나, `Content-Type: application/json` 본문의 객체로 필드를 받는다(문자열은 바이트·text, 수는 u32·수). 응답 본문은 핸들러의 `reply`(메시지 필드) |
+| `POST /inputs/이름` | 이벤트. text 입력은 본문이 text, 키 입력은 누름(down 뒤 up). 메시지는 고정 필드(u32, 수)를 쿼리(`?points=3`, 그룹 안이면 `?to.zip=7`)로, 가변 필드 하나를 본문으로 받거나, `Content-Type: application/json` 본문의 객체로 필드를 받는다(문자열은 바이트·text, 수는 u32·수, 그룹은 안의 객체). 응답 본문은 핸들러의 `reply`(메시지 필드) |
| `GET`·`PUT /objects/키` | 모듈에 키별 메시지 `show`·`edit`가 있으면 기존 문서 서버(`server.c`)와 같은 계약. `GET`은 `show`, `PUT`(`If-Match`/`If-None-Match`)은 `edit` |
- **실패는 실패 종류로 답한다.** 이벤트가 실패하면 `X-Pure-Error`에 종류를 싣는다. `not_found` 404, `exists` 409,
`limit`·`divide`·`clamp`·`non_finite`·`encoding` 422, UTF-8이 아닌 text 400. 값이 없는 보기는 404 `no_value`, 없는 이름은
404 `no_view`·`no_input`, 모자라거나 모르는 쿼리 필드는 400 `missing_field`·`query`다.
-- **JSON은 연결 스레드가 풀어 둔다.** 앱 입력의 ingress는 `[u32 페이로드 크기][페이로드][온 그대로의 JSON]`이다. 본문을
- 다 받으면 연결 스레드가 JSON을 메시지의 페이로드로 옮기고(이스케이프, 서로게이트 쌍, UTF-8, 수의 문법과 범위를 확인한다)
- 나서야 요청이 준비된다. 그래서 앱 소유자는 해석하지 않고, 응답은 여전히 요청에서 빌린다. 필드가 빠지거나(`missing_field`),
- 모르거나 두 번 오거나(`json_field`), 타입이 다르거나(`json_type`), JSON이 아니면(`json`) 400이고 요청은 실행되지 않는다.
+- **JSON은 연결 스레드가 제자리에서 읽는다.** JSON 본문을 가진 앱 입력의 ingress는 `[u32 페이로드 크기 | 2^31][필드마다
+ u32 시작과 크기][페이로드: 접두부, 그다음 온 그대로의 JSON]`이다. 본문을 다 받으면 연결 스레드가 JSON을 한 번 훑는다(8바이트씩).
+ 수는 접두부에 넣고, 문자열은 JSON 안의 그 자리를 표에 적는다. 이스케이프가 있는 문자열만 그 자리에 풀어 쓴다(언제나
+ 짧아진다). 이스케이프, 서로게이트 쌍, UTF-8, 수의 문법과 범위를 확인한 뒤에야 요청이 준비되고, 소유자는 표대로 메시지를
+ 적용한다(`pure_app_message_fields`: 가변 필드가 페이로드 어디에 있든 자리를 받는다). 그래서 앱 소유자는 해석하지 않고,
+ ingress는 본문 크기에 표만 더하며, 응답은 여전히 요청에서 빌린다. 필드가 빠지거나(`missing_field`), 모르거나 두 번 오거나
+ 쿼리와 본문에 함께 오거나(`json_field`), 타입이 다르거나(필드 자리에 그룹, 그룹 자리에 값, 배열, `true`·`false`·`null`:
+ `json_type`), JSON이 아니면(`json`) 400이고 요청은 실행되지 않는다. 256 KiB 본문의 요청이 CPU 0.25 ms다
+ ([082](../research/082-json-in-place.md)).
- **라우트는 작업 슬롯에 둔다.** 요청이 가리키는 것(문서, 보기, 입력)과 메시지 필드는 받을 때 풀어 슬롯별 표와 ingress에
둔다. 연결이 묶음 전에 끊겨도 받아들인 이벤트는 실행된다. 런타임 코어는 그대로다(키는 문서 키 또는 보기·입력 이름).
- **시간.** 요청의 이벤트는 적용되는 순간의 벽시계를 `now`로 본다. 앱을 돌리는 스레드(한 스레드면 그 스레드, 아니면