use composed key in multi tenant environment instead a cache per tenant (#981)

This commit is contained in:
Roland Praml authored and Rob Bygrave committed 2017-05-20 12:35:34 +12:00
1 parent 0c1a9786a3
commit d3ea01b76e
12 files changed
+202 -143

No files matched your search

+3 -1
View File
@@ -1,5 +1,7 @@
package io.ebean.cache;
import io.ebean.config.CurrentTenantProvider;
/**
* Defines method for constructing caches for beans and queries.
*/
@@ -8,6 +10,6 @@ public interface ServerCacheFactory {
/**
* Create the cache for the given type with options.
*/
ServerCache createCache(ServerCacheType type, String cacheKey, ServerCacheOptions cacheOptions);
ServerCache createCache(ServerCacheType type, String cacheKey, CurrentTenantProvider tenantProvider, ServerCacheOptions cacheOptions);
}
@@ -24,28 +24,28 @@ public class DefaultCacheAdapter implements ServerCacheManager {
@Override
public ServerCache getNaturalKeyCache(Class<?> beanType) {
return cacheManager.getNaturalKeyCache(beanType).get();
return cacheManager.getNaturalKeyCache(beanType);
}
@Override
public ServerCache getBeanCache(Class<?> beanType) {
return cacheManager.getBeanCache(beanType).get();
return cacheManager.getBeanCache(beanType);
}
@Override
public ServerCache getCollectionIdsCache(Class<?> beanType, String propertyName) {
return cacheManager.getCollectionIdsCache(beanType, propertyName).get();
return cacheManager.getCollectionIdsCache(beanType, propertyName);
}
@Override
public ServerCache getQueryCache(Class<?> beanType) {
return cacheManager.getQueryCache(beanType).get();
return cacheManager.getQueryCache(beanType);
}
@Override
public void clear(Class<?> beanType) {
cacheManager.getBeanCache(beanType).get().clear();
cacheManager.getQueryCache(beanType).get().clear();
cacheManager.getBeanCache(beanType).clear();
cacheManager.getQueryCache(beanType).clear();
}
@Override
@@ -9,7 +9,6 @@ import io.ebean.cache.ServerCacheType;
import io.ebean.config.CurrentTenantProvider;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
/**
* Manages the construction of caches.
@@ -39,12 +38,10 @@ class DefaultCacheHolder {
this.tenantProvider = tenantProvider;
}
Supplier<ServerCache> getCache(Class<?> beanType, String cacheKey, ServerCacheType type) {
ServerCache getCache(Class<?> beanType, String cacheKey, ServerCacheType type) {
return getCacheInternal(beanType, cacheKey, type);
if (tenantProvider == null) {
return new SimpleSupplier(getCacheInternal(beanType, cacheKey, type));
}
return new TenantSupplier(beanType, cacheKey, type);
}
private String key(String cacheKey, ServerCacheType type) {
@@ -62,7 +59,8 @@ class DefaultCacheHolder {
private ServerCache createCache(Class<?> beanType, ServerCacheType type, String key) {
ServerCacheOptions options = getCacheOptions(beanType, type);
return cacheFactory.createCache(type, key, options);
return cacheFactory.createCache(type, key, tenantProvider, options);
}
void clearAll() {
@@ -99,40 +97,4 @@ class DefaultCacheHolder {
return beanDefault.copy();
}
/**
* Multi-Tenant based cache supplier.
*/
private class TenantSupplier implements Supplier<ServerCache> {
final Class<?> beanType;
final String key;
final ServerCacheType type;
private TenantSupplier(Class<?> beanType, String key, ServerCacheType type) {
this.beanType = beanType;
this.key = key;
this.type = type;
}
@Override
public ServerCache get() {
String fullKey = key + "_" + tenantProvider.currentId();
return getCacheInternal(beanType, fullKey, type);
}
}
private static class SimpleSupplier implements Supplier<ServerCache> {
final ServerCache underlying;
private SimpleSupplier(ServerCache underlying) {
this.underlying = underlying;
}
@Override
public ServerCache get() {
return underlying;
}
}
}
@@ -4,6 +4,8 @@ import io.ebean.BackgroundExecutor;
import io.ebean.cache.ServerCache;
import io.ebean.cache.ServerCacheOptions;
import io.ebean.cache.ServerCacheStatistics;
import io.ebean.config.CurrentTenantProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -33,6 +35,44 @@ public class DefaultServerCache implements ServerCache {
*/
public static final CompareByLastAccess BY_LAST_ACCESS = new CompareByLastAccess();
/**
* We use a combined key, if this serverCache is per tenant.
*/
public static final class CacheKey implements Serializable {
private static final long serialVersionUID = 1L;
final Object tenantId;
final Object key;
CacheKey(Object tenantId, Object key) {
super();
this.tenantId = tenantId;
this.key = key;
}
@Override
public int hashCode() {
int result = key.hashCode();
result = 31 * result + ((tenantId == null) ? 0 : tenantId.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof CacheKey) {
CacheKey other = (CacheKey) obj;
if (other.key.equals(this.key)) {
if (other.tenantId == null && this.tenantId == null) {
return true;
} else if (this.tenantId != null) {
return this.tenantId.equals(other.tenantId);
}
}
}
return false;
}
}
/**
* The underlying map (ConcurrentHashMap or similar)
*/
@@ -51,6 +91,11 @@ public class DefaultServerCache implements ServerCache {
protected final LongAdder evictCount = new LongAdder();
protected final LongAdder evictMicros = new LongAdder();
/**
* if tenantContext is available, use it to generate cacheKey.
*/
protected final CurrentTenantProvider tenantProvider;
protected final Object monitor = new Object();
protected final String name;
@@ -66,24 +111,25 @@ public class DefaultServerCache implements ServerCache {
/**
* Construct using a ConcurrentHashMap and cache options.
*/
public DefaultServerCache(String name, ServerCacheOptions options) {
this(name, new ConcurrentHashMap<>(), options);
public DefaultServerCache(String name, CurrentTenantProvider tenantProvider, ServerCacheOptions options) {
this(name, new ConcurrentHashMap<>(), tenantProvider, options);
}
/**
* Construct passing in name, map and base eviction controls as ServerCacheOptions.
*/
public DefaultServerCache(String name, Map<Object, CacheEntry> map, ServerCacheOptions options) {
this(name, map, options.getMaxSize(), options.getMaxIdleSecs(), options.getMaxSecsToLive(), options.getTrimFrequency());
public DefaultServerCache(String name, Map<Object, CacheEntry> map, CurrentTenantProvider tenantProvider, ServerCacheOptions options) {
this(name, map, tenantProvider, options.getMaxSize(), options.getMaxIdleSecs(), options.getMaxSecsToLive(), options.getTrimFrequency());
}
/**
* Construct passing in name, map and base eviction controls.
*/
public DefaultServerCache(String name, Map<Object, CacheEntry> map, int maxSize, int maxIdleSecs, int maxSecsToLive, int trimFrequency) {
public DefaultServerCache(String name, Map<Object, CacheEntry> map, CurrentTenantProvider tenantProvider, int maxSize, int maxIdleSecs, int maxSecsToLive, int trimFrequency) {
this.name = name;
this.map = map;
this.maxSize = maxSize;
this.tenantProvider = tenantProvider;
this.maxIdleSecs = maxIdleSecs;
this.maxSecsToLive = maxSecsToLive;
this.trimFrequency = determineTrim(maxIdleSecs, maxSecsToLive, trimFrequency);
@@ -187,12 +233,21 @@ public class DefaultServerCache implements ServerCache {
map.clear();
}
private Object convertKey(Object key) {
if (tenantProvider != null) {
return new CacheKey(tenantProvider.currentId(), key);
} else {
return key;
}
}
/**
* Return a value from the cache.
*/
@Override
public Object get(Object key) {
key = convertKey(key);
CacheEntry entry = map.get(key);
if (entry == null) {
missCount.increment();
@@ -211,6 +266,9 @@ public class DefaultServerCache implements ServerCache {
*/
@Override
public Object put(Object key, Object value) {
key = convertKey(key);
CacheEntry entry = map.put(key, new CacheEntry(key, value));
if (entry == null) {
insertCount.increment();
@@ -226,6 +284,9 @@ public class DefaultServerCache implements ServerCache {
*/
@Override
public Object remove(Object key) {
key = convertKey(key);
CacheEntry entry = map.remove(key);
if (entry == null) {
return null;
@@ -5,6 +5,7 @@ import io.ebean.cache.ServerCache;
import io.ebean.cache.ServerCacheFactory;
import io.ebean.cache.ServerCacheOptions;
import io.ebean.cache.ServerCacheType;
import io.ebean.config.CurrentTenantProvider;
/**
@@ -29,9 +30,9 @@ class DefaultServerCacheFactory implements ServerCacheFactory {
}
@Override
public ServerCache createCache(ServerCacheType type, String cacheKey, ServerCacheOptions cacheOptions) {
public ServerCache createCache(ServerCacheType type, String cacheKey, CurrentTenantProvider tenantProvider, ServerCacheOptions cacheOptions) {
DefaultServerCache cache = new DefaultServerCache(cacheKey, cacheOptions);
DefaultServerCache cache = new DefaultServerCache(cacheKey, tenantProvider, cacheOptions);
if (executor != null) {
cache.periodicTrim(executor);
}
@@ -6,9 +6,6 @@ import io.ebean.cache.ServerCacheOptions;
import io.ebean.cache.ServerCacheType;
import io.ebean.config.CurrentTenantProvider;
import java.util.function.Supplier;
/**
* Manages the bean and query caches.
*/
@@ -48,12 +45,12 @@ public class DefaultServerCacheManager implements SpiCacheManager {
}
@Override
public Supplier<ServerCache> getCollectionIdsCache(Class<?> beanType, String propertyName) {
public ServerCache getCollectionIdsCache(Class<?> beanType, String propertyName) {
return cacheHolder.getCache(beanType, name(beanType) + "." + propertyName, ServerCacheType.COLLECTION_IDS);
}
@Override
public Supplier<ServerCache> getNaturalKeyCache(Class<?> beanType) {
public ServerCache getNaturalKeyCache(Class<?> beanType) {
return cacheHolder.getCache(beanType, name(beanType), ServerCacheType.NATURAL_KEY);
}
@@ -61,7 +58,7 @@ public class DefaultServerCacheManager implements SpiCacheManager {
* Return the query cache for a given bean type.
*/
@Override
public Supplier<ServerCache> getQueryCache(Class<?> beanType) {
public ServerCache getQueryCache(Class<?> beanType) {
return cacheHolder.getCache(beanType, name(beanType), ServerCacheType.QUERY);
}
@@ -69,7 +66,7 @@ public class DefaultServerCacheManager implements SpiCacheManager {
* Return the bean cache for a given bean type.
*/
@Override
public Supplier<ServerCache> getBeanCache(Class<?> beanType) {
public ServerCache getBeanCache(Class<?> beanType) {
return cacheHolder.getCache(beanType, name(beanType), ServerCacheType.BEAN);
}
@@ -2,8 +2,6 @@ package io.ebeaninternal.server.cache;
import io.ebean.cache.ServerCache;
import java.util.function.Supplier;
/**
* The cache service for server side caching of beans and query results.
*/
@@ -21,22 +19,22 @@ public interface SpiCacheManager {
/**
* Return the cache for mapping natural keys to id values.
*/
Supplier<ServerCache> getNaturalKeyCache(Class<?> beanType);
ServerCache getNaturalKeyCache(Class<?> beanType);
/**
* Return the cache for beans of a particular type.
*/
Supplier<ServerCache> getBeanCache(Class<?> beanType);
ServerCache getBeanCache(Class<?> beanType);
/**
* Return the cache for associated many properties of a bean type.
*/
Supplier<ServerCache> getCollectionIdsCache(Class<?> beanType, String propertyName);
ServerCache getCollectionIdsCache(Class<?> beanType, String propertyName);
/**
* Return the cache for query results of a particular type of bean.
*/
Supplier<ServerCache> getQueryCache(Class<?> beanType);
ServerCache getQueryCache(Class<?> beanType);
/**
* Clear all the caches.
@@ -26,7 +26,6 @@ import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
/**
* Helper for BeanDescriptor that manages the bean, query and collection caches.
@@ -60,9 +59,9 @@ final class BeanDescriptorCacheHelp<T> {
private final BeanPropertyAssocOne<?>[] propertiesOneImported;
private final String naturalKeyProperty;
private final Supplier<ServerCache> beanCache;
private final Supplier<ServerCache> naturalKeyCache;
private final Supplier<ServerCache> queryCache;
private final ServerCache beanCache;
private final ServerCache naturalKeyCache;
private final ServerCache queryCache;
/**
* Set to true if all persist changes need to notify the cache.
@@ -168,7 +167,7 @@ final class BeanDescriptorCacheHelp<T> {
if (queryLog.isDebugEnabled()) {
queryLog.debug(" CLEAR {}", cacheName);
}
queryCache.get().clear();
queryCache.clear();
}
}
@@ -189,7 +188,7 @@ final class BeanDescriptorCacheHelp<T> {
if (queryCache == null) {
throw new IllegalStateException("No query cache enabled on " + desc + ". Need explicit @Cache(enableQueryCache=true)");
}
BeanCollection<T> list = (BeanCollection<T>) queryCache.get().get(id);
BeanCollection<T> list = (BeanCollection<T>) queryCache.get(id);
if (queryLog.isDebugEnabled()) {
if (list == null) {
queryLog.debug(" GET {}({}) - cache miss", cacheName, id);
@@ -210,12 +209,12 @@ final class BeanDescriptorCacheHelp<T> {
if (queryLog.isDebugEnabled()) {
queryLog.debug(" PUT {}({})", cacheName, id);
}
queryCache.get().put(id, query);
queryCache.put(id, query);
}
void manyPropRemove(String propertyName, Object parentId) {
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName).get();
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName);
if (manyLog.isTraceEnabled()) {
manyLog.trace(" REMOVE {}({}).{}", cacheName, parentId, propertyName);
}
@@ -223,7 +222,7 @@ final class BeanDescriptorCacheHelp<T> {
}
void manyPropClear(String propertyName) {
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName).get();
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName);
if (manyLog.isDebugEnabled()) {
manyLog.debug(" CLEAR {}(*).{} ", cacheName, propertyName);
}
@@ -234,7 +233,7 @@ final class BeanDescriptorCacheHelp<T> {
* Return the CachedManyIds for a given bean many property. Returns null if not in the cache.
*/
private CachedManyIds manyPropGet(Object parentId, String propertyName) {
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName).get();
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, propertyName);
CachedManyIds entry = (CachedManyIds) collectionIdsCache.get(parentId);
if (entry == null) {
if (manyLog.isTraceEnabled()) {
@@ -285,7 +284,7 @@ final class BeanDescriptorCacheHelp<T> {
void cachePutManyIds(Object parentId, String manyName, CachedManyIds entry) {
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, manyName).get();
ServerCache collectionIdsCache = cacheManager.getCollectionIdsCache(beanType, manyName);
if (manyLog.isDebugEnabled()) {
manyLog.debug(" PUT {}({}).{} - ids:{}", cacheName, parentId, manyName, entry);
}
@@ -326,7 +325,7 @@ final class BeanDescriptorCacheHelp<T> {
}
// try to lookup the id using the natural key
Object id = naturalKeyCache.get().get(keyBindParam.getValue());
Object id = naturalKeyCache.get(keyBindParam.getValue());
if (natLog.isTraceEnabled()) {
natLog.trace(" LOOKUP {}({}) - id:{}", cacheName, keyBindParam.getValue(), id);
}
@@ -365,7 +364,7 @@ final class BeanDescriptorCacheHelp<T> {
if (beanCache == null) {
throw new IllegalStateException("No bean cache enabled for " + desc + ". Add the @Cache annotation.");
}
return beanCache.get();
return beanCache;
}
/**
@@ -376,7 +375,7 @@ final class BeanDescriptorCacheHelp<T> {
if (beanLog.isDebugEnabled()) {
beanLog.debug(" CLEAR {}", cacheName);
}
beanCache.get().clear();
beanCache.clear();
}
}
@@ -415,7 +414,7 @@ final class BeanDescriptorCacheHelp<T> {
if (natLog.isDebugEnabled()) {
natLog.debug(" PUT {}({}, {})", cacheName, naturalKey, id);
}
naturalKeyCache.get().put(naturalKey, id);
naturalKeyCache.put(naturalKey, id);
}
}
}
@@ -544,7 +543,7 @@ final class BeanDescriptorCacheHelp<T> {
if (beanLog.isDebugEnabled()) {
beanLog.debug(" REMOVE {}({})", cacheName, id);
}
beanCache.get().remove(id);
beanCache.remove(id);
}
for (BeanPropertyAssocOne<?> aPropertiesOneImported : propertiesOneImported) {
aPropertiesOneImported.cacheClear();
@@ -677,7 +676,7 @@ final class BeanDescriptorCacheHelp<T> {
void cacheNaturalKeyPut(Object id, Object newKey) {
if (newKey != null) {
naturalKeyCache.get().put(newKey, id);
naturalKeyCache.put(newKey, id);
}
}
@@ -712,7 +711,7 @@ final class BeanDescriptorCacheHelp<T> {
if (natLog.isDebugEnabled()) {
natLog.debug(".. update {} REMOVE({}) - old key for ({})", cacheName, oldKey, id);
}
naturalKeyCache.get().remove(oldKey);
naturalKeyCache.remove(oldKey);
}
}
}