Skip to content

Hydrate group listings with a single query instead of one lookup per group#51126

Open
CrystalDime wants to merge 1 commit into
keycloak:mainfrom
CrystalDime:group-listing-single-query
Open

Hydrate group listings with a single query instead of one lookup per group#51126
CrystalDime wants to merge 1 commit into
keycloak:mainfrom
CrystalDime:group-listing-single-query

Conversation

@CrystalDime

Copy link
Copy Markdown

Closes #51125

Problem

Listing groups (GET /admin/realms/{realm}/groups with and without search, and GET .../groups/{id}/children) issues one SQL statement per group whenever the group cache cannot serve it: a cold cache, and — permanently — realms larger than the bounded cache. Reproduction and end-to-end numbers are in the issue.

Cause

JpaRealmProvider#getTopLevelGroupsStream, JpaRealmProvider#searchForGroupByNameStream and the JPA GroupAdapter#getSubGroupsStream select only group ids and resolve each row through getGroupById(...); every cache miss becomes an em.find round trip — an N+1.

Fix

Select the GroupEntity rows themselves — the query keeps the same predicates, ordering, pagination and fine-grained-authz filters and changes only its SELECT list; the stream then maps each entity to its id before the existing by-id resolution. The selected entities are managed by the persistence context, so the unchanged per-id resolution through getGroupById(...) is served without any additional statement per group. The per-id hop is deliberately kept (rather than mapping entities straight to adapters as searchGroupsByAttributes does) so the group cache is still populated and all adapter and invalidation semantics stay as they are.

Testing

GroupListingQueryCountTest (tests/base, same pattern as CompositeRoleExpansionQueryCountTest): a page of 20 groups must be hydrated by exactly 1 prepared statement for the top-level listing, the name search, and the subgroups listing — previously 21 each. The test forces the cache-miss path deterministically via per-group invalidations and clearing the persistence context after a warmup pass.

AI disclosure

Per CONTRIBUTING.md: this change was developed with the assistance of an AI agent (code audit, patch drafting, test drafting, and benchmarking); it was reviewed and is understood and maintained by the submitter.

…group

getTopLevelGroupsStream, searchForGroupByNameStream and the JPA adapter's
getSubGroupsStream selected only group ids and then resolved every row
through getGroupById, issuing one extra query per group whenever the group
cache is cold. Selecting the entities instead places them in the persistence
context, so the existing by-id resolution (which keeps the group cache
populated and all adapter semantics unchanged) is served without any
additional statements.

Closes keycloak#51125

Signed-off-by: Devontay Eluett <deluett1@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Group listing issues one query per returned group after selecting only ids

1 participant