Skip to content

fix(compressor): detach constant byte scalars from source buffers - #86

Merged
ChristopherWirt merged 2 commits into
developfrom
cwirt/spark-write-memory-investigation-2026-11-04
Nov 4, 2026
Merged

ChristopherWirt merged 2 commits into
developfrom
cwirt/spark-write-memory-investigation-2026-11-04

Conversation

@ChristopherWirt

@ChristopherWirt ChristopherWirt commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Writing a constant string or binary column can keep each uncompressed input chunk alive until the file is closed.

  1. The constant compressor builds its ConstantArray from execute_scalar, which returns a zero-copy slice of the source's views or data buffer.
  2. The writer may buffer that constant until EOF, and it counts only the scalar's few bytes.
  3. The slice keeps the whole source buffer alive.

The fix copies UTF-8 and binary scalar bytes into their own allocation before building the constant. This includes bytes nested in struct or list values, such as a constant extension column backed by a struct. Numeric and all-null constants are unchanged.

Related upstream issue: vortex-data#10599

Testing

  • constant_bytes_release_source_buffer (rstest) covers UTF-8 and binary values, both inlined (8 B) and out-of-line (128 B), each nullable and non-nullable.
  • constant_nested_bytes_release_source_buffer covers an extension type backed by a struct of strings.
  • Both tests assert that the source buffer becomes uniquely owned while the compressed result is alive. Both fail without the fix with constant retained the source buffer.
  • cargo nextest run -p vortex-compressor: 393 passed.
  • cargo clippy -p vortex-compressor --all-targets --all-features -- -D warnings and cargo +nightly-2026-09-10 fmt -p vortex-compressor: clean.

Spark writer benchmark

Setup:

  • 8 constant 128-byte string columns, 524,288 rows, 8,192-row batches.
  • Spark 4.0.4 / Corretto 17, release JNI build of the latest commit.
  • Medians of 3 fresh JVMs per variant, in rotating order.
Metric Before After
Writer Arrow allocation peak 513.5 MiB 25.5 MiB
Process footprint (macOS) 640.2 MiB 179.9 MiB
Elapsed write time 1.19 s 1.28 s
  • All output files are byte-identical (same SHA-256).
  • The timings are noisy cold-JVM runs of about a second each. Across runs they overlap: 0.77–1.56 s before, 0.94–1.45 s after.
  • The benchmark covers the flat-string case. Its result matches the earlier run of the original commit (513.5 → 25.5 MiB).
  • On a realistic full-schema rewrite the overall memory gain was small. This fixes retention for constant byte columns specifically.

🤖 Generated with Claude Code

ChristopherWirt and others added 2 commits November 4, 2026 11:38
Signed-off-by: Christopher Wirt <cwirt@quantcast.com>
Copy string and binary bytes nested in tuple values too, so constant
struct- or list-backed extension columns also release the source chunk.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Christopher Wirt <cwirt@quantcast.com>
@ChristopherWirt
ChristopherWirt merged commit d1f46c9 into develop Nov 4, 2026
1 check passed
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.

2 participants