KEYCLOAK-2179 Cache fixes

This commit is contained in:
Stian Thorgersen
2015-12-02 21:42:48 +01:00
parent ee6753d31b
commit a218cc3034
5 changed files with 112 additions and 7 deletions

View File

@@ -29,7 +29,7 @@ public class ClientAdapter implements ClientModel {
private void getDelegateForUpdate() {
if (updated == null) {
cacheSession.registerApplicationInvalidation(getId());
updated = updated = cacheSession.getDelegate().getClientById(getId(), cachedRealm);
updated = cacheSession.getDelegate().getClientById(getId(), cachedRealm);
if (updated == null) throw new IllegalStateException("Not found in database");
}
}

View File

@@ -1,6 +1,16 @@
package org.keycloak.models.cache.infinispan;
import org.infinispan.Cache;
import org.infinispan.notifications.Listener;
import org.infinispan.notifications.cachelistener.annotation.CacheEntriesEvicted;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryInvalidated;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
import org.infinispan.notifications.cachelistener.event.CacheEntriesEvictedEvent;
import org.infinispan.notifications.cachelistener.event.CacheEntryCreatedEvent;
import org.infinispan.notifications.cachelistener.event.CacheEntryInvalidatedEvent;
import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.models.KeycloakSession;
@@ -8,6 +18,7 @@ import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.cache.CacheRealmProvider;
import org.keycloak.models.cache.CacheRealmProviderFactory;
import org.keycloak.models.cache.RealmCache;
import org.keycloak.models.cache.entities.CachedRealm;
import org.keycloak.models.cache.entities.CachedUser;
import java.util.concurrent.ConcurrentHashMap;
@@ -18,10 +29,14 @@ import java.util.concurrent.ConcurrentHashMap;
*/
public class InfinispanCacheRealmProviderFactory implements CacheRealmProviderFactory {
protected final ConcurrentHashMap<String, String> realmLookup = new ConcurrentHashMap<String, String>();
private static final Logger log = Logger.getLogger(InfinispanCacheRealmProviderFactory.class);
protected volatile InfinispanRealmCache realmCache;
protected final ConcurrentHashMap<String, String> realmLookup = new ConcurrentHashMap<>();
private boolean isNewInfinispan;
@Override
public CacheRealmProvider create(KeycloakSession session) {
lazyInit(session);
@@ -32,13 +47,25 @@ public class InfinispanCacheRealmProviderFactory implements CacheRealmProviderFa
if (realmCache == null) {
synchronized (this) {
if (realmCache == null) {
checkIspnVersion();
Cache<String, Object> cache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.REALM_CACHE_NAME);
cache.addListener(new CacheListener());
realmCache = new InfinispanRealmCache(cache, realmLookup);
}
}
}
}
protected void checkIspnVersion() {
try {
CacheEntryCreatedEvent.class.getMethod("getValue");
isNewInfinispan = true;
} catch (NoSuchMethodException nsme) {
isNewInfinispan = false;
}
}
@Override
public void init(Config.Scope config) {
}
@@ -57,4 +84,71 @@ public class InfinispanCacheRealmProviderFactory implements CacheRealmProviderFa
return "infinispan";
}
@Listener
public class CacheListener {
@CacheEntryCreated
public void created(CacheEntryCreatedEvent<String, Object> event) {
if (!event.isPre()) {
Object object;
// Try optimized version if available
if (isNewInfinispan) {
object = event.getValue();
} else {
String id = event.getKey();
object = event.getCache().get(id);
}
if (object != null) {
if (object instanceof CachedRealm) {
CachedRealm realm = (CachedRealm) object;
realmLookup.put(realm.getName(), realm.getId());
log.tracev("Realm added realm={0}", realm.getName());
}
}
}
}
@CacheEntryRemoved
public void removed(CacheEntryRemovedEvent<String, Object> event) {
if (event.isPre()) {
Object object = event.getValue();
if (object != null) {
remove(object);
}
}
}
@CacheEntryInvalidated
public void removed(CacheEntryInvalidatedEvent<String, Object> event) {
if (event.isPre()) {
Object object = event.getValue();
if (object != null) {
remove(object);
}
}
}
@CacheEntriesEvicted
public void userEvicted(CacheEntriesEvictedEvent<String, Object> event) {
for (Object object : event.getEntries().values()) {
remove(object);
}
}
private void remove(Object object) {
if (object instanceof CachedRealm) {
CachedRealm realm = (CachedRealm) object;
realmLookup.remove(realm.getName());
for (String c : realm.getClients().values()) {
realmCache.evictCachedApplicationById(c);
}
log.tracev("Realm removed realm={0}", realm.getName());
}
}
}
}

View File

@@ -103,6 +103,12 @@ public class InfinispanRealmCache implements RealmCache {
cache.remove(id);
}
@Override
public void evictCachedApplicationById(String id) {
logger.tracev("Evicting application {0}", id);
cache.evict(id);
}
@Override
public CachedGroup getGroup(String id) {
if (!enabled) return null;

View File

@@ -26,6 +26,8 @@ public interface RealmCache {
void invalidateApplication(CachedClient app);
void evictCachedApplicationById(String id);
void addCachedClient(CachedClient app);
void invalidateCachedApplicationById(String id);

View File

@@ -7,10 +7,12 @@ import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;
import org.keycloak.models.*;
import org.keycloak.models.cache.infinispan.ClientAdapter;
import org.keycloak.models.cache.infinispan.RealmAdapter;
import org.keycloak.testsuite.rule.KeycloakRule;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -27,10 +29,12 @@ public class CacheTest {
// load up cache
KeycloakSession session = kc.startSession();
RealmModel realm = session.realms().getRealmByName("test");
assertTrue(realm instanceof RealmAdapter);
ClientModel testApp = realm.getClientByClientId("test-app");
assertTrue(testApp instanceof ClientAdapter);
assertNotNull(testApp);
appId = testApp.getId();
Assert.assertTrue(testApp.isEnabled());
assertTrue(testApp.isEnabled());
kc.stopSession(session, true);
}
{
@@ -40,16 +44,18 @@ public class CacheTest {
// KEYCLOAK-1240 - obtain the realm via session.realms().getRealms()
RealmModel realm = null;
List<RealmModel> realms = session.realms().getRealms();
for (RealmModel current : realms) {
assertTrue(current instanceof RealmAdapter);
if ("test".equals(current.getName())) {
realm = current;
break;
}
}
Assert.assertTrue(realm instanceof RealmAdapter);
realm.setAccessCodeLifespanLogin(200);
ClientModel testApp = realm.getClientByClientId("test-app");
assertNotNull(testApp);
testApp.setEnabled(false);
kc.stopSession(session, true);
@@ -62,10 +68,7 @@ public class CacheTest {
ClientModel testApp = session.realms().getClientById(appId, realm);
Assert.assertFalse(testApp.isEnabled());
kc.stopSession(session, true);
}
}
@Test