Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Admin UI: slow response time listing second user page#45171

Merged
ahus1 merged 3 commits intokeycloak:mainfrom
pruivo:t_44860_list_users
Jan 7, 2026
Merged

Admin UI: slow response time listing second user page#45171
ahus1 merged 3 commits intokeycloak:mainfrom
pruivo:t_44860_list_users

Conversation

@pruivo
Copy link
Member

@pruivo pruivo commented Jan 6, 2026

Fixes #44860

Performance in local environment

I'm using a single Keycloak instance with PostgreSQL 17 running in Podman. I have a single realm and 1 million users. The time it takes to list the users is computed from the browser.

Baseline - main branch

About 1.6 seconds.

Screenshot From 2026-01-06 11-32-19

Only the JpaUserProvider optimization

The reasoning behind this change is that count distinct is an expensive operation for a database and can be skipped if the query does not involve any joins with other tables. I got a 5x faster response time.

Screenshot From 2026-01-06 11-35-11

This Pull Request

I went a step further and modified UserStorageManager to minimize executing the count where possible. In my deployment, the count is skipped altogether. If LDAP or any other external user storage is available, the count is still invoked, but I "optimized" by avoiding invoking count on the last storage provider from the list 🤞.

It is around 50x faster 🥳

Screenshot From 2026-01-06 11-39-35

@ahus1
Copy link
Member

ahus1 commented Jan 6, 2026

@pruivo - great work, and also great documentation to show the results.

The comment from @sguilhen reminded me that there might be one more possible optimization: If you do a count(...) (but not a count(distinct ...) you can apply first a LIMIT as shown here:

entriesInTable = getExecutor(database)
.queryForLong(new RawParameterizedSqlStatement(String.format("SELECT COUNT(*) FROM (SELECT 1 FROM %s LIMIT ?) t", getTableNameForSqlSelects(database, getTableName())), this.indexCreationThreshold + 1));
return entriesInTable;

So if one would rephrase the question from "give me the number of users" to "give me the number of users, but don't count more than X entries",

At the same time, I think the results of this PR are good enough, and I'll be happy to fully review and merge it once the build is green. At the moment, it seems to be blocked by Maven central not returning artifacts reliably.

@pruivo
Copy link
Member Author

pruivo commented Jan 6, 2026

@ahus1 unfortunately, you cannot do COUNT(*) with the JPA query builder. I've not found a way to use it, but it reduces the query time by half.

In any case, that trick is slower with my dataset (am I doing it right?). Using the PgAdmin explain feature, I get the following response times (the first one is the query that Hibernate creates)

  • ~120ms: select count(u.id) from public.user_entity as u where u.service_account_client_link is null and u.realm_id = 'realm-0'
  • ~50ms: select count(*) from (SELECT 1 from public.user_entity as u where u.service_account_client_link is null and u.realm_id = 'realm-0' limit 10)
  • ~45ms: select count(*) from public.user_entity as u where u.service_account_client_link is null and u.realm_id = 'realm-0'

@ahus1
Copy link
Member

ahus1 commented Jan 6, 2026

@pruivo - thank you for the additional input. The SQL looks good, but explain vs the real execution might still differ. It might not make much of a different for 1 M users, maybe only when we go to 30 M users.

Let's assume the current changes are good enough and proceed with PR as you originally suggested.

pruivo and others added 2 commits January 6, 2026 17:25
Signed-off-by: Pedro Ruivo <[email protected]>
Signed-off-by: Alexander Schwartz <[email protected]>
Copy link
Member

@ahus1 ahus1 left a comment

Choose a reason for hiding this comment

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

Hello Pedro thank you for the PR. I've reviewed it, and just moved one old comment and added one more comment about the "why" for the count.

This PR looks good to me and ready to be merged. If you agree and mark it as ready-to-review, I'll be happy to merge it. Thanks!

Copy link

@keycloak-github-bot keycloak-github-bot bot left a comment

Choose a reason for hiding this comment

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

Unreported flaky test detected, please review

@keycloak-github-bot
Copy link

Unreported flaky test detected

If the flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR.

org.keycloak.testsuite.webauthn.passwordless.PasskeysUsernameFormTest#webauthnLoginWithDiscoverableKey_reauthentication

Keycloak CI - WebAuthn IT

java.lang.AssertionError: Expected AppPage but was localhost (https://localhost:8543/auth/realms/test/login-actions/authenticate?session_code=0kCzL6ZpniPPRBOr_IORUErCqz8MsvDOJblmbivUo3w&execution=ac1c89c6-9ff6-48b0-bfc8-0c7a56e60764&client_id=test-app&tab_id=UsoDTfRN444&client_data=eyJydSI6Imh0dHBzOi8vbG9jYWxob3N0Ojg1NDMvYXV0aC9yZWFsbXMvbWFzdGVyL2FwcC9hdXRoIiwicnQiOiJjb2RlIn0)
	at org.junit.Assert.fail(Assert.java:89)
	at org.junit.Assert.assertTrue(Assert.java:42)
	at org.keycloak.testsuite.pages.AbstractPage.assertCurrent(AbstractPage.java:39)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
...

Report flaky test

@ahus1 ahus1 merged commit 695ee72 into keycloak:main Jan 7, 2026
82 checks passed
@pruivo pruivo deleted the t_44860_list_users branch January 7, 2026 10:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Admin UI: slow response time listing second user page

2 participants