Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixes for ConcurrentMultiEhcache

  • Loading branch information...
commit bd260d3200c0340dac574a2b7f1f7c3f14fce818 1 parent b09a8a0
@timf authored
View
29 src/main/java/org/dasein/util/ConcurrentMultiEhcache.java
@@ -33,7 +33,6 @@
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
-import net.sf.ehcache.Element;
import net.sf.ehcache.Statistics;
import net.sf.ehcache.management.ManagementService;
@@ -43,7 +42,8 @@
* <p>
* A concurrent multi-cache caches objects along multiple
* unique keys. You provide the list of unique keys and then this object will
- * manage the concurrent access of multiple threads into the cache.
+ * manage the concurrent access of multiple threads into the cache. Loading
+ * is locked per PK key-value and not across the entire cache.
* </p>
* <p>
* This class is backed up by multiple {@link net.sf.ehcache.Ehcache} instances (one for
@@ -261,6 +261,7 @@ public T find(String key, Object val, CacheLoader<T> loader, Object ... args) {
if (item != null) {
// Cache hit.
if( item instanceof CachedItem ) {
+ // respect this contract for now, it may interfere with ehcache settings
if( ((CachedItem)item).isValidForCache() ) {
return item;
} else {
@@ -277,12 +278,16 @@ public T find(String key, Object val, CacheLoader<T> loader, Object ... args) {
}
// Cache miss.
-
+ //
// First we get a lock per-ID-value of the object being loaded.
+ //
// This is stampede protection: many simultaneous cache-misses on the
// same value can otherwise cause many loaders to go off to get the
// item from the definitive source. That's unecessary work and extra
// time spent waiting by each of the threads involved in the cache-miss.
+ //
+ // Locking per PK key-value here instead of per-object-type is an essential
+ // difference vs. ConcurrentMultiCache (it's not just because of Ehcache).
final Lock lock = wrapper.getLoadingLock(val);
// Could add a timeout here ('tryLock' method), even a configurable one.
@@ -292,7 +297,7 @@ public T find(String key, Object val, CacheLoader<T> loader, Object ... args) {
// Did another thread just load this in the meantime?
item = wrapper.getItem(val);
if (item != null) {
- return null;
+ return item;
}
item = loader.load(args);
@@ -300,7 +305,7 @@ public T find(String key, Object val, CacheLoader<T> loader, Object ... args) {
return null;
}
- wrapper.getCache().put(new Element(val, item));
+ wrapper.put(val, item);
return item;
} finally {
lock.unlock();
@@ -379,6 +384,16 @@ private Object getValue(String key, T item) {
}
}
+ @Override
+ public T cache(T item) {
+ HashMap<String,Object> keys = getKeys(item);
+ for(String key : order) {
+ EhcacheWrapper<T> wrapper = caches.get(key);
+ wrapper.put(keys.get(key), item);
+ }
+ return item;
+ }
+
/**
* Releases the specified item from the cache. If it is still in the persistent
* store, it will be retrieved back into the cache on next query. Otherwise,
@@ -389,7 +404,7 @@ public void release(T item) {
HashMap<String,Object> keys = getKeys(item);
for(String key : order) {
EhcacheWrapper<T> wrapper = caches.get(key);
- wrapper.getCache().remove(keys.get(key));
+ wrapper.remove(keys.get(key));
}
}
@@ -402,7 +417,7 @@ public void release(T item) {
*/
public void releaseAll() {
for (EhcacheWrapper<T> wrapper: caches.values()) {
- wrapper.getCache().removeAll();
+ wrapper.removeAll();
}
}
View
50 src/main/java/org/dasein/util/EhcacheWrapper.java
@@ -17,11 +17,13 @@
package org.dasein.util;
+import net.sf.ehcache.CacheException;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import java.math.BigDecimal;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -47,6 +49,10 @@ public EhcacheWrapper(Ehcache cache) {
loadingLocks = new ConcurrentHashMap<Object, ReentrantLock>();
}
+ /**
+ * @param key unique ID
+ * @return item or null if not present
+ */
@Nullable
public <T> T getItem(Object key) {
final Element e = cache.get(key);
@@ -56,9 +62,47 @@ public EhcacheWrapper(Ehcache cache) {
return null;
}
- @Nonnull
- public Ehcache getCache() {
- return cache;
+ /**
+ * @param key key
+ * @param item An object. If Serializable it can fully participate in replication and the DiskStore.
+ * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
+ throws IllegalArgumentException if key/item are null
+ * @throws CacheException other issue
+ */
+ public void put(Object key, T item) throws IllegalArgumentException, IllegalStateException, CacheException {
+ if (key == null) {
+ throw new IllegalArgumentException("key is missing");
+ }
+ if (item == null) {
+ throw new IllegalArgumentException("item is missing");
+ }
+ if( key instanceof BigDecimal) {
+ key = Long.valueOf(((Number) key).longValue());
+ }
+ cache.put(new Element(key, item));
+ }
+
+ /**
+ * @param key key
+ * @return true if the element was removed, false if it was not found in the cache or if key is null
+ * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
+ */
+ public boolean remove(Object key) throws IllegalStateException {
+ if (key == null) {
+ return false;
+ }
+ if( key instanceof BigDecimal) {
+ key = Long.valueOf(((Number) key).longValue());
+ }
+ return cache.remove(key);
+ }
+
+ /**
+ * Removes all cached items.
+ * @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
+ */
+ public void removeAll() throws IllegalStateException {
+ cache.removeAll();
}
/**
Please sign in to comment.
Something went wrong with that request. Please try again.