Review M2a: accept with two follow-up tasks; record models and lessons
The branch passes every check, including make verify-device on straylight and repeated timing runs under load. Reading and probing found that inferproxy does not pass an upstream close on to a client that is still sending, and that the chunked body reader delivers a stream only when the caller's buffer fills or the stream ends. Both were also gaps in the tasks and tests, so tasks 14 and 15 carry the fixes with new tests checked against the reference. The Model column is corrected: tasks 04 to 06 and 08 to 13 were Ornith. Lessons gain four implementer tips and five task-writing tips; three rules are promoted to AGENTS.md. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
# M2a task 15: deliver streamed data as it arrives (review follow-up)
|
||||
|
||||
**Branch:** `m2a` (run `git switch m2a`; `git status --short` must be empty, otherwise stop)
|
||||
**Commit subject:** `Return chunked body data as soon as it is available`
|
||||
|
||||
## Goal
|
||||
|
||||
The M2a review found that the chunked body reader in `http.rs` keeps reading until the caller's
|
||||
buffer is full or the stream ends. With an 8 KiB buffer that means a streamed completion reaches
|
||||
`loopd` in bursts of about twenty events, and a short completion arrives all at once at its end.
|
||||
The reviewer measured it: with events sent 300 ms apart, every event was delivered at the moment
|
||||
the last one arrived. Three things follow: the thinking cap fires up to twenty chunks late, the
|
||||
text a user sees in M2b would come in lumps, and bytes already copied into the caller's buffer are
|
||||
lost when a later read in the same call times out.
|
||||
|
||||
Task 04 did not state the contract of `std::io::Read`, which is the plan's fault. The contract is:
|
||||
**a `read` returns as soon as it has any data to give. It blocks only when it has none.** The test
|
||||
you copy in sends a recording in pieces 300 ms apart and requires the first data within 200 ms and
|
||||
the rest spread out over the run.
|
||||
|
||||
## Files
|
||||
|
||||
- Copy (replacing the old one): `crates/loopd/tests/http.rs`
|
||||
- Modify: `crates/loopd/src/http.rs`, `docs/implementer-log.md`
|
||||
|
||||
## Required behaviour
|
||||
|
||||
In the chunked framing, `Body::read`:
|
||||
|
||||
1. Returns as soon as it has copied at least one byte of chunk data into the caller's buffer, even
|
||||
if the buffer is not full and even if the chunk's trailing `\r\n` has not arrived yet. Consume
|
||||
that `\r\n` at the start of the **next** call.
|
||||
2. Reads from the socket only when it has no data to give: at a chunk-size line, at a pending
|
||||
`\r\n`, or at the trailers.
|
||||
3. Everything else from task 04 still holds: the same bytes come out however they arrive, errors
|
||||
and limits are unchanged, and the 15 earlier tests still pass.
|
||||
|
||||
The length and close framings already behave this way.
|
||||
|
||||
## Steps
|
||||
|
||||
- [ ] **1. Copy.** `git switch m2a`, then
|
||||
`cp docs/plans/M2a/files/crates/loopd/tests/http.rs crates/loopd/tests/`
|
||||
- [ ] **2. See it fail.** `cargo test -p loopd --test http`. Expected: 1 of 16 fails,
|
||||
`streamed_data_is_delivered_as_it_arrives`, saying the reader is buffering the stream.
|
||||
- [ ] **3. Fix `read_chunked`.** Run `cargo fmt --all`.
|
||||
- [ ] **4. See it pass.** `cargo test -p loopd`. Expected: `http` 16 passed and every other file as
|
||||
before (the `chat`, `cap`, `sse` and `selftest` tests read through this code).
|
||||
- [ ] **5. Run the gate.** `make gate`. Expected last line: `gate: ok`.
|
||||
- [ ] **6. Log and commit.** `git add crates/loopd docs/implementer-log.md && git commit`
|
||||
|
||||
## Done when
|
||||
|
||||
- `cargo test -p loopd --test http` reports 16 passed; `make gate` prints `gate: ok`;
|
||||
`cmp crates/loopd/tests/http.rs docs/plans/M2a/files/crates/loopd/tests/http.rs` prints nothing.
|
||||
|
||||
## Stop and report if
|
||||
|
||||
- `the_result_does_not_depend_on_how_the_bytes_arrive` fails after the change: the fix has broken
|
||||
the framing somewhere.
|
||||
Reference in New Issue
Block a user