Fail to start if work cache is not replicated

Keycloak will now fail to start if the work cache is replicated.
Listeners require the data to be local.

Closes #33702

Signed-off-by: Pedro Ruivo <pruivo@redhat.com>
Signed-off-by: Alexander Schwartz <aschwart@redhat.com>
Co-authored-by: Alexander Schwartz <aschwart@redhat.com>
This commit is contained in:
Pedro Ruivo 2024-10-09 20:40:24 +01:00 committed by GitHub
parent 0e3554934e
commit 464fc90519
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 24 additions and 1 deletions

View file

@ -3,3 +3,9 @@
Previous releases ignored any change to `conf/cache-ispn.xml` if the `--cache-config-file` option was not provided. Previous releases ignored any change to `conf/cache-ispn.xml` if the `--cache-config-file` option was not provided.
Starting from this release, when `--cache-config-file` is not set, the default Infinispan XML configuration file is `conf/cache-ispn.xml` as this is both the expected behavior and the implied behavior given the docs of the current and previous releases. Starting from this release, when `--cache-config-file` is not set, the default Infinispan XML configuration file is `conf/cache-ispn.xml` as this is both the expected behavior and the implied behavior given the docs of the current and previous releases.
= Embedded Infinispan: `work` cache must be replicated
The embedded `work` cache needs to be configured as a `replicated-cache` for cache invalidation to work as expected.
Starting from this release, {project_name} check this at startup and will fail to start if it is not configured as such.

View file

@ -371,7 +371,7 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon
workBuilder.simpleCache(false); workBuilder.simpleCache(false);
workBuilder.clustering().cacheMode(async ? CacheMode.REPL_ASYNC : CacheMode.REPL_SYNC); workBuilder.clustering().cacheMode(async ? CacheMode.REPL_ASYNC : CacheMode.REPL_SYNC);
} }
defineClusteredCache(cacheManager, WORK_CACHE_NAME, builder.build()); defineClusteredCache(cacheManager, WORK_CACHE_NAME, workBuilder.build());
} }
return cacheManager; return cacheManager;

View file

@ -87,6 +87,7 @@ import static org.keycloak.connections.infinispan.InfinispanConnectionProvider.L
import static org.keycloak.connections.infinispan.InfinispanConnectionProvider.OFFLINE_CLIENT_SESSION_CACHE_NAME; import static org.keycloak.connections.infinispan.InfinispanConnectionProvider.OFFLINE_CLIENT_SESSION_CACHE_NAME;
import static org.keycloak.connections.infinispan.InfinispanConnectionProvider.OFFLINE_USER_SESSION_CACHE_NAME; import static org.keycloak.connections.infinispan.InfinispanConnectionProvider.OFFLINE_USER_SESSION_CACHE_NAME;
import static org.keycloak.connections.infinispan.InfinispanConnectionProvider.USER_SESSION_CACHE_NAME; import static org.keycloak.connections.infinispan.InfinispanConnectionProvider.USER_SESSION_CACHE_NAME;
import static org.keycloak.connections.infinispan.InfinispanConnectionProvider.WORK_CACHE_NAME;
import static org.keycloak.connections.infinispan.InfinispanConnectionProvider.skipSessionsCacheIfRequired; import static org.keycloak.connections.infinispan.InfinispanConnectionProvider.skipSessionsCacheIfRequired;
import static org.wildfly.security.sasl.util.SaslMechanismInformation.Names.SCRAM_SHA_512; import static org.wildfly.security.sasl.util.SaslMechanismInformation.Names.SCRAM_SHA_512;
@ -314,6 +315,7 @@ public class CacheManagerFactory {
} }
configureCacheMaxCount(builder, CachingOptions.CLUSTERED_MAX_COUNT_CACHES); configureCacheMaxCount(builder, CachingOptions.CLUSTERED_MAX_COUNT_CACHES);
configureSessionsCaches(builder); configureSessionsCaches(builder);
validateWorkCacheConfiguration(builder);
} }
configureCacheMaxCount(builder, CachingOptions.LOCAL_MAX_COUNT_CACHES); configureCacheMaxCount(builder, CachingOptions.LOCAL_MAX_COUNT_CACHES);
checkForRemoteStores(builder); checkForRemoteStores(builder);
@ -514,6 +516,21 @@ public class CacheManagerFactory {
} }
} }
private static void validateWorkCacheConfiguration(ConfigurationBuilderHolder builder) {
var cacheBuilder = builder.getNamedConfigurationBuilders().get(WORK_CACHE_NAME);
if (cacheBuilder == null) {
throw new RuntimeException("Unable to start Keycloak. '%s' cache is missing".formatted(WORK_CACHE_NAME));
}
if (builder.getGlobalConfigurationBuilder().cacheContainer().transport().getTransport() == null) {
// non-clustered, Keycloak started in dev mode?
return;
}
var cacheMode = cacheBuilder.clustering().cacheMode();
if (!cacheMode.isReplicated()) {
throw new RuntimeException("Unable to start Keycloak. '%s' cache must be replicated but is %s".formatted(WORK_CACHE_NAME, cacheMode.friendlyCacheModeString().toLowerCase()));
}
}
private static String requiredStringProperty(String propertyName) { private static String requiredStringProperty(String propertyName) {
return Configuration.getOptionalKcValue(propertyName).orElseThrow(() -> new RuntimeException("Property " + propertyName + " required but not specified")); return Configuration.getOptionalKcValue(propertyName).orElseThrow(() -> new RuntimeException("Property " + propertyName + " required but not specified"));
} }