Repository navigation
CWIST v3.4
CWIST v3.4 — gRPC client, proto codegen completeness, and WASM client-side support
Major changes compared to v3.3:
-
gRPC Client: Load Balancing & gRFC A6 Retries (714b9b6, bb04dae)
- HTTP/2 gRPC client (h2c cleartext or TLS with ALPN "h2"): unary and server-streaming calls, grpc-timeout deadlines, RST_STREAM cancellation.
- cwist_grpc_channel: dns/ipv4/ipv6 target resolution (doc/naming.md) into per-address subchannels; failed dials back off per doc/connection-backoff.md (1s initial, x1.6, 120s cap, +/-0.2 jitter, reset on SETTINGS handshake).
- Client-side load balancing (doc/load-balancing.md): pick_first (default, sticky with in-order failover) and round_robin (eager connect, READY-set rotation), plus aggregated channel connectivity state.
- gRFC A6 retry engine between the channel and the LB pick: maxAttempts (client-capped at 5), jittered exponential backoff, retryable status codes, call deadline shared across attempts, per-attempt remaining grpc-timeout, and the grpc-previous-rpc-attempts header on every retry.
- Transparent retries (A6): RPCs that never left the client retry until the deadline; RPCs refused before server application logic (RST_STREAM REFUSED_STREAM, GOAWAY last-stream-id below the stream) get one immediate retry — neither counts against maxAttempts nor the throttle.
- Server pushback: grpc-retry-pushback-ms honored in both directions (retry after exactly N ms / do not retry); retryThrottling token bucket per channel.
- JSON service config subset (doc/service_config.md): loadBalancingConfig/loadBalancingPolicy, methodConfig (name matching, retryPolicy, waitForReady, timeout), retryThrottling — with A6 validation rules.
- Server spec compliance: error responses are now Trailers-Only end-to-end (unary: single HEADERS frame with END_STREAM; streaming: Response-Headers delayed until the first message), so conforming clients can actually retry. cwist_grpc_stream_set_retry_pushback() emits pushback from handlers.
- tests/test_grpc_channel: LB stickiness/failover/rotation, retry matrix (retryable/non-retryable codes, maxAttempts exhaustion, pushback both ways, throttling, deadline across attempts), JSON config validation, GOAWAY transparent retry against a raw-socket fake, and resolver schemes.
-
cwist proto Codegen Completeness (b9a4fc7, 6ebd78a)
- oneof, map, fixed-width types (fixed32/64, sfixed32/64, double) for proto3 fields.
- protoc --descriptor_set_out binary input (auto-detected or via --descriptor-set), alongside the existing text path.
-
WASM Client-Side Support Wave (df01f68, f0ac2ef, 04907c9, 6e0ee35)
- cwist_app_dispatch_memory(): run cwist_app routing and handlers on in-memory request/response buffers with no sockets — the same C handlers run inside a Service Worker or a JS fetch-interception layer.
- make wasm: libcwist_wasm.a Emscripten target shipping the socket-independent core (app dispatch, mux/middleware, HTTP/1 parser/serializer, query map, cJSON, memory utilities).
- cwist_db_open_memory() / cwist_db_serialize(): official in-memory/blob database API over sqlite3_deserialize-style buffers.
- <cwist/wasm/typedarray.h>: zero-copy serialization helpers mapping C struct arrays onto HEAP TypedArrays instead of round-tripping through snprintf JSON.
-
gRPC Server Leftovers (cc4cd15)
- The builtin grpc.health.v1.Health Watch method now streams status changes live over the HTTP/2 transport path (snapshot fallback on the buffered path).
-
v3.4 Performance Wave (45e6552, 668625d, 48088c4, a046efe, 95ee64f, 86ded77, c5bc5cc)
- C1M reactor tail latency: cooperative request-batch yields (CWIST_HTTP_YIELD_BATCH), batch responses coalesced into one writev per turn (256 KiB stash), reactor wake eventfds registered with io_uring (IORING_REGISTER_EVENTFD), post bursts coalesced to a single wake.
- BDR cache: lock-free learn/read paths (CAS-published entries, atomic blob swaps, EBR reclamation) and hit-time revalidation hooks (cwist_bdr_put_revalidatable).
- Classic pool: CWIST_POOL_PREWARM / CWIST_POOL_IDLE_TIMEOUT_MS tunables; HTTPS handshake shepherd shard count tunable via CWIST_HTTPS_HS_SHARDS.
- WAF: signatures scanned with one Aho-Corasick pass.
-
Distribution (cef1524, 9a77fd8)
- Homebrew tap published: brew tap c4punks/cwist && brew install c4punks/cwist/cwist (verified end-to-end on Linuxbrew). vcpkg stays an in-tree draft under packaging/vcpkg/.
Known limits (see ROADMAP.md): client-side hedging (hedgingPolicy) is not implemented — gRPC C-core does not implement it either; the channel resolver supports dns (default), ipv4, and ipv6 targets, while unix/vsock transports, grpclb/xDS policies, and perAttemptRecvTimeout remain unimplemented.
--- Patch Updates (tag refreshed) ---
-
HTTP/2 Flow-Control Hardening — Vulnerability Defense (e121dac)
- Integer-overflow defense: the pacing token-bucket refill now caps the idle elapsed time at the exact bucket fill time (target_window / rate) before multiplying by the rate. Previously, a connection left idle for an arbitrarily long period combined with a very large computed pacing rate could overflow the uint64 multiplication (elapsed x rate) — the same arithmetic-defect class as the classic WINDOW_UPDATE integer overflows seen in RFC 7540/9113 implementations.
- Availability defense (self-inflicted DoS): pacing_rate_bytes_per_sec is floored at min_window x 10, so a single spiked RTT sample (retransmission burst, scheduler interference) can no longer collapse the pacing rate and throttle the connection to a crawl. The peer's flow-control windows still gate in-flight bytes; loopback/small-RTT paths stay effectively unpaced as before.
- Oscillation defense: each BDP window retune now moves at most a 2x/0.5x step from the current target, so a micro-interval scheduling-jitter sample cannot slam the window between its floor and ceiling under bursty load; fast-link convergence stays exponential, and a zero-length interval keeps the current target instead of collapsing it to the minimum.
-
HTTP/3 Concurrency Hardening — Vulnerability Defense (6b4b4a0, bd8ba21)
g_h3_dgramwas a single process-wide struct shared by every HTTP/3 connection with no lock:cwist_http3_send_datagram()on one connection couldfree()and overwrite another connection's still-pending payload mid-flight (double-free/UAF). Replaced with a per-connection, mutex-guarded FIFO datagram queue (h3_conn_ctx_t, torn down inon_conn_closed), which also fixes a same-connection bug where a second send before the first flushed silently dropped the first payload instead of queuing it.cwist_h3_free_session_ticket_key()read the SSL_CTX ex_data index without the samepthread_oncegatecwist_h3_setup_session_tickets()uses to write it; a context destroyed from a different thread than the one that created it had no happens-before edge to the writer. Both functions now take the same gate.- The
use_file_streamwrite loop loopedpread()+lsquic_stream_write()until EAGAIN with no bound: a large file transfer on a wide-open congestion window could hold the single H3 event-loop thread in back-to-back synchronous disk reads, delaying packet I/O/ACK processing for every other connection that thread services. Capped at 4 chunks peron_write()callback, re-armingwantwritebetween bursts. - Follow-up perf pass: removed the per-header
malloc/freepair inh3_process_stream_headers()(QPACK decode — was two heap round trips per header, tens of them per request), and moved therecvmmsg()batch scratch buffers (2.1MB, previously living on the H3 event-loop thread's stack) onto the heap, removing an implicit large-stack-thread requirement.
-
Benchmark Fairness: Spring Boot Tuned Run (93a5eb5)
- The "tuned low-latency run" (
wrk -t4 -c100vs the main-t12 -c400profile) was CWIST-only and published in the README as a standalone headline number, with no equivalent Spring Boot figure anywhere nearby — comparing CWIST's best case against a number Spring was never measured at, contradicting this repo's own "no framework gets a hand-tuned advantage the others do not get" claim. Spring Boot now gets an identical second boot under the identical-t4 -c100 -d10sprofile, replaying the same trained AOT cache used for its main run (no extra cold-start penalty CWIST doesn't also avoid), and the README callout now shows both side by side. Axum/Gin are not yet included in this second pass (documented as a known remaining asymmetry indocs/webserver-benchmark.md).
- The "tuned low-latency run" (