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