yuqi1129 commented on code in PR #12547:
URL: https://github.com/apache/gravitino/pull/12547#discussion_r3922031713


##########
docs/trino-connector/supported-catalog.md:
##########
@@ -80,6 +80,68 @@ The result is like:
  gt_hive      | hive     | 
{gravitino.bypass.hive.metastore.client.capability.check=false, 
metastore.uris=thrift://trino-ci-hive:9083}
 ```
 
+Check catalog registration status:
+
+`gravitino.system.catalog` lists the relational catalogs the Gravitino server 
knows about, minus any
+that match `gravitino.trino.skip-catalog-patterns`. A catalog listed there is 
not necessarily usable
+in Trino: registering it is a separate step that can fail. 
`gravitino.system.catalog_status` covers
+every catalog the connector considered, including the ones `catalog` filters 
out, and says why each
+one is or is not registered.
+
+```sql
+select catalog_name, status, last_error from gravitino.system.catalog_status;
+```
+
+The result is like:
+
+```test
+ catalog_name | status     | last_error
+--------------+------------+-------------------------------------------------
+ gt_hive      | REGISTERED | NULL
+ gt_iceberg   | FAILED     | Access Denied: Cannot create catalog gt_iceberg
+ gt_files     | UNSUPPORTED| Only relational catalogs are supported, the 
catalog type is FILESET
+```
+
+| Column               | Description                                           
                                       |
+|----------------------|----------------------------------------------------------------------------------------------|

Review Comment:
   Format the table.



##########
trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/CatalogConnectorManager.java:
##########
@@ -232,67 +401,197 @@ public GravitinoMetalake retrieveMetalake(String 
metalakeName) {
   }
 
   private void loadCatalogs(GravitinoMetalake metalake) {
-    List<String> catalogNames;
+    String metalakeName = metalake.name();
+    String[] allCatalogNames;
     try {
-      catalogNames =
-          Arrays.stream(metalake.listCatalogs())
-              .filter(id -> !skipCatalog(getTrinoCatalogName(metalake.name(), 
id)))
-              .collect(Collectors.toList());
+      allCatalogNames = metalake.listCatalogs();
     } catch (Exception e) {
-      LOG.error(e, "Failed to list catalogs in metalake %s.", metalake.name());
+      // 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.
+      recordMetalakeError(metalakeName, e);
       return;
     }
+    metalakeErrors.remove(metalakeName);
+
+    // The Trino names of every catalog the Gravitino server currently 
reports, including the
+    // catalogs that are intentionally not registered.
+    Set<String> presentTrinoNames = new HashSet<>();
+    List<String> catalogNames = new ArrayList<>();
+    for (String catalogName : allCatalogNames) {
+      String trinoCatalogName = getTrinoCatalogName(metalakeName, catalogName);
+      presentTrinoNames.add(trinoCatalogName);
+      if (skipCatalog(trinoCatalogName)) {
+        recordCatalogState(
+            CatalogRegistrationState.skipped(
+                metalakeName,
+                catalogName,
+                trinoCatalogName,
+                "Matched gravitino.trino.skip-catalog-patterns"),
+            null);
+        continue;
+      }
+      catalogNames.add(catalogName);
+    }
 
-    LOG.debug("Load metalake %s's catalogs. catalogs: %s.", metalake.name(), 
catalogNames);
+    LOG.debug("Load metalake %s's catalogs. catalogs: %s.", metalakeName, 
catalogNames);
 
     // Delete those catalogs that have been deleted in Gravitino server
-    Set<String> catalogNameStrings =
-        catalogNames.stream()
-            .map(id -> getTrinoCatalogName(metalake.name(), id))
-            .collect(Collectors.toSet());
+    Set<String> catalogNameStrings = new HashSet<>();
+    for (String catalogName : catalogNames) {
+      catalogNameStrings.add(getTrinoCatalogName(metalakeName, catalogName));
+    }
 
     for (Map.Entry<String, CatalogConnectorContext> entry : 
catalogConnectors.entrySet()) {
       if (!catalogNameStrings.contains(entry.getKey())
           &&
           // Skip the catalog doesn't belong to this metalake.
-          entry.getValue().getMetalake().name().equals(metalake.name())) {
+          entry.getValue().getMetalake().name().equals(metalakeName)) {
         try {
           unloadCatalog(entry.getValue().getCatalog());
         } catch (Exception e) {
-          LOG.error(e, "Failed to remove catalog %s.", entry.getKey());
+          // The catalog is gone from Gravitino but is still registered in 
Trino. Record it, or
+          // the pruning below would drop the row and the table would report 
nothing at all about
+          // a catalog that still shows up in SHOW CATALOGS.
+          GravitinoCatalog catalog = entry.getValue().getCatalog();
+          recordCatalogState(
+              CatalogRegistrationState.failed(
+                  metalakeName,
+                  catalog.getName(),
+                  entry.getKey(),
+                  catalog.getProvider(),
+                  "The catalog was deleted in Gravitino but could not be 
unregistered from Trino: "
+                      + toErrorMessage(e)),
+              e);
         }
       }
     }
 
+    // Drop the states of catalogs that no longer exist in the Gravitino 
server, including the
+    // states of catalogs that never had a connector. A catalog whose 
connector could not be
+    // removed from Trino is kept, so that its failure stays visible for as 
long as it is real.
+    catalogStates
+        .values()
+        .removeIf(
+            state ->
+                state.getMetalake().equals(metalakeName)
+                    && !presentTrinoNames.contains(state.getTrinoCatalogName())
+                    && 
!catalogConnectors.containsKey(state.getTrinoCatalogName()));
+
     // Load new catalogs belows to the metalake.
-    catalogNames.stream()
-        .forEach(
-            (String catalogName) -> {
-              try {
-                Catalog catalog = metalake.loadCatalog(catalogName);
-                Map<String, String> properties = propsWithSecrets(catalog);
-                GravitinoCatalog gravitinoCatalog =
-                    new GravitinoCatalog(metalake.name(), catalog, properties);
-                if 
(catalogConnectors.containsKey(getTrinoCatalogName(gravitinoCatalog))) {
-                  // Reload catalogs that have been updated in Gravitino 
server.
-                  reloadCatalog(gravitinoCatalog);
-                } else {
-                  if (catalog.type() == Catalog.Type.RELATIONAL
-                      && catalogConnectorFactory
-                          .getSupportedCatalogProviders()
-                          .contains(gravitinoCatalog.getProvider())) {
-                    loadCatalog(gravitinoCatalog);
-                  }
-                }
-              } catch (UnsupportedOperationException e) {
-                LOG.warn(
-                    "Unsupported catalog type for catalog %s in metalake %s: 
%s",
-                    catalogName, metalake.name(), e.getMessage());
-              } catch (Exception e) {
-                LOG.error(
-                    e, "Failed to load metalake %s's catalog %s.", 
metalake.name(), catalogName);
-              }
+    for (String catalogName : catalogNames) {
+      String trinoCatalogName = getTrinoCatalogName(metalakeName, catalogName);
+      // Known before the catalog is even loaded, since it only depends on the 
name.
+      boolean alreadyRegistered = 
catalogConnectors.containsKey(trinoCatalogName);
+      // 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);
+        // 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
+        // anything that quotes either of them back.
+        GravitinoCatalog gravitinoCatalog =
+            new GravitinoCatalog(metalakeName, catalog, visibleProps(catalog));
+        provider = gravitinoCatalog.getProvider();
+        if (alreadyRegistered) {
+          // Reload catalogs that have been updated in Gravitino server.
+          reloadCatalog(gravitinoCatalog);
+          recordCatalogState(
+              CatalogRegistrationState.succeeded(gravitinoCatalog, 
trinoCatalogName), null);
+        } else if (catalog.type() != Catalog.Type.RELATIONAL) {

Review Comment:
   [Confirmed correctness gap] The eligibility checks occur only after the 
alreadyRegistered branch. If a live catalog is deleted and recreated under the 
same name with an unsupported type/provider, it is reloaded (or treated as an 
unchanged REGISTERED catalog) instead of transitioning to UNSUPPORTED. 
testUnsupportedCatalogKeepsLastSuccessTime does not exercise this path because 
it never creates a live connector context, so alreadyRegistered remains false. 
Please determine the desired state before choosing reload/register/unload and 
add live REGISTERED -> UNSUPPORTED transition coverage.



##########
trino-connector/trino-connector/src/main/java/org/apache/gravitino/trino/connector/catalog/CatalogConnectorManager.java:
##########
@@ -232,67 +401,197 @@ public GravitinoMetalake retrieveMetalake(String 
metalakeName) {
   }
 
   private void loadCatalogs(GravitinoMetalake metalake) {
-    List<String> catalogNames;
+    String metalakeName = metalake.name();
+    String[] allCatalogNames;
     try {
-      catalogNames =
-          Arrays.stream(metalake.listCatalogs())
-              .filter(id -> !skipCatalog(getTrinoCatalogName(metalake.name(), 
id)))
-              .collect(Collectors.toList());
+      allCatalogNames = metalake.listCatalogs();
     } catch (Exception e) {
-      LOG.error(e, "Failed to list catalogs in metalake %s.", metalake.name());
+      // 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.
+      recordMetalakeError(metalakeName, e);
       return;
     }
+    metalakeErrors.remove(metalakeName);
+
+    // The Trino names of every catalog the Gravitino server currently 
reports, including the
+    // catalogs that are intentionally not registered.
+    Set<String> presentTrinoNames = new HashSet<>();
+    List<String> catalogNames = new ArrayList<>();
+    for (String catalogName : allCatalogNames) {
+      String trinoCatalogName = getTrinoCatalogName(metalakeName, catalogName);
+      presentTrinoNames.add(trinoCatalogName);
+      if (skipCatalog(trinoCatalogName)) {
+        recordCatalogState(
+            CatalogRegistrationState.skipped(
+                metalakeName,
+                catalogName,
+                trinoCatalogName,
+                "Matched gravitino.trino.skip-catalog-patterns"),
+            null);
+        continue;
+      }
+      catalogNames.add(catalogName);
+    }
 
-    LOG.debug("Load metalake %s's catalogs. catalogs: %s.", metalake.name(), 
catalogNames);
+    LOG.debug("Load metalake %s's catalogs. catalogs: %s.", metalakeName, 
catalogNames);
 
     // Delete those catalogs that have been deleted in Gravitino server
-    Set<String> catalogNameStrings =
-        catalogNames.stream()
-            .map(id -> getTrinoCatalogName(metalake.name(), id))
-            .collect(Collectors.toSet());
+    Set<String> catalogNameStrings = new HashSet<>();
+    for (String catalogName : catalogNames) {
+      catalogNameStrings.add(getTrinoCatalogName(metalakeName, catalogName));
+    }
 
     for (Map.Entry<String, CatalogConnectorContext> entry : 
catalogConnectors.entrySet()) {
       if (!catalogNameStrings.contains(entry.getKey())
           &&
           // Skip the catalog doesn't belong to this metalake.
-          entry.getValue().getMetalake().name().equals(metalake.name())) {
+          entry.getValue().getMetalake().name().equals(metalakeName)) {
         try {
           unloadCatalog(entry.getValue().getCatalog());
         } catch (Exception e) {
-          LOG.error(e, "Failed to remove catalog %s.", entry.getKey());
+          // The catalog is gone from Gravitino but is still registered in 
Trino. Record it, or
+          // the pruning below would drop the row and the table would report 
nothing at all about
+          // a catalog that still shows up in SHOW CATALOGS.
+          GravitinoCatalog catalog = entry.getValue().getCatalog();
+          recordCatalogState(
+              CatalogRegistrationState.failed(
+                  metalakeName,
+                  catalog.getName(),
+                  entry.getKey(),
+                  catalog.getProvider(),
+                  "The catalog was deleted in Gravitino but could not be 
unregistered from Trino: "

Review Comment:
   This unload branch handles both deleted catalogs and live catalogs that now 
match skip-catalog-patterns, because catalogNameStrings excludes skipped names. 
If unregistering a skipped catalog fails, this overwrites SKIPPED with FAILED 
and incorrectly says the catalog was deleted in Gravitino. presentTrinoNames 
can distinguish the two causes. Please add a live REGISTERED -> SKIPPED test 
for both successful and failed unregister.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to