LuciferYang commented on code in PR #13436:
URL: https://github.com/apache/gravitino/pull/13436#discussion_r4096249274


##########
core/src/main/java/org/apache/gravitino/metalake/MetalakeManager.java:
##########
@@ -401,30 +410,65 @@ public boolean dropMetalake(NameIdentifier ident, boolean 
force)
    *
    * <p>Callers typically {@code disableMetalake} before force-drop. {@link
    * CatalogManager#dropCatalog} requires catalog {@code 
metalake-in-use=true}, so a disabled
-   * metalake is briefly re-enabled for child cleanup. The metalake entity is 
deleted immediately
-   * afterward, so the temporary enable is not restored.
+   * metalake is briefly re-enabled for child cleanup. If the cleanup fails it 
is re-disabled here
+   * (best effort); if the cleanup succeeds this returns whether the metalake 
was temporarily
+   * enabled, so {@code dropMetalake} can re-disable it should the metalake 
delete then fail. Either
+   * way a user-disabled metalake does not stay enabled after a failed force 
drop.
+   *
+   * @param metalakeIdent the metalake whose child catalogs are force-dropped
+   * @return {@code true} if this temporarily enabled a user-disabled 
metalake, so the caller must
+   *     restore the disabled state if the subsequent metalake delete fails
    */
-  private void dropCatalogsUnderMetalake(NameIdentifier metalakeIdent) {
+  private boolean dropCatalogsUnderMetalake(NameIdentifier metalakeIdent) {
     if (catalogManager == null) {
-      return;
+      return false;
     }
     try {
-      if (!metalakeInUse(store, metalakeIdent)) {
-        enableMetalake(metalakeIdent);
-      }
-      List<CatalogEntity> catalogs =
-          store.list(Namespace.of(metalakeIdent.name()), CatalogEntity.class, 
EntityType.CATALOG);
-      for (CatalogEntity catalog : catalogs) {
-        catalogManager.dropCatalog(
-            NameIdentifier.of(metalakeIdent.name(), catalog.name()), true /* 
force */);
+      boolean wasDisabled = !metalakeInUse(store, metalakeIdent);

Review Comment:
   Good catch. I made the enable decision atomic: the check-and-flip now runs 
inside the metalake write lock via a new private `enableMetalakeIfDisabled`, 
and the restore is keyed on a flag that is set only when this operation 
actually performed the disabled→enabled flip, not on an earlier unlocked 
`metalakeInUse` read. A concurrent enable can no longer make the force drop 
believe it enabled the metalake and then re-disable it, and a failure partway 
through the enable still restores the disabled state.
   
   Tests: `testFailedForceDropRestoresWhenTemporaryEnableFailsMidway` covers a 
temporary enable that throws while pushing the in-use status to catalogs, and 
`testForceDropDeleteFailureDoesNotDisableAlreadyEnabledMetalake` covers an 
already-enabled metalake whose phase-2 delete fails (it must stay enabled).
   
   One residual I'll track as a follow-up: if a concurrent user disables and 
then re-enables during the catalog-cleanup or delete window, a later failure's 
restore would still undo that re-enable. Closing it fully needs optimistic 
concurrency on the metalake entity (a monotonic revision to CAS on), which we 
don't expose today (`getVersion()` is the schema version, not an entity 
revision), so it's a storage-layer change beyond this PR.



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