mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-11-14 14:46:25 +00:00
use composed key in multi tenant environment instead a cache per tenant (#981)
This commit is contained in:
1 parent
0c1a9786a3
commit
d3ea01b76e
12 files changed
+202
-143
No files matched your search
+3
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user