mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Valery Kharseko
04.55.2025 099f991f4a13c13f9a1bd0750e27a49ff861a73c
[#545] Add GroupManager writeLock performance (#551)

2 files modified
113 ■■■■■ changed files
opendj-server-legacy/src/main/java/org/opends/server/core/GroupManager.java 59 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/test/java/org/opends/server/core/GroupManagerTestCase.java 54 ●●●●● patch | view | raw | blame | history
opendj-server-legacy/src/main/java/org/opends/server/core/GroupManager.java
@@ -13,6 +13,7 @@
 *
 * Copyright 2007-2010 Sun Microsystems, Inc.
 * Portions Copyright 2011-2016 ForgeRock AS.
 * Portions Copyright 2025 3A Systems,LLC.
 */
package org.opends.server.core;
@@ -726,48 +727,34 @@
      return;
    }
    Group<?> group =null;
    lock.readLock().lock();
    try
    {
      if (!groupInstances.containsKey(oldEntry.getName()))
      {
        // If the modified entry is not in any group instance, it's probably
        // not a group, exit fast
        return;
      }
    try{
        group = groupInstances.get(oldEntry.getName());
    }
    finally
    {
      lock.readLock().unlock();
        lock.readLock().unlock();
    }
    lock.writeLock().lock();
    try
    {
      Group<?> group = groupInstances.get(oldEntry.getName());
      if (group != null)
      {
        if (!oldEntry.getName().equals(newEntry.getName())
            || !group.mayAlterMemberList()
            || updatesObjectClass(modifications))
        {
          groupInstances.remove(oldEntry.getName());
          // This updates the refreshToken
          createAndRegisterGroup(newEntry);
    if (group!=null) {
        try {
            if (!oldEntry.getName().equals(newEntry.getName())
                    || !group.mayAlterMemberList()
                    || updatesObjectClass(modifications)) {
                lock.writeLock().lock();
                try {
                    groupInstances.remove(oldEntry.getName());
                    // This updates the refreshToken
                    createAndRegisterGroup(newEntry);
                } finally {
                    lock.writeLock().unlock();
                }
            } else {
                group.updateMembers(modifications);
            }
        } catch (UnsupportedOperationException | DirectoryException e) {
            logger.traceException(e);
        }
        else
        {
          group.updateMembers(modifications);
        }
      }
    }
    catch (UnsupportedOperationException | DirectoryException e)
    {
      logger.traceException(e);
    }
    finally
    {
      lock.writeLock().unlock();
    }
  }
opendj-server-legacy/src/test/java/org/opends/server/core/GroupManagerTestCase.java
@@ -13,12 +13,17 @@
 *
 * Copyright 2008-2010 Sun Microsystems, Inc.
 * Portions Copyright 2011-2016 ForgeRock AS.
 * Portions Copyright 2025 3A Systems, LLC
 */
package org.opends.server.core;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.ResultCode;
@@ -2292,6 +2297,55 @@
    TestCaseUtils.clearBackend("userRoot");
  }
  @Test
  public void test_issue_535() throws Exception {
      TestCaseUtils.clearBackend("userRoot", "dc=example,dc=com");
      TestCaseUtils.addEntries(
              "dn: ou=Users,dc=example,dc=com",
              "objectClass: organizationalUnit",
              "objectClass: top",
              "ou: Users",
              "",
              "dn: ou=Groups,dc=example,dc=com",
              "objectClass: organizationalUnit",
              "objectClass: top",
              "ou: Groups",
              "",
              "dn: cn=Test User,ou=Users,dc=example,dc=com",
              "objectClass: inetOrgPerson",
              "objectClass: organizationalPerson",
              "objectClass: person",
              "objectClass: top",
              "uid: testuser",
              "cn: Test User",
              "sn: User",
              "userPassword: password123",
              "",
              "dn: cn=Level1,ou=Groups,dc=example,dc=com",
              "objectClass: groupOfNames",
              "objectClass: top",
              "cn: Level1",
              "member: cn=Test User,ou=Users,dc=example,dc=com",
              "",
              "dn: cn=Level2,ou=Groups,dc=example,dc=com",
              "objectClass: groupOfNames",
              "objectClass: top",
              "cn: Level2",
              "member: cn=Level1,ou=Groups,dc=example,dc=com",
              ""
      );
      ExecutorService executor = Executors.newFixedThreadPool(100);
      for (int i = 0; i < 10000; i++) {
          executor.submit(() -> {
              final ModifyRequest modifyRequest = newModifyRequest(DN.valueOf("cn=Level2,ou=Groups,dc=example,dc=com"));
              modifyRequest.addModification(REPLACE, "member", "cn=Test User,ou=Users,dc=example,dc=com");
              ModifyOperation modifyOperation = getRootConnection().processModify(modifyRequest);
              assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
          });
      }
      executor.shutdown();
      assertTrue(executor.awaitTermination(1, TimeUnit.MINUTES));
  }
  /**
   * Adds nested group entries.
   *