This is an automated email from the ASF dual-hosted git repository.
yuqi1129 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 3d068320f6 [#13296] test(core): cover that force-dropping a metalake
closes its catalogs (#13297)
3d068320f6 is described below
commit 3d068320f60eb2ed6c6c0de63bc0d78148153712
Author: Qi Yu <[email protected]>
AuthorDate: Fri Sep 18 14:02:36 2026 +0800
[#13296] test(core): cover that force-dropping a metalake closes its
catalogs (#13297)
### What changes were proposed in this pull request?
Add a regression test
`TestMetalakeManager#testForceDropMetalakeClosesCachedCatalogs` that
uses a real `CatalogManager`, creates and loads a catalog under a
metalake, force-drops the metalake and asserts that the catalog's
`CatalogWrapper` is evicted from the catalog cache and closed (its
catalog reference is released).
`main` already force-drops child catalogs through
`CatalogManager.dropCatalog` in `MetalakeManager.dropMetalake` (added as
part of #12420), but nothing pinned that behaviour; `branch-1.3` lacks
the logic entirely and leaks every catalog's connection pool when a
metalake is dropped. The cherry-pick of this PR to `branch-1.3` carries
the backport of the catalog-drop logic together with this test.
### Why are the changes needed?
Dropping a metalake must release the resources of its catalogs (for
example JDBC connection pools) exactly as dropping each catalog does.
Without this, a metalake with many JDBC catalogs on one PostgreSQL
server leaves its connections held until cache expiry or server restart.
Fix: #13296
### Does this PR introduce _any_ user-facing change?
No.
### How was this patch tested?
New unit test `testForceDropMetalakeClosesCachedCatalogs` in
`TestMetalakeManager`. It passes on `main` and fails on `branch-1.3`
without the backported fix (the wrapper stays cached). Ran
`TestMetalakeManager` and `TestCatalogManager` locally.
---
.../gravitino/metalake/TestMetalakeManager.java | 49 ++++++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git
a/core/src/test/java/org/apache/gravitino/metalake/TestMetalakeManager.java
b/core/src/test/java/org/apache/gravitino/metalake/TestMetalakeManager.java
index 862bef68d7..a9a69ec126 100644
--- a/core/src/test/java/org/apache/gravitino/metalake/TestMetalakeManager.java
+++ b/core/src/test/java/org/apache/gravitino/metalake/TestMetalakeManager.java
@@ -21,6 +21,7 @@ package org.apache.gravitino.metalake;
import static org.apache.gravitino.Configs.TREE_LOCK_CLEAN_INTERVAL;
import static org.apache.gravitino.Configs.TREE_LOCK_MAX_NODE_IN_MEMORY;
import static org.apache.gravitino.Configs.TREE_LOCK_MIN_NODE_IN_MEMORY;
+import static org.awaitility.Awaitility.await;
import static org.mockito.Mockito.doReturn;
import com.google.common.collect.ImmutableMap;
@@ -30,9 +31,12 @@ import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.gravitino.Catalog;
import org.apache.gravitino.Config;
+import org.apache.gravitino.Configs;
+import org.apache.gravitino.Entity;
import org.apache.gravitino.Entity.EntityType;
import org.apache.gravitino.EntityStore;
import org.apache.gravitino.GravitinoEnv;
@@ -50,6 +54,7 @@ import org.apache.gravitino.lock.LockManager;
import org.apache.gravitino.meta.AuditInfo;
import org.apache.gravitino.meta.BaseMetalake;
import org.apache.gravitino.meta.CatalogEntity;
+import org.apache.gravitino.secret.SecretManager;
import org.apache.gravitino.storage.RandomIdGenerator;
import org.apache.gravitino.storage.memory.TestMemoryEntityStore;
import
org.apache.gravitino.storage.memory.TestMemoryEntityStore.InMemoryEntityStore;
@@ -265,6 +270,50 @@ public class TestMetalakeManager {
metalakeManager.dropMetalake(ident3, true);
}
+ @Test
+ public void testForceDropMetalakeClosesCachedCatalogs() throws Exception {
+ // Dropping a metalake must release the resources of its catalogs (for
example JDBC
+ // connection pools) exactly as dropping each catalog does, instead of
leaving the cached
+ // catalog instances alive until cache expiry.
+ Config catalogConfig = new Config(false) {};
+ catalogConfig.set(Configs.CATALOG_LOAD_ISOLATED, false);
+ InMemoryEntityStore store = new InMemoryEntityStore();
+ store.initialize(catalogConfig);
+ CatalogManager catalogManager =
+ new CatalogManager(
+ catalogConfig, store, new RandomIdGenerator(), new
SecretManager(catalogConfig));
+ MetalakeManager manager = new MetalakeManager(store, new
RandomIdGenerator(), catalogManager);
+
+ NameIdentifier metalakeIdent =
NameIdentifier.of("force_drop_closes_catalogs_ml");
+ manager.createMetalake(metalakeIdent, "comment", ImmutableMap.of());
+ NameIdentifier catalogIdent = NameIdentifier.of(metalakeIdent.name(),
"cached_catalog");
+ catalogManager.createCatalog(
+ catalogIdent,
+ Catalog.Type.RELATIONAL,
+ "test",
+ "comment",
+ ImmutableMap.of(
+ "provider", "test", "key1", "value1", "key2", "value2", "key5-1",
"value3"));
+ // createCatalog caches the wrapper; loadCatalog keeps it warm the same
way a schema listing
+ // against the catalog would.
+ catalogManager.loadCatalog(catalogIdent);
+ CatalogManager.CatalogWrapper wrapper =
+ catalogManager.getCatalogCache().getIfPresent(catalogIdent);
+ Assertions.assertNotNull(wrapper);
+ Assertions.assertNotNull(wrapper.catalog());
+
+ Assertions.assertTrue(manager.dropMetalake(metalakeIdent, true));
+
+
Assertions.assertNull(catalogManager.getCatalogCache().getIfPresent(catalogIdent));
+ Assertions.assertFalse(store.exists(catalogIdent,
Entity.EntityType.CATALOG));
+ // The cache removal listener retires the wrapper asynchronously; once
cleaned up, the wrapper
+ // drops its catalog reference.
+ await().atMost(10, TimeUnit.SECONDS).until(() -> wrapper.catalog() ==
null);
+
+ catalogManager.close();
+ store.close();
+ }
+
@Test
public void testForceDropMetalakeAfterDisableDropsLeftoverCatalogs() throws
Exception {
// Mirrors IT tearDown: disableMetalake then dropMetalake(force=true)
while a catalog entity