Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Actor HTTP qualification

This record covers std.actor.http.server. Its core HTTP/TLS implementation has passed the checks recorded below. It is not yet labelled production-ready: the platform/sanitizer scope below is deliberately limited. TLS configuration retention after traps (issue #641) is addressed by heap-local reconstruction. The worker server’s qualification is separate evidence and remains in force for that implementation.

Gates

GateCurrent evidence
Shared protocol grammarResumable decoder reuses existing head/target/framing rules; focused decoder and existing HTTP regression suites passed during development
Admission isolationActor regression tests cover constructor error/trap/empty result, restart-constructor failure, full parent mailbox, and heap/timer/descriptor recovery
Bounded teardownActor tests cover full-mailbox shutdown, idempotence, reuse, and repeated live-stage drop with trapping node destructors
Real HTTP/TLS connectionstest_std_actor_http_server passed with runtime/allocation checks, including TLS close notification, keep-alive and nonblocking outbound TCP
Corpus and deterministic mutationstest_std_actor_http_corpus passed: 80 shared requests sent in 1-byte and 64-byte writes, plus 600 mutations
Coverage-guided fuzzingIncremental decoder: 1,688,343 libFuzzer executions in 61 seconds, seed 592601, zero findings; coverage instrumentation, not ASan/TSan. Whole actor lifecycle is covered by socket tests rather than this target
Slow clients and output back-pressureSocket tests passed for partial heads/bodies, handshakes and slow readers
Shutdown under load and signalsForced drain with a busy mailbox passed; SIGTERM completes an in-flight streamed response; HTTP/TLS application shutdown is tested, including a TLS peer omitting close notification
Repeated failure/resource stabilityRepeated handler traps, heap exhaustion, factory failures and 5,120 connections at concurrency 16 pass; fresh-configuration trap/normal-drop/construction-OOM regressions check actor heaps and process allocation balance
TLS and reverse proxiescurl 8.18.0, nginx 1.30.6 and Caddy 2.11.7 passed streamed/sized/Expect uploads, chunked replies, keep-alive, repeated 413 refusals and proxy TLS verification
Installed/relocated toolchainCopied compiler alone into an empty environment, compiled HTTP/TLS applications, removed compiler and source, then ran both native binaries through body/echo/trap/recovery/shutdown probes successfully
Comparative measurementsOriginal HTTP/TLS comparison: 540,000 successful requests; refreshed TLS comparison: 270,000 successful requests and 9,000 verified handshakes after issues #641/#644; actor mixed-fault checks passed

This table records evidence rather than inheriting a pass from a shared component. The B85 backlog entry owns remaining work. The design records the two-server goal and acceptance rationale. The remaining platform scope is x86-64 GNU/Linux; no macOS, ARM, ASan or TSan result is implied by these checks.

The actor-specific suites are in tests/test_std_actor_http_server.rs and tests/test_std_actor_http_corpus.rs; decoder tests are in tests/test_std_http_decoder.rs. Run the integrated suites with cargo test --locked --all-features --test integration test_std_actor_http_ and the decoder with the test_std_http_decoder:: filter. These results do not establish behavior on untested platforms.

Performance comparison

examples/http_server_bench.py selects the worker or actor implementation, with corresponding plain and TLS fixtures. It uses the same small response, 16 KiB response, POST echo, keep-alive and new-connection workloads. Compiler hash, revision, source, CPU affinity, settings, failures, throughput, latency, CPU time and memory are recorded with the results. Failed or incomplete requests make the driver fail; they are not a clean throughput result.

For an equal-core comparison, use one worker and one actor stage on the same server CPU, with the client on a separate CPU. Run each at least three times, alternating implementations against fresh processes. Compare the ordinary four-worker configuration separately; it has a different CPU budget. Match idle connection counts explicitly (--idle 984 fits both), and keep load-test concurrency within both admission limits. Use --tls for HTTPS.

Performance under faults is a separate measurement: combine healthy traffic with trapped handlers, heap exhaustion, slow clients and overload, and record healthy-request latency, recovery, lost connections and resource growth. Clean-request throughput alone does not measure the actor server’s purpose. Machine load and client bottlenecks must accompany any reported comparison.

The refreshed TLS comparison records current throughput, handshake latency, idle memory and trap recovery after issues #641 and #644; the original comparison below remains historical evidence.

Recorded comparison

These measurements predate the heap-local TLS configuration reconstruction for issue #641. Their source/compiler provenance and results remain unchanged. They do not establish current TLS setup costs or memory use; actor sessions now own a parsed configuration and rebuild their keys and verification state on construction. Plain HTTP does not use that path.

Measured on 2026-11-01 at source revision 12c021c9, with compiler SHA-256 53e6b1c791bd104ba9795a739b91211025d2b10201ed757ca0e053cfc5ccc8d4. Native fixtures were compiled with --release, on a Ryzen 7 7840HS, Linux 7.2.11 x86-64. One-worker and actor servers used CPU 0; four workers used CPUs 0, 2, 4 and 6 (four physical cores). Clients used separate physical cores 8, 10, 12 and 14. No compilation, qualification suite or fault load ran alongside these measurements. Machine load snapshots are retained in the raw results and reproduction sources.

Each configuration ran three times in alternating worker/actor order against fresh processes: 5,000 requests per workload per run, with 984 connections for the separate idle measurement. These are short loopback measurements, not sustained deployment-capacity claims. ApacheBench measured ordinary workloads; four curl processes measured new TLS connections. The result artifact contains all sample ranges, p50/p99 latency, server CPU time, RSS, failures and settings.

Median requests/second (all measured requests completed successfully):

HTTP

WorkloadOne worker / one coreActor / one coreFour workers / four cores
Small, 1 keep-alive client21,77930,07823,433
Small, 16 keep-alive clients25,54527,70396,802
Small, 64 keep-alive clients24,51513,59782,129
New connection, 16 clients15,16012,21515,035
16 KiB response, 16 clients18,57513,40272,903
1 KiB POST echo, 16 clients24,21525,07198,404

HTTPS

WorkloadOne worker / one coreActor / one coreFour workers / four cores
Small, 1 keep-alive client19,19526,60521,324
Small, 16 keep-alive clients22,56825,43079,376
Small, 64 keep-alive clients20,66311,97665,670
New connection, 16 clients3,7964,1094,285
16 KiB response, 16 clients14,90211,85953,654
1 KiB POST echo, 16 clients20,93923,10481,723

The actor wins several small-response cases on one core, while the worker wins at 64 clients and for 16 KiB responses. Four worker cores provide much higher concurrent throughput. HTTP p99 at 16 small-response clients was 1.013 ms for one worker, 1.625 ms for the actor, and 0.286 ms for four workers; a throughput improvement need not improve tail latency.

Median incremental idle memory per connection was 0.86 KiB (one worker), 21.73 KiB (actor), and 1.13 KiB (four workers) for HTTP. With TLS it was 41.07, 45.86 and 41.35 KiB respectively. These are resident-memory differences for this fixture, not the configured maximum actor heap reservation.

The 64-client actor HTTP samples recorded roughly 617,000 full-mailbox retry attempts for 5,000 completed responses, without lost messages or failed HTTP requests. These are the original measurements before the completion-credit fix for issue #644; the retry comparison records the changed behavior. The raw actor statistics also distinguish abrupt TLS peer closure from an HTTP request failure: curl/ApacheBench can receive the complete response and close without TLS close notification.

Reproduce a single configuration from the repository root; change --implementation actor to --implementation worker --workers 1, or use --implementation worker --workers 4 --server-cpus 0,2,4,6, and add --tls for HTTPS:

python3 examples/http_server_bench.py --compiler target/debug/plenty \
    --implementation actor --server-cpus 0 --client-cpus 8,10,12,14 \
    --requests 5000 --idle 984 --runs 3 --output target/actor-http-comparison

For the recorded samples, each invocation used --runs 1 and the six configurations alternated for three rounds. Adapt CPU affinity to the machine.

Completion retry comparison

Issue #644 replaces repeated runnable completion retries with one outstanding completion per connection, an admission acknowledgement, and a private 1 ms retry after a full admission mailbox. Retrying a full child acknowledgement is bounded by connection capacity; application mail and timers remain live.

The raw comparison freezes the module before the change at 5fad016c, the changed module, the shared fixture, and a replay script. Both snapshots were imported as the same local server module and compiled with the same compiler/runtime and --release; only the fixture’s module imports changed. The compiler SHA-256 is 7da9d5da6d3bbab8a5ae08ea18f5d8fa7ce755e38f6cb0bedf0654cc82875dcf. This is a controlled HTTP comparison on x86-64 GNU/Linux, not a new TLS or worker-server qualification. CPU affinities remain server 0, clients 8,10,12,14. Three alternating before/after runs of all six workloads completed 180,000 measured requests with zero failures or missing responses. Each run’s actor statistics include its additional /stop request and final drain; requests was exactly 5,001.

HTTP workloadBefore median requests/sAfter median requests/s
Keep-alive, 1 client27,52427,664
Keep-alive, 16 clients26,08734,867
Keep-alive, 64 clients12,05431,511
New connection, 16 clients10,81210,449
Keep-alive 16 KiB response, 16 clients12,22813,422
Keep-alive 1 KiB POST echo, 16 clients23,15529,999

At 64 clients, median full-mailbox attempts fell from 616,899 to 6,707 and mail deliveries from 319,895 to 26,840. This improves throughput, but p99 latency rose from 12.464 to 16.854 ms in this workload. The retry delay and credit exchange are a throughput/latency tradeoff; this does not establish an across-the-board latency improvement. Incremental idle RSS with 984 connections was 21.73 KiB before and 21.80 KiB after per connection; setup RSS and raw runs are in the artifact. No task-local builds ran during measurement; unrelated activity on the shared host was not controlled, and the recorded load snapshot was taken after the run. Short local runs do not establish long-term soak behavior or precise performance across environments.

Replay the frozen sources from a checkout with a compiler, ApacheBench and taskset; adapt the script’s affinities on other machines:

python3 - <<'PYREPLAY'
import json
from pathlib import Path
record = json.loads(Path("book/src/assets/actor-http-retry-comparison.json").read_text())
Path("/tmp/actor-http-retry-comparison.py").write_text(record["reproduction_script"])
PYREPLAY
python3 /tmp/actor-http-retry-comparison.py \
    book/src/assets/actor-http-retry-comparison.json target/debug/plenty \
    target/actor-http-retry-comparison

Mixed failures

The separate fault workload placed 32 stalled request heads beside four healthy clients and 100 intentional handler traps in each of four batches. All 4,000 healthy requests and 400 post-batch recovery requests succeeded on the actor server; all 400 trapping connections ended without an HTTP response. RSS stayed at 4,136 KiB and descriptor count at six after every batch. The server then answered its shutdown request and exited successfully, with zero rejected admissions or lost messages. Healthy batch p99 was 1.20–1.28 ms.

Both worker configurations passed their ordinary and stalled-head baselines, then exited on the first intentional runtime trap, as their execution contract specifies. This comparison demonstrates trap containment, not immunity to arbitrary native faults. CPU 6 and client CPU 7 are SMT siblings in this separate probe, so its latency is descriptive failure evidence rather than a throughput comparison. The result artifact embeds the temporary fault harness and its two fixtures, along with compiler and native-binary hashes.