feat(serve): registry names + docs (v0.2 chunk 4)

Registration:
- Each listener registers "urus.listener.{i}" from inside its ChildSpec
  factory. On a restart the stale binding points at a dead pid; smarm's
  registry evicts those lazily on register/whereis, so re-registering
  the same name is safe. Result ignored — a registry hiccup must not
  take the listener down.
- "urus.server" -> supervisor pid, registered via the JoinHandle's
  .pid() immediately after spawn rather than inside the closure: the
  binding exists before the supervisor runs an instruction, so an early
  whereis can't observe None.

Test: server_and_listeners_are_registered probes whereis from a route
handler — whereis must run inside the runtime, and the test thread is a
foreign OS thread with no runtime in its TLS.

Docs:
- README: Config documented in full (timeout semantics: keep_alive =
  idle-before-first-byte, request = Instant deadline first-byte ->
  head+body, slowloris-proof), Handle/shutdown_handle/serve_with_shutdown
  section with the 4-step shutdown sequence, named-actors note, test
  coverage list refreshed, Roadmap section now points at ROADMAP.md.
- ROADMAP: v0.2 chunks 1-4 marked landed; design detail stays in the
  chunk commit bodies.

Verified: cargo build --features smarm-trace clean; full suite (32
tests) green 3x; debug-grep clean.

This closes v0.2. Exit criteria all verified across chunks 1-3: crud
drains on stdin-Enter (~100ms), idle keep-alive reaped at
keep_alive_timeout, panicking listener restarts under load.
This commit is contained in:
Claude
2026-06-11 22:24:02 +00:00
parent a60687bfec
commit 8065ea561e
4 changed files with 115 additions and 17 deletions
+14 -4
View File
@@ -36,7 +36,7 @@ select, plus RFC 008 phase 1 fd arms / timed fd waits):
---
## v0.2 — OTP refresh
## v0.2 — OTP refresh ✅ (landed 2026-06, chunks 1-4 / commits 5fe6969..HEAD)
Adopt the primitives urus predates. No protocol surface changes; `Plug`/`Conn`
untouched. Chunks are independently landable, in order:
@@ -59,7 +59,11 @@ reuses it. No re-dup, no window where a pool slot has no fd. Covered by
injected before `accept` so the pending connection must survive into the
restarted listener).
### 2. Graceful shutdown
### 2. Graceful shutdown
Landed (c658ac0): `serve_with_shutdown(config, pipeline, signal)` +
`shutdown_handle()`. Drain-then-stop as decided below; design details in
the commit body. `serve_with` keeps v1 run-forever semantics by dropping
its own Handle.
`serve_with` gains a shutdown signal (an `urus::Handle` with `.shutdown()`,
backed by a channel + `request_stop` fan-out):
1. Stop listeners (no new accepts; listener fds close).
@@ -75,7 +79,11 @@ actor monitors-free self-deregisters via a drop guard cast. Pid set only
(no activity stamps — chunk 3 went per-conn timed waits, no reaper); still
the future ws/channels introspection point, so build it once here.
### 3. Enforce the timeouts we advertise
### 3. Enforce the timeouts we advertise
Landed (a60687b): timed waits as decided below; the leaning held — a
per-request `Instant` deadline keeps `request_timeout` an honest
whole-request wall clock (mid-head expiry: best-effort non-parking 408;
mid-body: close). Details in the commit body.
**Decided: smarm-native timed waits** (the fork's option (a), a reaper in
urus, died when RFC 008 landed at `393cdd0``wait_readable_timeout` /
`wait_writable_timeout` exist upstream now, so there is nothing for the
@@ -96,7 +104,9 @@ urus is the first real consumer of the timed wrappers — note the RFC 008
reviewer flag (`wait_fd` wraps in `NoPreempt`, select arms don't); the
crud example under load doubles as that confirmation.
### 4. Hygiene
### 4. Hygiene
Landed: registry names + whereis test (in-runtime probe via a handler —
tests are foreign threads), README rewrite, smarm-trace compile verified.
- Registry names: `urus.server`, `urus.listener.{n}` via `smarm::register`
(debuggability; `whereis` in tests).
- README: rewrite Roadmap section to point here; document `Handle`.