Created
April 12, 2017 13:28
-
-
Save MikeN123/192f0befc17a6efd1881ae3523f669dc to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.google.common.util.concurrent.Striped; | |
import org.infinispan.commons.api.BasicCache; | |
import org.infinispan.spring.provider.SpringCache; | |
import java.util.concurrent.Callable; | |
import java.util.concurrent.locks.Lock; | |
/** | |
* @see {@link LockingSpringEmbeddedCacheManager} | |
*/ | |
public class LockingSpringCache extends SpringCache { | |
private final Striped<Lock> stripedLock; | |
public LockingSpringCache(BasicCache<Object, Object> nativeCache, Striped<Lock> stripedLock) { | |
super(nativeCache); | |
this.stripedLock = stripedLock; | |
} | |
@Override | |
public <T> T get(Object key, Callable<T> valueLoader) { | |
Lock lock = stripedLock.get(key); | |
lock.lock(); | |
try { | |
// Do a simple get / (load+put if necessary), instead of using the SpringCache implementation with | |
// a loader. This is because that implementation does not properly wrap null objects. | |
// See https://issues.jboss.org/browse/ISPN-7224 | |
ValueWrapper valueWrapper = super.get(key); | |
if (valueWrapper == null) { | |
T value; | |
try { | |
value = valueLoader.call(); | |
} catch (Exception ex) { | |
throw new ValueRetrievalException(key, valueLoader, ex); | |
} | |
super.put(key, value); | |
return value; | |
} else { | |
return (T) valueWrapper.get(); | |
} | |
} finally { | |
lock.unlock(); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.google.common.util.concurrent.Striped; | |
import org.infinispan.manager.EmbeddedCacheManager; | |
import org.infinispan.spring.provider.SpringCache; | |
import org.infinispan.spring.provider.SpringEmbeddedCacheManager; | |
import java.util.concurrent.ConcurrentHashMap; | |
import java.util.concurrent.ConcurrentMap; | |
import java.util.concurrent.locks.Lock; | |
/** | |
* Provides proper (local) synchronization for Infinispan caches. It uses a Guava {@link Striped} object per cache, | |
* which provides 64 buckets that the keys are mapped to. Any {@code @Cacheable} annotation with {@code sync = true} | |
* will then take a lock for its key, before going into Infinispan. This way, only 1 invocation of the cached method | |
* will happen at any time (whereas with the Infinispan implementation, multiple invocations may occur, but only 1 | |
* result is returned). | |
* <p> | |
* See the discussion in https://issues.jboss.org/browse/ISPN-7224 | |
*/ | |
public class LockingSpringEmbeddedCacheManager extends SpringEmbeddedCacheManager { | |
private final EmbeddedCacheManager nativeCacheManager; | |
private final ConcurrentMap<String, Striped<Lock>> stripedLocks = new ConcurrentHashMap<>(); | |
/** | |
* @param nativeCacheManager Underlying cache manager | |
*/ | |
public LockingSpringEmbeddedCacheManager(EmbeddedCacheManager nativeCacheManager) { | |
super(nativeCacheManager); | |
this.nativeCacheManager = nativeCacheManager; | |
} | |
@Override | |
public SpringCache getCache(String name) { | |
return new LockingSpringCache(this.nativeCacheManager.getCache(name), | |
stripedLocks.computeIfAbsent(name, n -> Striped.lock(64))); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment