This is an automated email from the ASF dual-hosted git repository. yuqi1129 pushed a commit to branch issue_11027 in repository https://gitbox.apache.org/repos/asf/gravitino.git
commit 77081b94dab1dd1e33fdd5da29d2fab8709846a5 Author: yuqi <[email protected]> AuthorDate: Tue May 26 22:00:56 2026 +0800 fix(lance): Improve schema refresh robustness and test coverage - Change LOG.debug to LOG.warn for dataset load failures in loadTable so silent fallbacks are visible in production logs - Add defensive try-catch in schemaRefreshMode() to handle invalid enum values gracefully instead of propagating IllegalArgumentException - Add test for empty-schema table refresh in DECLARED_ONLY mode - Add test for graceful fallback when openDataset fails Co-Authored-By: Claude Opus 4.7 <[email protected]> --- .../lakehouse/lance/LanceTableOperations.java | 16 +++++-- .../lakehouse/lance/TestLanceTableOperations.java | 56 ++++++++++++++++++++++ 2 files changed, 67 insertions(+), 5 deletions(-) diff --git a/catalogs/catalog-lakehouse-generic/src/main/java/org/apache/gravitino/catalog/lakehouse/lance/LanceTableOperations.java b/catalogs/catalog-lakehouse-generic/src/main/java/org/apache/gravitino/catalog/lakehouse/lance/LanceTableOperations.java index e529385572..c9d955f52d 100644 --- a/catalogs/catalog-lakehouse-generic/src/main/java/org/apache/gravitino/catalog/lakehouse/lance/LanceTableOperations.java +++ b/catalogs/catalog-lakehouse-generic/src/main/java/org/apache/gravitino/catalog/lakehouse/lance/LanceTableOperations.java @@ -152,7 +152,7 @@ public class LanceTableOperations extends ManagedTableOperations { } columns = extractColumns(dataset.getSchema()); } catch (Exception e) { - LOG.debug( + LOG.warn( "Failed to load Lance schema from location {} for table {}. Return stored metadata.", location, ident, @@ -414,10 +414,16 @@ public class LanceTableOperations extends ManagedTableOperations { } private SchemaRefreshMode schemaRefreshMode() { - return Optional.ofNullable(catalogProperties.get(LanceConstants.LANCE_SCHEMA_REFRESH_MODE)) - .map(mode -> mode.trim().replace('-', '_').toUpperCase()) - .map(SchemaRefreshMode::valueOf) - .orElse(SchemaRefreshMode.DECLARED_ONLY); + String raw = catalogProperties.get(LanceConstants.LANCE_SCHEMA_REFRESH_MODE); + if (StringUtils.isBlank(raw)) { + return SchemaRefreshMode.DECLARED_ONLY; + } + try { + return SchemaRefreshMode.valueOf(raw.trim().replace('-', '_').toUpperCase()); + } catch (IllegalArgumentException e) { + LOG.warn("Unknown schema refresh mode '{}', falling back to DECLARED_ONLY", raw); + return SchemaRefreshMode.DECLARED_ONLY; + } } private boolean isDeclaredOnly(Table table) { diff --git a/catalogs/catalog-lakehouse-generic/src/test/java/org/apache/gravitino/catalog/lakehouse/lance/TestLanceTableOperations.java b/catalogs/catalog-lakehouse-generic/src/test/java/org/apache/gravitino/catalog/lakehouse/lance/TestLanceTableOperations.java index 617c7bb777..1b20195ae3 100644 --- a/catalogs/catalog-lakehouse-generic/src/test/java/org/apache/gravitino/catalog/lakehouse/lance/TestLanceTableOperations.java +++ b/catalogs/catalog-lakehouse-generic/src/test/java/org/apache/gravitino/catalog/lakehouse/lance/TestLanceTableOperations.java @@ -274,6 +274,62 @@ public class TestLanceTableOperations { .update(eq(ident), eq(TableEntity.class), eq(Entity.EntityType.TABLE), any()); } + @Test + public void testLoadEmptySchemaTableRefreshesFromDataset() throws Exception { + NameIdentifier ident = NameIdentifier.of("schema", "table"); + String location = tempDir.resolve("empty-schema-table").toString(); + TableEntity tableEntity = + tableEntity(ident, List.of(), Map.of(Table.PROPERTY_LOCATION, location)); + when(store.get(eq(ident), eq(Entity.EntityType.TABLE), eq(TableEntity.class))) + .thenReturn(tableEntity); + when(idGenerator.nextId()).thenReturn(10L, 11L); + when(store.update(eq(ident), eq(TableEntity.class), eq(Entity.EntityType.TABLE), any())) + .thenAnswer( + invocation -> { + @SuppressWarnings("unchecked") + Function<TableEntity, TableEntity> updater = invocation.getArgument(3); + return updater.apply(tableEntity); + }); + + Dataset dataset = mock(Dataset.class); + when(dataset.getSchema()) + .thenReturn( + new org.apache.arrow.vector.types.pojo.Schema( + List.of(Field.nullable("col1", new ArrowType.Utf8())))); + when(dataset.version()).thenReturn(5L); + Mockito.doReturn(dataset).when(lanceTableOps).openDataset(location, Map.of()); + + Table loadedTable = + PrincipalUtils.doAs(new UserPrincipal("tester"), () -> lanceTableOps.loadTable(ident)); + + Assertions.assertEquals(1, loadedTable.columns().length); + Assertions.assertEquals("col1", loadedTable.columns()[0].name()); + Assertions.assertEquals(Types.StringType.get(), loadedTable.columns()[0].dataType()); + Assertions.assertEquals("5", loadedTable.properties().get(LANCE_TABLE_VERSION)); + } + + @Test + public void testLoadTableFallsBackWhenDatasetOpenFails() throws Exception { + NameIdentifier ident = NameIdentifier.of("schema", "table"); + String location = tempDir.resolve("broken-table").toString(); + TableEntity tableEntity = + tableEntity( + ident, + List.of(), + Map.of(Table.PROPERTY_LOCATION, location, LANCE_TABLE_DECLARED, "true")); + when(store.get(eq(ident), eq(Entity.EntityType.TABLE), eq(TableEntity.class))) + .thenReturn(tableEntity); + Mockito.doThrow(new RuntimeException("dataset not found")) + .when(lanceTableOps) + .openDataset(location, Map.of()); + + Table loadedTable = lanceTableOps.loadTable(ident); + + Assertions.assertEquals(0, loadedTable.columns().length); + verify(store, never()) + .update(eq(ident), eq(TableEntity.class), eq(Entity.EntityType.TABLE), any()); + } + @Test public void testHandleLanceTableChangeRespectsOrder() { Table table = mock(Table.class);
