test(code-runtime-python): cover the finish-residual sealed side; use a block array for the open seal

The review's two remaining non-blocking items: (1) a case where the run ends
with a SEALED open hold (past MAX_PENDING_CHUNKS) — finish() must commit the
sealed prefix, verified to fail if finish drops openSealed. (2) openSealed is
now a block ARRAY (one joined block per seal) matching the fd-3 reader's
blocks and the stray capture's seal, instead of one repeated string concat
that leaned on V8 ConsString amortization.
This commit is contained in:
Chinesezjc
2026-08-31 15:06:50 +08:00
committed by Tianyi Cui
parent 74e9d97e37
commit 771872a73d
2 changed files with 38 additions and 17 deletions
@@ -1052,14 +1052,16 @@ export class PythonCodeRuntime extends CodeRuntime {
// ARRAY, so k tiny open frames cost O(k) — re-joining and re-walking the
// whole held text per frame would be O(k * budget).
let openParts: string[] = []
// Past MAX_PENDING_CHUNKS, the held fragments are coalesced into ONE
// sealed block (mirroring the fd-3 reader's `blocks` and the stray
// capture's seal): each fragment is a distinct array slot plus string
// object header — ~30x overhead the byte cap cannot see — so a
// budget-sized single-character open flood would otherwise accumulate
// thousands of slots. Sealing bounds the live fragment count exactly
// like the sibling paths; the merge reads sealed + current fragments.
let openSealed: string | undefined
// Past MAX_PENDING_CHUNKS, the held fragments are coalesced into sealed
// blocks (mirroring the fd-3 reader's `blocks` and the stray capture's
// seal): each fragment is a distinct array slot plus string object
// header — ~30x overhead the byte cap cannot see — so a budget-sized
// single-character open flood would otherwise accumulate thousands of
// slots. Sealing bounds the live fragment count exactly like the
// sibling paths; the merge reads sealed + current fragments. A block
// ARRAY (not one repeated string concat) matches the sibling shape and
// avoids depending on V8 ConsString amortization.
let openSealed: string[] = []
// Every truncation arm funnels here: the committed open prefix was
// ALREADY billed, so it is pushed BEFORE the marker — a flushed line is
// never lost (only the marker stays last), and no ledger re-charge
@@ -1067,9 +1069,9 @@ export class PythonCodeRuntime extends CodeRuntime {
// it.
const truncateLogs = (): void => {
logsTruncated = true
if (openSealed !== undefined || openParts.length > 0) {
logs.push((openSealed ?? '') + openParts.join(''))
openSealed = undefined
if (openSealed.length > 0 || openParts.length > 0) {
logs.push(openSealed.join('') + openParts.join(''))
openSealed = []
openParts = []
}
logs.push(logTruncationMarker(this.config.maxLogBytes))
@@ -1587,7 +1589,7 @@ export class PythonCodeRuntime extends CodeRuntime {
// the push, the merge result is unchanged.
if (message.text !== '') {
if (openParts.length >= MAX_PENDING_CHUNKS) {
openSealed = (openSealed ?? '') + openParts.join('')
openSealed.push(openParts.join(''))
openParts = []
}
openParts.push(message.text)
@@ -1610,10 +1612,10 @@ export class PythonCodeRuntime extends CodeRuntime {
truncateLogs()
} else {
logBudget -= Math.max(cost - 2, 0)
logs.push((openSealed ?? '') + openParts.join('') + message.text)
logs.push(openSealed.join('') + openParts.join('') + message.text)
}
}
openSealed = undefined
openSealed = []
openParts = []
return
}
@@ -2002,10 +2004,10 @@ export class PythonCodeRuntime extends CodeRuntime {
// logsTruncated implies the hold is already empty (truncateLogs
// committed and cleared it), so this is reachable only when the run
// ends with the hold still open and untruncated.
if (openSealed !== undefined || openParts.length > 0) {
logs.push((openSealed ?? '') + openParts.join(''))
if (openSealed.length > 0 || openParts.length > 0) {
logs.push(openSealed.join('') + openParts.join(''))
}
openSealed = undefined
openSealed = []
openParts = []
if (child.pid === undefined) {
settle(result)