KEYCLOAK-7745 JTA error if offline sessions can't be preloaded at startup within 5 minutes

This commit is contained in:
mposolda 2018-07-03 13:52:29 +02:00 committed by Marek Posolda
parent 64b391cc1b
commit 8c66f520af
6 changed files with 77 additions and 20 deletions

View file

@ -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;
}
}
} }

View file

@ -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);
});
} }
} }
}); });

View file

@ -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) {

View file

@ -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";
} }

View file

@ -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;
}
} }

View file

@ -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);