KEYCLOAK-7745 JTA error if offline sessions can't be preloaded at startup within 5 minutes
This commit is contained in:
parent
64b391cc1b
commit
8c66f520af
6 changed files with 77 additions and 20 deletions
|
@ -24,4 +24,15 @@ public class Environment {
|
||||||
|
|
||||||
public static final boolean IS_IBM_JAVA = System.getProperty("java.vendor").contains("IBM");
|
public static final boolean IS_IBM_JAVA = System.getProperty("java.vendor").contains("IBM");
|
||||||
|
|
||||||
|
public static final int DEFAULT_JBOSS_AS_STARTUP_TIMEOUT = 300;
|
||||||
|
|
||||||
|
public static int getServerStartupTimeout() {
|
||||||
|
String timeout = System.getProperty("jboss.as.management.blocking.timeout");
|
||||||
|
if (timeout != null) {
|
||||||
|
return Integer.parseInt(timeout);
|
||||||
|
} else {
|
||||||
|
return DEFAULT_JBOSS_AS_STARTUP_TIMEOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.keycloak.models.sessions.infinispan.events.AbstractAuthSessionCluster
|
||||||
import org.keycloak.models.sessions.infinispan.events.ClientRemovedSessionEvent;
|
import org.keycloak.models.sessions.infinispan.events.ClientRemovedSessionEvent;
|
||||||
import org.keycloak.models.sessions.infinispan.events.RealmRemovedSessionEvent;
|
import org.keycloak.models.sessions.infinispan.events.RealmRemovedSessionEvent;
|
||||||
import org.keycloak.models.sessions.infinispan.util.InfinispanKeyGenerator;
|
import org.keycloak.models.sessions.infinispan.util.InfinispanKeyGenerator;
|
||||||
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.models.utils.PostMigrationEvent;
|
import org.keycloak.models.utils.PostMigrationEvent;
|
||||||
import org.keycloak.provider.ProviderEvent;
|
import org.keycloak.provider.ProviderEvent;
|
||||||
import org.keycloak.provider.ProviderEventListener;
|
import org.keycloak.provider.ProviderEventListener;
|
||||||
|
@ -73,7 +74,12 @@ public class InfinispanAuthenticationSessionProviderFactory implements Authentic
|
||||||
@Override
|
@Override
|
||||||
public void onEvent(ProviderEvent event) {
|
public void onEvent(ProviderEvent event) {
|
||||||
if (event instanceof PostMigrationEvent) {
|
if (event instanceof PostMigrationEvent) {
|
||||||
registerClusterListeners(((PostMigrationEvent) event).getSession());
|
|
||||||
|
KeycloakModelUtils.runJobInTransaction(factory, (KeycloakSession session) -> {
|
||||||
|
|
||||||
|
registerClusterListeners(session);
|
||||||
|
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.infinispan.persistence.remote.RemoteStore;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
import org.keycloak.cluster.ClusterProvider;
|
import org.keycloak.cluster.ClusterProvider;
|
||||||
|
import org.keycloak.common.util.Environment;
|
||||||
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
|
@ -109,13 +110,19 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
|
||||||
@Override
|
@Override
|
||||||
public void onEvent(ProviderEvent event) {
|
public void onEvent(ProviderEvent event) {
|
||||||
if (event instanceof PostMigrationEvent) {
|
if (event instanceof PostMigrationEvent) {
|
||||||
KeycloakSession session = ((PostMigrationEvent) event).getSession();
|
|
||||||
|
|
||||||
keyGenerator = new InfinispanKeyGenerator();
|
int preloadTransactionTimeout = getTimeoutForPreloadingSessionsSeconds();
|
||||||
checkRemoteCaches(session);
|
log.debugf("Will preload sessions with transaction timeout %d seconds", preloadTransactionTimeout);
|
||||||
loadPersistentSessions(factory, getMaxErrors(), getSessionsPerSegment());
|
|
||||||
registerClusterListeners(session);
|
KeycloakModelUtils.runJobInTransactionWithTimeout(factory, (KeycloakSession session) -> {
|
||||||
loadSessionsFromRemoteCaches(session);
|
|
||||||
|
keyGenerator = new InfinispanKeyGenerator();
|
||||||
|
checkRemoteCaches(session);
|
||||||
|
loadPersistentSessions(factory, getMaxErrors(), getSessionsPerSegment());
|
||||||
|
registerClusterListeners(session);
|
||||||
|
loadSessionsFromRemoteCaches(session);
|
||||||
|
|
||||||
|
}, preloadTransactionTimeout);
|
||||||
|
|
||||||
} else if (event instanceof UserModel.UserRemovedEvent) {
|
} else if (event instanceof UserModel.UserRemovedEvent) {
|
||||||
UserModel.UserRemovedEvent userRemovedEvent = (UserModel.UserRemovedEvent) event;
|
UserModel.UserRemovedEvent userRemovedEvent = (UserModel.UserRemovedEvent) event;
|
||||||
|
@ -137,6 +144,11 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
|
||||||
return config.getInt("sessionsPerSegment", 100);
|
return config.getInt("sessionsPerSegment", 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getTimeoutForPreloadingSessionsSeconds() {
|
||||||
|
Integer timeout = config.getInt("sessionsPreloadTimeoutInSeconds", null);
|
||||||
|
return timeout != null ? timeout : Environment.getServerStartupTimeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadPersistentSessions(final KeycloakSessionFactory sessionFactory, final int maxErrors, final int sessionsPerSegment) {
|
public void loadPersistentSessions(final KeycloakSessionFactory sessionFactory, final int maxErrors, final int sessionsPerSegment) {
|
||||||
|
|
|
@ -244,6 +244,45 @@ public final class KeycloakModelUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap given runnable job into KeycloakTransaction. Set custom timeout for the JTA transaction (in case we're in the environment with JTA enabled)
|
||||||
|
*
|
||||||
|
* @param factory
|
||||||
|
* @param task
|
||||||
|
* @param timeoutInSeconds
|
||||||
|
*/
|
||||||
|
public static void runJobInTransactionWithTimeout(KeycloakSessionFactory factory, KeycloakSessionTask task, int timeoutInSeconds) {
|
||||||
|
JtaTransactionManagerLookup lookup = (JtaTransactionManagerLookup)factory.getProviderFactory(JtaTransactionManagerLookup.class);
|
||||||
|
try {
|
||||||
|
if (lookup != null) {
|
||||||
|
if (lookup.getTransactionManager() != null) {
|
||||||
|
try {
|
||||||
|
lookup.getTransactionManager().setTransactionTimeout(timeoutInSeconds);
|
||||||
|
} catch (SystemException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runJobInTransaction(factory, task);
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
if (lookup != null) {
|
||||||
|
if (lookup.getTransactionManager() != null) {
|
||||||
|
try {
|
||||||
|
// Reset to default transaction timeout
|
||||||
|
lookup.getTransactionManager().setTransactionTimeout(0);
|
||||||
|
} catch (SystemException e) {
|
||||||
|
// Shouldn't happen for Wildfly transaction manager
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static String getMasterRealmAdminApplicationClientId(String realmName) {
|
public static String getMasterRealmAdminApplicationClientId(String realmName) {
|
||||||
return realmName + "-realm";
|
return realmName + "-realm";
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
package org.keycloak.models.utils;
|
package org.keycloak.models.utils;
|
||||||
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
|
||||||
import org.keycloak.provider.ProviderEvent;
|
import org.keycloak.provider.ProviderEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,14 +25,4 @@ import org.keycloak.provider.ProviderEvent;
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
public class PostMigrationEvent implements ProviderEvent {
|
public class PostMigrationEvent implements ProviderEvent {
|
||||||
|
|
||||||
private final KeycloakSession session;
|
|
||||||
|
|
||||||
public PostMigrationEvent(KeycloakSession session) {
|
|
||||||
this.session = session;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KeycloakSession getSession() {
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,12 +162,12 @@ public class KeycloakApplication extends Application {
|
||||||
public void run(KeycloakSession session) {
|
public void run(KeycloakSession session) {
|
||||||
boolean shouldBootstrapAdmin = new ApplianceBootstrap(session).isNoMasterUser();
|
boolean shouldBootstrapAdmin = new ApplianceBootstrap(session).isNoMasterUser();
|
||||||
bootstrapAdminUser.set(shouldBootstrapAdmin);
|
bootstrapAdminUser.set(shouldBootstrapAdmin);
|
||||||
|
|
||||||
sessionFactory.publish(new PostMigrationEvent(session));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
sessionFactory.publish(new PostMigrationEvent());
|
||||||
|
|
||||||
singletons.add(new WelcomeResource(bootstrapAdminUser.get()));
|
singletons.add(new WelcomeResource(bootstrapAdminUser.get()));
|
||||||
|
|
||||||
setupScheduledTasks(sessionFactory);
|
setupScheduledTasks(sessionFactory);
|
||||||
|
|
Loading…
Reference in a new issue