Repository navigation
Report an over-long room answer instead of dropping it as a timeout - #2690
Conversation
An external room participant settles a line only when its newline arrives, so an answer that
outgrew the 4 KB in-flight buffer had its bytes erased; the remaining fragment was ignored, the
turn hit the reply timeout, and the room was told the process timed out. The round then counted a
member that did answer as silence, and the child was killed for a timeout that never happened.
Remember that the in-flight line overflowed instead of clearing it, drop only its bytes up to the
newline that ends it, and settle that turn as { failure: { reason: 'error', detail: 'answer
exceeded 4096 bytes' } } with one room line saying why. The process is kept for the next turn: a
timeout is a child that will not answer, an overflow is an answer too large to carry.
Fixes #2689
|
Verdict: REQUEST_CHANGES at d172d6b Ran at this tip: Why it does not pass
What is already good (not the refusal)
|
The overflow flag was set whatever line crossed the cap, and it settled whatever turn was open when that line's newline finally arrived, so a log line written while the seat was idle swallowed a turn opened during its tail (#2689). Remember the turn that owned the line - none when no turn was open - and settle only that one; a turn opened later waits for its own answer. Adds the crossing case to room-external.test.js and refreshes the core-host invariant sentence.
|
Written-by: Rework Addressed the Reviewer's REQUEST_CHANGES at What changed: the overflow flag is now tied to the turn that owned the line. When the buffer crosses 4096 bytes, Ran at 38c8f89: Red first: base Not run: full CI on |
|
Verdict: PASS at 38c8f89 Ran at this tip: Review
|
Written-by: Dev
Fixes #2689
A room answer whose JSON line outgrew the 4 KB stdout buffer was erased: the turn then timed out, the room was told the process timed out, and the child was killed although it had answered — a member that did answer was counted as silence. The overflow is now remembered, only that line's bytes are dropped up to the newline that ends it, and the turn settles as
{ failure: { reason: 'error', detail: 'answer exceeded 4096 bytes' } }with one room line saying why. The process is kept for the next turn: a timeout is a child that will not answer, an overflow is an answer too large to carry. A long line written with no turn open is discarded the same way and never parsed; a turn opened while its tail is still in flight waits for its own answer.Owner: Dev
Reviewer: Reviewer
Tip SHA: 38c8f89
Base: ce349fd (live main head)
Rework — Written-by: Rework
The REQUEST_CHANGES at
d172d6bdwas right:entry.overflowedwas not tied to the turn that owned the over-long line, so a log line that overflowed while the seat was idle settled a turn opened before its newline — falseanswered more than 4096 bytesroom line, child's real answer dropped.entry.overflowTurnnow records the owning turn (undefinedwhen none) when the line overflows; the newline settles only when that turn is still the open one, otherwise its bytes are dropped and the open turn waits for its own answer. One test added for the crossing case: open the seat, overflow while idle, start the turn, let the log newline land, then write the answer line. Tip38c8f89a.Checkpoint
{
"pr": 2690,
"claim": "Dev",
"issue": 2689,
"summary": "An external room participant erased its in-flight stdout buffer past 4 KB, so an answer whose JSON line was still unterminated was lost: the turn timed out and the child was killed. It now remembers the overflow and the turn that owned the line (none when the seat was idle), drops only that line's bytes up to its newline, and settles that turn as 'answer exceeded 4096 bytes' with one room line. The process is kept for the next turn because it did answer; a long log line with no turn open never parses and never settles a turn opened while its tail is in flight.",
"files": [
"core/src/host/room-participant-external.js",
"core/test/room-external.test.js",
"docs/subsystems/core-host.md",
"changelog.d/2689-external-long-answer.fixed.md",
"patches/ds-bot/files/src/host/room-participant-external.js"
],
"checks": {
"ran": "TZ=UTC node --test core/test/room-external.test.js; TZ=UTC node --test core/test/room-participant.test.js; node scripts/ds-bot-patches.mjs check; node .github/scripts/changelog.mjs check",
"result": "green",
"at": "38c8f89ae43778b92f907440b11cee45b899863e"
},
"next": "none — ready for re-review at 38c8f89"
}
Ran at this tip: 38c8f89
Red first
With
core/src/host/room-participant-external.jsback at the base blobce349fdf(new tests kept): 17/19, the two#2689answer cases red. With the source at the unfixed old tipd172d6bd: 18/19, the new crossing case red (not ok 18 - a log line that overflowed while idle does not settle a turn opened before its newline (#2689)). With the fix: 19/19.Not run
npm run verifyand the smoke test, as instructed for this task.node scripts/ds-bot-patches.mjs generate— needs git, which this machine does not have. Thepatches/ds-bot/files/mirrors of the two changedcore/files are byte-identical (same git blob sha), andcheckconfirmscore/equalsds-bot/+ patches.arch-docsandbuild-client.mjs --check— this tarball has no git and nocore/node_modules; the change does not touchcore/src/client/, anddocs/subsystems/core-host.mdis updated in the same diff.