aboutsummaryrefslogtreecommitdiffstats
path: root/docs/research/100-moved-receiver.md
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-09-29 15:17:40 +0100
committereunhhu <[email protected]>2026-09-29 15:17:40 +0100
commit7ad006f6f2d52c105ce6767d34fbd8a7abed4012 (patch)
tree65597b55e38bafbc326df5a84fb9b8174b44f14f /docs/research/100-moved-receiver.md
parent088f85ed72b61e879964a4cba6a0c0faa135facb (diff)
downloadpure-7ad006f6f2d52c105ce6767d34fbd8a7abed4012.tar.gz
pure-7ad006f6f2d52c105ce6767d34fbd8a7abed4012.zip
Say that a receiver moved to another machine is followed, and how (100)
H3 of design 011 stands: with ten thousand devices sending 2,000 records a second, the node's store, key and memo carried to another address, nothing stopped for a 100 ms step, the moved node made no X25519, and every record was applied once. Each device's first record after the move waits one probe (30-45 ms here), as its first packet finds no way there. The search found the listener answering from an address other than the one a packet came to, and a link that looked for its receiver after its probe went, losing every answer. H1-H4 are all measured now; 61 B's CPU a delivery is at the edge of the criterion, to be looked at before step 5 (adoption and a specification).
Diffstat (limited to 'docs/research/100-moved-receiver.md')
-rw-r--r--docs/research/100-moved-receiver.md125
1 files changed, 125 insertions, 0 deletions
diff --git a/docs/research/100-moved-receiver.md b/docs/research/100-moved-receiver.md
new file mode 100644
index 0000000..40d5f2f
--- /dev/null
+++ b/docs/research/100-moved-receiver.md
@@ -0,0 +1,125 @@
+# 100 — 받는 쪽을 다른 기계로 옮겨도 링크가 따라간다
+
+2026-09-29. [설계 011](../design/011-pure-transport.md) 4단계의 경로 바뀜과 저장소 옮기기(H3, M3)다.
+
+착수 기준:
+
+- **바꿀 결정.** H3이다. 저장소를 다른 프로세스나 주소로 옮겨도 링크가 이어진다.
+ - QUIC은 연결을 다른 프로세스로 옮기지 못한다. 서버의 주소가 바뀌는 길은 핸드셰이크 때 알린 preferred_address 하나다
+ (RFC 9000 §9.6). 옮긴 서버에는 모든 클라이언트가 새로 연결해야 한다.
+- **최소 확인.**
+ - 받는 쪽의 저장소, 키, 메모를 다른 기계로 옮긴다. 옛 주소는 사라지고, 보내는 쪽이 쓰는 이름이 새 주소를 가리킨다.
+ - 끊긴 시간, 잃거나 겹친 기록, 비대칭 연산을 본다. M2와 같은 부하(기기 1만 개, 초당 2,000건)로 잰다.
+- **중단 기준(011).** H3의 검사에서 한 번이라도 잃거나 겹치면, 되돌리고 원인을 찾는다.
+
+## 쉬운 말로
+
+- 알아낸 것:
+ - 처음 해 보니 링크가 옮겨 간 받는 쪽을 영영 찾지 못했다. 링크는 시작할 때 이름을 한 번만 풀었다.
+ - 그 검사를 만들다 더 오래된 결함이 나왔다.
+ - 받는 쪽은 노드의 모든 주소에서 받지만, 답은 시스템이 고른 주소에서 나갔다.
+ - 보내는 쪽이 노드의 두 번째 주소로 보내면 답이 하나도 닿지 않았다. 기록은 도착했는데 확인이 오지 않았다.
+ - 이제 패킷이 도착한 주소에서 답한다. QUIC 구현들이 하는 대로다.
+ - 이제 링크는 다음 둘 중 하나가 일어나면 이름을 다시 풀고 새 소켓으로 보낸다.
+ - 탐침 세 번이 연달아 답을 받지 못했다.
+ - 보내다가 시스템이 길이 없다고 했다(길 없음, 받는 이 없음).
+ - 받는 쪽이 옮겨 가도, 보내는 쪽의 망이 바뀌어도 같은 규칙이다.
+ - 기기 1만 개가 초당 2,000건을 보내는 중에 받는 쪽을 다른 주소로 옮겼다.
+ - 저장이 멈춘 100 ms 칸이 없다.
+ - 옮긴 노드는 X25519를 하지 않았다. 잃거나 겹친 기록도 없다.
+ - 같은 주소에서 다시 띄운 경우(M2)와 다른 점은 하나다. 옮긴 뒤 기기마다 첫 기록이 탐침 한 번(이 루프백에서 30–45 ms)을
+ 기다린다.
+- 아직 모르는 것:
+ - 보내는 쪽의 망이 바뀌는 경우(와이파이에서 LTE로)는 같은 규칙으로 다룬다. 다만 한 기계의 루프백으로는 옛 망을 끊을 수 없어
+ 따로 재지 않았다.
+ - 실제 DNS에서는 이름이 바뀌어 퍼지는 시간(TTL)이 든다. 재지 않았다.
+- 사용 경험의 차이: 장애 조치가 "저장소를 옮기고 이름을 바꾸는 일"이 된다. 기기들은 핸드셰이크 없이 다음 패킷으로 따라온다.
+
+## 한 것
+
+### 경로는 두 주소다
+
+받는 쪽이 한 run에 대해 기억하는 경로를 (상대의 주소, 패킷이 도착한 노드의 주소) 한 쌍으로 바꿨다(48 B, 전에는 28 B).
+QUIC이 경로를 4-튜플로 보는 것과 같다.
+
+- 리스너는 패킷마다 도착한 주소를 받는다(IP_PKTINFO). IPv4도 IPv6 소켓 하나에서 `::ffff:a.b.c.d`로 받는다.
+- 답은 그 주소에서 나간다.
+- 도착한 노드의 주소가 바뀐 패킷은 상대 주소가 바뀐 패킷과 똑같이 다룬다. 가장 새 번호일 때만 경로를 옮기고, 새 경로는
+ CHALLENGE로 확인한다(098).
+
+### 받는 쪽을 다시 찾는다
+
+링크(`link/link.c`)가 받는 쪽의 이름을 다시 풀고, 새 소켓을 그 주소에 연결한다. 옛 소켓은 닫는다. 다시 찾는 때는 둘이다.
+
+- 탐침 세 번이 연달아 답을 받지 못했을 때, 그리고 그 뒤 탐침마다. 탐침 간격은 두 배씩 늘어 1초에서 멈춘다.
+- 보내다가 시스템이 길이 없다고 했을 때(ECONNREFUSED, EHOSTUNREACH, ENETUNREACH, ENETDOWN, EADDRNOTAVAIL). 이때는 다음
+ 패킷을 보내기 전에 곧바로 찾는다.
+
+두 가지를 조심했다.
+
+- **쉬던 링크는 찾지 않는다.** 답을 들은 지 오래됐다는 것만으로는 찾지 않는다. 5초마다 기록 하나를 보내는 기기가 기록마다
+ 이름을 풀고 소켓을 새로 열 것이기 때문이다. 답을 받지 못한 탐침만 센다.
+- **탐침을 보내기 전에 찾는다.** 처음에는 탐침을 보낸 뒤에 소켓을 바꿨다. 그러자 그 탐침의 답이 방금 닫은 소켓으로 돌아왔고,
+ 다음 탐침에서도 같은 일이 되풀이됐다. 소켓만 번갈아 바뀌고 답은 하나도 받지 못했다.
+
+새 소켓은 새 포트이므로, 받는 쪽은 새 경로로 보고 확인한다. 확인 전의 답은 받은 바이트의 3배까지이고, 보내는 쪽의 다음
+패킷에 RESPONSE가 실려 간다(099).
+
+### 검사와 하네스
+
+- `make check`의 `server_links_moved`는 네트워크와 `/etc/hosts`를 따로 가진 이름공간(`unshare -rmn`)에서 돈다.
+ 1. 보내는 노드는 `pure://receiver.test`로 보낸다. 이 이름은 127.0.0.2를 가리킨다.
+ 2. 주문이 흐르는 중에 받는 노드를 kill -9 한다.
+ 3. 저장소, 키, 메모를 다른 디렉터리로 복사한다.
+ 4. 127.0.0.2를 라우팅에서 닿지 않게 하고, 이름을 127.0.0.3으로 바꾼 뒤, 복사본을 거기서 띄운다.
+ 5. 모든 주문이 한 번씩 적용되고, 옮긴 노드가 X25519를 하지 않으면 통과다.
+- M2 하네스에 `--move`를 더했다([run.py](../../research/experiments/019-idle-links/run.py)). 같은 이름공간에서 같은 옮기기를
+ 한다. 부하 생성기의 기기들도 `link.c`와 같은 규칙으로 다시 찾는다.
+
+## 결과
+
+M2와 같은 부하다. 기기 1만 개가 5초마다 주문 하나를 보낸다(초당 2,000건). 80초를 돌리고 60초에 받는 쪽을 kill -9 한다.
+에폭 수락은 약 40초에 끝나므로, 60초에는 모든 기기가 에폭 1에 있다. 두 라운드씩이다. 원시 결과는
+`research/experiments/019-idle-links/2026-09-29-move-linux-aarch64.jsonl`에 있다.
+
+| | 같은 주소에서 다시 띄움(M2) | 다른 주소로 옮김(M3) |
+|---|---|---|
+| 죽인 뒤 다시 뜨기까지(옮기기 포함) | 18 ms | 24–33 ms |
+| 저장이 멈춘 100 ms 칸 | 0 | 0 |
+| 옮긴 뒤 5초에 만든 기록의 p50 / p99 | 0.6–0.7 / 7.9–8.4 ms | 28–29 / 44–45 ms |
+| 전체 p99 / 최대 | 5.0–5.1 / 36–39 ms | 36–37 / 107–111 ms |
+| 다시 보낸 기록 / 보낸 패킷 | 33 / 160,039–160,043 | 10,022 / 170,028–170,033 |
+| 다시 뜬 노드의 X25519 | 0 | 0 |
+| 다시 뜬 뒤 10초의 CPU, 노드 / 생성기 | 1.17–1.40 / 0.72–0.83초 | 1.08–1.24 / 0.89–1.00초 |
+| 적용 | 160,006–160,010, 모두 한 번 | 160,006–160,011, 모두 한 번 |
+
+- 옮긴 뒤 기기마다 첫 기록 하나가 한 번 더 간다(10,022건).
+ - 옛 주소로 간 첫 패킷은 시스템이 "길 없음"으로 돌려준다.
+ - 기기는 곧바로 이름을 다시 풀지만, 그 기록은 탐침이 울릴 때(30–45 ms 뒤) 새 주소로 간다.
+ - 기기가 5초마다 보내므로, 옮긴 뒤 5초 동안 모든 기기가 한 번씩 이것을 겪는다.
+- 하네스의 "따라잡기"는 옮긴 쪽이 1.4–3.1초로 나온다. 밀린 것이 아니다.
+ - 100 ms 칸의 저장량은 옮긴 직후부터 약 200으로 같다.
+ - 5초 동안 기록마다 약 30 ms씩 늦으니, 누적량이 "정상 속도의 99 %"에 조금 못 미치는 동안이 그만큼 이어진 것이다.
+- quinn은 같은 주소의 재시작에서도 1초 멈추고, 10.9초 뒤 따라잡았다. 그동안 핸드셰이크 1만 번에 서버 CPU 약 7초를 썼다(095).
+ 옮긴 서버라면 적어도 이만큼 든다. 옮긴 서버에는 연결이 하나도 없기 때문이다.
+
+## 판정
+
+- H3이 선다. 받는 쪽의 저장소, 키, 메모를 다른 기계로 옮기면, 기기들은 핸드셰이크 없이 다음 패킷으로 따라온다. 잃거나 겹친
+ 기록이 없고, 옮긴 노드의 비대칭 연산은 0이다.
+- 011의 네 가설을 모두 쟀다.
+ - H1(쉬는 링크의 비용, 094), H2(kill -9, 095), H3(이 문서)은 선다.
+ - H4(전달당 CPU, 096)는 1 KiB와 16 KiB에서 quinn보다 25 % 적다. 61 B에서는 1.06–1.12배라, 채택 기준(10 %)의 경계에 있다.
+ - 보안 검사(098)와 전방 비밀성(099)도 통과했다.
+- 남은 것은 011의 5단계다. 채택 여부를 정하고 명세를 쓴다. 61 B의 전달당 CPU를 먼저 다시 본다.
+
+## 한계와 다음
+
+- **보내는 쪽의 망이 바뀌는 경우.** 규칙은 같다. 옛 망의 소켓이 보내지 못하거나 탐침이 답을 받지 못하면 새 소켓으로 간다.
+ 다만 실제 인터페이스를 바꿔 가며 재지 않았다.
+- **이름 풀기는 링크 자신의 스레드에서 한다.** 실제 DNS가 느리면 그동안 그 링크는 기다린다. 다른 링크는 기다리지 않는다.
+- **새 소켓은 새 경로다.** 받는 쪽은 새 포트를 확인할 때까지 답을 받은 바이트의 3배로 묶는다. 기록 하나에 대한 답은 그보다
+ 작아서 M3에서는 걸리지 않았다.
+- **옮긴 뒤의 첫 기록.** 이 기록을 탐침 없이 곧바로 새 주소로 다시 보내면 30–45 ms를 더 줄일 수 있다. 다만 보낼 것이 없는
+ 링크를 깨울 방법을 따로 두어야 해서 하지 않았다.
+- 다음은 011의 5단계, 채택 여부와 명세다.