mirror of
https://github.com/ModernRelay/omnigraph.git
synced 2026-07-06 02:52:11 +02:00
iss-merge-rowid-overlap-corrupts-filtered-reads / lance#7444: an
update-style merge_insert over a merge-written fragment legally reuses the
updated rows' stable row ids (row-id-lineage spec: updates preserve
_rowid) while the superseded fragment keeps its full sequence plus a
deletion vector. A later delete leaves the overlapping id range sparsely
tiled, and lance-table 7.0.0's RowIdIndex::new asserted dense tiling —
failing every filtered read that builds the id→address map ("Wrong range"
debug assert; "all columns in a record batch must have the same length"
or a silently-wrong batch in release).
The upstream fix (lance#7480, merged 2026-07-01) landed hours AFTER
v8.0.0 was cut, so no release ≤ 8.0.0 carries it. Consume it now as a
vendored pin: vendor/lance-table is the pristine published 7.0.0 source
plus ONLY the #7480 rowids/index.rs hunk (drop the false tiling assert;
hard-error on the true invariant — one live id claimed by two fragments)
and upstream's regression unit test, wired via [patch.crates-io]. The fix
is read-side only, so already-written graphs become readable as-is — no
data repair.
Removal condition (see vendor/lance-table/README.omnigraph.md): drop the
vendor dir + patch entry at the first Lance bump whose lance-table ships
lance#7480 (9.0.0, or a backported 8.0.1). The surface guard
filtered_scan_tolerates_merge_update_row_id_overlap keeps that honest in
both directions.
Turns the previous commit's red tests green. Full workspace gate passes
(cargo test --workspace --locked --no-fail-fast, 68 suites).
210 lines
No EOL
8.9 KiB
Protocol Buffer
210 lines
No EOL
8.9 KiB
Protocol Buffer
// SPDX-License-Identifier: Apache-2.0
|
|
// SPDX-FileCopyrightText: Copyright The Lance Authors
|
|
|
|
syntax = "proto3";
|
|
|
|
package lance.file.v2;
|
|
|
|
import "google/protobuf/any.proto";
|
|
import "google/protobuf/empty.proto";
|
|
|
|
// # Lance v2.X File Format
|
|
//
|
|
// The Lance file format is a barebones format for serializing columnar data
|
|
// into a file.
|
|
//
|
|
// * Each Lance file contains between 0 and 4Gi columns
|
|
// * Each column contains between 0 and 4Gi pages
|
|
// * Each page contains between 0 and 2^64 items
|
|
// * Different pages within a column can have different items counts
|
|
// * Columns may have up to 2^64 items
|
|
// * Different columns within a file can have different item counts
|
|
//
|
|
// The Lance file format does not have any notion of a type system or schemas.
|
|
// From the perspective of the file format all data is arbitrary buffers of
|
|
// bytes with an extensible metadata block to describe the data. It is up to
|
|
// the user to interpret these bytes meaningfully.
|
|
//
|
|
// Data buffers are written to the file first. These data buffers can be
|
|
// referenced from three different places in the file:
|
|
//
|
|
// * Page encodings can reference data buffers. This is the most common way
|
|
// that actual data is stored.
|
|
// * Column encodings can reference data buffers. For example, a column encoding
|
|
// may reference data buffer(s) containing statistics or dictionaries.
|
|
// * Finally, the global buffer offset table can reference data buffers. This
|
|
// is useful for storing data that is shared across multiple columns.
|
|
// This is also useful for global file metadata (e.g. a schema that describes
|
|
// the file)
|
|
//
|
|
// ## File Layout
|
|
//
|
|
// Note: the number of buffers (BN) is independent of the number of columns (CN)
|
|
// and pages.
|
|
//
|
|
// Buffers often need to be aligned. 64-byte alignment is common when
|
|
// working with SIMD operations. 4096-byte alignment is common when
|
|
// working with direct I/O. In order to ensure these buffers are aligned
|
|
// writers may need to insert padding before the buffers.
|
|
//
|
|
// If direct I/O is required then most (but not all) fields described
|
|
// below must be sector aligned. We have marked these fields with an
|
|
// asterisk for clarity. Readers should assume there will be optional
|
|
// padding inserted before these fields.
|
|
//
|
|
// All footer fields are unsigned integers written with little endian
|
|
// byte order.
|
|
//
|
|
// ├──────────────────────────────────┤
|
|
// | Data Pages |
|
|
// | Data Buffer 0* |
|
|
// | ... |
|
|
// | Data Buffer BN* |
|
|
// ├──────────────────────────────────┤
|
|
// | Column Metadatas |
|
|
// | |A| Column 0 Metadata* |
|
|
// | Column 1 Metadata* |
|
|
// | ... |
|
|
// | Column CN Metadata* |
|
|
// ├──────────────────────────────────┤
|
|
// | Column Metadata Offset Table |
|
|
// | |B| Column 0 Metadata Position* |
|
|
// | Column 0 Metadata Size |
|
|
// | ... |
|
|
// | Column CN Metadata Position |
|
|
// | Column CN Metadata Size |
|
|
// ├──────────────────────────────────┤
|
|
// | Global Buffers Offset Table |
|
|
// | |C| Global Buffer 0 Position* |
|
|
// | Global Buffer 0 Size |
|
|
// | ... |
|
|
// | Global Buffer GN Position |
|
|
// | Global Buffer GN Size |
|
|
// ├──────────────────────────────────┤
|
|
// | Footer |
|
|
// | A u64: Offset to column meta 0 |
|
|
// | B u64: Offset to CMO table |
|
|
// | C u64: Offset to GBO table |
|
|
// | u32: Number of global bufs |
|
|
// | u32: Number of columns |
|
|
// | u16: Major version |
|
|
// | u16: Minor version |
|
|
// | "LANC" |
|
|
// ├──────────────────────────────────┤
|
|
//
|
|
// File Layout-End
|
|
//
|
|
// ## Data Pages
|
|
//
|
|
// A lot of flexibility is provided in how data is stored. A page's buffers do
|
|
// not strictly need to be contiguous on the disk. However, it is recommended
|
|
// that buffers within a page be grouped together for best performance.
|
|
//
|
|
// Data pages should be large. The only time a page should be written to disk
|
|
// is when the writer needs to flush the page to disk because it has accumulated
|
|
// too much data. Pages are not read in sequential order and if pages are too
|
|
// small then the seek overhead (or request overhead) will be problematic. We
|
|
// generally advise that pages be at least 8MB or larger.
|
|
//
|
|
// ## Encodings
|
|
//
|
|
// Specific encodings are not part of this minimal format. They are provided
|
|
// by extensions. Readers and writers should be designed so that encodings can
|
|
// be easily added and removed. Ideally, they should allow for this without
|
|
// requiring recompilation through some kind of plugin system.
|
|
|
|
// The deferred encoding is used to place the encoding itself in a different
|
|
// part of the file. This is most commonly used to allow encodings to be shared
|
|
// across different columns. For example, when writing a file with thousands of
|
|
// columns, where many pages have the exact same encoding, it can be useful
|
|
// to cut down on the size of the metadata by using a deferred encoding.
|
|
message DeferredEncoding {
|
|
// Location of the buffer containing the encoding.
|
|
//
|
|
// * If sharing encodings across columns then this will be in a global buffer
|
|
// * If sharing encodings across pages within a column this could be in a
|
|
// column metadata buffer.
|
|
// * This could also be a page buffer if the encoding is not shared, needs
|
|
// to be written before the file ends, and the encoding is too large to load
|
|
// unless we first determine the page needs to be read. This combination
|
|
// seems unusual.
|
|
uint64 buffer_location = 1;
|
|
uint64 buffer_length = 2;
|
|
}
|
|
|
|
// The encoding is placed directly in the metadata section
|
|
message DirectEncoding {
|
|
// The bytes that make up the encoding embedded directly in the metadata
|
|
//
|
|
// This is the most common approach.
|
|
bytes encoding = 1;
|
|
}
|
|
|
|
// An encoding stores the information needed to decode a column or page
|
|
//
|
|
// For example, it could describe if the page is using bit packing, and how many bits
|
|
// there are in each individual value.
|
|
//
|
|
// At the column level it can be used to wrap columns with dictionaries or statistics.
|
|
message Encoding {
|
|
oneof location {
|
|
// The encoding is stored elsewhere and not part of this protobuf message
|
|
DeferredEncoding indirect = 1;
|
|
// The encoding is stored within this protobuf message
|
|
DirectEncoding direct = 2;
|
|
// There is no encoding information
|
|
google.protobuf.Empty none = 3;
|
|
}
|
|
}
|
|
|
|
// ## Metadata
|
|
|
|
// Each column has a metadata block that is placed at the end of the file.
|
|
// These may be read individually to allow for column projection.
|
|
message ColumnMetadata {
|
|
|
|
// This describes a page of column data.
|
|
message Page {
|
|
// The file offsets for each of the page buffers
|
|
//
|
|
// The number of buffers is variable and depends on the encoding. There
|
|
// may be zero buffers (e.g. constant encoded data) in which case this
|
|
// could be empty.
|
|
repeated uint64 buffer_offsets = 1;
|
|
// The size (in bytes) of each of the page buffers
|
|
//
|
|
// This field will have the same length as `buffer_offsets` and
|
|
// may be empty.
|
|
repeated uint64 buffer_sizes = 2;
|
|
// Logical length (e.g. # rows) of the page
|
|
uint64 length = 3;
|
|
// The encoding used to encode the page
|
|
Encoding encoding = 4;
|
|
// The priority of the page
|
|
//
|
|
// For tabular data this will be the top-level row number of the first row
|
|
// in the page (and top-level rows should not split across pages).
|
|
uint64 priority = 5;
|
|
}
|
|
// Encoding information about the column itself. This typically describes
|
|
// how to interpret the column metadata buffers. For example, it could
|
|
// describe how statistics or dictionaries are stored in the column metadata.
|
|
Encoding encoding = 1;
|
|
// The pages in the column
|
|
repeated Page pages = 2;
|
|
// The file offsets of each of the column metadata buffers
|
|
//
|
|
// There may be zero buffers.
|
|
repeated uint64 buffer_offsets = 3;
|
|
// The size (in bytes) of each of the column metadata buffers
|
|
//
|
|
// This field will have the same length as `buffer_offsets` and
|
|
// may be empty.
|
|
repeated uint64 buffer_sizes = 4;
|
|
} // Metadata-End
|
|
|
|
// ## Where is the rest?
|
|
//
|
|
// This file format is extremely minimal. It is a building block for
|
|
// creating more useful readers and writers and not terribly useful by itself.
|
|
// Other protobuf files will describe how this can be extended. |