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

jerryshao pushed a commit to branch branch-1.3
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/branch-1.3 by this push:
     new 9c4fd069af [Cherry-pick to branch-1.3] [#12851] fix(iceberg-rest): 
Share the managed memory catalog in auxiliary mode (#12852) (#12923)
9c4fd069af is described below

commit 9c4fd069afe9214650687ebb0f88cd47b177e880
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Fri Sep 4 18:47:30 2026 +0800

    [Cherry-pick to branch-1.3] [#12851] fix(iceberg-rest): Share the managed 
memory catalog in auxiliary mode (#12852) (#12923)
    
    **Cherry-pick Information:**
    - Original commit: 30687f68ff8c2a276a4a0fbf13211435da36f7f9
    - Target branch: `branch-1.3`
    - Status: ✅ **Conflicts resolved**
    
    **Resolution:**
    - Preserved the existing `branch-1.3` authentication and credential
    handling.
    - Applied the catalog UUID, shared in-memory catalog, and catalog-drop
    cleanup changes.
    - Removed all committed conflict markers without bringing in unrelated
    main-only changes.
    
    **Validation:**
    - `./gradlew spotlessApply`
    - `./gradlew :iceberg:iceberg-common:test --tests
    org.apache.gravitino.iceberg.common.utils.TestIcebergCatalogUtil --tests
    org.apache.gravitino.iceberg.common.ops.TestIcebergCatalogWrapper`
    - `./gradlew :catalogs:catalog-lakehouse-iceberg:test --tests
    org.apache.gravitino.catalog.lakehouse.iceberg.TestIcebergCatalogOperations
    --tests
    org.apache.gravitino.catalog.lakehouse.iceberg.TestIcebergSchema`
    - `./gradlew :iceberg:iceberg-rest-server:test --tests
    
org.apache.gravitino.iceberg.service.provider.TestDynamicIcebergConfigProvider
    --tests
    
org.apache.gravitino.iceberg.service.TestIcebergCatalogWrapperManagerForREST`
    - `./gradlew :core:test --tests
    org.apache.gravitino.catalog.TestCatalogManager`
    - Conflict-marker scan and `git diff --check`
    
    ---------
    
    Co-authored-by: roryqi <[email protected]>
    Co-authored-by: roryqi <[email protected]>
---
 .../lakehouse/iceberg/IcebergConstants.java        |  3 +
 .../iceberg/IcebergCatalogOperations.java          | 16 +++-
 .../iceberg/TestIcebergCatalogOperations.java      | 24 ++++++
 .../lakehouse/iceberg/TestIcebergSchema.java       |  3 +-
 .../apache/gravitino/catalog/CatalogManager.java   | 12 +++
 .../gravitino/connector/CatalogDropAware.java      | 29 ++++++++
 .../gravitino/catalog/TestCatalogManager.java      | 13 ++++
 .../iceberg/common/ops/IcebergCatalogWrapper.java  |  6 +-
 .../iceberg/common/utils/IcebergCatalogUtil.java   | 35 +++++++++
 .../common/ops/TestIcebergCatalogWrapper.java      | 27 +++++++
 .../common/utils/TestIcebergCatalogUtil.java       | 86 ++++++++++++++++++++++
 .../service/IcebergCatalogWrapperManager.java      |  4 +
 .../provider/DynamicIcebergConfigProvider.java     |  4 +-
 .../TestIcebergCatalogWrapperManagerForREST.java   | 46 +++++++++++-
 .../provider/TestDynamicIcebergConfigProvider.java | 32 ++++++++
 15 files changed, 334 insertions(+), 6 deletions(-)

diff --git 
a/catalogs/catalog-common/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergConstants.java
 
b/catalogs/catalog-common/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergConstants.java
index 354153b303..2d29e9e754 100644
--- 
a/catalogs/catalog-common/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergConstants.java
+++ 
b/catalogs/catalog-common/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergConstants.java
@@ -24,6 +24,9 @@ public class IcebergConstants {
   public static final String CATALOG_BACKEND = "catalog-backend";
   public static final String CATALOG_BACKEND_IMPL = "catalog-backend-impl";
 
+  /** Internal property containing the unique identifier of a Gravitino 
catalog. */
+  public static final String CATALOG_UUID = "catalog_uuid";
+
   public static final String GRAVITINO_JDBC_USER = "jdbc-user";
   public static final String ICEBERG_JDBC_USER = "jdbc.user";
 
diff --git 
a/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalogOperations.java
 
b/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalogOperations.java
index 457e5cbd55..d881404ef1 100644
--- 
a/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalogOperations.java
+++ 
b/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalogOperations.java
@@ -31,12 +31,14 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.stream.Collectors;
+import javax.annotation.Nullable;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
 import org.apache.gravitino.SchemaChange;
 import 
org.apache.gravitino.catalog.lakehouse.iceberg.ops.IcebergCatalogWrapperHelper;
+import org.apache.gravitino.connector.CatalogDropAware;
 import org.apache.gravitino.connector.CatalogInfo;
 import org.apache.gravitino.connector.CatalogOperations;
 import org.apache.gravitino.connector.HasPropertyMetadata;
@@ -56,6 +58,7 @@ import 
org.apache.gravitino.iceberg.common.authentication.SupportsKerberos;
 import org.apache.gravitino.iceberg.common.ops.IcebergCatalogWrapper;
 import 
org.apache.gravitino.iceberg.common.ops.IcebergCatalogWrapper.IcebergTableChange;
 import 
org.apache.gravitino.iceberg.common.ops.KerberosAwareIcebergCatalogProxy;
+import org.apache.gravitino.iceberg.common.utils.IcebergCatalogUtil;
 import org.apache.gravitino.iceberg.common.utils.IcebergIdentifierUtils;
 import org.apache.gravitino.meta.AuditInfo;
 import org.apache.gravitino.rel.Column;
@@ -90,7 +93,7 @@ import org.slf4j.LoggerFactory;
 
 /** Operations for interacting with an Apache Iceberg catalog in Apache 
Gravitino. */
 public class IcebergCatalogOperations
-    implements CatalogOperations, SupportsSchemas, TableCatalog, ViewCatalog {
+    implements CatalogOperations, SupportsSchemas, TableCatalog, ViewCatalog, 
CatalogDropAware {
 
   private static final String ICEBERG_TABLE_DOES_NOT_EXIST_MSG = "Iceberg 
table does not exist: %s";
 
@@ -98,6 +101,7 @@ public class IcebergCatalogOperations
 
   @VisibleForTesting IcebergCatalogWrapper icebergCatalogWrapper;
 
+  @VisibleForTesting @Nullable String catalogUuid;
   private IcebergCatalogWrapperHelper icebergCatalogWrapperHelper;
   private IcebergViewCatalogOperations icebergViewCatalogOperations;
 
@@ -122,7 +126,8 @@ public class IcebergCatalogOperations
 
     Map<String, String> resultConf = Maps.newHashMap(prefixMap);
     resultConf.putAll(gravitinoConfig);
-    resultConf.put("catalog_uuid", info.id().toString());
+    this.catalogUuid = info.id().toString();
+    resultConf.put(IcebergConstants.CATALOG_UUID, catalogUuid);
     IcebergConfig icebergConfig = new IcebergConfig(resultConf);
 
     IcebergCatalogWrapper rawWrapper = new 
IcebergCatalogWrapper(icebergConfig);
@@ -137,6 +142,13 @@ public class IcebergCatalogOperations
     this.icebergViewCatalogOperations = new 
IcebergViewCatalogOperations(icebergCatalogWrapper);
   }
 
+  @Override
+  public void onCatalogDropped() {
+    if (catalogUuid != null) {
+      IcebergCatalogUtil.removeMemoryCatalog(catalogUuid);
+    }
+  }
+
   /** Closes the Iceberg catalog and releases the associated client pool. */
   @Override
   public void close() {
diff --git 
a/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/TestIcebergCatalogOperations.java
 
b/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/TestIcebergCatalogOperations.java
index dfbd129a23..4844452f08 100644
--- 
a/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/TestIcebergCatalogOperations.java
+++ 
b/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/TestIcebergCatalogOperations.java
@@ -28,7 +28,9 @@ import org.apache.gravitino.Catalog;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
 import org.apache.gravitino.exceptions.GravitinoRuntimeException;
+import org.apache.gravitino.iceberg.common.IcebergConfig;
 import org.apache.gravitino.iceberg.common.ops.IcebergCatalogWrapper;
+import org.apache.gravitino.iceberg.common.utils.IcebergCatalogUtil;
 import org.apache.iceberg.rest.responses.ListNamespacesResponse;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
@@ -95,4 +97,26 @@ public class TestIcebergCatalogOperations {
     Assertions.assertTrue(Arrays.stream(result).anyMatch(id -> 
"db1".equals(id.name())));
     Assertions.assertTrue(Arrays.stream(result).anyMatch(id -> 
"db2".equals(id.name())));
   }
+
+  @Test
+  public void testCatalogDropRemovesInternedMemoryCatalog() {
+    String catalogUuid = "catalog-uuid";
+    IcebergConfig config =
+        new IcebergConfig(
+            ImmutableMap.of(
+                IcebergConstants.CATALOG_BACKEND,
+                "memory",
+                IcebergConstants.CATALOG_UUID,
+                catalogUuid));
+    Object original = 
IcebergCatalogUtil.loadCatalogBackend(IcebergCatalogBackend.MEMORY, config);
+
+    IcebergCatalogOperations catalogOperations = new 
IcebergCatalogOperations();
+    catalogOperations.catalogUuid = catalogUuid;
+    catalogOperations.onCatalogDropped();
+
+    Object replacement =
+        IcebergCatalogUtil.loadCatalogBackend(IcebergCatalogBackend.MEMORY, 
config);
+    Assertions.assertNotSame(original, replacement);
+    IcebergCatalogUtil.removeMemoryCatalog(catalogUuid);
+  }
 }
diff --git 
a/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/TestIcebergSchema.java
 
b/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/TestIcebergSchema.java
index 9c9f6f5de5..c028c24b2e 100644
--- 
a/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/TestIcebergSchema.java
+++ 
b/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/TestIcebergSchema.java
@@ -187,7 +187,8 @@ public class TestIcebergSchema {
   private IcebergCatalog initIcebergCatalog(String name) {
     CatalogEntity entity =
         CatalogEntity.builder()
-            .withId(1L)
+            // Each test creates a different catalog, so it must also use a 
distinct catalog ID.
+            .withId(Integer.toUnsignedLong(name.hashCode()))
             .withName(name)
             .withNamespace(Namespace.of(META_LAKE_NAME))
             .withType(IcebergCatalog.Type.RELATIONAL)
diff --git 
a/core/src/main/java/org/apache/gravitino/catalog/CatalogManager.java 
b/core/src/main/java/org/apache/gravitino/catalog/CatalogManager.java
index d7e2739f07..64acb6ff55 100644
--- a/core/src/main/java/org/apache/gravitino/catalog/CatalogManager.java
+++ b/core/src/main/java/org/apache/gravitino/catalog/CatalogManager.java
@@ -79,6 +79,7 @@ import org.apache.gravitino.Namespace;
 import org.apache.gravitino.Schema;
 import org.apache.gravitino.StringIdentifier;
 import org.apache.gravitino.connector.BaseCatalog;
+import org.apache.gravitino.connector.CatalogDropAware;
 import org.apache.gravitino.connector.CatalogOperations;
 import org.apache.gravitino.connector.HasPropertyMetadata;
 import org.apache.gravitino.connector.SupportsSchemas;
@@ -1011,6 +1012,17 @@ public class CatalogManager implements 
CatalogDispatcher, Closeable {
             boolean deleted = store.delete(ident, EntityType.CATALOG, true);
             if (deleted) {
               markLocalMutation(ident);
+              try {
+                catalogWrapper.doWithCatalogOps(
+                    operations -> {
+                      if (operations instanceof CatalogDropAware) {
+                        ((CatalogDropAware) operations).onCatalogDropped();
+                      }
+                      return null;
+                    });
+              } catch (Exception e) {
+                LOG.warn("Failed to clean up resources for dropped catalog 
{}", ident, e);
+              }
             }
             catalogCache.invalidate(ident);
             return deleted;
diff --git 
a/core/src/main/java/org/apache/gravitino/connector/CatalogDropAware.java 
b/core/src/main/java/org/apache/gravitino/connector/CatalogDropAware.java
new file mode 100644
index 0000000000..2972808c1c
--- /dev/null
+++ b/core/src/main/java/org/apache/gravitino/connector/CatalogDropAware.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.connector;
+
+import org.apache.gravitino.annotation.Evolving;
+
+/** Supports cleanup that must run only when a catalog is permanently dropped. 
*/
+@Evolving
+public interface CatalogDropAware {
+
+  /** Performs cleanup after the catalog metadata has been permanently 
dropped. */
+  void onCatalogDropped();
+}
diff --git 
a/core/src/test/java/org/apache/gravitino/catalog/TestCatalogManager.java 
b/core/src/test/java/org/apache/gravitino/catalog/TestCatalogManager.java
index 4424dc848f..1be48b0d3a 100644
--- a/core/src/test/java/org/apache/gravitino/catalog/TestCatalogManager.java
+++ b/core/src/test/java/org/apache/gravitino/catalog/TestCatalogManager.java
@@ -57,6 +57,7 @@ import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
 import org.apache.gravitino.Schema;
 import org.apache.gravitino.connector.BaseCatalog;
+import org.apache.gravitino.connector.CatalogDropAware;
 import org.apache.gravitino.connector.CatalogOperations;
 import org.apache.gravitino.connector.capability.Capability;
 import org.apache.gravitino.connector.capability.CapabilityResult;
@@ -1108,10 +1109,21 @@ public class TestCatalogManager {
         Mockito.mock(CatalogManager.CatalogWrapper.class, 
Mockito.RETURNS_DEEP_STUBS);
     Capability capability = Mockito.mock(Capability.class);
     CapabilityResult unsupportedResult = CapabilityResult.unsupported("Not 
managed");
+    CatalogOperations operations =
+        Mockito.mock(
+            CatalogOperations.class,
+            Mockito.withSettings().extraInterfaces(CatalogDropAware.class));
     
Mockito.doReturn(catalogWrapper).when(catalogManager).loadCatalogAndWrap(ident);
     Mockito.doReturn(catalog).when(catalogWrapper).catalog();
     Mockito.doReturn(capability).when(catalogWrapper).capabilities();
     Mockito.doReturn(unsupportedResult).when(capability).managedStorage(any());
+    Mockito.doAnswer(
+            invocation -> {
+              ThrowableFunction<CatalogOperations, ?> function = 
invocation.getArgument(0);
+              return function.apply(operations);
+            })
+        .when(catalogWrapper)
+        .doWithCatalogOps(any());
 
     catalogManager.getCatalogCache().put(ident, catalogWrapper);
     boolean dropped = catalogManager.dropCatalog(ident);
@@ -1119,6 +1131,7 @@ public class TestCatalogManager {
     Assertions.assertTrue(dropped);
     Assertions.assertFalse(entityStore.exists(ident, EntityType.CATALOG));
     
Assertions.assertNull(catalogManager.getCatalogCache().getIfPresent(ident));
+    Mockito.verify((CatalogDropAware) operations).onCatalogDropped();
   }
 
   @Test
diff --git 
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/IcebergCatalogWrapper.java
 
b/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/IcebergCatalogWrapper.java
index 529c352cae..566c5289d0 100644
--- 
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/IcebergCatalogWrapper.java
+++ 
b/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/ops/IcebergCatalogWrapper.java
@@ -29,6 +29,7 @@ import lombok.Setter;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.gravitino.catalog.hadoop.fs.FileSystemUtils;
 import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergCatalogBackend;
+import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergConstants;
 import org.apache.gravitino.iceberg.common.IcebergConfig;
 import org.apache.gravitino.iceberg.common.cache.SupportsMetadataLocation;
 import org.apache.gravitino.iceberg.common.cache.TableMetadataCache;
@@ -390,7 +391,10 @@ public class IcebergCatalogWrapper implements 
AutoCloseable {
     } else {
       LOG.info("Closing IcebergCatalogWrapper before catalog is initialized");
     }
-    if (loadedCatalog instanceof AutoCloseable) {
+    boolean internedMemoryCatalog =
+        catalogBackend == IcebergCatalogBackend.MEMORY
+            && 
icebergConfig.getAllConfig().containsKey(IcebergConstants.CATALOG_UUID);
+    if (!internedMemoryCatalog && loadedCatalog instanceof AutoCloseable) {
       // JdbcCatalog and ClosableHiveCatalog implement AutoCloseable and will 
handle their own
       // cleanup
       ((AutoCloseable) loadedCatalog).close();
diff --git 
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/utils/IcebergCatalogUtil.java
 
b/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/utils/IcebergCatalogUtil.java
index 6c12e60455..f849b77ad5 100644
--- 
a/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/utils/IcebergCatalogUtil.java
+++ 
b/iceberg/iceberg-common/src/main/java/org/apache/gravitino/iceberg/common/utils/IcebergCatalogUtil.java
@@ -23,11 +23,13 @@ import static 
org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Maps;
+import java.io.IOException;
 import java.sql.SQLException;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergCatalogBackend;
 import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergConstants;
 import org.apache.gravitino.exceptions.ConnectionFailedException;
@@ -56,7 +58,19 @@ public class IcebergCatalogUtil {
 
   private static final Logger LOG = 
LoggerFactory.getLogger(IcebergCatalogUtil.class);
 
+  private static final ConcurrentHashMap<String, InMemoryCatalog> 
MEMORY_CATALOGS =
+      new ConcurrentHashMap<>();
+
   private static InMemoryCatalog loadMemoryCatalog(IcebergConfig 
icebergConfig) {
+    String catalogUuid = 
icebergConfig.getAllConfig().get(IcebergConstants.CATALOG_UUID);
+    if (catalogUuid == null) {
+      return createMemoryCatalog(icebergConfig);
+    }
+    return MEMORY_CATALOGS.computeIfAbsent(
+        catalogUuid, ignored -> createMemoryCatalog(icebergConfig));
+  }
+
+  private static InMemoryCatalog createMemoryCatalog(IcebergConfig 
icebergConfig) {
     String icebergCatalogName = icebergConfig.getCatalogBackendName();
     InMemoryCatalog memoryCatalog = new 
MemoryCatalogWithMetadataLocationSupport();
     Map<String, String> resultProperties = 
icebergConfig.getIcebergCatalogProperties();
@@ -68,6 +82,27 @@ public class IcebergCatalogUtil {
     return memoryCatalog;
   }
 
+  /**
+   * Removes the in-memory Iceberg catalog associated with a permanently 
dropped Gravitino catalog.
+   *
+   * @param catalogUuid the unique Gravitino catalog identifier
+   */
+  public static void removeMemoryCatalog(String catalogUuid) {
+    InMemoryCatalog memoryCatalog = MEMORY_CATALOGS.remove(catalogUuid);
+    if (memoryCatalog != null) {
+      try {
+        memoryCatalog.close();
+      } catch (IOException e) {
+        LOG.warn("Failed to close dropped in-memory Iceberg catalog {}", 
catalogUuid, e);
+      }
+    }
+  }
+
+  @VisibleForTesting
+  static void clearMemoryCatalogs() {
+    MEMORY_CATALOGS.clear();
+  }
+
   private static HiveCatalog loadHiveCatalog(IcebergConfig icebergConfig) {
     ClosableHiveCatalog hiveCatalog = new 
HiveCatalogWithMetadataLocationSupport();
     HdfsConfiguration hdfsConfiguration = new HdfsConfiguration();
diff --git 
a/iceberg/iceberg-common/src/test/java/org/apache/gravitino/iceberg/common/ops/TestIcebergCatalogWrapper.java
 
b/iceberg/iceberg-common/src/test/java/org/apache/gravitino/iceberg/common/ops/TestIcebergCatalogWrapper.java
index e34f1becb3..e6928e56a8 100644
--- 
a/iceberg/iceberg-common/src/test/java/org/apache/gravitino/iceberg/common/ops/TestIcebergCatalogWrapper.java
+++ 
b/iceberg/iceberg-common/src/test/java/org/apache/gravitino/iceberg/common/ops/TestIcebergCatalogWrapper.java
@@ -30,8 +30,12 @@ import 
org.apache.gravitino.catalog.lakehouse.iceberg.IcebergConstants;
 import org.apache.gravitino.iceberg.common.IcebergConfig;
 import org.apache.gravitino.iceberg.common.cache.SupportsMetadataLocation;
 import org.apache.gravitino.iceberg.common.cache.TableMetadataCache;
+import org.apache.gravitino.iceberg.common.utils.IcebergCatalogUtil;
 import org.apache.iceberg.TableMetadata;
+import org.apache.iceberg.catalog.Catalog;
+import org.apache.iceberg.catalog.Namespace;
 import org.apache.iceberg.catalog.TableIdentifier;
+import org.apache.iceberg.rest.requests.CreateNamespaceRequest;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
@@ -57,6 +61,29 @@ public class TestIcebergCatalogWrapper {
         });
   }
 
+  @Test
+  public void testMemoryCatalogSurvivesWrapperClose() throws Exception {
+    String catalogUuid = "catalog-1";
+    IcebergConfig config =
+        new IcebergConfig(
+            Map.of(
+                IcebergConstants.CATALOG_BACKEND,
+                "memory",
+                IcebergConstants.CATALOG_UUID,
+                catalogUuid));
+    IcebergCatalogWrapper firstWrapper = new IcebergCatalogWrapper(config);
+    Catalog firstCatalog = firstWrapper.getCatalog();
+    Namespace namespace = Namespace.of("preserved");
+    
firstWrapper.createNamespace(CreateNamespaceRequest.builder().withNamespace(namespace).build());
+
+    firstWrapper.close();
+    IcebergCatalogWrapper secondWrapper = new IcebergCatalogWrapper(config);
+
+    Assertions.assertSame(firstCatalog, secondWrapper.getCatalog());
+    Assertions.assertTrue(secondWrapper.namespaceExists(namespace));
+    IcebergCatalogUtil.removeMemoryCatalog(catalogUuid);
+  }
+
   @Test
   public void testMetadataCacheShouldInitializeOnFirstAccessAndClose(@TempDir 
Path warehouseDir)
       throws Exception {
diff --git 
a/iceberg/iceberg-common/src/test/java/org/apache/gravitino/iceberg/common/utils/TestIcebergCatalogUtil.java
 
b/iceberg/iceberg-common/src/test/java/org/apache/gravitino/iceberg/common/utils/TestIcebergCatalogUtil.java
index ade4237456..89cf6f7897 100644
--- 
a/iceberg/iceberg-common/src/test/java/org/apache/gravitino/iceberg/common/utils/TestIcebergCatalogUtil.java
+++ 
b/iceberg/iceberg-common/src/test/java/org/apache/gravitino/iceberg/common/utils/TestIcebergCatalogUtil.java
@@ -36,6 +36,7 @@ import org.apache.iceberg.inmemory.InMemoryCatalog;
 import org.apache.iceberg.jdbc.JdbcCatalog;
 import org.apache.iceberg.jdbc.JdbcCatalogWithMetadataLocationSupport;
 import org.apache.iceberg.types.Types;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
@@ -44,6 +45,91 @@ public class TestIcebergCatalogUtil {
 
   @TempDir private Path warehouse;
 
+  @AfterEach
+  void tearDown() {
+    IcebergCatalogUtil.clearMemoryCatalogs();
+  }
+
+  @Test
+  void testMemoryCatalogIsInternedByCatalogUuid() {
+    Map<String, String> properties =
+        Map.of(
+            IcebergConstants.CATALOG_BACKEND, "memory", 
IcebergConstants.CATALOG_UUID, "catalog-1");
+    IcebergConfig config = new IcebergConfig(properties);
+
+    InMemoryCatalog first =
+        (InMemoryCatalog)
+            
IcebergCatalogUtil.loadCatalogBackend(IcebergCatalogBackend.MEMORY, config);
+    Namespace namespace = Namespace.of("shared");
+    first.createNamespace(namespace);
+    InMemoryCatalog second =
+        (InMemoryCatalog)
+            
IcebergCatalogUtil.loadCatalogBackend(IcebergCatalogBackend.MEMORY, config);
+
+    Assertions.assertSame(first, second);
+    Assertions.assertTrue(second.namespaceExists(namespace));
+  }
+
+  @Test
+  void testMemoryCatalogsWithDifferentUuidsAreIsolated() {
+    IcebergConfig firstConfig =
+        new IcebergConfig(
+            Map.of(
+                IcebergConstants.CATALOG_BACKEND,
+                "memory",
+                IcebergConstants.CATALOG_UUID,
+                "catalog-1"));
+    IcebergConfig secondConfig =
+        new IcebergConfig(
+            Map.of(
+                IcebergConstants.CATALOG_BACKEND,
+                "memory",
+                IcebergConstants.CATALOG_UUID,
+                "catalog-2"));
+
+    Catalog first =
+        IcebergCatalogUtil.loadCatalogBackend(IcebergCatalogBackend.MEMORY, 
firstConfig);
+    Catalog second =
+        IcebergCatalogUtil.loadCatalogBackend(IcebergCatalogBackend.MEMORY, 
secondConfig);
+
+    Assertions.assertNotSame(first, second);
+  }
+
+  @Test
+  void testMemoryCatalogWithoutUuidIsNotInterned() {
+    IcebergConfig config = new 
IcebergConfig(Map.of(IcebergConstants.CATALOG_BACKEND, "memory"));
+
+    Catalog first = 
IcebergCatalogUtil.loadCatalogBackend(IcebergCatalogBackend.MEMORY, config);
+    Catalog second = 
IcebergCatalogUtil.loadCatalogBackend(IcebergCatalogBackend.MEMORY, config);
+
+    Assertions.assertNotSame(first, second);
+  }
+
+  @Test
+  void testDroppedMemoryCatalogIsRemoved() {
+    String catalogUuid = "catalog-1";
+    IcebergConfig config =
+        new IcebergConfig(
+            Map.of(
+                IcebergConstants.CATALOG_BACKEND,
+                "memory",
+                IcebergConstants.CATALOG_UUID,
+                catalogUuid));
+    InMemoryCatalog original =
+        (InMemoryCatalog)
+            
IcebergCatalogUtil.loadCatalogBackend(IcebergCatalogBackend.MEMORY, config);
+    Namespace namespace = Namespace.of("removed");
+    original.createNamespace(namespace);
+
+    IcebergCatalogUtil.removeMemoryCatalog(catalogUuid);
+    InMemoryCatalog replacement =
+        (InMemoryCatalog)
+            
IcebergCatalogUtil.loadCatalogBackend(IcebergCatalogBackend.MEMORY, config);
+
+    Assertions.assertNotSame(original, replacement);
+    Assertions.assertFalse(replacement.namespaceExists(namespace));
+  }
+
   @Test
   void testLoadCatalog() {
     Catalog catalog;
diff --git 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/IcebergCatalogWrapperManager.java
 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/IcebergCatalogWrapperManager.java
index ce2df6910a..3a3c518f30 100644
--- 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/IcebergCatalogWrapperManager.java
+++ 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/IcebergCatalogWrapperManager.java
@@ -30,6 +30,7 @@ import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import org.apache.gravitino.GravitinoEnv;
 import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergCatalogBackend;
+import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergConstants;
 import org.apache.gravitino.exceptions.NoSuchCatalogException;
 import org.apache.gravitino.iceberg.common.IcebergConfig;
 import org.apache.gravitino.iceberg.common.authentication.AuthenticationConfig;
@@ -84,6 +85,9 @@ public class IcebergCatalogWrapperManager implements 
AutoCloseable {
               ident -> {
                 if (ident.namespace().level(0).equals(metalakeName)) {
                   catalogWrapperCache.invalidate(ident.name());
+                  if 
(ident.name().equals(configProvider.getDefaultCatalogName())) {
+                    
catalogWrapperCache.invalidate(IcebergConstants.ICEBERG_REST_DEFAULT_CATALOG);
+                  }
                 }
               });
     }
diff --git 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/provider/DynamicIcebergConfigProvider.java
 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/provider/DynamicIcebergConfigProvider.java
index 43ec66812e..5195502b3f 100644
--- 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/provider/DynamicIcebergConfigProvider.java
+++ 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/provider/DynamicIcebergConfigProvider.java
@@ -116,7 +116,9 @@ public class DynamicIcebergConfigProvider implements 
IcebergConfigProvider {
     // fields into the properties map so the JDBC backend can connect.
     Map<String, String> catalogProperties;
     if (catalog instanceof BaseCatalog) {
-      catalogProperties = ((BaseCatalog<?>) 
catalog).propertiesWithCredentialProviders();
+      BaseCatalog<?> baseCatalog = (BaseCatalog<?>) catalog;
+      catalogProperties = new 
HashMap<>(baseCatalog.propertiesWithCredentialProviders());
+      catalogProperties.put(IcebergConstants.CATALOG_UUID, 
baseCatalog.entity().id().toString());
     } else {
       catalogProperties = new HashMap<>(catalog.properties());
       if (catalog instanceof SupportsCredentials) {
diff --git 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/TestIcebergCatalogWrapperManagerForREST.java
 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/TestIcebergCatalogWrapperManagerForREST.java
index 2d4386b730..45eb62f69e 100644
--- 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/TestIcebergCatalogWrapperManagerForREST.java
+++ 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/TestIcebergCatalogWrapperManagerForREST.java
@@ -21,9 +21,13 @@ package org.apache.gravitino.iceberg.service;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
 import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.reflect.FieldUtils;
 import org.apache.gravitino.GravitinoEnv;
+import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.catalog.CatalogManager;
 import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergConstants;
 import org.apache.gravitino.iceberg.common.IcebergConfig;
@@ -42,11 +46,12 @@ import org.mockito.Mockito;
 public class TestIcebergCatalogWrapperManagerForREST {
 
   private static final String DEFAULT_CATALOG = "memory";
+  private static CatalogManager mockCatalogManager;
 
   @BeforeAll
   public static void setup() throws IllegalAccessException {
     // Mock CatalogManager for GravitinoEnv to avoid initialization errors
-    CatalogManager mockCatalogManager = Mockito.mock(CatalogManager.class);
+    mockCatalogManager = Mockito.mock(CatalogManager.class);
     FieldUtils.writeField(GravitinoEnv.getInstance(), "catalogManager", 
mockCatalogManager, true);
   }
 
@@ -145,6 +150,45 @@ public class TestIcebergCatalogWrapperManagerForREST {
     Assertions.assertEquals(CatalogWrapperForREST.class, wrapper.getClass());
   }
 
+  @Test
+  public void testDefaultCatalogAliasInvalidatedWhenCatalogRemoved() throws 
Exception {
+    Mockito.clearInvocations(mockCatalogManager);
+    AtomicReference<Consumer<NameIdentifier>> removeListener = new 
AtomicReference<>();
+    Mockito.doAnswer(
+            invocation -> {
+              removeListener.set(invocation.getArgument(0));
+              return null;
+            })
+        .when(mockCatalogManager)
+        .addCatalogCacheRemoveListener(Mockito.any());
+
+    IcebergConfigProvider configProvider = 
Mockito.mock(IcebergConfigProvider.class);
+    Mockito.when(configProvider.getDefaultCatalogName()).thenReturn("test");
+    IcebergConfig icebergConfig =
+        new IcebergConfig(
+            ImmutableMap.of(
+                IcebergConstants.CATALOG_BACKEND,
+                "memory",
+                IcebergConstants.WAREHOUSE,
+                "/tmp/warehouse"));
+    Mockito.when(
+            
configProvider.getIcebergCatalogConfig(IcebergConstants.ICEBERG_REST_DEFAULT_CATALOG))
+        .thenReturn(Optional.of(icebergConfig));
+
+    try (IcebergCatalogWrapperManager manager =
+        new IcebergCatalogWrapperManager(Maps.newHashMap(), configProvider, 
true, "metalake")) {
+      IcebergRESTServerContext.create(configProvider, false, false, true, 
manager);
+      CatalogWrapperForREST original =
+          
manager.getCatalogWrapper(IcebergConstants.ICEBERG_REST_DEFAULT_CATALOG);
+
+      removeListener.get().accept(NameIdentifier.of("metalake", "test"));
+
+      CatalogWrapperForREST replacement =
+          
manager.getCatalogWrapper(IcebergConstants.ICEBERG_REST_DEFAULT_CATALOG);
+      Assertions.assertNotSame(original, replacement);
+    }
+  }
+
   private static IcebergCatalogWrapperManager newManager() {
     Map<String, String> config = Maps.newHashMap();
     IcebergConfigProvider configProvider = 
IcebergConfigProviderFactory.create(config);
diff --git 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/provider/TestDynamicIcebergConfigProvider.java
 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/provider/TestDynamicIcebergConfigProvider.java
index d5b2024152..b9a7b2ebfd 100644
--- 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/provider/TestDynamicIcebergConfigProvider.java
+++ 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/service/provider/TestDynamicIcebergConfigProvider.java
@@ -35,10 +35,12 @@ import org.apache.gravitino.GravitinoEnv;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.catalog.CatalogDispatcher;
 import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergConstants;
+import org.apache.gravitino.connector.BaseCatalog;
 import org.apache.gravitino.exceptions.NoSuchCatalogException;
 import org.apache.gravitino.iceberg.common.IcebergConfig;
 import org.apache.gravitino.iceberg.common.ops.IcebergCatalogWrapper;
 import 
org.apache.gravitino.iceberg.service.authorization.IcebergRESTServerContext;
+import org.apache.gravitino.meta.CatalogEntity;
 import org.apache.gravitino.utils.NameIdentifierUtil;
 import org.apache.iceberg.hive.HiveCatalog;
 import org.apache.iceberg.jdbc.JdbcCatalog;
@@ -584,4 +586,34 @@ public class TestDynamicIcebergConfigProvider {
     executor.shutdown();
     executor.awaitTermination(5, TimeUnit.SECONDS);
   }
+
+  @Test
+  public void testAuxiliaryCatalogConfigIncludesCatalogUuid() {
+    String metalakeName = "test_metalake";
+    String catalogName = "memory_catalog";
+    Map<String, String> catalogProperties =
+        Map.of(
+            IcebergConstants.CATALOG_BACKEND,
+            "memory",
+            IcebergConstants.CATALOG_BACKEND_NAME,
+            catalogName);
+
+    @SuppressWarnings("unchecked")
+    BaseCatalog<?> baseCatalog = Mockito.mock(BaseCatalog.class);
+    CatalogEntity catalogEntity = Mockito.mock(CatalogEntity.class);
+    Mockito.when(baseCatalog.provider()).thenReturn("lakehouse-iceberg");
+    
Mockito.when(baseCatalog.propertiesWithCredentialProviders()).thenReturn(catalogProperties);
+    Mockito.when(baseCatalog.entity()).thenReturn(catalogEntity);
+    Mockito.when(catalogEntity.id()).thenReturn(9L);
+
+    DynamicIcebergConfigProvider provider = new DynamicIcebergConfigProvider();
+    provider.initialize(Map.of(IcebergConstants.GRAVITINO_METALAKE, 
metalakeName));
+    setMockCatalogFetcher(provider, Map.of(catalogName, baseCatalog));
+
+    Optional<IcebergConfig> config = 
provider.getIcebergCatalogConfig(catalogName);
+
+    Assertions.assertTrue(config.isPresent());
+    Assertions.assertEquals(
+        "9", 
config.get().getIcebergCatalogProperties().get(IcebergConstants.CATALOG_UUID));
+  }
 }

Reply via email to