Skip to content

[#12407] fix(lance): Hydrate empty schema before table alteration - #12895

Merged
jerryshao merged 3 commits into
apache:mainfrom
yuqi1129:fix/12407-lance-empty-columns
Sep 4, 2026
Merged

[#12407] fix(lance): Hydrate empty schema before table alteration#12895
jerryshao merged 3 commits into
apache:mainfrom
yuqi1129:fix/12407-lance-empty-columns

Conversation

@yuqi1129

@yuqi1129 yuqi1129 commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

  • Recheck the Lance dataset schema before altering a table whose stored columns are empty, including when lance.version is already recorded.
  • Reuse the existing repair-on-load path to persist hydrated columns before applying the physical alteration and its new version.
  • Abort before modifying the dataset when the required schema cannot be initialized or loaded.
  • Preserve ordinary loadTable behavior for confirmed zero-column datasets and remove the lance.empty-schema-checked-version property from the earlier iteration.

Why are the changes needed?

Registering an existing Lance table accepts an empty column list and defers schema hydration. alterTable previously used the metadata-only parent load method, so an index alteration could update the Lance dataset and persist its latest version while the Gravitino columns remained empty. A later default load would interpret that version as confirmation of a genuinely zero-column schema and skip the dataset read.

This patch prevents that concrete inconsistent state. It was found while investigating #12407, but the issue does not show that an alter operation caused the reported UI symptom, so the API and authorization path still needs separate investigation.

Related: #12407

Does this PR introduce any user-facing change?

No API or configuration key is added. For a table with empty stored columns, alterTable now performs a schema read first and fails without changing the dataset if that read cannot complete. Ordinary loadTable behavior is unchanged.

How was this patch tested?

Added tests covering:

  • a registered table with empty columns and no stored version;
  • a previously confirmed zero-column table whose dataset was initialized at a newer version;
  • a schema-read failure that must not execute the physical alteration or update metadata.

Ran:

  • ./gradlew :catalogs:catalog-lakehouse-generic:test -PskipWeb=true -PskipDockerTests=true
  • ./gradlew :docs:build -PskipWeb=true
  • ./gradlew :catalogs:catalog-lakehouse-generic:spotlessApply

Copilot AI lite review requested due to automatic review settings September 3, 2026 14:25
@yuqi1129 yuqi1129 self-assigned this Sep 3, 2026

Copilot AI 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.

🟢 Approval recommended

The refresh logic change is targeted, aligns with the stated bug scenario, and is backed by updated documentation plus regression tests covering the key recovery and no-op cases.

Pull request overview

This PR fixes Lance table schema refresh behavior in the lakehouse-generic catalog so that tables with empty stored columns can recover their schema from the underlying Lance dataset even when lance.version is already recorded, preventing tables from getting “stuck” with empty columns.

Changes:

  • Updates loadTable refresh logic to always re-examine the Lance dataset when stored columns are empty (in both DECLARED_AND_EMPTY and VERSION_CHECK flows).
  • Avoids redundant metadata writes when the dataset is still empty and the recorded version is unchanged.
  • Updates documentation and expands regression tests to cover recovery, no-op rechecks for truly empty datasets, and fallback behavior when dataset open fails.
File summaries
File Description
docs/lakehouse-generic-lance-table.md Updates schema refresh mode and zero-column dataset behavior documentation to match the new recheck/repair logic.
catalogs/catalog-lakehouse-generic/src/main/java/org/apache/gravitino/catalog/lakehouse/lance/LanceTableOperations.java Adjusts schema refresh gating so empty stored columns trigger dataset schema re-examination and avoids no-op metadata updates for unchanged empty datasets.
catalogs/catalog-lakehouse-generic/src/test/java/org/apache/gravitino/catalog/lakehouse/lance/TestLanceTableOperations.java Adds/updates regression tests for repairing empty stored columns with a recorded version, avoiding redundant updates for truly empty datasets, and preserving version on fallback.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

…ts own

Repairing an empty column list on every load is correct but costly: in
the default DECLARED_AND_EMPTY mode any table with no stored columns
opened its Lance dataset on every loadTable, and a genuinely empty
dataset whose version moved rewrote its metadata each time.

The reason lance.version could not be trusted as a "checked, really
empty" marker is that alterTable records it after any dataset change
without reading the schema, so a table registered with no columns ends
up carrying a version its column metadata never came from. Record the
confirmation separately instead: recordCheckedEmptyVersion now also
writes lance.empty-schema-checked-version, and only a schema read that
returned no columns ever writes it.

A load skips the dataset when the marker still matches lance.version.
Any path that records a newer version invalidates the marker without
knowing about it, columns read from the dataset clear it, and version
metadata that cannot be parsed confirms nothing. Tables already stuck
with an unconfirmed empty column list carry no marker, so they are
re-examined once, repaired, and then stop paying for the check.

Both refresh modes now share one isDatasetReadNeeded predicate; the only
difference left between them is whether the dataset version is also
consulted.

Tests: a confirmed-empty table opens no dataset, a repaired table stops
opening one, a marker left behind by an older version is ignored, and
unparsable version metadata is treated as unconfirmed.

Claude-Session: https://claude.ai/code/session_01DZwZThqoBB7NojLLpdYPfX
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

Code Coverage Report

Overall Project 69.37% +0.04% 🟢
Files changed 70.87% 🟢

Module Coverage
aliyun 19.74% 🔴
api 51.57% 🟢
authorization-common 85.96% 🟢
authorization-ranger 4.38% 🔴
aws 53.54% 🟢
azure 32.1% 🔴
catalog-common 20.87% -0.04% 🔴
catalog-fileset 82.17% 🟢
catalog-glue 69.24% 🟢
catalog-hive 82.96% 🟢
catalog-jdbc-common 45.69% 🟢
catalog-jdbc-doris 82.69% 🟢
catalog-jdbc-mysql 79.33% 🟢
catalog-jdbc-postgresql 83.39% 🟢
catalog-jdbc-starrocks 79.16% 🟢
catalog-kafka 76.99% 🟢
catalog-lakehouse-generic 60.88% +3.13% 🟢
catalog-lakehouse-hudi 79.1% 🟢
catalog-lakehouse-iceberg 85.86% -1.99% 🟢
catalog-lakehouse-paimon 84.29% 🟢
catalog-model 77.99% 🟢
cli 44.51% 🟢
client-java 77.5% 🟢
common 56.34% 🟢
core 83.97% -0.32% 🟢
filesystem-hadoop3 76.48% 🟢
flink 0.0% 🔴
flink-common 52.1% 🟢
flink-runtime 0.0% 🔴
gcp 32.2% 🔴
hadoop-auth 68.0% 🟢
hadoop-common 17.84% 🔴
hive-metastore-common 53.4% 🟢
iceberg-aliyun-bundle 0.0% 🔴
iceberg-common 64.76% -6.97% 🟢
iceberg-rest-server 76.22% -0.93% 🟢
idp-basic 86.42% +0.56% 🟢
integration-test-common 0.0% 🔴
jobs 62.92% 🟢
lance-common 32.52% 🔴
lance-rest-server 68.15% 🟢
lineage 59.39% 🟢
optimizer 83.24% 🟢
optimizer-api 21.95% 🔴
server 89.87% -0.7% 🟢
server-common 81.08% -1.05% 🟢
spark 28.57% 🔴
spark-common 52.04% 🟢
tencent 81.78% 🟢
trino-connector 57.5% 🟢
Files
Module File Coverage
catalog-common IcebergConstants.java 0.0% 🔴
catalog-lakehouse-generic LanceTableOperations.java 62.92% 🟢
catalog-lakehouse-iceberg IcebergCatalogOperations.java 78.57% 🟢
core CatalogManager.java 72.41% 🟢
iceberg-common IcebergCatalogUtil.java 63.72% 🟢
IcebergCatalogWrapper.java 28.65% 🔴
iceberg-rest-server IcebergCatalogWrapperManager.java 70.49% 🟢
DynamicIcebergConfigProvider.java 51.52% 🔴
idp-basic IdpGroupMetaService.java 95.45% 🟢
IdpRESTUtils.java 79.49% 🟢
server AccessControlNotAllowedFilter.java 100.0% 🟢
StatisticOperations.java 90.8% 🟢
ExceptionHandlers.java 84.26% 🟢
server-common Utils.java 53.64% 🔴

@yuqi1129
yuqi1129 requested a review from jerryshao September 4, 2026 07:07
@yuqi1129 yuqi1129 changed the title [#12407] fix(lance): Repair empty stored columns with recorded version [#12407] fix(lance): Hydrate empty schema before table alteration Sep 4, 2026
@yuqi1129 yuqi1129 added the branch-1.3 Automatically cherry-pick commit to branch-1.3 label Sep 4, 2026
@jerryshao
jerryshao requested a lite review from Copilot September 4, 2026 10:09

Copilot AI 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.

🟢 Approval recommended

The change directly addresses the inconsistent-state bug described, keeps normal loadTable behavior intact, and is backed by targeted tests for both success and failure paths.

Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@jerryshao
jerryshao merged commit 1db4894 into apache:main Sep 4, 2026
38 checks passed
yuqi1129 added a commit that referenced this pull request Sep 4, 2026
… before table alteration (#12895) (#12926)

**Cherry-pick Information:**
- Original commit: 1db4894
- Target branch: `branch-1.3`
- Status: ✅ **Conflicts resolved**

Resolved the test-source conflict and verified the backport with
`:catalogs:catalog-lakehouse-generic:test`.

---------

Co-authored-by: Qi Yu <yuqi@datastrato.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

branch-1.3 Automatically cherry-pick commit to branch-1.3

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants