Repository navigation
Bound cache row decoding during central sync - #1873
Conversation
Neutral local review: approveFull PR review against Independent Codex review: no new findings. Claude was unavailable (the user reported no tokens); the permitted single-reviewer fallback was used. No Claude review or approval is claimed. Blast radius: low. Human merge hold remains. The conflict resolution retains missing privacy-fence column padding. CPU/memory review found no unbounded allocation in the changed output-window path; smaller reads trade some call overhead for bounded nested expansion. Physical page buffers and oversized individual values retain their existing costs. Validation: 7,088 local tests passed, 3 skipped; typecheck/declaration build passed; GitHub Node 22/24/26 CI green. A packed global install using published dependencies passed the 128 MiB regression, exporting all 3,854 eligible rows with retries/replay/privacy/watermark checks. Substituting current master's reader in that disposable install reproduced V8 heap exhaustion; the fixed reader was restored. Fourteen focused current-head safety tests also passed. Codex reviewed the diff and traced callers but could not run tests in its dependency-free checkout; the test results above were executed separately by the coordinating session. No findings require disposition. Independent Codex reportFix ValidationsCentral forward heap exhaustion from full-batch VARIANT expansion
Sparse selections, row order, privacy withholding, replay, and watermark safety
Existing HTTP chunking did not already fix the failure
FindingsNo new findings. No Finding
CPU and memory pass: no concern found. The changed scanner bounds expanded output vectors to 256 selected rows for contiguous selections and to a physical span under 256 rows for sparse index selections; CPU may do more small reads for very sparse matches, but that is the intended tradeoff to avoid whole-row-group object expansion. Evidence Bundle
|
Ship risk:
|
Decision mapScanned five hunks across three files. Most of the diff builds the memory/retry/privacy regression. One implementation choice deserves attention: Bound decoding to 256 physical rows per windowlet end = Math.min(count, start + 256)
Generated by decision-map as part of the local Neutral flow. This directs review attention; it adds no separate verdict. |
|
This seems to be cause of significant performance regression, causing CI tests to timeout. |
Central sync can exhaust the V8 heap when compact Parquet groups expand repeated tool definitions into whole-column object graphs before yielding rows. The HTTP chunk limit does not bound those upstream allocations.
Read output vectors in windows spanning at most 256 physical rows, including sparse selections. Preserve predicates, row order, deletes, privacy filtering, missing-column padding, and end-of-partition watermark acknowledgement. The storage-to-forward regression uses real Iceberg VARIANT data under a 128 MiB heap cap and checks retries, partial failure, replay/deduplication, and descending sequence order.
The upstream fix is released in Hyparquet 1.31.1 (hyparam/hyparquet#179). This branch includes current master, which uses Hyparquet 1.31.1 and Icebird 0.8.29. The dependency blocker is resolved; validation uses published packages with no local patches.
Validation (2026-09-21):
CPU and memory: nested output expansion is limited to the current physical window; sparse matches cannot widen it to an entire row group. The regression bounds decoder work as well as live heap. Physical page decoding, dictionaries, encoded-buffer caches, and unusually large individual values retain their existing costs. Watermarks still advance only after the whole partition is acknowledged.