aboutsummaryrefslogtreecommitdiffstats
path: root/docs/design/008-module-format.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/design/008-module-format.md')
-rw-r--r--docs/design/008-module-format.md34
1 files changed, 30 insertions, 4 deletions
diff --git a/docs/design/008-module-format.md b/docs/design/008-module-format.md
index b70b8f0..4b9855c 100644
--- a/docs/design/008-module-format.md
+++ b/docs/design/008-module-format.md
@@ -13,8 +13,9 @@ v3은 **text 상태와 커서**, text 상수, text 입력, text 보기를 더했
v4는 **레코드**, 레코드의 **리스트** 상태, 리스트의 **인덱스** 상태를 더했다.
v5는 값을 가진 채 합류하는 분기(조건식)와, 리스트 항목마다 행을 내는 **표 보기**, 한 번 계산한 값을 여러 번
쓰게 하는 스택 복사(PICK·SLIDE, 툴체인의 함수 인라인용)를 더한다. 이전 판은 받지 않는다(생산자는 aup 하나다).
-v5에는 뒤에 집계(FOLD), 걸러진 표, 마크, 그리고 **키 있는 리스트**와 text 비교가 더해졌다. 모두 전의 v5 모듈이 그대로
-유효하도록 더한 것이다(새 명령과 이전에 0이던 자리의 값).
+v5에는 뒤에 집계(FOLD), 걸러진 표, 마크, 그리고 **키 있는 리스트**와 text 비교가 더해졌다. 그 뒤 핸들러에서 리스트를
+도는 **걷기**(EACH)와 **메시지의 행**(레코드의 리스트인 메시지 필드와, 그 행을 담는 행 목록 상태)이 더해졌다. 모두 전의
+v5 모듈이 그대로 유효하도록 더한 것이다(새 명령과 이전에 0이던 자리의 값).
## 바이트 배치
@@ -27,10 +28,10 @@ v5에는 뒤에 집계(FOLD), 걸러진 표, 마크, 그리고 **키 있는 리�
| 16 | u32 명령 수, u32 문자열 바이트 수 |
| 24 | u16 필드, u16 컬렉션, u16 상수, u16 레코드 |
| 32 | u32 상수 블롭 바이트 수, u16 레코드 필드, u16 0 |
-| 40 | 상태 × 16 B: u32 이름, u32 타입(0 수, 1 text, 2 커서, 3 리스트, 4 인덱스), 8 B 값: 수는 f64 초기값, text는 u32 초기 상수, 커서는 u32 대상 text 상태와 u32 플래그(1이면 마크: 그 자리에 넣은 글 앞에 남는다), 리스트는 u32 레코드와 u32 키(0이면 없음, 아니면 키 필드 + 1), 인덱스는 u32 대상 리스트 상태 |
+| 40 | 상태 × 16 B: u32 이름, u32 타입(0 수, 1 text, 2 커서, 3 리스트, 4 인덱스), 8 B 값: 수는 f64 초기값, text는 u32 초기 상수, 커서는 u32 대상 text 상태와 u32 플래그(1이면 마크: 그 자리에 넣은 글 앞에 남는다), 리스트는 u32 레코드와 u32 키(0이면 없음, 아니면 키 필드 + 1, 256이면 메시지의 행 목록: 이름은 그 필드의 경로 `입력.필드`이고 키가 없다), 인덱스는 u32 대상 리스트 상태 |
| | 컬렉션 × 12 B: u32 이름, u32 1(바이트), u32 1(키별) |
| | 입력 × 16 B: u32 이름, u8 종류(1 키, 2 메시지, 3 text, 4 타이머, 5 출력: 앱이 보내는 메시지, 모양은 메시지와 같고 핸들러가 없다), u8 키별 여부, u16 0, u32 키 이름(키 입력)·주기(타이머, 1 ms–24시간을 ms로) 또는 0, u16 첫 필드, u16 필드 수 |
-| | 필드 × 8 B: u32 이름(식별자, 또는 그룹 안의 필드면 점으로 이은 경로 `user.name`), u32 타입(1 u32le, 2 바이트, 3 수(binary64), 4 text). 페이로드는 접두부와 가변 필드의 바이트다. 접두부에는 선언 순서로 고정 필드의 값과 가변 필드(바이트·text)의 u32 길이가 들어가는데, 마지막 가변 필드는 길이 없이 나머지를 차지한다. 가변 필드의 바이트는 접두부 뒤에 선언 순서로 이어진다(가변 필드가 하나면 예전의 "바이트 꼬리"와 같다) |
+| | 필드 × 8 B: u32 이름(식별자, 또는 그룹 안의 필드면 점으로 이은 경로 `user.name`), u32 타입(1 u32le, 2 바이트, 3 수(binary64), 4 text, 5 행: 비트 8–23에 그 행이 채우는 행 목록 상태). 페이로드는 접두부와 가변 필드의 바이트다. 접두부에는 선언 순서로 고정 필드의 값과 가변 필드(바이트·text)의 u32 길이가 들어가는데, 마지막 가변 필드는 길이 없이 나머지를 차지한다. 가변 필드의 바이트는 접두부 뒤에 선언 순서로 이어진다(가변 필드가 하나면 예전의 "바이트 꼬리"와 같다) |
| | 상수 × 8 B: u32 블롭 오프셋, u32 길이 (UTF-8) |
| | 레코드 × 8 B: u32 이름, u16 첫 레코드 필드, u16 필드 수(1–16) |
| | 레코드 필드 × 16 B: u32 이름, u32 타입(0 수, 1 text), 8 B 기본값: 수는 f64, text는 u32 상수 |
@@ -106,6 +107,8 @@ v5에는 뒤에 집계(FOLD), 걸러진 표, 마크, 그리고 **키 있는 리�
| 70 | FLOOR | n → n | 내림 |
| 71 | MOD | n n → n | 나눗셈을 내림한 나머지. 결과는 나누는 수의 부호를 따른다(0도). 0으로 나누면 `divide` |
| 72 | SEND | f… → | 출력 `인자`의 필드 값을 필드 순서대로 받아(u32·수는 n, 바이트는 b, text는 t) 메시지 페이로드(접두부와 가변 필드)로 만들고 이벤트의 일부로 보낸다. u32 필드의 값이 0–2³²−1의 정수가 아니면 `limit`. 핸들러 전용 |
+| 73 | OUTER_ROW | → i | 열 안의 집계 블록에서 그 열의 행(집계의 항목은 ROW). 걷기 안의 집계에서는 걷는 항목 |
+| 74 | EACH | | 걷기. `인자`의 비트 8–15는 리스트 상태, 16–31은 뒤따르는 블록의 길이 L. 그 L개 명령이 리스트가 시작할 때 가진 항목마다 차례로 핸들러의 코드로 돈다(`ROW`가 항목). 블록은 상태, 그 리스트의 항목(필드 바꾸기, 지우기), 다른 리스트를 바꿀 수 있지만 걷는 리스트에 넣을 수는 없다. 걷는 항목이나 그 앞의 항목을 지우면 걷기가 따라가 다음 항목을 건너뛰지 않는다. 핸들러 전용 |
`f`는 레코드 필드의 타입(수 또는 text)이고, `k`는 그 리스트의 키 필드 타입이다. 수 연산 결과가 유한하지 않으면 실패한다. 실패하면 그 이벤트의 모든
핸들러가 한 일이 취소된다.
@@ -139,6 +142,12 @@ v5에는 뒤에 집계(FOLD), 걸러진 표, 마크, 그리고 **키 있는 리�
리스트만 가리키고 키 필드 타입의 값을 받는다. FIND의 결과는 그 리스트의 인덱스다.
- 걸러진 표의 필터 블록은 그 리스트의 열로 검증한다. 읽기만 하고 수 하나를 남기며, 안에 집계를 둘 수 없다.
타입 다음 바이트는 표에만 쓸 수 있다.
+- 걷기(EACH)의 블록은 그 핸들러의 코드로 따로 검증하되 `ROW`(걷는 리스트의 항목)를 읽을 수 있다. 값을 남기지 않고,
+ 걷는 리스트에 넣지(LIST_APPEND·LIST_INSERT) 못하며, 안에 다시 걷기를 둘 수 없고(비용 상한), 응답(REPLY)하지 않는다(응답은
+ 이벤트에 하나다). 안의 집계는 걷는 항목을 OUTER_ROW로 읽는다. 둘레의 블록은 EACH와 그 블록을 명령 하나로 보고, 앞의 분기가
+ 블록 안으로 들어오지 못한다. 걷는 리스트는 줄기만 하므로 한 걷기는 시작할 때의 항목 수 × 블록 길이를 넘지 않는다.
+- 행 필드(타입 5)는 메시지 입력에만 있고(출력은 행을 보내지 않는다), 가리키는 상태는 행 목록이며 그 이름이 `입력 이름.필드
+ 경로`와 같다. 그래서 행 목록 하나는 필드 하나가 채운다. FIELD는 행 필드를 읽지 않는다(행은 리스트로 읽는다).
- 표는 리스트 상태를 가리키고 열은 앞에 나온 표를 가리킨다. 표 밖의 이름은 한 이름공간이고, 열 이름은 표
안에서만 유일하다.
@@ -158,6 +167,18 @@ v5에는 뒤에 집계(FOLD), 걸러진 표, 마크, 그리고 **키 있는 리�
이벤트 동안 리스트 변경은 되돌리기 기록에 쌓이고, 실패하면 거꾸로 되돌리며 성공하면 지운 항목과 바뀐 text를 해제한다.
항목의 text 필드를 읽으면 이벤트 공간에 복사한다.
+**메시지의 행.** 행 필드는 가변 필드이고, 바이트는 그 행 목록의 저장 형식(항목마다 필드 순서대로 수는 binary64, text는
+u32 길이와 바이트)이다. 메시지가 도착하면 핸들러가 돌기 전에 행 목록을 그 행들로 채우고, 유한하지 않은 수, UTF-8이 아닌
+text, 끝이 맞지 않는 바이트는 메시지를 `limit`으로 실패시킨다. 행 목록은 모든 이벤트가 끝날 때(성공이든 실패든) 비워지고,
+저장되지 않으며, 그 변경은 되돌리기 기록에 쌓이지 않는다(비울 것이므로). 그래서 핸들러는 메시지의 행을 보통의 리스트로
+읽고(걷기, 집계, `count`), 한 이벤트 안의 임시 표로 쓸 수도 있다. 한 메시지의 모든 행은 한 트랜잭션이다. 행 하나가 실패하면
+(없는 키, `require`) 어느 행도 적용되지 않는다.
+
+**걷기.** EACH는 걷는 항목의 위치를 인덱스처럼 들고 블록을 항목마다 돌린다. 블록이 걷는 리스트에서 항목을 지우면(LIST_REMOVE)
+그 위치가 걷는 항목 앞이면 한 칸 당기고, 걷는 항목 자신이면 다음 차례에 같은 위치(곧 다음 항목)를 돈다. 블록은 걷는 리스트에
+넣을 수 없으므로 걷기는 시작할 때 있던 항목 가운데 차례가 올 때까지 남은 것을 한 번씩, 순서대로 돈다. 블록이 실패하면 이벤트가
+실패하고 모든 변경이 되돌려진다. SQL의 `UPDATE … WHERE`·`DELETE … WHERE`와 한 요청의 여러 행 넣기가 한 이벤트가 된다.
+
**키 있는 리스트**는 항목을 키 순서(수는 값, text는 바이트)로 두고 키마다 항목 하나만 둔다. 맵·집합·정렬된 맵을 따로
두지 않는다. 리스트 하나가 순서의 규칙만 달리 가진다. 그래서 표 보기, 걸러진 표, 집계, 인덱스, 저장, 모듈 교체가 모두
그대로다. 넣기(LIST_APPEND)는 반씩 나눠 찾은 자리에 넣고, 같은 키가 있으면 이벤트가 `exists`로 실패한다(호스트에는
@@ -304,6 +325,11 @@ text·리스트가 남을 때만 위치를 유지한다.
쿼리와 본문에 함께 오거나(`json_field`), 타입이 다르거나(필드 자리에 그룹, 그룹 자리에 값, 배열, `true`·`false`·`null`:
`json_type`), JSON이 아니면(`json`) 400이고 요청은 실행되지 않는다. 256 KiB 본문의 요청이 CPU 0.25 ms다
([082](../research/082-json-in-place.md)).
+- **행은 객체의 배열이다.** 행 필드의 값은 레코드 필드를 이름으로 가진 객체들의 배열이다(수는 수 필드, 문자열은 text 필드,
+ 빠진 필드는 기본값). 연결 스레드가 행을 저장 형식으로 옮겨(수는 JSON보다 길 수 있다) 요청의 입력을 그만큼 늘리고
+ (`pure_runtime_grow`: 늘린 만큼 예산에 계상, 모자라면 503 `payload_budget`) 본문 뒤에 둔다. 가변 필드가 행 필드 하나뿐인
+ 메시지는 JSON이 아닌 본문을 그 행의 저장 형식 그대로 받는다(대량 넣기: 3만 행 JSON 한 요청이 초당 약 24만 행). `--reply`의
+ 응답 JSON도 같은 방식으로 행을 준다(없으면 행 없음). 행이 든 메시지는 전달 묶음(`X-Pure-Count`)에 들어가지 않는다.
- **라우트는 작업 슬롯에 둔다.** 요청이 가리키는 것(문서, 보기, 입력)과 메시지 필드는 받을 때 풀어 슬롯별 표와 ingress에
둔다. 연결이 묶음 전에 끊겨도 받아들인 이벤트는 실행된다. 런타임 코어는 그대로다(키는 문서 키 또는 보기·입력 이름).
- **시간.** 요청의 이벤트는 적용되는 순간의 벽시계를 `now`로 본다. 앱을 돌리는 스레드(한 스레드면 그 스레드, 아니면