From 5a300cfde18082fa35ca529a83fabcf363896b1d Mon Sep 17 00:00:00 2001 From: Valera V Harseko Date: Fri, 3 Jul 2026 22:58:57 +0300 Subject: [PATCH] Avoid global write lock on every delete in GroupManager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GroupManager.doPostDelete() runs for every delete operation and unconditionally took the manager's global write lock just to call removeSubtree() on the group instances map — which is empty or tiny on most servers, and almost never contains anything at or below the deleted entry. This serialized all concurrent delete threads on one exclusive lock. Add a read-lock fast path that checks containsSubtree() (an O(1) hierarchical containsKey) and returns before taking the write lock, mirroring the pattern already used by SubentryManager.doPostDelete(). The write-locked removal still runs whenever a group is actually registered at or below the deleted DN. An interleaved A/B under concurrent add+delete load shows no measurable throughput change on an 8-core host: the critical section was tiny and the delete path is dominated by JE record-lock waits on shared index keys. This is a structural fix — removing an unconditional global exclusive lock from the delete path — rather than a measured win. --- .../org/opends/server/core/GroupManager.java | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/GroupManager.java b/opendj-server-legacy/src/main/java/org/opends/server/core/GroupManager.java index 458a6ab00f..9493b62430 100644 --- a/opendj-server-legacy/src/main/java/org/opends/server/core/GroupManager.java +++ b/opendj-server-legacy/src/main/java/org/opends/server/core/GroupManager.java @@ -13,7 +13,7 @@ * * Copyright 2007-2010 Sun Microsystems, Inc. * Portions Copyright 2011-2016 ForgeRock AS. - * Portions Copyright 2025 3A Systems,LLC. + * Portions Copyright 2025-2026 3A Systems,LLC. */ package org.opends.server.core; @@ -678,6 +678,23 @@ private void doPostDelete(PluginOperation deleteOperation, Entry entry) return; } + // Fast path: this hook runs for every delete, but almost no deleted + // entries have a group registered at or below them. Check with the read + // lock first instead of serializing all delete threads on the exclusive + // lock (same pattern as SubentryManager.doPostDelete). + lock.readLock().lock(); + try + { + if (!groupInstances.containsSubtree(entry.getName())) + { + return; + } + } + finally + { + lock.readLock().unlock(); + } + lock.writeLock().lock(); try {