You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This proposal adds two related low-level capabilities:
A single lance-file primitive for concatenating compatible encoded Lance files without decoding and re-encoding their page payloads.
A FileFragment API for writing independently computed physical-row slices of one or more existing columns and combining a complete set of slices into a DataReplacementGroup.
The API is intentionally a mechanism, not a distributed execution framework. Lance does not own task planning, scheduling, retries, progress, recovery, or commit granularity. Those remain the responsibility of Ray, Spark, or another caller-controlled coordinator.
No Lance file layout, manifest feature, reader layout, or transaction operation is added.
Motivation
Binary-copy compaction currently owns both compatibility checks and buffer-copy rewriting inside the dataset layer. This duplicates file-format knowledge and risks eligibility checks drifting from the actual rewrite behavior.
Distributed column recomputation has the same underlying file operation. Workers can independently compute and encode row-aligned pieces of one or more columns, but the final replacement needs a regular Lance file covering the entire fragment. Concatenating those already encoded files should not require decoding Arrow arrays or rerunning the UDF.
These two callers should share the encoded-file transformation while retaining their own dataset semantics:
Compaction owns fragment grouping, deletions, row IDs, index remapping, and transactions.
A distributed column writer owns work partitioning, retries, result collection, and publication.
Each input is a complete immutable Lance file. The primitive does not expose an internal plan and does not slice ranges out of a file.
It reads metadata once, determines the actions required for page, column, and file buffers, copies page payloads unchanged, relocates offsets and row-relative metadata, and writes a new footer. Exact-version details such as structural headers, alignment, and footer behavior remain in version-specific hooks.
Unsupported means the files cannot be safely concatenated without re-encoding. Missing or corrupt inputs, short reads, and I/O failures remain errors and must not be treated as a compatibility fallback. output_factory is only invoked after compatibility has been established. A single compatible full input can return Reused without creating another object.
write_columns is the full-fragment path. The plural name reflects that schema may contain one or more existing top-level fields.
write_columns_slice writes the same fields for a fragment-local, half-open physical row interval. slice is used deliberately: Lance range reads can have logical semantics and apply deletion vectors, while this API is positional. The input must contain exactly rows.end - rows.start rows, including values at deleted positions.
ColumnSlice is an opaque immutable value describing a completed ordinary Lance file. It contains only the storage facts needed to validate and combine it, such as:
fragment ID and source read version;
physical row interval and row count;
ordered target field IDs;
the staged DataFile.
It is not a plan, task, writer, progress record, retry policy, or retention handle. It must support a version-tagged bytes round trip so workers can return it to a coordinator. The Python object should be picklable and expose equivalent to_bytes / from_bytes methods.
concat_column_slices sorts slices by physical start position and requires all inputs to:
come from the same fragment snapshot;
cover exactly the same ordered target fields;
cover [0, fragment.physical_rows()) without gaps or overlaps.
Duplicate, overlapping, incomplete, or mixed-field inputs are errors; there is no last-write-wins rule. The method re-reads the actual file metadata instead of trusting only serialized declarations. It calls concat_files in physical-row order, uses the single-file reuse path when possible, and falls back to ordered decode/re-encode on Unsupported. Missing or corrupt slice files remain errors.
Neither successful nor failed concatenation deletes the input files. Their lifecycle remains caller-owned. Only complete fragment coverage can produce a publishable DataReplacementGroup.
Python API
Python should ship with the Rust API, using existing LanceFragment conventions:
The Python binding resolves column names against the dataset schema and delegates schema, field-ID, row-count, and coverage validation to Rust. rows must have finite non-negative start and stop, with step equal to None or 1.
Example
Workers can independently write multiple columns for disjoint physical slices:
The existing try-binary-copy mode falls back to decode/re-encode on Unsupported; a forced binary-copy mode returns a typed error. File compatibility, buffer transformation, page-priority relocation, and footer handling have one authority in lance-file.
Relationship to existing proposals
Two‑Tier Manifest Architecture to Enable Lock‑free Parallel Column Writes #6641 proposes a two-tier manifest to change commit coordination for parallel column writers. This proposal does not change manifest or commit semantics. It supplies a lower-level mechanism that an external coordinator can use before one existing DataReplacement commit.
Proposal: Data Overlay files for fast targeted writes #7401 adds overlay files for sparse cell-level updates. Column slices instead produce a complete positional replacement for one or more columns of a fragment and add no read-time overlay.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Summary
This proposal adds two related low-level capabilities:
lance-fileprimitive for concatenating compatible encoded Lance files without decoding and re-encoding their page payloads.FileFragmentAPI for writing independently computed physical-row slices of one or more existing columns and combining a complete set of slices into aDataReplacementGroup.The API is intentionally a mechanism, not a distributed execution framework. Lance does not own task planning, scheduling, retries, progress, recovery, or commit granularity. Those remain the responsibility of Ray, Spark, or another caller-controlled coordinator.
No Lance file layout, manifest feature, reader layout, or transaction operation is added.
Motivation
Binary-copy compaction currently owns both compatibility checks and buffer-copy rewriting inside the dataset layer. This duplicates file-format knowledge and risks eligibility checks drifting from the actual rewrite behavior.
Distributed column recomputation has the same underlying file operation. Workers can independently compute and encode row-aligned pieces of one or more columns, but the final replacement needs a regular Lance file covering the entire fragment. Concatenating those already encoded files should not require decoding Arrow arrays or rerunning the UDF.
These two callers should share the encoded-file transformation while retaining their own dataset semantics:
Encoded file concatenation
The proposed one-shot primitive is conceptually:
Each input is a complete immutable Lance file. The primitive does not expose an internal plan and does not slice ranges out of a file.
It reads metadata once, determines the actions required for page, column, and file buffers, copies page payloads unchanged, relocates offsets and row-relative metadata, and writes a new footer. Exact-version details such as structural headers, alignment, and footer behavior remain in version-specific hooks.
Unsupportedmeans the files cannot be safely concatenated without re-encoding. Missing or corrupt inputs, short reads, and I/O failures remain errors and must not be treated as a compatibility fallback.output_factoryis only invoked after compatibility has been established. A single compatible full input can returnReusedwithout creating another object.FileFragment API
The proposed Rust surface is:
write_columnsis the full-fragment path. The plural name reflects thatschemamay contain one or more existing top-level fields.write_columns_slicewrites the same fields for a fragment-local, half-open physical row interval.sliceis used deliberately: Lance range reads can have logical semantics and apply deletion vectors, while this API is positional. The input must contain exactlyrows.end - rows.startrows, including values at deleted positions.ColumnSliceis an opaque immutable value describing a completed ordinary Lance file. It contains only the storage facts needed to validate and combine it, such as:DataFile.It is not a plan, task, writer, progress record, retry policy, or retention handle. It must support a version-tagged bytes round trip so workers can return it to a coordinator. The Python object should be picklable and expose equivalent
to_bytes/from_bytesmethods.concat_column_slicessorts slices by physical start position and requires all inputs to:[0, fragment.physical_rows())without gaps or overlaps.Duplicate, overlapping, incomplete, or mixed-field inputs are errors; there is no last-write-wins rule. The method re-reads the actual file metadata instead of trusting only serialized declarations. It calls
concat_filesin physical-row order, uses the single-file reuse path when possible, and falls back to ordered decode/re-encode onUnsupported. Missing or corrupt slice files remain errors.Neither successful nor failed concatenation deletes the input files. Their lifecycle remains caller-owned. Only complete fragment coverage can produce a publishable
DataReplacementGroup.Python API
Python should ship with the Rust API, using existing
LanceFragmentconventions:The Python binding resolves column names against the dataset schema and delegates schema, field-ID, row-count, and coverage validation to Rust.
rowsmust have finite non-negativestartandstop, withstepequal toNoneor1.Example
Workers can independently write multiple columns for disjoint physical slices:
The coordinator collects the results, combines complete fragment coverage, and commits using the existing transaction API:
Multiple fragments can be processed independently and their
DataReplacementGroups committed in one transaction.Compaction reuse
Compaction does not call the column-slice APIs. It only passes each output group's complete data files, in fragment row order, to
concat_files:The existing try-binary-copy mode falls back to decode/re-encode on
Unsupported; a forced binary-copy mode returns a typed error. File compatibility, buffer transformation, page-priority relocation, and footer handling have one authority inlance-file.Relationship to existing proposals
DataReplacementcommit.All reactions