Skip to content

Instantly share code, notes, and snippets.

@MikeN123
Created April 12, 2017 13:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MikeN123/192f0befc17a6efd1881ae3523f669dc to your computer and use it in GitHub Desktop.
Save MikeN123/192f0befc17a6efd1881ae3523f669dc to your computer and use it in GitHub Desktop.
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();
}
}
}
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