Skip to content

fix(serializers): byte-aware Arrow batch sizing holds the memory bound on skewed frames (LAB-110) - #244

Merged
27Bslash6 merged 1 commit into
mainfrom
agent/winston/fd29ce21
Jul 25, 2026
Merged

fix(serializers): byte-aware Arrow batch sizing holds the memory bound on skewed frames (LAB-110)#244
27Bslash6 merged 1 commit into
mainfrom
agent/winston/fd29ce21

Conversation

@27Bslash6

@27Bslash6 27Bslash6 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Fixes #161 · Closes LAB-110

Problem

_bounded_chunksize estimated bytes-per-row as a uniform average (table.nbytes // table.num_rows) and capped record batches by row count only. On a skewed frame, a clustered run of wide rows lands in one batch far over the 8 MiB target — and since zstd compresses per-batch, the compressor's working set spikes in exactly the OOM regime the bounded-batching feature exists to prevent.

Measured on the issue's case (100k tiny rows + 50 clustered 2 MiB cells): worst batch 95.4 MiB — 11.9x the target.

Fix

Replace the uniform estimate with _write_bounded_batches: per batch, binary-search the largest row count whose slice actually fits the byte budget.

  • Table.slice() is zero-copy and .nbytes accounts for slice offsets, so each probe is O(columns), not O(bytes).
  • Batch bytes are monotone in row count, so the binary search is valid.
  • A single row wider than the target gets its own 1-row batch (rows can't split).
  • Empty tables write a schema-only IPC file, as before.

Post-fix, the same skewed frame's worst batch is 7.6 MiB (under target). Uniform frames still get maximal, near-target batches — no fragmentation — and a 122 MiB uniform frame serializes in ~0.34 s including zstd, so the probe overhead is noise.

Acceptance criteria

  • ✅ Skewed-frame regression test (issue's exact 100k + 50×2 MiB case) asserts no batch exceeds 8 MiB × 1.05 tolerance (TestBoundedBatching)
  • ✅ Batch sizing accounts for per-row byte variance (actual slice bytes, not a uniform average)
  • ✅ Uniform-frame behavior preserved (test_uniform_frame_batching_not_fragmented guards against fragmentation; all 59 existing serializer tests pass)
  • ✅ No on-wire Arrow IPC format change — IPC files were already multi-batch; only internal batch boundaries move. Cross-SDK readers unaffected.

Testing

  • tests/unit/test_arrow_serializer.py: 62 tests pass (3 new in TestBoundedBatching: skewed frame, uniform no-fragmentation, single-oversized-row)
  • Doctests on the module pass; ruff check + ruff format clean; pre-commit (incl. basedpyright) green
  • Full quick suite: all failures/errors are pre-existing environment flakes in tests/performance/ + fixture-teardown noise, reproduced identically on clean main; serializer benchmarks pass

Docs

No user-facing doc documents record-batch sizing internals (checked docs/, README, feature matrices — the only "batch" mentions are about batching queries). The in-code comments/docstring describing the strategy are updated in this diff.

Summary by CodeRabbit

  • Bug Fixes

    • Improved Arrow data batching to keep serialized batches within the intended size limit, including for variable-width and unusually large rows.
    • Preserved reliable round-tripping of serialized data across a wider range of dataset shapes.
  • Tests

    • Added coverage for skewed, uniform, and oversized-row datasets to verify batch sizing and serialization reliability.

…d on skewed frames (LAB-110)

_bounded_chunksize estimated bytes-per-row as a uniform average
(table.nbytes // table.num_rows) and capped record batches by row count
only, so a clustered run of wide rows (e.g. JSON-blob cells) produced
batches ~12x over the 8 MiB target — and zstd compresses per-batch, so
the compressor working set spiked in exactly the OOM regime the bound
exists to prevent (#161).

Replace the estimate with _write_bounded_batches: binary-search the
largest row count whose slice ACTUALLY fits the byte budget per batch.
Table.slice() is zero-copy and .nbytes accounts for slice offsets, so
each probe is O(columns); batch bytes are monotone in row count, so the
search is valid. A single row wider than the target gets its own batch.
Uniform frames still get maximal near-target batches (no fragmentation;
122 MiB frame serializes in ~0.34s incl. zstd).

On the issue's 100k tiny rows + 50x2 MiB cells case the worst batch
drops from 95.4 MiB (11.9x target) to 7.6 MiB. No wire-format change:
Arrow IPC files were already multi-batch; only the internal batch
boundaries move.

Closes #161

Co-authored-by: multica-agent <github@multica.ai>
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e198fa7d-f956-4661-baad-0765a10c2a91

📥 Commits

Reviewing files that changed from the base of the PR and between 9029ca3 and c2f88f3.

📒 Files selected for processing (2)
  • src/cachekit/serializers/arrow_serializer.py
  • tests/unit/test_arrow_serializer.py

Walkthrough

Arrow serialization now writes byte-bounded IPC record batches using binary-search row selection. Tests inspect decompressed batches for size limits, fragmentation, oversized-row handling, and DataFrame round trips.

Changes

Arrow batching

Layer / File(s) Summary
Byte-bounded batch writing
src/cachekit/serializers/arrow_serializer.py
Adds _write_bounded_batches() to select record-batch sizes from actual sliced-table byte counts and wires it into ArrowSerializer.serialize().
Batch boundary validation
tests/unit/test_arrow_serializer.py
Adds coverage for skewed frames, uniform frames, and rows exceeding the target size, including IPC batch inspection and round-trip checks.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ArrowSerializer
  participant _write_bounded_batches
  participant PyArrowIPCWriter
  ArrowSerializer->>_write_bounded_batches: pass Arrow table
  _write_bounded_batches->>_write_bounded_batches: binary-search rows within byte budget
  _write_bounded_batches->>PyArrowIPCWriter: write selected table slices
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and accurately describes the main change: byte-aware Arrow batch sizing for skewed frames.
Description check ✅ Passed The description covers the problem, fix, acceptance criteria, testing, and docs, so it is mostly complete.
Linked Issues check ✅ Passed The PR implements byte-aware batching and adds a skewed-frame regression test, matching the requirements of issue #161.
Out of Scope Changes check ✅ Passed The changes stay focused on Arrow serializer batching and related tests, with no obvious unrelated code added.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/winston/fd29ce21

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@27Bslash6 27Bslash6 changed the title LAB-110: fix(serializers): byte-aware Arrow batch sizing holds the memory bound on skewed frames fix(serializers): byte-aware Arrow batch sizing holds the memory bound on skewed frames (LAB-110) Jul 24, 2026
@27Bslash6

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@27Bslash6

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@27Bslash6

Copy link
Copy Markdown
Contributor Author

@27Bslash6 — automated review gate cleared for LAB-110, ready for your signoff / merge.

  • CI: all checks green (tests 3.9–3.14, CodeQL, Format & Lint, Security, Supply Chain, codecov patch).
  • CodeRabbit: forced full review (@coderabbitai full review) — APPROVED, "No actionable comments were generated." The earlier pass was a Fair-Usage rate-limit skip, not a real review; the full review re-ran from scratch and is genuinely clean.
  • Crypto/protocol gate: not applicable — the diff only changes internal Arrow IPC record-batch boundaries (byte-aware sizing via zero-copy Table.slice().nbytes binary search). No encryption / AAD / key-derivation / cache-key / ByteStorage-wire-format change; the on-wire Arrow IPC format is unchanged and cross-SDK readers are unaffected.

Over to you for human review and merge.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Changed files and diff ==='
git diff --unified=80 HEAD^ HEAD -- \
  src/cachekit/serializers/arrow_serializer.py \
  tests/unit/test_arrow_serializer.py

echo '=== Serializer structure ==='
ast-grep outline src/cachekit/serializers/arrow_serializer.py --items all --view expanded

echo '=== Test structure ==='
ast-grep outline tests/unit/test_arrow_serializer.py --items all --view expanded

Length of output: 35111


@27Bslash6 I independently reviewed the batch-sizing implementation and its regression coverage. No actionable findings.

The binary search correctly chooses the largest byte-bounded slice, always progresses for oversized single rows, and retains schema-only behaviour for empty tables. The new tests directly validate skewed, uniform, and oversize-row batch boundaries as well as round trips. The change is scoped to internal Arrow IPC record-batch boundaries and does not alter the envelope or public wire format.

I cannot merge the PR directly. For an explicit CodeRabbit approval, post @coderabbitai approve.

@27Bslash6
27Bslash6 merged commit e67db58 into main Jul 25, 2026
34 of 35 checks passed
@27Bslash6
27Bslash6 deleted the agent/winston/fd29ce21 branch July 25, 2026 00:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Arrow batch-size estimate breaks the bounded-memory guarantee on skewed/wide frames

1 participant