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


##########
core/src/main/java/org/apache/gravitino/catalog/CapabilityHelpers.java:
##########
@@ -47,17 +47,56 @@
 import org.apache.gravitino.rel.partitions.Partition;
 import org.apache.gravitino.rel.partitions.Partitions;
 import org.apache.gravitino.rel.partitions.RangePartition;
+import org.apache.gravitino.utils.ThrowableFunction;
 
 public class CapabilityHelpers {
 
-  public static Capability getCapability(NameIdentifier ident, CatalogManager 
catalogManager) {
+  /**
+   * Executes {@code fn} with the {@link Capability} of the catalog identified 
by {@code ident},
+   * inside the catalog's classloader boundary. This prevents the catalog's 
{@link
+   * org.apache.gravitino.utils.IsolatedClassLoader} from being closed while 
capability methods are
+   * executing.
+   *
+   * <p>If the first attempt encounters a stale (already-closed) {@link
+   * CatalogManager.CatalogWrapper}, the wrapper is evicted from the cache and 
the call is retried
+   * once with a freshly loaded wrapper. Retry is safe because {@code fn} is 
restricted to pure
+   * normalization and never performs external I/O, so re-execution is 
idempotent. The retry is
+   * triggered only on {@link CatalogManager.CatalogWrapperClosedException}, 
which is thrown
+   * exclusively at wrapper-entry before {@code fn} has run, preventing 
accidental replay of
+   * partially-executed non-idempotent operations.
+   *
+   * <p>Callers should use this method instead of calling {@code 
capabilities()} on a raw wrapper
+   * invoke {@link Capability} methods so that the classloader lifecycle is 
properly bounded.
+   *
+   * @param ident any {@link NameIdentifier} that belongs to the target catalog
+   * @param catalogManager the catalog manager used to load the catalog
+   * @param fn function to execute with the catalog's {@link Capability}
+   * @param <R> return type
+   * @return the result of {@code fn}
+   */
+  public static <R> R withCapability(
+      NameIdentifier ident, CatalogManager catalogManager, 
ThrowableFunction<Capability, R> fn) {
     NameIdentifier catalogIdent = getCatalogIdentifier(ident);
-    CatalogManager.CatalogWrapper c = 
catalogManager.loadCatalogAndWrap(catalogIdent);
-    try {
-      return c.capabilities();
-    } catch (Exception e) {
-      throw new RuntimeException("Failed to get capabilities for catalog: " + 
catalogIdent, e);
+    CatalogManager.CatalogWrapperClosedException lastClosed = null;
+    for (int i = 0; i < 2; i++) {
+      CatalogManager.CatalogWrapper c = 
catalogManager.loadCatalogAndWrap(catalogIdent);
+      try {
+        return c.doWithCapabilityOps(fn);

Review Comment:
   Do we need to sleep or wait a random number of milliseconds before the next 
retry?



-- 
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