Skip to content

feat: compute table size including blobs - #8127

Closed
dantasse wants to merge 2 commits into
lance-format:mainfrom
dantasse:dantasse/blob_sizes
Closed

feat: compute table size including blobs#8127
dantasse wants to merge 2 commits into
lance-format:mainfrom
dantasse:dantasse/blob_sizes

Conversation

@dantasse

Copy link
Copy Markdown
Contributor

Right now table size calculations don't include blob v2 size, so they're sort of inaccurate. This will add a field blob_bytes which maintains a count of the size of each blob as it's written, and then rolls it into the bytes_on_disk totals in statistics.

@github-actions github-actions Bot added A-python Python bindings A-java Java bindings + JNI A-format On-disk format: protos and format spec docs enhancement New feature or request labels Jul 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Important

This PR touches the Lance format specification.

Substantive changes to the format specification — the .proto definitions
and the spec docs under docs/src/format/ — require a PMC vote before merge.
Minor edits such as typo fixes, wording, or formatting are excluded; use your
judgment.

If this is a meaningful format change:

  • Start a vote following the Lance community voting process.
    Format specification modifications need 3 binding +1 votes (excluding the
    proposer), held on GitHub Discussions, with a minimum voting period of 1 week.
  • Once the vote passes, link the completed vote in this PR. It should not be
    merged until the vote is linked.

@lance-gatekeeper lance-gatekeeper Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gate recommendation: request changes. The write-time manifest tally is the right general direction, but it must cover every supported Blob v2 writer and avoid scaling manifest memory with every field of every file. Prefer routing single-fragment writes through the same tallying boundary and persisting sparse per-blob-field records with explicit unknown semantics, while retaining additive decoding for old manifests.

// Record the preprocessor's blob payload tally, aligned to `fields`.
// Left empty ("not recorded") when the schema has no blob v2 fields or
// when the preprocessor could not attribute every payload byte.
let blob_bytes = match self.preprocessor.as_ref().and_then(|pre| pre.blob_bytes()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new tally is only finalized by V2WriterAdapter. The public distributed-write path FileFragment::create still calls FragmentCreateBuilder::write_v2_impl, which constructs DataFile::new_unstarted and never runs or records a BlobPreprocessor; a valid prepared inline blob therefore contributes zero bytes to the promised statistic. Route this writer through the same preprocessing/finalization boundary (or populate the same tally explicitly) so all current Blob v2 writers honor the contract.

Reproducer

Add rust/lance/tests/gate_blob_stats_single_fragment.rs:

use std::sync::Arc;

use arrow_array::{RecordBatch, RecordBatchIterator};
use arrow_schema::Schema;
use bytes::Bytes;
use lance::BlobDescriptorArrayBuilder;
use lance::dataset::{WriteParams, fragment::FileFragment};
use lance_file::version::LanceFileVersion;

#[tokio::test]
async fn single_fragment_writer_records_inline_blob_payload_bytes() {
    let temp_dir = tempfile::tempdir().unwrap();
    let mut blobs = BlobDescriptorArrayBuilder::new("blob");
    blobs.push_inline(Bytes::from_static(b"payload")).unwrap();
    let (field, array) = blobs.finish().unwrap().into_parts();
    let schema = Arc::new(Schema::new(vec![field]));
    let batch = RecordBatch::try_new(schema.clone(), vec![array]).unwrap();

    let fragment = FileFragment::create(
        temp_dir.path().to_str().unwrap(),
        0,
        RecordBatchIterator::new(vec![Ok(batch)], schema),
        Some(WriteParams {
            data_storage_version: Some(LanceFileVersion::V2_2),
            ..Default::default()
        }),
    )
    .await
    .unwrap();

    assert_eq!(fragment.files[0].blob_bytes.as_ref(), &[7]);
}

Run cargo test -p lance --test gate_blob_stats_single_fragment. Expected [7]; observed [].

Comment thread protos/table.proto
// non-blob fields). When empty, blob payload sizes were not recorded —
// either the file was written before this field existed or the writer could
// not know them (e.g. externally created data files).
repeated uint64 blob_bytes = 8;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This dense vector scales with all file fields, although almost all entries are usually zero. V2WriterAdapter fills one u64 for every field_id, and every DataFile now carries a 16-byte fat Arc<[u64]>. At the manifest interner's documented 20M-fragment scale, a 50-field table with one blob field adds roughly 8 GB of decoded tally storage plus about 320 MB of per-file struct space, before allocator/protobuf overhead. Persist sparse (field_id, bytes) records with explicit unknown semantics, and benchmark a nonempty-tally manifest at scale.

@codecov

codecov Bot commented Jul 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.22777% with 22 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
rust/lance/src/dataset/blob.rs 93.35% 15 Missing and 3 partials ⚠️
rust/lance-table/src/format/fragment.rs 94.73% 0 Missing and 3 partials ⚠️
rust/lance/src/dataset/statistics.rs 99.07% 0 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@dantasse

dantasse commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

moved to this issue, will close this PR for now:
#8193

@dantasse dantasse closed this Aug 3, 2026
@dantasse
dantasse deleted the dantasse/blob_sizes branch August 3, 2026 17:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-format On-disk format: protos and format spec docs A-java Java bindings + JNI A-python Python bindings enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant