Skip to content

[autobackport: sssd-2-9] sdap: eliminate O(N^2) loop in sdap_add_incomplete_groups()#8477

Merged
alexey-tikhonov merged 1 commit into
SSSD:sssd-2-9from
sssd-bot:SSSD-sssd-backport-pr8454-to-sssd-2-9
Feb 24, 2026
Merged

[autobackport: sssd-2-9] sdap: eliminate O(N^2) loop in sdap_add_incomplete_groups()#8477
alexey-tikhonov merged 1 commit into
SSSD:sssd-2-9from
sssd-bot:SSSD-sssd-backport-pr8454-to-sssd-2-9

Conversation

@sssd-bot

Copy link
Copy Markdown
Contributor

This is an automatic backport of PR#8454 sdap: eliminate O(N^2) loop in sdap_add_incomplete_groups() to branch sssd-2-9, created by @alexey-tikhonov.

Please make sure this backport is correct.

Note

The commits were cherry-picked without conflicts.

You can push changes to this pull request

git remote add sssd-bot [email protected]:sssd-bot/sssd.git
git fetch sssd-bot refs/heads/SSSD-sssd-backport-pr8454-to-sssd-2-9
git checkout SSSD-sssd-backport-pr8454-to-sssd-2-9
git push sssd-bot SSSD-sssd-backport-pr8454-to-sssd-2-9 --force

Original commits
f91c7bb - sdap: eliminate O(N^2) loop in sdap_add_incomplete_groups()

Backported commits

  • 7abb017 - sdap: eliminate O(N^2) loop in sdap_add_incomplete_groups()

Original Pull Request Body

Those patch is based on a profiling of a following test case:

  • users tu1 is a member of 5k LDAP groups (RFC2307 case, no nested groups)
  • SSSD stared with an empty cache
  • time id [email protected] | tr ',' '\n' | wc -l is executed

This patch provides a x2 performance gain for described test case on a laptop. See commit message for details.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request successfully eliminates the $O(N^2)$ complexity in sdap_add_incomplete_groups() by iterating directly over the LDAP attributes instead of matching names back to attributes. This provides a significant performance boost for users with many group memberships. However, the current implementation introduces a regression in domain detection for multi-domain environments (like AD Global Catalog) and a memory leak within the processing loop. These issues should be addressed to ensure correctness and maintain efficiency.

Comment thread src/providers/ldap/sdap_async_initgroups.c
Comment on lines +95 to 104
ret = sysdb_search_group_by_name(tmp_ctx, subdomain, groupname,
NULL, &msg);
if (ret == EOK) {
continue;
} else if (ret == ENOENT) {
missing[mi] = talloc_strdup(missing, sysdb_groupnames[i]);
DEBUG(SSSDBG_TRACE_LIBS, "Group #%d [%s][%s] is not cached, " \
"need to add a fake entry\n",
i, sysdb_groupnames[i], missing[mi]);
mi++;
continue;
} else if (ret != ENOENT) {
DEBUG(SSSDBG_CRIT_FAILURE, "search for group failed [%d]: %s\n",
ret, strerror(ret));
DEBUG(SSSDBG_CRIT_FAILURE,
"search for group failed [%d]: %s\n",
ret, strerror(ret));
goto done;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There is a memory leak in the loop. The msg structure returned by sysdb_search_group_by_name is allocated on tmp_ctx but is not freed when the group is already cached (the continue path) or after it is determined that the group is missing. In a scenario with 5,000 groups, this can lead to significant unnecessary memory accumulation during the function's execution.

        ret = sysdb_search_group_by_name(tmp_ctx, subdomain, groupname,
                                         NULL, &msg);
        if (ret == EOK) {
            talloc_zfree(msg);
            continue;
        } else if (ret != ENOENT) {
            DEBUG(SSSDBG_CRIT_FAILURE,
                  "search for group failed [%d]: %s\n",
                  ret, strerror(ret));
            goto done;
        }
        talloc_zfree(msg);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is valid.
Moreover, taking into account msg value isn't actually used - code only cares about sysdb_search_group_by_name() ret code - maybe it's worth introducing a new helper function in sysdb to check object presence, but to avoid mem allocations.

Nonetheless, since this is a backport PR, I'd prefer to do this a new, separate PR, rather than diverging original and backport PRs.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe it's worth introducing a new helper function in sysdb to check object presence

I tried this - #8482 - and didn't see any meaningful perf improvements.

@sumit-bose sumit-bose left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi,

original and backport matches, ACK.

bye,
Sumit

@alexey-tikhonov
alexey-tikhonov removed the request for review from justin-stephenson February 24, 2026 16:21
@alexey-tikhonov alexey-tikhonov added no-backport This should go to target branch only. Accepted labels Feb 24, 2026
`sdap_add_incomplete_groups()` had two separate steps: first it
iterated the group name list checking each against sysdb to build
a 'missing' list, then for each missing group it scanned the entire
'ldap_groups' array calling to find matching LDAP  attributes.
This resulted in O(N^2) behavior when all groups were missing (i.e.
empty cache).

Replace this with a single O(N) loop that iterates 'ldap_groups'
directly: check sysdb, and if missing create the incomplete entry
immediately.

The 'sysdb_groupnames' parameter is removed as it is not used
anymore.

This patch also has an interesting side effect: it also makes
`sysdb_update_members()` executed in the `sdap_initgr_common_store()`
after `sdap_add_incomplete_groups()` faster. Most probably this
is because previosuly O(N^2) allocations of `groupname` (by
`sdap_get_group_primary_name()`) trashed memory, purging ldb/tdb
data from the cache.

Implementation assisted-by: Claude Code (Opus 4.6)

Reviewed-by: Justin Stephenson <[email protected]>
Reviewed-by: Sumit Bose <[email protected]>
(cherry picked from commit f91c7bb)
@sssd-bot

Copy link
Copy Markdown
Contributor Author

The pull request was accepted by @alexey-tikhonov with the following PR CI status:


🟢 CodeQL (success)
🟢 rpm-build:centos-stream-9-x86_64:upstream (success)
🟢 Build / make-distcheck (success)
🟢 ci / prepare (success)
🟢 ci / system (centos-9) (success)
🟢 Static code analysis / codeql (success)
🟢 Static code analysis / pre-commit (success)
🟢 Static code analysis / python-system-tests (success)


There are unsuccessful or unfinished checks. Make sure that the failures are not related to this pull request before merging.

@sssd-bot
sssd-bot force-pushed the SSSD-sssd-backport-pr8454-to-sssd-2-9 branch from 7abb017 to 32aa87e Compare February 24, 2026 16:22
@alexey-tikhonov
alexey-tikhonov merged commit 281ee45 into SSSD:sssd-2-9 Feb 24, 2026
9 checks passed
@alexey-tikhonov alexey-tikhonov added the Performance Performance related patches label Feb 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Accepted no-backport This should go to target branch only. Performance Performance related patches

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants