Avoid global write lock on every delete in GroupManager#682
Open
vharseko wants to merge 1 commit into
Open
Conversation
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.
maximthomas
approved these changes
Jul 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Found while profiling the ADD/DELETE hot paths with the series methodology
(#660, #667–#670, #672, #674, #678–#681).
GroupManagerregisters aninternal
POST_OPERATION_DELETEhook that runs for every delete andunconditionally takes the manager's global write lock just to call
removeSubtree()on the group-instances map:On most servers that map is empty or tiny, and almost no deleted entry has
a group registered at or below it — yet all concurrent delete threads
serialize on this one exclusive lock. The neighbouring
SubentryManager.doPostDelete()already avoids exactly this with aread-locked fast path.
Change
Add the same fast path to
GroupManager.doPostDelete(): checkgroupInstances.containsSubtree(dn)under the read lock (an O(1)hierarchical
containsKey—DITCacheMapmaintains ancestor nodes, thesame semantics
AuthenticatedUsersrelies on) and return before touchingthe write lock. When a group actually exists at or below the deleted DN,
the original write-locked
removeSubtree()still runs. No new races: thesnapshot check is equivalent to the write-locked removal finding nothing.
Honest performance note
An interleaved A/B (old→new→new→old→old→new,
addrate -C randommixedadd+delete, 200 persistent connections, same harness as the series) shows
no measurable throughput difference — the cleanest late pair is
1,428 vs 1,454 ops/s (+1.8%), within host noise. The removed critical
section is nanoseconds (
HashMap.removeon an empty map) while the deletepath itself is dominated by JE record-lock waits on shared index keys
(~21,700
LockManager.waitForLockevents / 822 s per 80 s JFR window inthe baseline profile). This is a structural fix — an unconditional
global exclusive lock has no place on the per-delete path, and it becomes
a real ceiling on wide machines with high deletion rates — not a claimed
local win.
Testing
mvn -P precommit -pl opendj-server-legacy verify:GroupManagerTestCase(25 — group registration/deregistration, i.e. theslow path behind the new fast path),
IsMemberOfVirtualAttributeProviderTestCase(47),
VirtualStaticGroupTestCase(14),DeleteOperationTestCase(194),AddOperationTestCase(137): 417 tests, 0 failures.Files
opendj-server-legacy/src/main/java/org/opends/server/core/GroupManager.java