From d3ea01b76e0755e55e8fb480fd9f725a82e7552f Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Sat, 20 May 2017 02:35:34 +0200 Subject: [PATCH] use composed key in multi tenant environment instead a cache per tenant (#981) --- .../io/ebean/cache/ServerCacheFactory.java | 4 +- .../server/cache/DefaultCacheAdapter.java | 12 +-- .../server/cache/DefaultCacheHolder.java | 48 ++--------- .../server/cache/DefaultServerCache.java | 71 ++++++++++++++-- .../cache/DefaultServerCacheFactory.java | 5 +- .../cache/DefaultServerCacheManager.java | 11 +-- .../server/cache/SpiCacheManager.java | 10 +-- .../deploy/BeanDescriptorCacheHelp.java | 35 ++++---- .../server/cache/DefaultCacheHolderTest.java | 81 +++++++++++++------ .../cache/DefaultServerCacheManagerTest.java | 54 ++++++++----- .../server/cache/DefaultServerCacheTest.java | 12 +-- .../DefaultServerCache_RunEvictionTest.java | 2 +- 12 files changed, 202 insertions(+), 143 deletions(-) diff --git a/src/main/java/io/ebean/cache/ServerCacheFactory.java b/src/main/java/io/ebean/cache/ServerCacheFactory.java index 2ba1c4ed8..269cb9ae1 100644 --- a/src/main/java/io/ebean/cache/ServerCacheFactory.java +++ b/src/main/java/io/ebean/cache/ServerCacheFactory.java @@ -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); } diff --git a/src/main/java/io/ebeaninternal/server/cache/DefaultCacheAdapter.java b/src/main/java/io/ebeaninternal/server/cache/DefaultCacheAdapter.java index 9d22db12b..b5c47b054 100644 --- a/src/main/java/io/ebeaninternal/server/cache/DefaultCacheAdapter.java +++ b/src/main/java/io/ebeaninternal/server/cache/DefaultCacheAdapter.java @@ -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 diff --git a/src/main/java/io/ebeaninternal/server/cache/DefaultCacheHolder.java b/src/main/java/io/ebeaninternal/server/cache/DefaultCacheHolder.java index 3b5f91e31..996f82684 100644 --- a/src/main/java/io/ebeaninternal/server/cache/DefaultCacheHolder.java +++ b/src/main/java/io/ebeaninternal/server/cache/DefaultCacheHolder.java @@ -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 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 { - - 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 { - - final ServerCache underlying; - - private SimpleSupplier(ServerCache underlying) { - this.underlying = underlying; - } - - @Override - public ServerCache get() { - return underlying; - } - } - } diff --git a/src/main/java/io/ebeaninternal/server/cache/DefaultServerCache.java b/src/main/java/io/ebeaninternal/server/cache/DefaultServerCache.java index 47cb08a09..f9187db0c 100644 --- a/src/main/java/io/ebeaninternal/server/cache/DefaultServerCache.java +++ b/src/main/java/io/ebeaninternal/server/cache/DefaultServerCache.java @@ -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 map, ServerCacheOptions options) { - this(name, map, options.getMaxSize(), options.getMaxIdleSecs(), options.getMaxSecsToLive(), options.getTrimFrequency()); + public DefaultServerCache(String name, Map 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 map, int maxSize, int maxIdleSecs, int maxSecsToLive, int trimFrequency) { + public DefaultServerCache(String name, Map 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; diff --git a/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheFactory.java b/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheFactory.java index 76ef67f71..197052dcb 100644 --- a/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheFactory.java +++ b/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheFactory.java @@ -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); } diff --git a/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheManager.java b/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheManager.java index 09a100c69..c8b52a6f7 100644 --- a/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheManager.java +++ b/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheManager.java @@ -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 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 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 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 getBeanCache(Class beanType) { + public ServerCache getBeanCache(Class beanType) { return cacheHolder.getCache(beanType, name(beanType), ServerCacheType.BEAN); } diff --git a/src/main/java/io/ebeaninternal/server/cache/SpiCacheManager.java b/src/main/java/io/ebeaninternal/server/cache/SpiCacheManager.java index 6396f4ae3..bd80836e7 100644 --- a/src/main/java/io/ebeaninternal/server/cache/SpiCacheManager.java +++ b/src/main/java/io/ebeaninternal/server/cache/SpiCacheManager.java @@ -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 getNaturalKeyCache(Class beanType); + ServerCache getNaturalKeyCache(Class beanType); /** * Return the cache for beans of a particular type. */ - Supplier getBeanCache(Class beanType); + ServerCache getBeanCache(Class beanType); /** * Return the cache for associated many properties of a bean type. */ - Supplier getCollectionIdsCache(Class beanType, String propertyName); + ServerCache getCollectionIdsCache(Class beanType, String propertyName); /** * Return the cache for query results of a particular type of bean. */ - Supplier getQueryCache(Class beanType); + ServerCache getQueryCache(Class beanType); /** * Clear all the caches. diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java index 6731c9ade..f06436090 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java @@ -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 { private final BeanPropertyAssocOne[] propertiesOneImported; private final String naturalKeyProperty; - private final Supplier beanCache; - private final Supplier naturalKeyCache; - private final Supplier 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 { if (queryLog.isDebugEnabled()) { queryLog.debug(" CLEAR {}", cacheName); } - queryCache.get().clear(); + queryCache.clear(); } } @@ -189,7 +188,7 @@ final class BeanDescriptorCacheHelp { if (queryCache == null) { throw new IllegalStateException("No query cache enabled on " + desc + ". Need explicit @Cache(enableQueryCache=true)"); } - BeanCollection list = (BeanCollection) queryCache.get().get(id); + BeanCollection list = (BeanCollection) queryCache.get(id); if (queryLog.isDebugEnabled()) { if (list == null) { queryLog.debug(" GET {}({}) - cache miss", cacheName, id); @@ -210,12 +209,12 @@ final class BeanDescriptorCacheHelp { 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 { } 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 { * 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 { 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 { } // 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 { 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 { if (beanLog.isDebugEnabled()) { beanLog.debug(" CLEAR {}", cacheName); } - beanCache.get().clear(); + beanCache.clear(); } } @@ -415,7 +414,7 @@ final class BeanDescriptorCacheHelp { 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 { 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 { 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 { if (natLog.isDebugEnabled()) { natLog.debug(".. update {} REMOVE({}) - old key for ({})", cacheName, oldKey, id); } - naturalKeyCache.get().remove(oldKey); + naturalKeyCache.remove(oldKey); } } } diff --git a/src/test/java/io/ebeaninternal/server/cache/DefaultCacheHolderTest.java b/src/test/java/io/ebeaninternal/server/cache/DefaultCacheHolderTest.java index 42e90d062..3094c9ad5 100644 --- a/src/test/java/io/ebeaninternal/server/cache/DefaultCacheHolderTest.java +++ b/src/test/java/io/ebeaninternal/server/cache/DefaultCacheHolderTest.java @@ -1,22 +1,18 @@ package io.ebeaninternal.server.cache; -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; import org.tests.model.basic.Contact; import org.tests.model.basic.Customer; import org.junit.Test; -import java.util.function.Supplier; - import static org.assertj.core.api.Assertions.assertThat; public class DefaultCacheHolderTest { - private String tenantId; + private ThreadLocal tenantId = new ThreadLocal<>(); private final ServerCacheFactory cacheFactory = new DefaultServerCacheFactory(); private final ServerCacheOptions defaultOptions = new ServerCacheOptions(); @@ -38,29 +34,69 @@ public class DefaultCacheHolderTest { } private DefaultServerCache cache(DefaultCacheHolder holder, Class type, String name) { - Supplier cache = holder.getCache(type, name, ServerCacheType.BEAN); - return (DefaultServerCache) cache.get(); + return (DefaultServerCache) holder.getCache(type, name, ServerCacheType.BEAN); } @Test public void getCache_multiTenant() throws Exception { - DefaultCacheHolder holder = new DefaultCacheHolder(cacheFactory, defaultOptions, defaultOptions, new MyTenantProv()); + DefaultCacheHolder holder = new DefaultCacheHolder(cacheFactory, defaultOptions, defaultOptions, tenantId::get); - tenantId = "ten_1"; + tenantId.set("ten_1"); DefaultServerCache cache = cache(holder, Customer.class, "customer"); - assertThat(cache.getName()).isEqualTo("customer_ten_1_B"); + assertThat(cache.getName()).isEqualTo("customer_B"); - tenantId = "ten_2"; - DefaultServerCache cache2 = cache(holder, Customer.class, "customer"); - assertThat(cache2).isNotSameAs(cache); - assertThat(cache2.getName()).isEqualTo("customer_ten_2_B"); + cache.put("1", "value-for-tenant1"); + cache.put("2", "an other value-for-tenant1"); + + assertThat(cache.size()).isEqualTo(2); + + tenantId.set("ten_2"); - tenantId = "ten_1"; - DefaultServerCache cache3 = cache(holder, Customer.class, "customer"); - assertThat(cache3).isSameAs(cache); + cache.put("1", "value-for-tenant2"); + cache.put("2", "an other value-for-tenant2"); + + assertThat(cache.size()).isEqualTo(4); + + assertThat(cache.get("1")).isEqualTo("value-for-tenant2"); + assertThat(cache.get("2")).isEqualTo("an other value-for-tenant2"); + + + tenantId.set("ten_1"); + + assertThat(cache.get("1")).isEqualTo("value-for-tenant1"); + assertThat(cache.get("2")).isEqualTo("an other value-for-tenant1"); + + Exception exInThread[] = new Exception[1]; + Thread t = new Thread() { + @Override + public void run() { + try { + assertThat(cache.get("1")).isNull(); + tenantId.set("ten_2"); + + cache.put("1", "value-for-tenant2"); + cache.put("2", "an other value-for-tenant2"); + + tenantId.set(null); + + cache.clear(); + } catch (Exception e) { + exInThread[0] = e; + } + }; + }; + + // do some async work + t.start(); + t.join(); + if (exInThread[0] != null) { + throw exInThread[0]; + } + assertThat(cache.size()).isEqualTo(0); } + @Test public void clearAll() throws Exception { DefaultCacheHolder holder = new DefaultCacheHolder(cacheFactory, defaultOptions, defaultOptions, null); @@ -74,7 +110,7 @@ public class DefaultCacheHolderTest { @Test public void clearAll_multiTenant() throws Exception { - DefaultCacheHolder holder = new DefaultCacheHolder(cacheFactory, defaultOptions, defaultOptions, new MyTenantProv()); + DefaultCacheHolder holder = new DefaultCacheHolder(cacheFactory, defaultOptions, defaultOptions, tenantId::get); DefaultServerCache cache = cache(holder, Customer.class, "customer"); cache.put("foo", "foo"); assertThat(cache.size()).isEqualTo(1); @@ -82,12 +118,5 @@ public class DefaultCacheHolderTest { holder.clearAll(); assertThat(cache.size()).isEqualTo(0); } - - private class MyTenantProv implements CurrentTenantProvider { - - @Override - public String currentId() { - return tenantId; - } - } + } diff --git a/src/test/java/io/ebeaninternal/server/cache/DefaultServerCacheManagerTest.java b/src/test/java/io/ebeaninternal/server/cache/DefaultServerCacheManagerTest.java index 759c1c455..5abefa2a2 100644 --- a/src/test/java/io/ebeaninternal/server/cache/DefaultServerCacheManagerTest.java +++ b/src/test/java/io/ebeaninternal/server/cache/DefaultServerCacheManagerTest.java @@ -2,7 +2,6 @@ package io.ebeaninternal.server.cache; import io.ebean.cache.ServerCacheFactory; import io.ebean.cache.ServerCacheOptions; -import io.ebean.config.CurrentTenantProvider; import org.tests.model.basic.Contact; import org.tests.model.basic.Customer; import org.junit.Test; @@ -12,13 +11,13 @@ import static org.junit.Assert.assertTrue; public class DefaultServerCacheManagerTest { - private String tenantId; + private ThreadLocal tenantId = new ThreadLocal<>(); private final ServerCacheFactory cacheFactory = new DefaultServerCacheFactory(); private DefaultServerCacheManager manager = new DefaultServerCacheManager(true, null, cacheFactory, new ServerCacheOptions(), new ServerCacheOptions()); - private DefaultServerCacheManager multiTenantManager = new DefaultServerCacheManager(true, new MyTenantProv(), cacheFactory, new ServerCacheOptions(), new ServerCacheOptions()); + private DefaultServerCacheManager multiTenantManager = new DefaultServerCacheManager(true, tenantId::get, cacheFactory, new ServerCacheOptions(), new ServerCacheOptions()); @Test public void getCache_normal() throws Exception { @@ -35,31 +34,51 @@ public class DefaultServerCacheManagerTest { assertThat(cache2.getName()).isEqualTo("org.tests.model.basic.Contact_B"); - DefaultServerCache natKeyCache = (DefaultServerCache) manager.getNaturalKeyCache(Customer.class).get(); + DefaultServerCache natKeyCache = (DefaultServerCache) manager.getNaturalKeyCache(Customer.class); assertThat(natKeyCache.getName()).isEqualTo("org.tests.model.basic.Customer_N"); - DefaultServerCache queryCache = (DefaultServerCache) manager.getQueryCache(Customer.class).get(); + DefaultServerCache queryCache = (DefaultServerCache) manager.getQueryCache(Customer.class); assertThat(queryCache.getName()).isEqualTo("org.tests.model.basic.Customer_Q"); - DefaultServerCache collCache = (DefaultServerCache) manager.getCollectionIdsCache(Customer.class, "contacts").get(); + DefaultServerCache collCache = (DefaultServerCache) manager.getCollectionIdsCache(Customer.class, "contacts"); assertThat(collCache.getName()).isEqualTo("org.tests.model.basic.Customer.contacts_C"); } private DefaultServerCache cache(DefaultServerCacheManager manager, Class beanType) { - return (DefaultServerCache) manager.getBeanCache(beanType).get(); + return (DefaultServerCache) manager.getBeanCache(beanType); } @Test public void getCache_multiTenant() throws Exception { - tenantId = "ten1"; + tenantId.set("ten1"); DefaultServerCache cache = cache(multiTenantManager, Customer.class); - assertThat(cache.getName()).isEqualTo("org.tests.model.basic.Customer_ten1_B"); + assertThat(cache.getName()).isEqualTo("org.tests.model.basic.Customer_B"); + cache.put("1", "tenant1"); - tenantId = "ten2"; - DefaultServerCache cache1 = cache(multiTenantManager, Customer.class); - assertThat(cache1).isNotSameAs(cache); - assertThat(cache1.getName()).isEqualTo("org.tests.model.basic.Customer_ten2_B"); + tenantId.set("ten2"); + + assertThat(cache.get("1")).isNull(); + + tenantId.set("ten1"); + + assertThat(cache.get("1")).isNotNull(); + + } + + @Test + public void getCache_singleTenant() throws Exception { + + tenantId.set("ten1"); + DefaultServerCache cache = cache(manager, Customer.class); + assertThat(cache.getName()).isEqualTo("org.tests.model.basic.Customer_B"); + + cache.put("1", "tenant1"); + + tenantId.set("ten2"); + + assertThat(cache.get("1")).isEqualTo("tenant1"); + } @Test @@ -69,13 +88,4 @@ public class DefaultServerCacheManagerTest { assertTrue(multiTenantManager.isLocalL2Caching()); } - - private class MyTenantProv implements CurrentTenantProvider { - - @Override - public String currentId() { - return tenantId; - } - } - } diff --git a/src/test/java/io/ebeaninternal/server/cache/DefaultServerCacheTest.java b/src/test/java/io/ebeaninternal/server/cache/DefaultServerCacheTest.java index 32e1063e0..00ff68631 100644 --- a/src/test/java/io/ebeaninternal/server/cache/DefaultServerCacheTest.java +++ b/src/test/java/io/ebeaninternal/server/cache/DefaultServerCacheTest.java @@ -15,7 +15,7 @@ public class DefaultServerCacheTest { cacheOptions.setMaxSecsToLive(600); cacheOptions.setTrimFrequency(60); - return new DefaultServerCache("foo", cacheOptions); + return new DefaultServerCache("foo", null, cacheOptions); } @Test @@ -53,35 +53,35 @@ public class DefaultServerCacheTest { @Test public void trimFreq_halfIdle() throws Exception { - DefaultServerCache cache = new DefaultServerCache("", null, 10000, 10, 20, 0); + DefaultServerCache cache = new DefaultServerCache("", null, null, 10000, 10, 20, 0); assertEquals(cache.trimFrequency, 4); } @Test public void trimFreq_halfIdle_withRounding() throws Exception { - DefaultServerCache cache = new DefaultServerCache("", null, 10000, 11, 20, 0); + DefaultServerCache cache = new DefaultServerCache("", null, null, 10000, 11, 20, 0); assertEquals(cache.trimFrequency, 4); } @Test public void trimFreq_halfTTL() throws Exception { - DefaultServerCache cache = new DefaultServerCache("", null, 10000, 0, 20, 0); + DefaultServerCache cache = new DefaultServerCache("", null, null, 10000, 0, 20, 0); assertEquals(cache.trimFrequency, 9); } @Test public void trimFreq_halfTTL_withRounding() throws Exception { - DefaultServerCache cache = new DefaultServerCache("", null, 10000, 0, 21, 0); + DefaultServerCache cache = new DefaultServerCache("", null, null, 10000, 0, 21, 0); assertEquals(cache.trimFrequency, 9); } @Test public void trimFreq_explicit() throws Exception { - DefaultServerCache cache = new DefaultServerCache("", null, 10000, 10, 20, 42); + DefaultServerCache cache = new DefaultServerCache("", null, null, 10000, 10, 20, 42); assertEquals(cache.trimFrequency, 42); } diff --git a/src/test/java/io/ebeaninternal/server/cache/DefaultServerCache_RunEvictionTest.java b/src/test/java/io/ebeaninternal/server/cache/DefaultServerCache_RunEvictionTest.java index 2bb0b3774..cc2c20601 100644 --- a/src/test/java/io/ebeaninternal/server/cache/DefaultServerCache_RunEvictionTest.java +++ b/src/test/java/io/ebeaninternal/server/cache/DefaultServerCache_RunEvictionTest.java @@ -19,7 +19,7 @@ public class DefaultServerCache_RunEvictionTest { cacheOptions.setMaxSecsToLive(2); cacheOptions.setTrimFrequency(1); - return new DefaultServerCache("foo", cacheOptions); + return new DefaultServerCache("foo", null, cacheOptions); } private final DefaultServerCache cache;