From 86436ac3acbcaf68f63556968b4814ed83e5b1c4 Mon Sep 17 00:00:00 2001 From: rob bygrave Date: Wed, 24 Apr 2019 15:41:07 +1200 Subject: [PATCH] #1679 - ENH: Support enabling/disabling L2 cache by "region" at runtime --- pom.xml | 2 +- .../java/io/ebean/config/ServerConfig.java | 16 +++++ .../cache/DefaultServerCacheManager.java | 32 +++++---- .../server/core/CacheOptions.java | 18 ++++- .../server/core/DefaultServer.java | 66 +------------------ .../deploy/meta/DeployBeanDescriptor.java | 4 +- .../server/deploy/parse/AnnotationClass.java | 5 +- src/test/java/io/ebean/BaseTestCase.java | 2 +- .../ebean/cache/ServerCacheManagerTest.java | 48 ++++++++++++++ .../io/ebean/config/ServerConfigTest.java | 5 ++ 10 files changed, 111 insertions(+), 87 deletions(-) create mode 100644 src/test/java/io/ebean/cache/ServerCacheManagerTest.java diff --git a/pom.xml b/pom.xml index a3ed6d149..f6eb3b058 100644 --- a/pom.xml +++ b/pom.xml @@ -117,7 +117,7 @@ io.ebean ebean-annotation - 4.8 + 4.9 diff --git a/src/main/java/io/ebean/config/ServerConfig.java b/src/main/java/io/ebean/config/ServerConfig.java index 9efda763d..9b394a03e 100644 --- a/src/main/java/io/ebean/config/ServerConfig.java +++ b/src/main/java/io/ebean/config/ServerConfig.java @@ -479,6 +479,7 @@ public class ServerConfig { */ private boolean disableL2Cache; + private String enabledL2Regions; /** * Should the javax.validation.constraints.NotNull enforce a notNull column in DB. @@ -2914,6 +2915,7 @@ public class ServerConfig { collectQueryPlans = p.getBoolean("collectQueryPlans", collectQueryPlans); docStoreOnly = p.getBoolean("docStoreOnly", docStoreOnly); disableL2Cache = p.getBoolean("disableL2Cache", disableL2Cache); + enabledL2Regions = p.get("enabledL2Regions", enabledL2Regions); notifyL2CacheInForeground = p.getBoolean("notifyL2CacheInForeground", notifyL2CacheInForeground); explicitTransactionBeginMode = p.getBoolean("explicitTransactionBeginMode", explicitTransactionBeginMode); autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode); @@ -3136,6 +3138,20 @@ public class ServerConfig { this.expressionNativeIlike = expressionNativeIlike; } + /** + * Return the enabled L2 cache regions. + */ + public String getEnabledL2Regions() { + return enabledL2Regions; + } + + /** + * Set the enabled L2 cache regions (comma delimited). + */ + public void setEnabledL2Regions(String enabledL2Regions) { + this.enabledL2Regions = enabledL2Regions; + } + /** * Return true if L2 cache is disabled. */ diff --git a/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheManager.java b/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheManager.java index bf4fadac6..773846b79 100644 --- a/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheManager.java +++ b/src/main/java/io/ebeaninternal/server/cache/DefaultServerCacheManager.java @@ -61,25 +61,29 @@ public class DefaultServerCacheManager implements SpiCacheManager { @Override public void setEnabledRegions(String regions) { + if (regions != null) { + List enabledRegionNames = Arrays.asList(regions.split(",")); - List enabledRegions = Arrays.asList(regions.split(",")); - List disabledRegions = new ArrayList<>(); + List disabled = new ArrayList<>(); + List enabled = new ArrayList<>(); - for (SpiCacheRegion region : regionMap.values()) { - if (enabledRegions.contains(region.getName())) { - if (!region.isEnabled()) { - region.setEnabled(true); - log.debug("Cache region[{}] enabled", region.getName()); - } - } else { - disabledRegions.add(region.getName()); - if (region.isEnabled()) { - region.setEnabled(false); - log.debug("Cache region[{}] disabled", region.getName()); + for (SpiCacheRegion region : regionMap.values()) { + if (enabledRegionNames.contains(region.getName())) { + enabled.add(region.getName()); + if (!region.isEnabled()) { + region.setEnabled(true); + log.debug("Cache region[{}] enabled", region.getName()); + } + } else { + disabled.add(region.getName()); + if (region.isEnabled()) { + region.setEnabled(false); + log.debug("Cache region[{}] disabled", region.getName()); + } } } + log.info("Cache regions enabled:{} disabled:{}", enabled, disabled); } - log.info("Cache regions enabled:{} disabled:{}", enabledRegions, disabledRegions); } @Override diff --git a/src/main/java/io/ebeaninternal/server/core/CacheOptions.java b/src/main/java/io/ebeaninternal/server/core/CacheOptions.java index 5fd2fcf54..b2ef8073c 100644 --- a/src/main/java/io/ebeaninternal/server/core/CacheOptions.java +++ b/src/main/java/io/ebeaninternal/server/core/CacheOptions.java @@ -12,7 +12,9 @@ public class CacheOptions { */ public static final CacheOptions NO_CACHING = new CacheOptions(); - public static final CacheOptions INVALIDATE_QUERY_CACHE = new CacheOptions(true); + private static final String R0 = "r0"; + + private static final CacheOptions INVALIDATE_QUERY_CACHE_R0 = new CacheOptions(true); private final boolean invalidateQueryCache; private final boolean enableBeanCache; @@ -21,6 +23,14 @@ public class CacheOptions { private final String[] naturalKey; private final String region; + public static CacheOptions invalidateQueryCache(String region) { + if (R0.equals(region)) { + return INVALIDATE_QUERY_CACHE_R0; + } else { + return new CacheOptions(true, region); + } + } + /** * Construct for no caching. */ @@ -37,12 +47,16 @@ public class CacheOptions { * Construct for invalidateQueryCache. */ private CacheOptions(boolean invalidateQueryCache) { + this(invalidateQueryCache, R0); + } + + private CacheOptions(boolean invalidateQueryCache, String region) { this.invalidateQueryCache = invalidateQueryCache; this.enableBeanCache = false; this.enableQueryCache = false; this.readOnly = false; this.naturalKey = null; - this.region = null; + this.region = region; } /** diff --git a/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/src/main/java/io/ebeaninternal/server/core/DefaultServer.java index 4606d7fde..26b73d843 100644 --- a/src/main/java/io/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/io/ebeaninternal/server/core/DefaultServer.java @@ -446,6 +446,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { if (encryptKeyManager != null) { encryptKeyManager.initialise(); } + serverCacheManager.setEnabledRegions(serverConfig.getEnabledL2Regions()); } /** @@ -484,7 +485,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { */ private void shutdownInternal(boolean shutdownDataSource, boolean deregisterDriver) { - logger.debug("Shutting down EbeanServer {}", serverName); + logger.debug("Shutting down instance:{}", serverName); if (shutdown) { // already shutdown return; @@ -790,23 +791,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { transactionManager.exitScopedTransaction(returnOrThrowable, opCode); } - /** - * Returns the current transaction (or null) from the scope. - */ @Override public SpiTransaction currentServerTransaction() { return transactionManager.getActive(); } - /** - * Start a transaction with 'REQUIRED' semantics. - *

- * If a transaction already exists that transaction will be used. - *

- *

- * Note that the transaction is stored in a ThreadLocal variable. - *

- */ @Override public Transaction beginTransaction() { return beginTransaction(TxScope.required()); @@ -817,12 +806,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { return transactionManager.beginScopedTransaction(txScope); } - /** - * Start a transaction with a specific Isolation Level. - *

- * Note that the transaction is stored in a ThreadLocal variable. - *

- */ @Override public Transaction beginTransaction(TxIsolation isolation) { // start an explicit transaction @@ -837,10 +820,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { return t; } - /** - * Return the current transaction or null if there is not one currently in - * scope. - */ @Override public Transaction currentTransaction() { return transactionManager.getActive(); @@ -851,50 +830,16 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { currentTransaction().flush(); } - /** - * Commit the current transaction. - */ @Override public void commitTransaction() { currentTransaction().commit(); } - /** - * Rollback the current transaction. - */ @Override public void rollbackTransaction() { currentTransaction().rollback(); } - /** - * If the current transaction has already been committed do nothing otherwise - * rollback the transaction. - *

- * Useful to put in a finally block to ensure the transaction is ended, rather - * than a rollbackTransaction() in each catch block. - *

- *

- * Code example:
- *

- *

-   * <code>
-   * Ebean.startTransaction();
-   * try {
-   * 	// do some fetching and or persisting
-   *
-   * 	// commit at the end
-   * 	Ebean.commitTransaction();
-   *
-   * } finally {
-   * 	// if commit didn't occur then rollback the transaction
-   * 	Ebean.endTransaction();
-   * }
-   * </code>
-   * 
- *

- *

- */ @Override public void endTransaction() { Transaction transaction = transactionManager.getInScope(); @@ -903,13 +848,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { } } - /** - * return the next unique identity value. - *

- * Uses the BeanDescriptor deployment information to determine the sequence to - * use. - *

- */ @Override public Object nextId(Class beanType) { BeanDescriptor desc = getBeanDescriptor(beanType); diff --git a/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java b/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java index b43b0d6fc..bf51ceea6 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java +++ b/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java @@ -473,8 +473,8 @@ public class DeployBeanDescriptor { /** * Set that this type invalidates query caches. */ - public void setInvalidateQueryCache() { - this.cacheOptions = CacheOptions.INVALIDATE_QUERY_CACHE; + public void setInvalidateQueryCache(String region) { + this.cacheOptions = CacheOptions.invalidateQueryCache(region); } /** diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationClass.java b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationClass.java index 657c94797..a04b8db5e 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationClass.java +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationClass.java @@ -202,13 +202,12 @@ public class AnnotationClass extends AnnotationParser { } else { InvalidateQueryCache invalidateQueryCache = AnnotationUtil.findAnnotationRecursive(cls, InvalidateQueryCache.class); if (invalidateQueryCache != null) { - descriptor.setInvalidateQueryCache(); + descriptor.setInvalidateQueryCache(invalidateQueryCache.region()); } } } - Set namedQueries = AnnotationUtil.findAnnotationsRecursive(cls, NamedQuery.class); - for (NamedQuery namedQuery : namedQueries) { + for (NamedQuery namedQuery : AnnotationUtil.findAnnotationsRecursive(cls, NamedQuery.class)) { descriptor.addNamedQuery(namedQuery.name(), namedQuery.query()); } } diff --git a/src/test/java/io/ebean/BaseTestCase.java b/src/test/java/io/ebean/BaseTestCase.java index 17dc0efc1..ee10521e9 100644 --- a/src/test/java/io/ebean/BaseTestCase.java +++ b/src/test/java/io/ebean/BaseTestCase.java @@ -48,7 +48,7 @@ public abstract class BaseTestCase { DB_CLOCK_DELTA = 100; } logger.debug("... preStart"); - if (!AgentLoader.loadAgentFromClasspath("ebean-agent", "debug=1")) { + if (!AgentLoader.loadAgentByMainClass("io.ebean.enhance.Transformer", "debug=1")) { logger.info("avaje-ebeanorm-agent not found in classpath - not dynamically loaded"); } try { diff --git a/src/test/java/io/ebean/cache/ServerCacheManagerTest.java b/src/test/java/io/ebean/cache/ServerCacheManagerTest.java new file mode 100644 index 000000000..337a80116 --- /dev/null +++ b/src/test/java/io/ebean/cache/ServerCacheManagerTest.java @@ -0,0 +1,48 @@ +package io.ebean.cache; + +import io.ebean.DB; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class ServerCacheManagerTest { + + @Test + public void test() { + + ServerCacheManager cacheManager = DB.getDefault().getServerCacheManager(); + + for (ServerCacheRegion region : cacheManager.allRegions()) { + assertTrue(region.isEnabled()); + } + + cacheManager.setEnabledRegions("r0,doesNotExist"); + assertThat(cacheManager.getRegion("email").isEnabled()).isFalse(); + assertThat(cacheManager.getRegion("r0").isEnabled()).isTrue(); + + cacheManager.setEnabledRegions("r0"); + assertThat(cacheManager.getRegion("email").isEnabled()).isFalse(); + assertThat(cacheManager.getRegion("r0").isEnabled()).isTrue(); + + cacheManager.setEnabledRegions(null); + assertThat(cacheManager.getRegion("email").isEnabled()).isFalse(); + assertThat(cacheManager.getRegion("r0").isEnabled()).isTrue(); + + cacheManager.setEnabledRegions("email"); + assertThat(cacheManager.getRegion("email").isEnabled()).isTrue(); + assertThat(cacheManager.getRegion("r0").isEnabled()).isFalse(); + + cacheManager.setAllRegionsEnabled(false); + for (ServerCacheRegion region : cacheManager.allRegions()) { + assertFalse(region.isEnabled()); + } + + cacheManager.setAllRegionsEnabled(true); + for (ServerCacheRegion region : cacheManager.allRegions()) { + assertTrue(region.isEnabled()); + } + + } +} diff --git a/src/test/java/io/ebean/config/ServerConfigTest.java b/src/test/java/io/ebean/config/ServerConfigTest.java index e5487b2b9..52965604d 100644 --- a/src/test/java/io/ebean/config/ServerConfigTest.java +++ b/src/test/java/io/ebean/config/ServerConfigTest.java @@ -68,6 +68,7 @@ public class ServerConfigTest { props.setProperty("mappingLocations", "classpath:/foo;bar"); props.setProperty("namingConvention", "io.ebean.config.MatchingNamingConvention"); props.setProperty("idGeneratorAutomatic", "true"); + props.setProperty("enabledL2Regions", "r0,users,orgs"); serverConfig.loadFromProperties(props); @@ -85,6 +86,7 @@ public class ServerConfigTest { assertEquals(PersistBatch.ALL, serverConfig.getPersistBatchOnCascade()); assertEquals(PlatformConfig.DbUuid.BINARY, serverConfig.getPlatformConfig().getDbUuid()); assertEquals(JsonConfig.DateTime.ISO8601, serverConfig.getJsonDateTime()); + assertEquals("r0,users,orgs", serverConfig.getEnabledL2Regions()); assertEquals(42, serverConfig.getJdbcFetchSizeFindEach()); assertEquals(43, serverConfig.getJdbcFetchSizeFindList()); @@ -111,6 +113,9 @@ public class ServerConfigTest { assertEquals(PersistBatch.ALL, serverConfig.getPersistBatch()); assertEquals(PersistBatch.ALL, serverConfig.getPersistBatchOnCascade()); + + serverConfig.setEnabledL2Regions("r0,orgs"); + assertEquals("r0,orgs", serverConfig.getEnabledL2Regions()); } @Test