perf(transcode): remove per-request route allocations on the hot path#42
Conversation
Two avoidable allocations were paid on every proxied request: - The handler closure captured the RouteEntry by value, so axum's per-request clone of the handler deep-cloned its String fields (http_path, grpc_path, response_body). Wrap the entry in Arc so the per-request clone is a single refcount bump. - grpc_path was re-parsed into a PathAndQuery on every request in both the unary and streaming handlers. Parse it once at route-build time and store the PathAndQuery (its clone is a cheap Bytes refcount bump); an invalid path now skips the route at startup instead of returning a per-request 500. No behavior change on the success path. SIMD/wildcopy are not applicable here: the byte-level work lives in serde_json / prost / hyper, and the proxy is I/O-bound on the upstream gRPC call. Closes #41
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughSummary by CodeRabbitRelease Notes
Walkthrough
ChangesPer-request allocation removal on the hot path
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
|
Summary
Removes two avoidable allocations paid on every proxied request (the hottest path in the proxy).
Changes
Arc<RouteEntry>instead of deep clone. The handler closure capturedRouteEntryby value, so axum's per-request clone of the handler deep-cloned itsStringfields (http_path,grpc_path,response_body). Wrapping the entry inArcmakes the per-request clone a single refcount bump.grpc_pathwas re-parsed into aPathAndQueryon every request in both the unary and streaming handlers. It's now parsed once at route-build time and stored asPathAndQuery(whose clone is a cheapBytesrefcount bump). An invalid path now skips the route at startup with a log, instead of a per-request 500 branch.Not done (deliberately)
serde_json/prost/hyper(already optimized), and the proxy is I/O-bound on the upstream gRPC RTT. Adding SIMD to the glue would be premature and wouldn't move P99.serde_json::Valueintermediate in request building could be skipped by constructing theDynamicMessagedirectly via reflection, but that's a larger refactor with real correctness surface (coercion, oneofs, nested fields) and little payoff on an I/O-bound path. Left as a possible future optimization.Testing
No behavior change on the success path.
cargo nextest run --features redis: 119 passed. clippy (all-features) + fmt clean.Closes #41