Skip to content

test(bench): measure row lineage placement inline versus spilled - #9340

Draft
BubbleCal wants to merge 2 commits into
yang/oss-2269-5-row-lineage-updatefrom
yang/oss-2269-6-row-lineage-bench
Draft

BubbleCal wants to merge 2 commits into
yang/oss-2269-5-row-lineage-updatefrom
yang/oss-2269-6-row-lineage-bench

Conversation

@BubbleCal

@BubbleCal BubbleCal commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Last of the §5.3 stack (#8931, #9250), on top of #9347. The benchmark behind the numbers in #9337, from #8953 and extended to the table-config switch and the shuffled workload.

Stack

  1. feat(format): define hidden row lineage columns #9253 feat(format): define hidden row lineage columns
  2. feat(dataset): read and write spilled row lineage columns #9336 feat(dataset): read and write spilled row lineage columns
  3. feat(dataset): spill row lineage at compaction #9337 feat(dataset): spill row lineage at compaction
  4. feat(table): load spilled row lineage ahead of a commit #9338 feat(table): load spilled row lineage ahead of a commit
  5. feat(dataset): spill row lineage when updating rows #9339 feat(dataset): spill row lineage when updating rows
  6. feat(dataset): write compaction's spilled lineage into the fragment's data file #9347 feat(dataset): write compaction's spilled lineage into the fragment's data file
  7. this PR: test(bench): row lineage spill benchmark

What it measures

LANCE_ENABLE_UNSTABLE_SPILLED_ROW_LINEAGE=1 cargo bench --bench rowid_spill, with BENCH_FRAGMENTS, BENCH_ROWS_PER_FRAGMENT, BENCH_DELETE_PERCENT, BENCH_APPENDS and BENCH_SCENARIOS to size it. Two workloads: deleted (30% of rows deleted, then compacted; the sequences still run-encode) and shuffled (every row rewritten in random order, then compacted; no run structure survives). Within each, the inline arm never opts in and the spilled arm sets lance.row_lineage.spill=true. Per arm it reports manifest and transaction-file bytes split by sequence, cold open, small-append commit latency, one sequence load, the row id index build, a take by row id once the index exists, compaction wall time and bytes on disk. The take is checked against the value the row should hold, so a spilled sequence that resolved to the wrong row fails the benchmark rather than looking fast.

Results and their reading are in #9337.

Refs #8931, #9250

🤖 Generated with Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

ACTION NEEDED
Lance follows the Conventional Commits specification for release automation.

The PR title and description are used as the merge commit message. Please update your PR title and description to match the specification.

For details on the error please inspect the "PR Title Check" action.

@BubbleCal
BubbleCal force-pushed the yang/oss-2269-6-row-lineage-bench branch from f65221b to 3bb5b86 Compare September 17, 2026 08:39
@BubbleCal BubbleCal changed the title bench(rowids): measure row lineage placement inline versus spilled test(bench): measure row lineage placement inline versus spilled Sep 17, 2026
@github-actions github-actions Bot added the chore label Sep 17, 2026
@BubbleCal
BubbleCal force-pushed the yang/oss-2269-6-row-lineage-bench branch from 3bb5b86 to 3fe94b4 Compare September 17, 2026 08:47
@BubbleCal
BubbleCal added this pull request to stack #9343 September 17, 2026 08:56
BubbleCal and others added 2 commits September 17, 2026 18:38
… data file

Compaction computes the output fragments' row lineage before writing: the
row ids and versions carry over from the inputs and the output file sizes
are fixed in advance. When the table's policy spills a sequence type, its
values ride along as hidden uint64 columns of the batches being written,
under the reserved field ids, and each output fragment's metadata marks
them as spilled into its own data file. A compacted fragment on a table
that opts in has one file instead of two, and a scan that projects _rowid
reads it from the file it already has open.

The write path lets the columns through: write_fragments sets the three
hidden fields aside before the schema is checked against the dataset's and
puts them back on the written schema under their reserved ids, and
Schema::validate admits exactly those ids under those names. The field id
constants move to lance-core for that.

Binary-copy compaction cannot add columns to the files it copies, so it
keeps writing a separate lineage file, as does the update path.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Two workloads bracket what the run encoding can do with a sequence: 30%
of rows deleted then compacted, where the row ids still encode as a range
plus a bitmap, and every row rewritten in random order then compacted,
where no run structure survives. Within each, the two arms differ only in
the table's lance.row_lineage.spill setting, and the benchmark reports
manifest and transaction-file bytes, cold open, small-append commit
latency, sequence load, row id index build, a take by row id, compaction
wall time and bytes on disk.

Co-authored-by: Will Jones <willjones127@gmail.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant