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

yuqi1129 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 aac721418d [Cherry-pick to branch-1.3] [#11406] fix(core): reload 
closed catalog cache wrapper (#11407) (#11416)
aac721418d is described below

commit aac721418da05e8edb826fc552b63c1abeeb1e0d
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Thu Jun 4 17:50:55 2026 +0800

    [Cherry-pick to branch-1.3] [#11406] fix(core): reload closed catalog cache 
wrapper (#11407) (#11416)
    
    **Cherry-pick Information:**
    - Original commit: 0d7e459d6026dcdbfe32e07b26766d42cc5febf7
    - Target branch: `branch-1.3`
    - Status: ✅ Clean cherry-pick (no conflicts)
    
    Co-authored-by: Qi Yu <[email protected]>
---
 .../apache/gravitino/catalog/CatalogManager.java   | 13 ++++-
 .../gravitino/catalog/TestCatalogManager.java      | 60 ++++++++++++++++++++++
 2 files changed, 72 insertions(+), 1 deletion(-)

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 203b8c5555..91bd5b08b6 100644
--- a/core/src/main/java/org/apache/gravitino/catalog/CatalogManager.java
+++ b/core/src/main/java/org/apache/gravitino/catalog/CatalogManager.java
@@ -1001,13 +1001,24 @@ public class CatalogManager implements 
CatalogDispatcher, Closeable {
 
   /**
    * Loads the catalog with the specified identifier, wraps it in a 
CatalogWrapper, and caches the
-   * wrapper for reuse.
+   * wrapper for reuse. If the cached wrapper has already been closed (its 
underlying catalog is
+   * null), the stale entry is evicted and a fresh wrapper is loaded and 
cached.
    *
    * @param ident The identifier of the catalog to load.
    * @return The wrapped CatalogWrapper containing the loaded catalog.
    * @throws NoSuchCatalogException If the specified catalog does not exist.
    */
   public CatalogWrapper loadCatalogAndWrap(NameIdentifier ident) throws 
NoSuchCatalogException {
+    CatalogWrapper wrapper = catalogCache.get(ident, 
this::loadCatalogInternal);
+    if (wrapper.catalog() != null) {
+      return wrapper;
+    }
+
+    // The cached wrapper has already been closed (catalog() == null), e.g. by 
a prior
+    // dropCatalog or cache eviction. Evict the stale entry and reload a fresh 
one.
+    // Use a conditional remove so we do not clobber a wrapper that another 
thread may
+    // have concurrently reloaded into the cache between our initial get and 
this remove.
+    catalogCache.asMap().remove(ident, wrapper);
     return catalogCache.get(ident, this::loadCatalogInternal);
   }
 
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 5d978dd3b0..4880d7f258 100644
--- a/core/src/test/java/org/apache/gravitino/catalog/TestCatalogManager.java
+++ b/core/src/test/java/org/apache/gravitino/catalog/TestCatalogManager.java
@@ -1027,6 +1027,66 @@ public class TestCatalogManager {
     
Assertions.assertNull(catalogManager.getCatalogCache().getIfPresent(ident));
   }
 
+  @Test
+  void testDropCatalogReloadsClosedCachedWrapper() throws Exception {
+    NameIdentifier ident = NameIdentifier.of("metalake", 
"closed_cache_drop_test");
+    Map<String, String> props =
+        ImmutableMap.of(
+            "provider",
+            "test",
+            PROPERTY_KEY1,
+            "value1",
+            PROPERTY_KEY2,
+            "value2",
+            PROPERTY_KEY5_PREFIX + "1",
+            "value3");
+
+    Catalog catalog =
+        catalogManager.createCatalog(ident, Catalog.Type.RELATIONAL, provider, 
"comment", props);
+    Assertions.assertDoesNotThrow(() -> catalogManager.disableCatalog(ident));
+    CatalogEntity entity = entityStore.get(ident, EntityType.CATALOG, 
CatalogEntity.class);
+    FieldUtils.writeField(catalog, "entity", entity, true);
+
+    CatalogManager.CatalogWrapper closedWrapper = 
catalogManager.loadCatalogAndWrap(ident);
+    closedWrapper.close();
+    Assertions.assertSame(closedWrapper, 
catalogManager.getCatalogCache().getIfPresent(ident));
+
+    boolean dropped = catalogManager.dropCatalog(ident);
+
+    Assertions.assertTrue(dropped);
+    Assertions.assertFalse(entityStore.exists(ident, EntityType.CATALOG));
+    
Assertions.assertNull(catalogManager.getCatalogCache().getIfPresent(ident));
+  }
+
+  @Test
+  void testLoadCatalogAndWrapDoesNotInvalidateConcurrentlyReloadedWrapper() {
+    NameIdentifier ident = NameIdentifier.of("metalake", 
"concurrent_cache_reload_test");
+
+    CatalogManager.CatalogWrapper closedWrapper = 
Mockito.mock(CatalogManager.CatalogWrapper.class);
+    CatalogManager.CatalogWrapper freshWrapper = 
Mockito.mock(CatalogManager.CatalogWrapper.class);
+    BaseCatalog<?> freshCatalog = Mockito.mock(BaseCatalog.class);
+    Mockito.doReturn(freshCatalog).when(freshWrapper).catalog();
+    Mockito.doAnswer(
+            invocation -> {
+              catalogManager.getCatalogCache().put(ident, freshWrapper);
+              return null;
+            })
+        .when(closedWrapper)
+        .catalog();
+
+    try {
+      catalogManager.getCatalogCache().put(ident, closedWrapper);
+
+      CatalogManager.CatalogWrapper loadedWrapper = 
catalogManager.loadCatalogAndWrap(ident);
+
+      Assertions.assertSame(freshWrapper, loadedWrapper);
+      Assertions.assertSame(freshWrapper, 
catalogManager.getCatalogCache().getIfPresent(ident));
+      Mockito.verify(freshWrapper, Mockito.never()).close();
+    } finally {
+      catalogManager.getCatalogCache().invalidate(ident);
+    }
+  }
+
   @Test
   void testAlterMutableProperties() {
     NameIdentifier ident = NameIdentifier.of("metalake", "test51");

Reply via email to