This is an automated email from the ASF dual-hosted git repository.

diqiu50 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new 62240804b7 [#13697] improvement(trino-connector): Load catalogs with a 
single detailed listing on refresh (#13698)
62240804b7 is described below

commit 62240804b7c4a79da147b7f659b840ff50ce934a
Author: Yuhui <[email protected]>
AuthorDate: Thu Oct 8 20:48:11 2026 +0800

    [#13697] improvement(trino-connector): Load catalogs with a single detailed 
listing on refresh (#13698)
    
    ### What changes were proposed in this pull request?
    
    `CatalogConnectorManager` now reads all catalogs of a metalake with one
    `listCatalogsInfo()` call per refresh, instead of `listCatalogs()`
    followed by `loadCatalog()` for each catalog.
    
    ### Why are the changes needed?
    
    Each refresh sent 1 + N requests per metalake and wrote N + 1 audit
    records, even when no catalog had changed.
    
    Fix: #13697
    
    ### Does this PR introduce _any_ user-facing change?
    
    A catalog that the server cannot resolve, or whose type the client does
    not know, now fails the listing of its metalake, which is reported as a
    metalake error in the load status, instead of failing only that catalog.
    Registered catalogs stay registered meanwhile.
    
    ### How was this patch tested?
    
    Updated unit tests in `TestCatalogConnectorManager`. Existing tests
    pass.
---
 .../connector/catalog/CatalogConnectorManager.java | 21 +++----
 .../catalog/TestCatalogConnectorManager.java       | 72 +++++++++++++---------
 2 files changed, 53 insertions(+), 40 deletions(-)

diff --git 
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/CatalogConnectorManager.java
 
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/CatalogConnectorManager.java
index a7280cf53f..1446d97dbc 100644
--- 
a/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/CatalogConnectorManager.java
+++ 
b/trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/CatalogConnectorManager.java
@@ -27,6 +27,7 @@ import io.trino.spi.connector.ConnectorContext;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
@@ -454,9 +455,13 @@ public class CatalogConnectorManager {
 
   private void loadCatalogs(GravitinoMetalake metalake) {
     String metalakeName = metalake.name();
-    String[] allCatalogNames;
+    // Keyed by name in listing order. Read with their details in a single 
request, so that a
+    // refresh costs the server one call instead of one per catalog.
+    Map<String, Catalog> allCatalogs = new LinkedHashMap<>();
     try {
-      allCatalogNames = metalake.listCatalogs();
+      for (Catalog catalog : metalake.listCatalogsInfo()) {
+        allCatalogs.put(catalog.name(), catalog);
+      }
     } catch (Exception e) {
       // Keep the existing catalog states untouched, a transient listing 
failure must not turn
       // healthy catalogs into failed ones. The load status system table 
reports the cause.
@@ -469,7 +474,7 @@ public class CatalogConnectorManager {
     // catalogs that are intentionally not registered.
     Set<String> presentTrinoNames = new HashSet<>();
     List<String> catalogNames = new ArrayList<>();
-    for (String catalogName : allCatalogNames) {
+    for (String catalogName : allCatalogs.keySet()) {
       String trinoCatalogName = getTrinoCatalogName(metalakeName, catalogName);
       presentTrinoNames.add(trinoCatalogName);
       if (skipCatalog(trinoCatalogName)) {
@@ -562,7 +567,7 @@ public class CatalogConnectorManager {
       // Tracked outside the try so that a failure can still report the 
provider it knows about.
       String provider = null;
       try {
-        Catalog catalog = metalake.loadCatalog(catalogName);
+        Catalog catalog = allCatalogs.get(catalogName);
         // Registration deliberately carries only the visible properties. The 
resolved secrets are
         // added by each node in createCatalogConnectorContext(), so that they 
never reach the
         // CREATE CATALOG statement, the catalog properties file Trino 
persists from it, or
@@ -602,14 +607,6 @@ public class CatalogConnectorManager {
           recordCatalogState(
               CatalogRegistrationState.succeeded(gravitinoCatalog, 
trinoCatalogName), null);
         }
-      } catch (UnsupportedOperationException e) {
-        // The client library does not recognize this catalog's type, e.g. 
DTOConverters.toCatalog
-        // throws for a type it cannot map. This is the same "we know about 
it, we just don't
-        // support it" case as the type/provider checks above, not a 
registration failure.
-        recordCatalogState(
-            CatalogRegistrationState.unsupported(
-                metalakeName, catalogName, trinoCatalogName, provider, 
toErrorMessage(e)),
-            e);
       } catch (Exception e) {
         // reloadCatalog() unregisters the old connector before 
re-registering; if the
         // re-register then fails, the catalog is no longer in 
catalogConnectors even though
diff --git 
a/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/TestCatalogConnectorManager.java
 
b/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/TestCatalogConnectorManager.java
index 2586d526dd..3a848acd57 100644
--- 
a/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/TestCatalogConnectorManager.java
+++ 
b/trino-connector/trino-connector/src/test/java/org/apache/gravitino/trino/connector/catalog/TestCatalogConnectorManager.java
@@ -263,15 +263,14 @@ public class TestCatalogConnectorManager {
   @Test
   public void testFailureBeforeProviderIsKnownKeepsPreviousProvider() throws 
Exception {
     LoadFixture fixture = new LoadFixture();
-    fixture.withCatalogs(mockCatalog("memory", "memory", 
Catalog.Type.RELATIONAL));
+    Catalog catalog = mockCatalog("memory", "memory", Catalog.Type.RELATIONAL);
+    fixture.withCatalogs(catalog);
     CatalogConnectorManager manager = fixture.createManager(ImmutableMap.of());
     manager.loadMetalakeSync();
     assertEquals("memory", singleState(manager).getProvider());
 
     // The next attempt fails before the provider can even be read off the 
catalog.
-    Mockito.doThrow(new RuntimeException("Connection reset"))
-        .when(fixture.metalake)
-        .loadCatalog("memory");
+    when(catalog.properties()).thenThrow(new RuntimeException("simulated: 
invalid properties"));
     manager.loadMetalakeSync();
 
     CatalogRegistrationState state = singleState(manager);
@@ -324,7 +323,7 @@ public class TestCatalogConnectorManager {
 
     Mockito.doThrow(new RuntimeException("Connection refused"))
         .when(fixture.metalake)
-        .listCatalogs();
+        .listCatalogsInfo();
     manager.loadMetalakeSync();
 
     Map<String, String> metalakeErrors = manager.getMetalakeErrors();
@@ -453,7 +452,7 @@ public class TestCatalogConnectorManager {
 
     Mockito.doThrow(new RuntimeException("Connection refused"))
         .when(fixture.metalake)
-        .listCatalogs();
+        .listCatalogsInfo();
     manager.loadMetalakeSync();
     assertEquals(1, manager.getMetalakeErrors().size());
     // A metalake that cannot be listed is not a healthy loop.
@@ -488,7 +487,7 @@ public class TestCatalogConnectorManager {
     doThrow(new TrinoException(GravitinoErrorCode.GRAVITINO_RUNTIME_ERROR, 
"Access Denied"))
         .when(fixture.catalogRegister)
         .unregisterCatalog(any());
-    Mockito.doReturn(new String[0]).when(fixture.metalake).listCatalogs();
+    Mockito.doReturn(new Catalog[0]).when(fixture.metalake).listCatalogsInfo();
     manager.loadMetalakeSync();
 
     // The catalog is gone from Gravitino but still registered in Trino: the 
state must say so
@@ -544,9 +543,7 @@ public class TestCatalogConnectorManager {
     when(context.getCatalog())
         .thenReturn(new GravitinoCatalog("test", "memory", "memory", 
ImmutableMap.of(), 0L));
 
-    Mockito.doThrow(new RESTException("simulated: the server rejected the 
load"))
-        .when(fixture.metalake)
-        .loadCatalog("memory");
+    when(catalog.properties()).thenThrow(new RuntimeException("simulated: 
invalid properties"));
     manager.loadMetalakeSync();
 
     // The catalog is still registered and queryable, so a failed refresh must 
not flip it to
@@ -558,21 +555,19 @@ public class TestCatalogConnectorManager {
   }
 
   @Test
-  public void testUnknownCatalogTypeIsRecordedAsUnsupported() throws Exception 
{
+  public void testUnknownCatalogTypeFailsTheMetalakeListing() throws Exception 
{
     LoadFixture fixture = new LoadFixture();
-    fixture.withCatalogs(mockCatalog("memory", "memory", 
Catalog.Type.RELATIONAL));
     CatalogConnectorManager manager = fixture.createManager(ImmutableMap.of());
 
-    // The client library cannot map this catalog's type, e.g. 
DTOConverters.toCatalog throwing
-    // for a type it does not know. That is a "not supported", not a 
registration failure.
+    // The client library cannot map a listed catalog's type, e.g. 
DTOConverters.toCatalog throwing
+    // for a type it does not know, which fails the listing of the whole 
metalake.
     Mockito.doThrow(new UnsupportedOperationException("Unsupported catalog 
type: UNKNOWN"))
         .when(fixture.metalake)
-        .loadCatalog("memory");
+        .listCatalogsInfo();
     manager.loadMetalakeSync();
 
-    CatalogRegistrationState state = singleState(manager);
-    assertEquals(CatalogRegistrationState.Status.UNSUPPORTED, 
state.getStatus());
-    assertTrue(state.getLastError().contains("Unsupported catalog type"));
+    assertTrue(manager.getMetalakeErrors().get("test").contains("Unsupported 
catalog type"));
+    assertTrue(manager.getCatalogRegistrationStates().isEmpty());
     assertFalse(manager.catalogConnectorExist("memory"));
   }
 
@@ -634,6 +629,28 @@ public class TestCatalogConnectorManager {
     assertNull(state.getLastError());
   }
 
+  @Test
+  public void testCatalogsAreLoadedWithOneDetailedListing() throws Exception {
+    LoadFixture fixture = new LoadFixture();
+    Catalog first = mockCatalog("a", "memory", Catalog.Type.RELATIONAL);
+    Catalog second = mockCatalog("b", "memory", Catalog.Type.RELATIONAL);
+    Mockito.doReturn(new Catalog[] {first, 
second}).when(fixture.metalake).listCatalogsInfo();
+    CatalogConnectorManager manager = fixture.createManager(ImmutableMap.of());
+
+    manager.loadMetalakeSync();
+    manager.loadMetalakeSync();
+
+    // Every refresh reads all catalogs with one request instead of one 
request per catalog.
+    verify(fixture.metalake, times(2)).listCatalogsInfo();
+    verify(fixture.metalake, never()).listCatalogs();
+    verify(fixture.metalake, never()).loadCatalog(any());
+    List<CatalogRegistrationState> states = 
manager.getCatalogRegistrationStates();
+    assertEquals(2, states.size());
+    for (CatalogRegistrationState state : states) {
+      assertEquals(CatalogRegistrationState.Status.REGISTERED, 
state.getStatus());
+    }
+  }
+
   @Test
   public void testDeletedCatalogWithConnectorIsRemoved() throws Exception {
     LoadFixture fixture = new LoadFixture();
@@ -650,7 +667,7 @@ public class TestCatalogConnectorManager {
         .thenReturn(new GravitinoCatalog("test", "memory", "memory", 
ImmutableMap.of(), 0L));
 
     // The catalog is deleted in Gravitino and unregisters cleanly from Trino.
-    Mockito.doReturn(new String[0]).when(fixture.metalake).listCatalogs();
+    Mockito.doReturn(new Catalog[0]).when(fixture.metalake).listCatalogsInfo();
     manager.loadMetalakeSync();
 
     verify(fixture.catalogRegister, times(1)).unregisterCatalog("memory");
@@ -797,7 +814,7 @@ public class TestCatalogConnectorManager {
 
     // The losing catalog is deleted in Gravitino. The winner still holds the 
Trino name, which
     // must not keep the loser's row alive.
-    Mockito.doReturn(new String[0]).when(dev).listCatalogs();
+    Mockito.doReturn(new Catalog[0]).when(dev).listCatalogsInfo();
     manager.loadMetalakeSync();
 
     assertTrue(manager.getCatalogRegistrationStates("dev").isEmpty());
@@ -830,7 +847,7 @@ public class TestCatalogConnectorManager {
     assertNull(manager.getCatalogConnector("dev", "memory"));
 
     // The winning catalog is deleted in Gravitino, which frees the Trino name 
for the loser.
-    Mockito.doReturn(new String[0]).when(fixture.metalake).listCatalogs();
+    Mockito.doReturn(new Catalog[0]).when(fixture.metalake).listCatalogsInfo();
     owner[0] = "dev";
     manager.loadMetalakeSync();
 
@@ -878,8 +895,7 @@ public class TestCatalogConnectorManager {
     GravitinoMetalake devMetalake = mock(GravitinoMetalake.class);
     when(devMetalake.name()).thenReturn("dev");
     Catalog devCatalog = mockCatalog("sandbox", "memory", 
Catalog.Type.RELATIONAL);
-    Mockito.doReturn(new String[] 
{"sandbox"}).when(devMetalake).listCatalogs();
-    Mockito.doReturn(devCatalog).when(devMetalake).loadCatalog("sandbox");
+    Mockito.doReturn(new Catalog[] 
{devCatalog}).when(devMetalake).listCatalogsInfo();
     Mockito.doReturn(new GravitinoMetalake[] {fixture.metalake, devMetalake})
         .when(fixture.client)
         .listMetalakes();
@@ -969,7 +985,7 @@ public class TestCatalogConnectorManager {
     LoadFixture fixture = new LoadFixture();
     Mockito.doThrow(new RESTException("simulated: listing failed"))
         .when(fixture.metalake)
-        .listCatalogs();
+        .listCatalogsInfo();
     CatalogConnectorManager manager = fixture.createManager(ImmutableMap.of());
 
     manager.loadMetalakeSync();
@@ -1151,7 +1167,7 @@ public class TestCatalogConnectorManager {
     LoadFixture fixture = new LoadFixture();
     Mockito.doThrow(new RESTException("simulated: listing failed"))
         .when(fixture.metalake)
-        .listCatalogs();
+        .listCatalogsInfo();
     CatalogConnectorManager manager = fixture.createManager(ImmutableMap.of());
     manager.loadMetalakeSync();
     assertFalse(manager.getLoadOutcome().getMetalakeErrors().isEmpty());
@@ -1171,7 +1187,7 @@ public class TestCatalogConnectorManager {
     LoadFixture fixture = new LoadFixture();
     Mockito.doThrow(new RESTException("simulated: listing failed"))
         .when(fixture.metalake)
-        .listCatalogs();
+        .listCatalogsInfo();
     CatalogConnectorManager manager = fixture.createManager(ImmutableMap.of());
     manager.loadMetalakeSync();
     assertTrue(manager.describeRegistrationFailure("test", 
"memory").contains("listing failed"));
@@ -1691,7 +1707,7 @@ public class TestCatalogConnectorManager {
         names[i] = catalogs[i].name();
         Mockito.doReturn(catalogs[i]).when(metalake).loadCatalog(names[i]);
       }
-      Mockito.doReturn(names).when(metalake).listCatalogs();
+      Mockito.doReturn(catalogs).when(metalake).listCatalogsInfo();
     }
 
     CatalogConnectorManager createManager(Map<String, String> extraConfig) {
@@ -1728,7 +1744,7 @@ public class TestCatalogConnectorManager {
         names[i] = catalogs[i].name();
         Mockito.doReturn(catalogs[i]).when(second).loadCatalog(names[i]);
       }
-      Mockito.doReturn(names).when(second).listCatalogs();
+      Mockito.doReturn(catalogs).when(second).listCatalogsInfo();
       Mockito.doReturn(second).when(client).loadMetalake(name);
       Mockito.doReturn(new GravitinoMetalake[] {metalake, 
second}).when(client).listMetalakes();
       return second;

Reply via email to