Making DefaultLazyLoader thread safe

Closes #40531

Signed-off-by: Alexander Schwartz <aschwart@redhat.com>
This commit is contained in:
Alexander Schwartz 2025-06-16 20:09:44 +02:00 committed by Pedro Igor
parent 9aaf40cc17
commit cde47667fd

View File

@ -24,8 +24,9 @@ import java.util.function.Supplier;
import org.keycloak.models.KeycloakSession;
/**
* Default implementation of {@link DefaultLazyLoader} that only fetches data once. This implementation is not thread-safe
* and cached data is assumed to not be shared across different threads to sync state.
* Default implementation of {@link DefaultLazyLoader} that only fetches data once. This implementation is thread-safe
* as cached data is used in instanced of {@link org.keycloak.models.cache.infinispan.entities.CachedRealm} which are shared
* between multiple threads within a Keycloak instance.
*
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
*/
@ -33,7 +34,7 @@ public class DefaultLazyLoader<S, D> implements LazyLoader<S, D> {
private final Function<S, D> loader;
private final Supplier<D> fallback;
private D data;
private volatile D data;
public DefaultLazyLoader(Function<S, D> loader, Supplier<D> fallback) {
this.loader = loader;
@ -43,11 +44,15 @@ public class DefaultLazyLoader<S, D> implements LazyLoader<S, D> {
@Override
public D get(KeycloakSession session, Supplier<S> sourceSupplier) {
if (data == null) {
runWithoutAuthorization(session, () -> {
// make sure caching does not include partial results when FGAP is enabled
S source = sourceSupplier.get();
data = source == null ? fallback.get() : loader.apply(source);
});
synchronized (this) {
if (data == null) {
runWithoutAuthorization(session, () -> {
// make sure caching does not include partial results when FGAP is enabled
S source = sourceSupplier.get();
data = source == null ? fallback.get() : loader.apply(source);
});
}
}
}
return data;
}