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 5a0afd4b75 [Cherry-pick to branch-1.3] [#13964] fix(idp-basic): Make 
service-admin bootstrap idempotent under races (#13965) (#14015)
5a0afd4b75 is described below

commit 5a0afd4b75658541028ca8c0bf9cc57d7b66abc8
Author: MaSai <[email protected]>
AuthorDate: Wed Oct 21 12:06:23 2026 +0800

    [Cherry-pick to branch-1.3] [#13964] fix(idp-basic): Make service-admin 
bootstrap idempotent under races (#13965) (#14015)
    
    **Cherry-pick Information:**
    - Original commit: fdb03ed44844b636f3d77a5652c968e98e36c20a
    - Target branch: `branch-1.3`
    - Status: ✅ Clean cherry-pick (no conflicts)
    
    Adapted for `branch-1.3` (2-arg `newUserPO`; no `enabled` flag).
    Replaces [#13988](https://github.com/apache/gravitino/pull/13988).
    
    Co-authored-by: Cursor <[email protected]>
---
 .../apache/gravitino/idp/IdpUserGroupManager.java  | 15 ++++++++-
 .../gravitino/idp/TestIdpUserGroupManager.java     | 37 ++++++++++++++++++++++
 2 files changed, 51 insertions(+), 1 deletion(-)

diff --git 
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/IdpUserGroupManager.java
 
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/IdpUserGroupManager.java
index e866708892..a1a9d05982 100644
--- 
a/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/IdpUserGroupManager.java
+++ 
b/plugins/idp-basic/src/main/java/org/apache/gravitino/idp/IdpUserGroupManager.java
@@ -29,6 +29,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.gravitino.Config;
 import org.apache.gravitino.Configs;
 import org.apache.gravitino.auth.AuthConstants;
+import org.apache.gravitino.exceptions.AlreadyExistsException;
 import org.apache.gravitino.exceptions.NotFoundException;
 import org.apache.gravitino.exceptions.UnauthorizedException;
 import org.apache.gravitino.idp.basic.IdpCredentialValidator;
@@ -44,6 +45,8 @@ import 
org.apache.gravitino.idp.storage.service.IdpGroupMetaService;
 import org.apache.gravitino.idp.storage.service.IdpUserMetaService;
 import org.apache.gravitino.storage.IdGenerator;
 import org.apache.gravitino.storage.relational.utils.POConverters;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Manager for built-in IdP users and groups. It mirrors {@link
@@ -51,6 +54,8 @@ import 
org.apache.gravitino.storage.relational.utils.POConverters;
  */
 public class IdpUserGroupManager implements Closeable {
 
+  private static final Logger LOG = 
LoggerFactory.getLogger(IdpUserGroupManager.class);
+
   private static final IdpUserMetaService USER_SERVICE = 
IdpUserMetaService.getInstance();
   private static final IdpGroupMetaService GROUP_SERVICE = 
IdpGroupMetaService.getInstance();
 
@@ -113,7 +118,15 @@ public class IdpUserGroupManager implements Closeable {
     IdpCredentialValidator.validatePassword(initialAdminPassword);
     String passwordHash = passwordHasher.hash(initialAdminPassword);
     for (String serviceAdmin : missingServiceAdmins) {
-      USER_SERVICE.insertIdpUser(newUserPO(serviceAdmin, passwordHash));
+      try {
+        USER_SERVICE.insertIdpUser(newUserPO(serviceAdmin, passwordHash));
+      } catch (AlreadyExistsException e) {
+        // Another node may have created the same service admin between the 
existence check and
+        // insert. Treat the unique-constraint conflict as success so 
bootstrap stays idempotent.
+        LOG.info(
+            "Configured service admin {} already exists; skipping create 
during bootstrap",
+            serviceAdmin);
+      }
     }
   }
 
diff --git 
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/TestIdpUserGroupManager.java
 
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/TestIdpUserGroupManager.java
index 089db3082e..0d3ba514e9 100644
--- 
a/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/TestIdpUserGroupManager.java
+++ 
b/plugins/idp-basic/src/test/java/org/apache/gravitino/idp/TestIdpUserGroupManager.java
@@ -35,7 +35,14 @@ import com.google.common.collect.Lists;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.ArrayList;
 import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Stream;
 import org.apache.gravitino.Config;
 import org.apache.gravitino.auth.AuthenticatorType;
@@ -297,6 +304,36 @@ public class TestIdpUserGroupManager {
         "initAdminSame2", manager.authenticate("initAdminSame2", 
VALID_PASSWORD).name());
   }
 
+  @Test
+  public void 
testInitializeConfiguredServiceAdminsIgnoresConcurrentCreateRace() throws 
Exception {
+    loadServiceAdminConfig(BASIC_AUTHENTICATOR, "initAdminRace1");
+
+    int threads = 8;
+    ExecutorService executor = Executors.newFixedThreadPool(threads);
+    CountDownLatch start = new CountDownLatch(1);
+    List<Future<?>> futures = new ArrayList<>();
+    try {
+      for (int i = 0; i < threads; i++) {
+        futures.add(
+            executor.submit(
+                () -> {
+                  start.await();
+                  manager.initializeConfiguredServiceAdmins(config, 
VALID_PASSWORD);
+                  return null;
+                }));
+      }
+      start.countDown();
+      for (Future<?> future : futures) {
+        future.get(30, TimeUnit.SECONDS);
+      }
+    } finally {
+      executor.shutdownNow();
+    }
+
+    Assertions.assertEquals(
+        "initAdminRace1", manager.authenticate("initAdminRace1", 
VALID_PASSWORD).name());
+  }
+
   private static Config createH2Config(Path h2Path) {
     Config backendConfig = new Config(false) {};
     backendConfig.set(ENTITY_STORE, RELATIONAL_ENTITY_STORE);

Reply via email to