feat(core): HTTP/2 Phase 5 — frame layer
Implements HTTP/2 frame reading, validation, and writing: FrameType (the 10 RFC 9113 types + per-type validation descriptor), FrameFlags (with the deliberate END_STREAM/ACK bit collision documented), FrameHeader (a flyweight, never allocated per frame), Http2FrameReader (length-prefixed reader over BufferedByteSource, mirroring RequestParser's buffer/ compaction discipline), FrameValidator (table-driven, specific RFC error code per violation -- not a uniform code per type), Padding (RFC 9113 6.1/6.2), and FrameWriteBuffer (beginFrame/endFrame length back-patching over Phase 4's ByteWriter). All 10 frame types round-trip correctly; every RFC-mandated rejection has its own test asserting the specific error code; the reader is fuzz-tested against 10,000,000 random inputs (~14s). The zero-alloc contract is measured, not asserted: reading + validating + consuming a frame is 0.002 B/op, writing one is ~10^-4 B/op -- both indistinguishable from zero (DEC-21). Found and fixed EX-37 while writing Http2FrameReaderTest: BufferedByteSource's deadline mechanism (EX-07's actual fix) NPE'd against a null socket, which every isolated unit test in this codebase uses -- it had zero dedicated test coverage of its own. Fixed to treat a null socket as "no OS-level timeout to bound" rather than a misuse, and given BufferedByteSourceTest, which did not exist before. 449/449 tests green, both with and without -Pjmh. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
704a00a551
commit
0e1bbed42c
@@ -733,3 +733,35 @@ once Phase 6 lands `Request`/`RequestBody` pooling — re-run this exact benchma
|
||||
this entry (or add a new one) with the "after" number, closing the loop Phase 4 opened.
|
||||
|
||||
---
|
||||
|
||||
## DEC-21 — Phase 5's zero-alloc contract, measured
|
||||
|
||||
**Context.** Phase 5's plan states: "Reading, validating and discarding a frame: 0 B/op ...
|
||||
Writing a frame header: 0 B/op." Measured with JMH `-prof gc` (JDK 21.0.11, JMH 1.37,
|
||||
`FrameLayerBenchmark`, `src/jmh/java`) rather than left as an unverified assertion, per this
|
||||
project's own standing practice of measuring every stated performance/allocation claim
|
||||
(`DEC-09`, `DEC-20`).
|
||||
|
||||
**Measurement.** `readValidateAndDiscard` (`Http2FrameReader.readFrame` +
|
||||
`FrameValidator.validate` + one byte read from the payload + `consumeFrame`, against a warm,
|
||||
already-grown buffer, matching real keep-alive-connection steady state): 299.846 ± 19.722 ns/op,
|
||||
**0.002 B/op** — indistinguishable from zero (compare `DEC-20`'s harness-floor discussion: even
|
||||
this near-zero figure is most plausibly measurement noise around the true 0, not a real
|
||||
allocation, since nothing in the read/validate/consume path can be shown by inspection to
|
||||
allocate on the warm path). `writeFrame` (`FrameWriteBuffer.beginFrame` + one `writeBytes` call +
|
||||
`endFrame`, against an already-grown `ByteWriter`): 14.262 ± 1.084 ns/op, **≈10⁻⁴ B/op** —
|
||||
likewise indistinguishable from zero.
|
||||
|
||||
**Decision.** Contract verified as stated; no design change required. Both numbers are recorded
|
||||
here as the baseline Phase 17's eventual CI allocation gate should hold this component to.
|
||||
|
||||
**Consequence.** None beyond the recorded numbers — this entry exists so a future regression
|
||||
(e.g. a later phase accidentally introducing an allocation on this path while adding HPACK or
|
||||
stream-state integration) has a concrete "was 0, now isn't" baseline to diff against, per this
|
||||
project's standing insistence that every non-obvious performance claim trace to an actual number.
|
||||
|
||||
**Revisit when.** Not expected to be revisited; re-measure if `FrameHeader`, `Http2FrameReader`,
|
||||
or `FrameWriteBuffer` are ever modified in a way that could plausibly affect their allocation
|
||||
profile.
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user