aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/design/019-pushing-views.md33
1 files changed, 24 insertions, 9 deletions
diff --git a/docs/design/019-pushing-views.md b/docs/design/019-pushing-views.md
index b7e59aa..3d4642f 100644
--- a/docs/design/019-pushing-views.md
+++ b/docs/design/019-pushing-views.md
@@ -20,6 +20,8 @@
(브라우저의 `EventSource`가 그렇게 보낸다). 인자, 표의 쪽(`from`·`count`), 앱의 접두(`/앱/`), 토큰(`read`)이 모두 같다.
- **소유자는 거의 아무것도 하지 않는다.** 저장 뒤에 스냅숏을 공개할 때(지금도 한다) 구독자가 있으면 연결 스레드를 깨울 뿐이고,
깨우기는 스레드가 다음을 처리하기 전까지 한 번으로 모인다. 계산과 보내기는 연결 스레드가 한다.
+- **미는 값은 쓰기의 빠르기와 상관없다.** 연결 스레드는 미는 일을 `--push-every-ms`(기본 10 ms)마다 많아야 한 번 한다. 초당 4만 번의
+ 쓰기가 와도 구독 무리 하나는 초당 많아야 100번 계산하고 보낸다. 값을 민다는 것을 숫자로 적은 것이다.
## 모양
@@ -31,6 +33,8 @@ HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-store
+retry: 1000
+
id: 41
data: 120
@@ -38,11 +42,15 @@ id: 57
data: 135
```
-- 구독하면 곧 지금의 값이 한 번 온다. 그 뒤로는 공개된 스냅숏에서 그 보기의 값이 앞서 보낸 것과 다를 때마다 온다. `id`는 그 값을
+- 첫 줄은 `retry: 1000`이다(끊기면 1초 뒤에 다시 붙는다). 구독하면 곧 지금의 값이 한 번 온다. 그 뒤로는 공개된 스냅숏에서 그 보기의 값이 앞서 보낸 것과 다를 때마다 온다. `id`는 그 값을
계산한 스냅숏의 바뀜 번호다(늘기만 한다). 값의 바이트는 같은 보기의 `GET` 본문과 같다(수, text, 표의 JSON). 여러 줄의 text는
줄마다 `data:` 한 줄이다(받는 쪽이 줄바꿈으로 잇는다).
- 값이 없는 보기(인자를 받지 못한 보기, 결함난 보기)는 `event: none`과 그 까닭(`data: work` 같은)을 보낸다.
-- 앱이 멈추거나 모듈이 바뀌면(017의 3단계) 그 앱의 스트림은 끝난다. `EventSource`는 다시 붙고, 새 모듈의 보기를 구독한다.
+- 앱의 모듈이 바뀌면(017의 3단계) 구독은 새 모듈에서 같은 이름의 보기를 다시 찾아 이어 간다(링크와 출구가 이름으로 이어지는 것과
+ 같다). 그 이름의 보기가 없어졌거나 종류(수, text, 표 …)나 인자가 바뀌었으면 끊는다. 앱이 멈추면 끊는다. 끊긴 `EventSource`는 다시
+ 붙는데, 다시 붙은 요청이 200이 아니면(멈춘 앱의 503) 브라우저는 더 붙지 않는다. 그래서 이어 갈 수 있는 것은 끊지 않는다.
+- 토큰은 구독할 때 확인한다(`read`). 앱의 토큰 파일이 바뀌면(SIGHUP) 열린 구독은 새 표로 다시 확인되고, 더는 허락되지 않는
+ 구독은 끊긴다.
- 느린 구독자: 보내던 이벤트를 다 보내기 전에 새 값이 오면, 기다리던 이벤트를 새 값으로 바꾼다. 구독자 하나가 쥐는 것은 많아야
이벤트 둘(보내던 것의 나머지와 다음 것)이다. 보내던 것을 `--timeout-ms` 동안 하나도 못 보내면 끊는다.
- 구독자의 수는 `--subscribers N`(기본 65,536)이 묶는다. 다 차면 503 `busy`다.
@@ -52,9 +60,9 @@ data: 135
- `pure-serve`의 연결 스레드는 머리를 읽고 길과 토큰을 확인한 뒤, 보기의 `GET`이 이벤트 스트림을 바라면 그 연결을 구독자로 바꾼다:
답의 머리를 쓰고, 구독(앱, 모듈, 보기, 인자의 문자열)의 무리에 넣고, 연결 기록은 돌려준다(소켓은 닫지 않는다).
- 무리는 연결 스레드마다 있다. 무리는 마지막으로 계산한 스냅숏의 바뀜 번호와 그 값의 바이트를 가진다.
-- 소유자는 스냅숏을 공개할 때(`publish`) 그 앱에 구독자가 있으면, 아직 깨우지 않은 연결 스레드를 깨운다(스레드마다 깃발 하나,
- eventfd). 스레드는 깨면 깃발을 내리고 자기 무리마다 새 스냅숏을 한 번 계산해, 값이 다르면 이벤트를 한 번 만들어 무리의 구독자에게
- 보낸다.
+- 소유자는 스냅숏을 공개할 때(`publish`)와 앱이 멈출 때, 그 앱에 구독자가 있으면 아직 깨우지 않은 연결 스레드를 깨운다(스레드마다
+ 깃발 하나, eventfd). 스레드는 깨면 깃발을 내리고, 마지막 미는 일에서 `--push-every-ms`가 지났으면(아니면 그때까지 미루고) 자기
+ 무리마다 새 스냅숏을 한 번 계산해, 값이 다르면 이벤트를 한 번 만들어 무리의 구독자에게 보낸다. 새 구독의 첫 값도 같은 길로 간다.
## 단계
@@ -65,19 +73,26 @@ data: 135
## 착수 기준
- **바꿀 결정:** `pure-serve`가 보기의 변화를 SSE로 밀지. 아래를 채우지 못하면 클라이언트는 폴링한다(지금처럼).
+- 이 기준은 처음 적은 것(`f2675f0`)을 구현하기 전에 고친 것이다: 처음의 CPU 기준은 신선도가 다른 둘(초당 100번 미는 것과 100 ms
+ 폴링)을 견줘서 산수로 실패했다. 모듈이 바뀔 때 끊는 것은 브라우저가 다시 붙지 못하는 틈을 만들어서 이어 가는 것으로 바꿨다.
- **최소 확인**(`tests/check.py`의 `server_push`):
- 구독하면 지금의 값이 먼저 온다. 쓰기를 50 ms마다 하면 바뀐 값이 하나도 빠짐없이 차례로 온다. 쓰기를 몰아서 하면 사이 값은
빠질 수 있어도 마지막 값은 반드시 오고, 먼저 온 값보다 오래된 값(`id`가 작은 것)은 오지 않는다.
- 보기의 값을 바꾸지 않는 쓰기에는 이벤트가 없다.
- 인자를 받는 보기, 표의 쪽, 여러 줄의 text, 계산하는 보기(018), 토큰(없으면 401, `read`면 된다), 여러 앱의 호스트의 `/앱/` 접두.
- - SIGHUP으로 앱의 모듈을 바꾸면 그 앱의 스트림이 끝나고, 다시 붙으면 새 모듈의 값이 온다.
- - 읽지 않는 구독자에게 값을 1만 번 바꿔도 호스트의 메모리가 그만큼 늘지 않는다. 그 구독자가 다시 읽으면 마지막 값이 온다.
+ - SIGHUP으로 앱의 모듈을 바꾸면 같은 이름의 보기는 스트림이 끊기지 않고 새 모듈의 값이 온다. 보기가 없어진 모듈로 바꾸면 끊긴다.
+ 토큰 파일에서 구독의 토큰을 빼면 그 구독이 끊긴다.
+ - 받기 버퍼를 작게 한(`SO_RCVBUF`) 읽지 않는 구독자에게 큰 값(표의 쪽)을 1만 번 바꿔도 호스트의 메모리가 그만큼 늘지 않고,
+ 기다리던 이벤트가 새 값으로 바뀐 것이 한 번 이상이다(호스트의 통계). 그 구독자가 다시 읽으면 마지막 값이 온다.
- 구독자들이 끊으면 그 기록이 모두 돌아온다(호스트가 멈출 때 알리는 줄의 수가 0으로 돌아온다).
- Pure, aup, Vega의 모든 검사가 초록이다.
- **통과 조건**(RAM의 Pi 5):
- - 신선도와 값: 구독자 1,000이 카운터 보기를 보는 동안 쓰기가 초당 100번 온다. 쓰기의 답을 받은 때부터 구독자가 그 값(이나 더 새
- 값)을 받기까지의 p99가 20 ms 이하다. 그동안 호스트의 CPU가, 같은 클라이언트 1,000이 100 ms마다 폴링할 때(신선도 100 ms)의
+ - 신선도: 구독자 1,000이 카운터 보기를 보는 동안 쓰기가 초당 1번, 10번, 100번 온다. 쓰기의 답을 받은 때부터 구독자가 그 값(이나
+ 더 새 값)을 받기까지의 p99가 20 ms 이하다.
+ - 값(대시보드, 005가 말한 경우): 쓰기가 초당 1번일 때 호스트의 CPU가 같은 클라이언트 1,000이 100 ms마다 폴링할 때(신선도 100 ms)의
10분의 1 이하다.
+ - 값(같은 신선도): 쓰기가 초당 10번과 100번일 때 호스트의 CPU를, 같은 클라이언트 1,000이 20 ms마다 폴링할 때(위의 p99와 같은
+ 신선도)와 견주어 밝힌다(기준은 두지 않는다: 초당 100번 바뀌는 값을 1,000에게 미는 것은 초당 10만 번 보내는 일이다).
- 크기: 구독자 1만에서 Pure의 메모리(호스트의 RSS 증가 / 구독자 수, 커널의 소켓 버퍼는 RSS에 들지 않는다)가 구독자마다 1 KiB 이하다.
- 퍼뜨리기: 구독자 1만이 한 보기를 볼 때 값 하나가 바뀌면 모두가 받기까지의 p99가 100 ms 이하다.
- 지금의 길: TPC-C와 은행 질의를 전과 후 번갈아 여섯 쌍 재서 중앙값이 3% 넘게 느려지지 않고, 은행 질의의 명령 수가 1% 넘게