From 502e9daaf28163df7572202f88bdc87fa1862d20 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Thu, 1 Dec 2016 00:27:32 +1300 Subject: [PATCH] #493 - ENH: Multi-tenant support - cache tidy up --- .../avaje/ebean/cache/ServerCacheOptions.java | 3 +- .../server/cache/DefaultCacheAdapter.java | 6 ++ .../server/cache/DefaultCacheHolder.java | 29 +++--- .../cache/DefaultServerCacheManager.java | 4 +- .../server/cache/DefaultCacheHolderTest.java | 93 +++++++++++++++++++ ...faultCacheHolder_getCacheOptions_Test.java | 2 +- .../cache/DefaultServerCacheManagerTest.java | 81 ++++++++++++++++ 7 files changed, 197 insertions(+), 21 deletions(-) create mode 100644 src/test/java/com/avaje/ebeaninternal/server/cache/DefaultCacheHolderTest.java create mode 100644 src/test/java/com/avaje/ebeaninternal/server/cache/DefaultServerCacheManagerTest.java diff --git a/src/main/java/com/avaje/ebean/cache/ServerCacheOptions.java b/src/main/java/com/avaje/ebean/cache/ServerCacheOptions.java index 1da6527c1..106dbf4e0 100644 --- a/src/main/java/com/avaje/ebean/cache/ServerCacheOptions.java +++ b/src/main/java/com/avaje/ebean/cache/ServerCacheOptions.java @@ -44,7 +44,7 @@ public class ServerCacheOptions { * Apply any settings from the default settings that have not already been * specifically set. */ - public void applyDefaults(ServerCacheOptions defaults) { + public ServerCacheOptions applyDefaults(ServerCacheOptions defaults) { if (maxSize == 0) { maxSize = defaults.getMaxSize(); } @@ -57,6 +57,7 @@ public class ServerCacheOptions { if (trimFrequency == 0) { trimFrequency = defaults.getTrimFrequency(); } + return this; } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/cache/DefaultCacheAdapter.java b/src/main/java/com/avaje/ebeaninternal/server/cache/DefaultCacheAdapter.java index 465bcf60b..929a3fd43 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cache/DefaultCacheAdapter.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cache/DefaultCacheAdapter.java @@ -3,6 +3,12 @@ package com.avaje.ebeaninternal.server.cache; import com.avaje.ebean.cache.ServerCache; import com.avaje.ebean.cache.ServerCacheManager; +/** + * Adapts SpiCacheManager to ServerCacheManager. + *

+ * Used to hide the Supplier part of the SpiCacheManager API from public use. + *

+ */ public class DefaultCacheAdapter implements ServerCacheManager { private final SpiCacheManager cacheManager; diff --git a/src/main/java/com/avaje/ebeaninternal/server/cache/DefaultCacheHolder.java b/src/main/java/com/avaje/ebeaninternal/server/cache/DefaultCacheHolder.java index 1b2addfcb..eeb19a929 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cache/DefaultCacheHolder.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cache/DefaultCacheHolder.java @@ -20,19 +20,22 @@ class DefaultCacheHolder { private final ServerCacheFactory cacheFactory; - private final ServerCacheOptions defaultOptions; + private final ServerCacheOptions beanDefault; + private final ServerCacheOptions queryDefault; private final CurrentTenantProvider tenantProvider; /** * Create with a cache factory and default cache options. * - * @param cacheFactory the factory for creating the cache - * @param defaultOptions the default options for tuning the cache + * @param cacheFactory the factory for creating the cache + * @param beanDefault the default options for tuning bean caches + * @param queryDefault the default options for tuning query caches */ - DefaultCacheHolder(ServerCacheFactory cacheFactory, ServerCacheOptions defaultOptions, CurrentTenantProvider tenantProvider) { + DefaultCacheHolder(ServerCacheFactory cacheFactory, ServerCacheOptions beanDefault, ServerCacheOptions queryDefault, CurrentTenantProvider tenantProvider) { this.cacheFactory = cacheFactory; - this.defaultOptions = defaultOptions; + this.beanDefault = beanDefault; + this.queryDefault = queryDefault; this.tenantProvider = tenantProvider; } @@ -72,10 +75,6 @@ class DefaultCacheHolder { * Return the cache options for a given bean type. */ ServerCacheOptions getCacheOptions(Class beanType, ServerCacheType type) { - - if (beanType == null) { - return defaultOptions.copy(); - } switch (type) { case QUERY: return getQueryOptions(beanType); @@ -87,21 +86,17 @@ class DefaultCacheHolder { private ServerCacheOptions getQueryOptions(Class cls) { CacheQueryTuning tuning = cls.getAnnotation(CacheQueryTuning.class); if (tuning != null) { - ServerCacheOptions o = new ServerCacheOptions(tuning); - o.applyDefaults(defaultOptions); - return o; + return new ServerCacheOptions(tuning).applyDefaults(queryDefault); } - return defaultOptions.copy(); + return queryDefault.copy(); } private ServerCacheOptions getBeanOptions(Class cls) { CacheBeanTuning tuning = cls.getAnnotation(CacheBeanTuning.class); if (tuning != null) { - ServerCacheOptions o = new ServerCacheOptions(tuning); - o.applyDefaults(defaultOptions); - return o; + return new ServerCacheOptions(tuning).applyDefaults(beanDefault); } - return defaultOptions.copy(); + return beanDefault.copy(); } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/cache/DefaultServerCacheManager.java b/src/main/java/com/avaje/ebeaninternal/server/cache/DefaultServerCacheManager.java index 45cc3ac35..27348eb03 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cache/DefaultServerCacheManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cache/DefaultServerCacheManager.java @@ -22,9 +22,9 @@ public class DefaultServerCacheManager implements SpiCacheManager { * Create with a cache factory and default cache options. */ public DefaultServerCacheManager(boolean localL2Caching, CurrentTenantProvider tenantProvider, ServerCacheFactory cacheFactory, - ServerCacheOptions defaultBeanOptions, ServerCacheOptions defaultQueryOptions) { + ServerCacheOptions beanDefault, ServerCacheOptions queryDefault) { this.localL2Caching = localL2Caching; - this.cacheHolder = new DefaultCacheHolder(cacheFactory, defaultBeanOptions, tenantProvider); + this.cacheHolder = new DefaultCacheHolder(cacheFactory, beanDefault, queryDefault, tenantProvider); } /** diff --git a/src/test/java/com/avaje/ebeaninternal/server/cache/DefaultCacheHolderTest.java b/src/test/java/com/avaje/ebeaninternal/server/cache/DefaultCacheHolderTest.java new file mode 100644 index 000000000..cc9a55188 --- /dev/null +++ b/src/test/java/com/avaje/ebeaninternal/server/cache/DefaultCacheHolderTest.java @@ -0,0 +1,93 @@ +package com.avaje.ebeaninternal.server.cache; + +import com.avaje.ebean.cache.ServerCache; +import com.avaje.ebean.cache.ServerCacheFactory; +import com.avaje.ebean.cache.ServerCacheOptions; +import com.avaje.ebean.cache.ServerCacheType; +import com.avaje.ebean.config.CurrentTenantProvider; +import com.avaje.tests.model.basic.Contact; +import com.avaje.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 final ServerCacheFactory cacheFactory = new DefaultServerCacheFactory(); + private final ServerCacheOptions defaultOptions = new ServerCacheOptions(); + + @Test + public void getCache_normal() throws Exception { + + DefaultCacheHolder holder = new DefaultCacheHolder(cacheFactory, defaultOptions, defaultOptions, null); + + DefaultServerCache cache = cache(holder, Customer.class, "customer"); + assertThat(cache.getName()).isEqualTo("customer_B"); + + DefaultServerCache cache1 = cache(holder, Customer.class, "customer"); + assertThat(cache1).isSameAs(cache); + + DefaultServerCache cache2 = cache(holder, Contact.class, "contact"); + assertThat(cache1).isNotSameAs(cache2); + assertThat(cache2.getName()).isEqualTo("contact_B"); + } + + private DefaultServerCache cache(DefaultCacheHolder holder, Class type, String name) { + Supplier cache = holder.getCache(type, name, ServerCacheType.BEAN); + return (DefaultServerCache) cache.get(); + } + + @Test + public void getCache_multiTenant() throws Exception { + + DefaultCacheHolder holder = new DefaultCacheHolder(cacheFactory, defaultOptions, defaultOptions, new MyTenantProv()); + + tenantId = "ten_1"; + DefaultServerCache cache = cache(holder, Customer.class, "customer"); + assertThat(cache.getName()).isEqualTo("customer_ten_1_B"); + + tenantId = "ten_2"; + DefaultServerCache cache2 = cache(holder, Customer.class, "customer"); + assertThat(cache2).isNotSameAs(cache); + assertThat(cache2.getName()).isEqualTo("customer_ten_2_B"); + + tenantId = "ten_1"; + DefaultServerCache cache3 = cache(holder, Customer.class, "customer"); + assertThat(cache3).isSameAs(cache); + } + + @Test + public void clearAll() throws Exception { + DefaultCacheHolder holder = new DefaultCacheHolder(cacheFactory, defaultOptions, defaultOptions, null); + DefaultServerCache cache = cache(holder, Customer.class, "customer"); + cache.put("foo", "foo"); + assertThat(cache.size()).isEqualTo(1); + holder.clearAll(); + + assertThat(cache.size()).isEqualTo(0); + } + + @Test + public void clearAll_multiTenant() throws Exception { + DefaultCacheHolder holder = new DefaultCacheHolder(cacheFactory, defaultOptions, defaultOptions, new MyTenantProv()); + DefaultServerCache cache = cache(holder, Customer.class, "customer"); + cache.put("foo", "foo"); + assertThat(cache.size()).isEqualTo(1); + + holder.clearAll(); + assertThat(cache.size()).isEqualTo(0); + } + + private class MyTenantProv implements CurrentTenantProvider { + + @Override + public String currentId() { + return tenantId; + } + } +} diff --git a/src/test/java/com/avaje/ebeaninternal/server/cache/DefaultCacheHolder_getCacheOptions_Test.java b/src/test/java/com/avaje/ebeaninternal/server/cache/DefaultCacheHolder_getCacheOptions_Test.java index 74526498b..f6e24611f 100644 --- a/src/test/java/com/avaje/ebeaninternal/server/cache/DefaultCacheHolder_getCacheOptions_Test.java +++ b/src/test/java/com/avaje/ebeaninternal/server/cache/DefaultCacheHolder_getCacheOptions_Test.java @@ -19,7 +19,7 @@ public class DefaultCacheHolder_getCacheOptions_Test { defaultOptions.setMaxSize(10000); defaultOptions.setMaxSecsToLive(120); - this.cacheHolder = new DefaultCacheHolder(null, defaultOptions, null); + this.cacheHolder = new DefaultCacheHolder(null, defaultOptions, defaultOptions, null); } @Test diff --git a/src/test/java/com/avaje/ebeaninternal/server/cache/DefaultServerCacheManagerTest.java b/src/test/java/com/avaje/ebeaninternal/server/cache/DefaultServerCacheManagerTest.java new file mode 100644 index 000000000..fb82f658c --- /dev/null +++ b/src/test/java/com/avaje/ebeaninternal/server/cache/DefaultServerCacheManagerTest.java @@ -0,0 +1,81 @@ +package com.avaje.ebeaninternal.server.cache; + +import com.avaje.ebean.cache.ServerCacheFactory; +import com.avaje.ebean.cache.ServerCacheOptions; +import com.avaje.ebean.config.CurrentTenantProvider; +import com.avaje.tests.model.basic.Contact; +import com.avaje.tests.model.basic.Customer; +import org.junit.Test; + +import static org.assertj.core.api.StrictAssertions.assertThat; +import static org.junit.Assert.assertTrue; + +public class DefaultServerCacheManagerTest { + + private String tenantId; + + 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()); + + @Test + public void getCache_normal() throws Exception { + + + DefaultServerCache cache = cache(manager, Customer.class); + assertThat(cache.getName()).isEqualTo("com.avaje.tests.model.basic.Customer_B"); + + DefaultServerCache cache1 = cache(manager, Customer.class); + assertThat(cache1).isSameAs(cache); + + DefaultServerCache cache2 = cache(manager, Contact.class); + assertThat(cache1).isNotSameAs(cache2); + assertThat(cache2.getName()).isEqualTo("com.avaje.tests.model.basic.Contact_B"); + + + DefaultServerCache natKeyCache = (DefaultServerCache) manager.getNaturalKeyCache(Customer.class).get(); + assertThat(natKeyCache.getName()).isEqualTo("com.avaje.tests.model.basic.Customer_N"); + + DefaultServerCache queryCache = (DefaultServerCache) manager.getQueryCache(Customer.class).get(); + assertThat(queryCache.getName()).isEqualTo("com.avaje.tests.model.basic.Customer_Q"); + + DefaultServerCache collCache = (DefaultServerCache) manager.getCollectionIdsCache(Customer.class, "contacts").get(); + assertThat(collCache.getName()).isEqualTo("com.avaje.tests.model.basic.Customer.contacts_C"); + } + + private DefaultServerCache cache(DefaultServerCacheManager manager, Class beanType) { + return (DefaultServerCache) manager.getBeanCache(beanType).get(); + } + + @Test + public void getCache_multiTenant() throws Exception { + + tenantId = "ten1"; + DefaultServerCache cache = cache(multiTenantManager, Customer.class); + assertThat(cache.getName()).isEqualTo("com.avaje.tests.model.basic.Customer_ten1_B"); + + tenantId = "ten2"; + DefaultServerCache cache1 = cache(multiTenantManager, Customer.class); + assertThat(cache1).isNotSameAs(cache); + assertThat(cache1.getName()).isEqualTo("com.avaje.tests.model.basic.Customer_ten2_B"); + } + + @Test + public void isLocalL2Caching() throws Exception { + + assertTrue(manager.isLocalL2Caching()); + assertTrue(multiTenantManager.isLocalL2Caching()); + } + + + private class MyTenantProv implements CurrentTenantProvider { + + @Override + public String currentId() { + return tenantId; + } + } + +}