perf(http): reuse response results through request lifecycle - #35
Conversation
Benchmark d’intégration — toutes les optimisations HTTPJ’ai assemblé localement, sans pousser de branche d’intégration, les têtes de Méthode : même orb et même suite que le baseline initial, Node 24.19, serveur CPU 2, autocannon CPU 4/6, 3 répétitions, chauffe 2 s + mesure 5 s, ordre randomisé. Tous les runs retenus ont 0 erreur, 0 timeout et 0 non-2xx.
Comparaison dans le même run final :
Conclusion honnête : le cumul apporte un gros saut et dépasse Adonis dans plusieurs cas, mais ne dépasse pas encore Nest/Fastify hors POST. Par rapport à l’intégration précédente |
Amp-Thread-ID: https://ampcode.com/threads/T-01a019cf-7de3-7163-8c4d-39257b622dbe Co-authored-by: Upd4ting <upd4ting@gmail.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a019cf-7de3-7163-8c4d-39257b622dbe Co-authored-by: Upd4ting <upd4ting@gmail.com>
26512bb to
42ea1c2
Compare
Co-authored-by: Upd4ting <upd4ting@gmail.com>
🔗 Linked issue
Consumes the published
@antelopejs/interface-api@0.0.11release produced by AntelopeJS/interface-api#14.❓ Type of change
📚 Description
The HTTP listener previously created a 404
HTTPResultbefore route matching, then commonly replaced it after the handler and cloned it again even when no monitor existed. Those replacements calledgetHeaders(), which also forced the header store to exist.This change:
HTTPResultreplaces the context response;HTTPResult.peekHeaders()API directly; the compatibility cast and allocatinggetHeaders()fallback are no longer needed.No response bypass was added: status, content type, body, HEAD, stream, and error paths still finish through
HTTPResult.sendResponse()/sendHeadResponse(). The package now requires@antelopejs/interface-api >=0.0.11 <1.0.0.@antelopejs/interface-coredeliberately remains unchanged because its pending #9 work is expected to require another release. Promise sync behavior, static routes, URL/context construction, and dynamic extraction are deliberately outside this PR.Cumulative end-to-end benchmark
Baseline:
origin/mainat0bf67c2b93615a2c427e205fa3cd7a04a582b210, containing #29–#33 and explicitly excluding #34. Candidate:bbd94e4(#29–#33 + this PR). Both variants use published@antelopejs/interface-api@0.0.11.Node 22.19.0, real loopback
requestListener, 50 connections, pipelining 10, 1 s warm-up + 3 s measured, six alternating repetitions. HEAD uses a 50-connection Node loader because autocannon 8 does not count completed HEAD responses. Values are mean req/s; p99 and population CV are reported for both variants. Transport errors and timeouts were zero throughout; non-2xx responses are expected for the error scenario.HTTPResultThe direct response paths improve by 1.5–8.4%. Stream and error are neutral within run variance. The incremental diff introduces no
async,await, or Promise on #33's synchronous HTTP path.Allocation and GC profile
Five alternating fresh-process repetitions per scenario, 2,000-request warm-up, forced GC, server-only V8 allocation sampling at 4 KiB, then 10,000 external HTTP requests. Sampled bytes include Node internals and have 3.8–16.4% CV, so they are directional rather than exact byte accounting.
HTTPResultAPI-owned sampled allocation falls on seven of ten scenarios, including -40.8% for headerless GET, -59.9% for a returned headerless
HTTPResult, and -50.7% for stream. Total sampled allocation and retained heap are noisy and do not show a universal reduction; headers, middleware, and HEAD remain dominated by their required stores/snapshots and Node internals.A separate instrumented contract request for a returned headerless
HTTPResultobserved:peekHeaders()callsundefinedgetHeaders()callsThis confirms the candidate directly observes the absent header store without materializing an empty object.
Contract and validation coverage
0bf67c2(perf(api): compile request controller resolution #29–perf(server): add synchronous HTTP request path #33); perf(server): create request URLs lazily #34 is not in ancestry@antelopejs/interface-api@0.0.11, frozen installpnpm run buildpnpm dlx @antelopejs/core module test .— 163 passing, including perf(server): add synchronous HTTP request path #33 synchronous/thenable contractspnpm run lint— clean📝 Checklist
Greptile Summary
The PR reduces HTTP response allocation by reusing response objects through the request lifecycle while retaining replacement and monitor-snapshot behavior.
peekHeadersAPI directly.Confidence Score: 5/5
The PR appears safe to merge, with no concrete blocking or non-blocking defects identified in the changed behavior.
The revised lifecycle retains response sending through HTTPResult, preserves headers across replacements, isolates monitor snapshots, and is covered across the principal response paths.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Incoming HTTP request] --> B[Resolve route] B -->|No match| C[Create 404 HTTPResult] B -->|Match| D[Create reusable HTTPResult] C --> E[Execute applicable lifecycle] D --> E E --> F[Prefix handlers] F --> G[Route handler] G --> H[Postfix handlers] H --> I{Matching monitors?} I -->|Yes| J[Clone isolated response snapshot] I -->|No| K[Skip snapshot] J --> L[Send final response] K --> LReviews (1): Last reviewed commit: "perf(http): reuse response results throu..." | Re-trigger Greptile