From f291f0a1baba3fa2ae4a6a856dd733e6a791318b Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Thu, 06 Aug 2026 07:57:04 +0000
Subject: [PATCH] Avoid full attribute walk for types without subordinate types (#679)

---
 opendj-server-legacy/src/main/java/org/opends/server/types/Entry.java |  111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 111 insertions(+), 0 deletions(-)

diff --git a/opendj-server-legacy/src/main/java/org/opends/server/types/Entry.java b/opendj-server-legacy/src/main/java/org/opends/server/types/Entry.java
index 5fc7a76..f826fca 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/types/Entry.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/types/Entry.java
@@ -34,6 +34,7 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
+import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
@@ -663,9 +664,87 @@
     return Collections.emptyList();
   }
 
+  /**
+   * The set of attribute types having at least one subordinate type in a
+   * schema, cached per schema instance. Subtype-inclusive attribute lookups
+   * run for every entry read (compare assertions, search filters, password
+   * policy attributes, ...): when the requested type has no subordinates,
+   * walking every attribute of the entry degenerates to an exact map lookup,
+   * so cache which types actually need the walk. The schema and the set
+   * computed for it are held in one immutable holder swapped atomically, so
+   * a reader can never pair a set with the wrong schema.
+   */
+  private static final class TypesWithSubordinates
+  {
+    private final Schema schema;
+    private final Set<AttributeType> types;
+
+    private TypesWithSubordinates(Schema schema, Set<AttributeType> types)
+    {
+      this.schema = schema;
+      this.types = types;
+    }
+  }
+
+  private static volatile TypesWithSubordinates typesWithSubordinates =
+      new TypesWithSubordinates(null, Collections.<AttributeType> emptySet());
+
+  private static boolean mayHaveSubordinateTypes(AttributeType attrType)
+  {
+    if (attrType.isPlaceHolder())
+    {
+      // isSuperTypeOf() matches place-holder types against schema-defined
+      // types by name, which an exact map lookup cannot honor: keep the walk.
+      return true;
+    }
+    Schema schema;
+    try
+    {
+      schema = DirectoryServer.getInstance().getServerContext().getSchema();
+    }
+    catch (Exception e)
+    {
+      // No server schema available (offline tools, early startup):
+      // conservatively assume subtypes may exist.
+      return true;
+    }
+    if (schema == null)
+    {
+      return true;
+    }
+    TypesWithSubordinates cached = typesWithSubordinates;
+    if (cached.schema != schema)
+    {
+      Set<AttributeType> withSubordinates = new HashSet<>();
+      for (AttributeType type : schema.getAttributeTypes())
+      {
+        for (AttributeType superior = type.getSuperiorType(); superior != null;
+            superior = superior.getSuperiorType())
+        {
+          withSubordinates.add(superior);
+        }
+      }
+      cached = new TypesWithSubordinates(schema, withSubordinates);
+      typesWithSubordinates = cached;
+    }
+    return cached.types.contains(attrType);
+  }
+
   private void addAttributeTypeOrSubTypeValue(Collection<Attribute> results, AttributeType attrType,
       Map<AttributeType, List<Attribute>> attrsMap)
   {
+    if (!mayHaveSubordinateTypes(attrType))
+    {
+      // No type in the schema extends attrType: isSuperTypeOf() can only
+      // match the type itself, so use an exact lookup instead of walking
+      // every attribute of the entry.
+      List<Attribute> attributes = attrsMap.get(attrType);
+      if (attributes != null)
+      {
+        results.addAll(attributes);
+      }
+      return;
+    }
     for (Map.Entry<AttributeType, List<Attribute>> mapEntry : attrsMap.entrySet())
     {
       if (attrType.isSuperTypeOf(mapEntry.getKey()))
@@ -678,6 +757,21 @@
   private void addAttributeTypeOrSubTypeValue(Collection<Attribute> results, AttributeDescription attrDesc,
       Map<AttributeType, List<Attribute>> attrsMap)
   {
+    if (!mayHaveSubordinateTypes(attrDesc.getAttributeType()))
+    {
+      List<Attribute> attributes = attrsMap.get(attrDesc.getAttributeType());
+      if (attributes != null)
+      {
+        for (Attribute attribute : attributes)
+        {
+          if (attrDesc.isSuperTypeOf(attribute.getAttributeDescription()))
+          {
+            results.add(attribute);
+          }
+        }
+      }
+      return;
+    }
     for (Map.Entry<AttributeType, List<Attribute>> mapEntry : attrsMap.entrySet())
     {
       if (!attrDesc.getAttributeType().isSuperTypeOf(mapEntry.getKey()))
@@ -697,6 +791,23 @@
 
   private boolean hasAttributeOrSubType(AttributeDescription attrDesc, Map<AttributeType, List<Attribute>> attrsMap)
   {
+    if (!mayHaveSubordinateTypes(attrDesc.getAttributeType()))
+    {
+      List<Attribute> attributes = attrsMap.get(attrDesc.getAttributeType());
+      if (attributes != null)
+      {
+        for (Attribute attribute : attributes)
+        {
+          // It's possible that there could be an attribute without any values,
+          // which we should treat as not having the requested attribute.
+          if (!attribute.isEmpty() && attrDesc.isSuperTypeOf(attribute.getAttributeDescription()))
+          {
+            return true;
+          }
+        }
+      }
+      return false;
+    }
     for (Map.Entry<AttributeType, List<Attribute>> mapEntry : attrsMap.entrySet())
     {
       if (!attrDesc.getAttributeType().isSuperTypeOf(mapEntry.getKey()))

--
Gitblit v1.10.0