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

Skip to content

Reduce database calls for evaluation of client roles#46224

Closed
pruivo wants to merge 3 commits intokeycloak:mainfrom
pruivo:t_43726
Closed

Reduce database calls for evaluation of client roles#46224
pruivo wants to merge 3 commits intokeycloak:mainfrom
pruivo:t_43726

Conversation

@pruivo
Copy link
Member

@pruivo pruivo commented Feb 11, 2026

Closes #43726

The previous algorithm made n+1 database calls, where n is the number of dots in the client/role name.
This PR changes to a flat 2 database calls.

@ahus1 ahus1 self-assigned this Feb 11, 2026
@pruivo pruivo force-pushed the t_43726 branch 3 times, most recently from 8c7ee47 to 3aca30b Compare February 13, 2026 14:26
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.

Thank you for the pull request. Please see below for some comments.

Comment on lines +125 to +136
static {
var metrics = new CaffeineStatsCounter(Metrics.globalRegistry, "role.name.cache");
var cache = Caffeine.newBuilder()
.maximumSize(10000)
// do not keep entries forever, as the combination of realm and client roles might lead to different matches eventually
.expireAfterWrite(Duration.ofHours(1))
.recordStats(() -> metrics)
.<RoleCacheKey, RoleCacheValue>build();
metrics.registerSizeMetric(cache);
ROLE_NAME_FROM_STRING_CACHE = cache;
}

Copy link
Member

Choose a reason for hiding this comment

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

Could you please try to move some of this to the DefaultAlternativeLookupProvider?

You can get hold of the session using KeycloakSessionUtil.getKeycloakSession() ... this would make sure that there is no leaking of information between session factories.

Comment on lines +1054 to +1055
var clients = realm.searchClientByClientIdStream(clientIdToTest, null, null)
.collect(Collectors.toMap(ClientModel::getClientId, Function.identity()));
Copy link
Member

Choose a reason for hiding this comment

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

I had a look and it will do a full table scan on the clients table as it will run a LIKE operator with % at the beginning and the end.

A future enhancement might be that this method would allow to have percentage at the end, and then it could use an index range scan. Given that the results are now cached, this might be acceptable, still one would need to weight the different lookups.

Sometimes client IDs are URLs (used in OIDC federation), so one could have a lot of clients starting with the same prefix before the dot, and then one would pull a lot of data from the database.

I would like to be conservative here and would prefer the original code that loops over the dots as the response time would depend only on the number of dots in the role name, and not on the total number of clients in the database.

}
}

private record CachedRole(RoleModel cachedRole) {}
Copy link
Member

Choose a reason for hiding this comment

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

Could we go without the CachedRole record as a simplification?

Comment on lines +1085 to +1087
if (role != null) {
return new CachedRole(role);
}
Copy link
Member Author

Choose a reason for hiding this comment

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

@ahus1, this (and below) if check will introduce unnecessary invalidations.
The search for the client id will return the same client, and it will invoke client.getRole() again.

Copy link
Member

Choose a reason for hiding this comment

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

The role might have been removed from the client, therefore I thought it would be good to invalidate it.
It is Friday and getting late, maybe I'm getting tired. Happy to discuss it more on Monday.

@pruivo pruivo closed this Feb 13, 2026
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.

Slow evaluation of client roles with dots for role mapper and others

2 participants