Skip to content

Commit

Permalink
Allow concurrent remote cache operations
Browse files Browse the repository at this point in the history
Closes #25388

Signed-off-by: Alexander Schwartz <aschwart@redhat.com>
(cherry picked from commit 5b1b3ca)
  • Loading branch information
ahus1 committed Dec 15, 2023
1 parent 65588e3 commit eeebae6
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -222,21 +222,13 @@ public void viewChanged(ViewChangedEvent event) {
}

logger.debugf("Nodes %s removed from cluster. Removing tasks locked by this nodes", removedNodesAddresses.toString());
/*
workaround for Infinispan 12.1.7.Final to prevent a deadlock while
DefaultInfinispanConnectionProviderFactory is shutting down PersistenceManagerImpl
that acquires a writeLock and this removal that acquires a readLock.
First seen with https://issues.redhat.com/browse/ISPN-13664 and still occurs probably due to
https://issues.redhat.com/browse/ISPN-13666 in 13.0.10
Tracked in https://github.com/keycloak/keycloak/issues/9871
*/
synchronized (DefaultInfinispanConnectionProviderFactory.class) {
DefaultInfinispanConnectionProviderFactory.runWithReadLockOnCacheManager(() -> {
if (workCache.getStatus() == ComponentStatus.RUNNING) {
workCache.entrySet().removeIf(new LockEntryPredicate(removedNodesAddresses));
} else {
logger.warn("work cache is not running, ignoring event");
}
}
});
}
} catch (Throwable t) {
logger.error("caught exception in ViewChangeListener", t);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,17 +160,9 @@ void notify(String taskKey, ClusterEvent event, boolean ignoreSender, ClusterPro
// Add directly to remoteCache. Will notify remote listeners on all nodes in all DCs
Retry.executeWithBackoff((int iteration) -> {
try {
/*
workaround for Infinispan 12.1.7.Final to prevent a deadlock while
DefaultInfinispanConnectionProviderFactory is shutting down PersistenceManagerImpl
that acquires a writeLock and this put that acquires a readLock.
First seen with https://issues.redhat.com/browse/ISPN-13664 and still occurs probably due to
https://issues.redhat.com/browse/ISPN-13666 in 13.0.10
Tracked in https://github.com/keycloak/keycloak/issues/9871
*/
synchronized (DefaultInfinispanConnectionProviderFactory.class) {
workRemoteCache.put(eventKey, wrappedEvent, 120, TimeUnit.SECONDS);
}
DefaultInfinispanConnectionProviderFactory.runWithReadLockOnCacheManager(() ->
workRemoteCache.put(eventKey, wrappedEvent, 120, TimeUnit.SECONDS)
);
} catch (HotRodClientException re) {
if (logger.isDebugEnabled()) {
logger.debugf(re, "Failed sending notification to remote cache '%s'. Key: '%s', iteration '%s'. Will try to retry the task",
Expand Down Expand Up @@ -252,10 +244,9 @@ private void hotrodEventReceived(String key) {
https://issues.redhat.com/browse/ISPN-13666 in 13.0.10
Tracked in https://github.com/keycloak/keycloak/issues/9871
*/
Object value;
synchronized (DefaultInfinispanConnectionProviderFactory.class) {
value = remoteCache.get(key);
}
Object value = DefaultInfinispanConnectionProviderFactory.runWithReadLockOnCacheManager(() ->
remoteCache.get(key)
);
eventReceived(key, (Serializable) value);

});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

package org.keycloak.connections.infinispan;

import org.infinispan.Cache;
import org.infinispan.client.hotrod.ProtocolVersion;
import org.infinispan.commons.dataconversion.MediaType;
import org.infinispan.configuration.cache.CacheMode;
Expand All @@ -29,7 +28,6 @@
import org.infinispan.jboss.marshalling.core.JBossUserMarshaller;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.persistence.manager.PersistenceManager;
import org.infinispan.persistence.remote.configuration.RemoteStoreConfigurationBuilder;
import org.infinispan.transaction.LockingMode;
import org.infinispan.transaction.TransactionMode;
Expand All @@ -55,6 +53,10 @@
import java.util.Iterator;
import java.util.ServiceLoader;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Supplier;

import static org.keycloak.connections.infinispan.InfinispanUtil.configureTransport;
import static org.keycloak.connections.infinispan.InfinispanUtil.createCacheConfigurationBuilder;
Expand All @@ -68,6 +70,7 @@
*/
public class DefaultInfinispanConnectionProviderFactory implements InfinispanConnectionProviderFactory, EnvironmentDependentProviderFactory {

private static final ReadWriteLock READ_WRITE_LOCK = new ReentrantReadWriteLock();
private static final Logger logger = Logger.getLogger(DefaultInfinispanConnectionProviderFactory.class);

private Config.Scope config;
Expand All @@ -87,24 +90,54 @@ public InfinispanConnectionProvider create(KeycloakSession session) {
return new DefaultInfinispanConnectionProvider(cacheManager, remoteCacheProvider, topologyInfo);
}

/*
workaround for Infinispan 12.1.7.Final to prevent a deadlock while
DefaultInfinispanConnectionProviderFactory is shutting down PersistenceManagerImpl
that acquires a writeLock and this removal that acquires a readLock.
First seen with https://issues.redhat.com/browse/ISPN-13664 and still occurs probably due to
https://issues.redhat.com/browse/ISPN-13666 in 13.0.10
Tracked in https://github.com/keycloak/keycloak/issues/9871
*/
public static void runWithReadLockOnCacheManager(Runnable task) {
Lock lock = DefaultInfinispanConnectionProviderFactory.READ_WRITE_LOCK.readLock();
lock.lock();
try {
task.run();
} finally {
lock.unlock();
}
}

public static <T> T runWithReadLockOnCacheManager(Supplier<T> task) {
Lock lock = DefaultInfinispanConnectionProviderFactory.READ_WRITE_LOCK.readLock();
lock.lock();
try {
return task.get();
} finally {
lock.unlock();
}
}

public static void runWithWriteLockOnCacheManager(Runnable task) {
Lock lock = DefaultInfinispanConnectionProviderFactory.READ_WRITE_LOCK.writeLock();
lock.lock();
try {
task.run();
} finally {
lock.unlock();
}
}

@Override
public void close() {
/*
workaround for Infinispan 12.1.7.Final to prevent a deadlock while
DefaultInfinispanConnectionProviderFactory is shutting down PersistenceManagerImpl
that acquires a writeLock and this removal that acquires a readLock.
First seen with https://issues.redhat.com/browse/ISPN-13664 and still occurs probably due to
https://issues.redhat.com/browse/ISPN-13666 in 13.0.10
Tracked in https://github.com/keycloak/keycloak/issues/9871
*/
synchronized (DefaultInfinispanConnectionProviderFactory.class) {
runWithWriteLockOnCacheManager(() -> {
if (cacheManager != null && !containerManaged) {
cacheManager.stop();
}
if (remoteCacheProvider != null) {
remoteCacheProvider.stop();
}
}
});
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,11 @@ private <K> K generateKey(KeycloakSession session, Cache<K, ?> cache, KeyGenerat
boolean wantsLocalKey = !session.getProvider(StickySessionEncoderProvider.class).shouldAttachRoute();

if (wantsLocalKey && cache.getCacheConfiguration().clustering().cacheMode().isClustered()) {
KeyAffinityService<K> keyAffinityService = keyAffinityServices.get(cacheName);
if (keyAffinityService == null) {
keyAffinityService = createKeyAffinityService(cache, keyGenerator);
keyAffinityServices.put(cacheName, keyAffinityService);

KeyAffinityService<K> keyAffinityService = keyAffinityServices.computeIfAbsent(cacheName, s -> {
KeyAffinityService<K> k = createKeyAffinityService(cache, keyGenerator);
log.debugf("Registered key affinity service for cache '%s'", cacheName);
}

return k;
});
return keyAffinityService.getKeyForAddress(cache.getCacheManager().getAddress());
} else {
return keyGenerator.getKey();
Expand Down

0 comments on commit eeebae6

Please sign in to comment.