From cd56b3ca3cf334f1981acd2620d4774e300e38b8 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Wed, 8 Jun 2016 14:38:08 +1200 Subject: [PATCH] #726 - L2 cache returns stale data -> change such that when using "local L2 caching" the changes to L2 cache propagate in foreground --- .../avaje/ebean/cache/ServerCacheManager.java | 9 ++++++ .../cache/DefaultServerCacheManager.java | 17 +++++++---- .../server/core/DefaultContainer.java | 30 ++++++++++--------- .../server/core/InternalConfiguration.java | 7 +++-- .../AutoCommitTransactionManager.java | 4 +-- .../ExplicitTransactionManager.java | 4 +-- .../transaction/PostCommitProcessing.java | 23 ++++++++++---- .../transaction/TransactionManager.java | 13 ++++---- .../java/com/avaje/ebean/BaseTestCase.java | 6 +--- 9 files changed, 72 insertions(+), 41 deletions(-) diff --git a/src/main/java/com/avaje/ebean/cache/ServerCacheManager.java b/src/main/java/com/avaje/ebean/cache/ServerCacheManager.java index 50bcffceb..90d2e8cc5 100644 --- a/src/main/java/com/avaje/ebean/cache/ServerCacheManager.java +++ b/src/main/java/com/avaje/ebean/cache/ServerCacheManager.java @@ -5,6 +5,15 @@ package com.avaje.ebean.cache; */ public interface ServerCacheManager { + /** + * Return true if the L2 caching is local. + *

+ * Local L2 caching means that the cache updates should occur in foreground + * rather than background processing. + *

+ */ + boolean isLocalL2Caching(); + /** * Return true if there is an active bean cache for this type of bean. */ 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 f8abeb657..7f93d27d6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cache/DefaultServerCacheManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cache/DefaultServerCacheManager.java @@ -20,10 +20,13 @@ public class DefaultServerCacheManager implements ServerCacheManager { private final DefaultCacheHolder collectionIdsCache; + private final boolean localL2Caching; + /** * Create with a cache factory and default cache options. */ - public DefaultServerCacheManager(ServerCacheFactory cacheFactory, ServerCacheOptions defaultBeanOptions, ServerCacheOptions defaultQueryOptions) { + public DefaultServerCacheManager(boolean localL2Caching, ServerCacheFactory cacheFactory, ServerCacheOptions defaultBeanOptions, ServerCacheOptions defaultQueryOptions) { + this.localL2Caching = localL2Caching; this.beanCache = new DefaultCacheHolder(cacheFactory, defaultBeanOptions); this.queryCache = new DefaultCacheHolder(cacheFactory, defaultQueryOptions); this.naturalKeyCache = new DefaultCacheHolder(cacheFactory, defaultBeanOptions); @@ -34,7 +37,11 @@ public class DefaultServerCacheManager implements ServerCacheManager { * Construct when l2 cache is disabled. */ public DefaultServerCacheManager() { - this(new DefaultServerCacheFactory(), new ServerCacheOptions(), new ServerCacheOptions()); + this(true, new DefaultServerCacheFactory(), new ServerCacheOptions(), new ServerCacheOptions()); + } + + public boolean isLocalL2Caching() { + return localL2Caching; } /** @@ -49,7 +56,9 @@ public class DefaultServerCacheManager implements ServerCacheManager { queryCache.clearCache(beanName); } - + /** + * Clear all caches. + */ public void clearAll() { beanCache.clearAll(); queryCache.clearAll(); @@ -57,7 +66,6 @@ public class DefaultServerCacheManager implements ServerCacheManager { collectionIdsCache.clearAll(); } - public ServerCache getCollectionIdsCache(Class beanType, String propertyName) { return collectionIdsCache.getCache(beanType.getName() + "." + propertyName, ServerCacheType.COLLECTION_IDS); } @@ -87,5 +95,4 @@ public class DefaultServerCacheManager implements ServerCacheManager { return beanCache.isCaching(beanType.getName()); } - } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java index 8cdbb98ad..25dd5097d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java @@ -170,6 +170,7 @@ public class DefaultContainer implements SpiContainer { queryOptions.setMaxIdleSecs(serverConfig.getQueryCacheMaxIdleTime()); queryOptions.setMaxSecsToLive(serverConfig.getQueryCacheMaxTimeToLive()); + boolean localL2Caching = false; ServerCachePlugin plugin = serverConfig.getServerCachePlugin(); if (plugin == null) { ServiceLoader cacheFactories = ServiceLoader.load(ServerCachePlugin.class); @@ -179,13 +180,14 @@ public class DefaultContainer implements SpiContainer { plugin = iterator.next(); logger.debug("using ServerCacheFactory {}", plugin.getClass()); } else { - // use the built in default + // use the built in default l2 caching which is local cache based + localL2Caching = true; plugin = new DefaultServerCachePlugin(); } } ServerCacheFactory factory = plugin.create(serverConfig, executor); - return new DefaultServerCacheManager(factory, beanOptions, queryOptions); + return new DefaultServerCacheManager(localL2Caching, factory, beanOptions, queryOptions); } /** @@ -194,20 +196,20 @@ public class DefaultContainer implements SpiContainer { */ private BootupClasses getBootupClasses(ServerConfig serverConfig) { - BootupClasses bootupClasses = getBootupClasses1(serverConfig); - bootupClasses.addIdGenerators(serverConfig.getIdGenerators()); - bootupClasses.addPersistControllers(serverConfig.getPersistControllers()); - bootupClasses.addPostLoaders(serverConfig.getPostLoaders()); - bootupClasses.addFindControllers(serverConfig.getFindControllers()); - bootupClasses.addTransactionEventListeners(serverConfig.getTransactionEventListeners()); - bootupClasses.addPersistListeners(serverConfig.getPersistListeners()); - bootupClasses.addQueryAdapters(serverConfig.getQueryAdapters()); - bootupClasses.addServerConfigStartup(serverConfig.getServerConfigStartupListeners()); - bootupClasses.addChangeLogInstances(serverConfig); + BootupClasses bootup = getBootupClasses1(serverConfig); + bootup.addIdGenerators(serverConfig.getIdGenerators()); + bootup.addPersistControllers(serverConfig.getPersistControllers()); + bootup.addPostLoaders(serverConfig.getPostLoaders()); + bootup.addFindControllers(serverConfig.getFindControllers()); + bootup.addTransactionEventListeners(serverConfig.getTransactionEventListeners()); + bootup.addPersistListeners(serverConfig.getPersistListeners()); + bootup.addQueryAdapters(serverConfig.getQueryAdapters()); + bootup.addServerConfigStartup(serverConfig.getServerConfigStartupListeners()); + bootup.addChangeLogInstances(serverConfig); // run any ServerConfigStartup instances - bootupClasses.runServerConfigStartup(serverConfig); - return bootupClasses; + bootup.runServerConfigStartup(serverConfig); + return bootup; } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java index 4f8e62dab..bba89a1c1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java @@ -338,15 +338,16 @@ public class InternalConfiguration { */ public TransactionManager createTransactionManager(DocStoreUpdateProcessor indexUpdateProcessor) { + boolean localL2 = cacheManager.isLocalL2Caching(); if (serverConfig.isExplicitTransactionBeginMode()) { - return new ExplicitTransactionManager(serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager, this.getBootupClasses()); + return new ExplicitTransactionManager(localL2, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager, this.getBootupClasses()); } if (isAutoCommitMode()) { - return new AutoCommitTransactionManager(serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager, this.getBootupClasses()); + return new AutoCommitTransactionManager(localL2, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager, this.getBootupClasses()); } - return new TransactionManager(serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager, this.getBootupClasses()); + return new TransactionManager(localL2, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, beanDescriptorManager, this.getBootupClasses()); } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/AutoCommitTransactionManager.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/AutoCommitTransactionManager.java index 440905f1a..af04c6ad5 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/AutoCommitTransactionManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/AutoCommitTransactionManager.java @@ -17,10 +17,10 @@ import java.sql.Connection; */ public class AutoCommitTransactionManager extends TransactionManager { - public AutoCommitTransactionManager(ServerConfig serverConfig, ClusterManager clusterManager, BackgroundExecutor backgroundExecutor, + public AutoCommitTransactionManager(boolean localL2Caching, ServerConfig serverConfig, ClusterManager clusterManager, BackgroundExecutor backgroundExecutor, DocStoreUpdateProcessor indexUpdateProcessor, BeanDescriptorManager descMgr, BootupClasses bootupClasses) { - super(serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, descMgr, bootupClasses); + super(localL2Caching, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, descMgr, bootupClasses); } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/ExplicitTransactionManager.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/ExplicitTransactionManager.java index 90d46f79e..c93d4a592 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/ExplicitTransactionManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/ExplicitTransactionManager.java @@ -17,10 +17,10 @@ import java.sql.Connection; */ public class ExplicitTransactionManager extends TransactionManager { - public ExplicitTransactionManager(ServerConfig serverConfig, ClusterManager clusterManager, BackgroundExecutor backgroundExecutor, + public ExplicitTransactionManager(boolean localL2Caching, ServerConfig serverConfig, ClusterManager clusterManager, BackgroundExecutor backgroundExecutor, DocStoreUpdateProcessor indexUpdateProcessor, BeanDescriptorManager descMgr, BootupClasses bootupClasses) { - super(serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, descMgr, bootupClasses); + super(localL2Caching, serverConfig, clusterManager, backgroundExecutor, indexUpdateProcessor, descMgr, bootupClasses); } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/PostCommitProcessing.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/PostCommitProcessing.java index dee275590..f47dcb04e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/PostCommitProcessing.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/PostCommitProcessing.java @@ -84,9 +84,15 @@ public final class PostCommitProcessing { /** * Notify the local part of L2 cache. */ - void notifyLocalCache(boolean viewInvalidation) { + void notifyLocalCache() { processTableEvents(event.getEventTables()); - cacheChanges = event.buildCacheChanges(viewInvalidation); + if (manager.localL2Caching) { + // process l2 cache changes in foreground + processCacheChanges(event.buildCacheChanges(manager.viewInvalidation)); + } else { + // collect l2 cache changes for delayed background processing + cacheChanges = event.buildCacheChanges(manager.viewInvalidation); + } } /** @@ -148,9 +154,7 @@ public final class PostCommitProcessing { Runnable backgroundNotify() { return new Runnable() { public void run() { - if (cacheChanges != null) { - manager.processViewInvalidation(cacheChanges.apply()); - } + processCacheChanges(cacheChanges); localPersistListenersNotify(); notifyCluster(); processDocStoreUpdates(); @@ -158,6 +162,15 @@ public final class PostCommitProcessing { }; } + /** + * Apply the changes to the L2 caches. + */ + private void processCacheChanges(CacheChangeSet cacheChanges) { + if (cacheChanges != null) { + manager.processViewInvalidation(cacheChanges.apply()); + } + } + private void localPersistListenersNotify() { if (persistBeanRequests != null) { for (int i = 0; i < persistBeanRequests.size(); i++) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionManager.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionManager.java index 645952c1d..aa9db8dad 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/TransactionManager.java @@ -105,14 +105,17 @@ public class TransactionManager { */ private final ChangeLogListener changeLogListener; - private final boolean viewInvalidation; + protected final boolean localL2Caching; + + protected final boolean viewInvalidation; /** * Create the TransactionManager */ - public TransactionManager(ServerConfig config, ClusterManager clusterManager, BackgroundExecutor backgroundExecutor, DocStoreUpdateProcessor docStoreUpdateProcessor, - BeanDescriptorManager descMgr, BootupClasses bootupClasses) { + public TransactionManager(boolean localL2Caching, ServerConfig config, ClusterManager clusterManager, BackgroundExecutor backgroundExecutor, + DocStoreUpdateProcessor docStoreUpdateProcessor, BeanDescriptorManager descMgr, BootupClasses bootupClasses) { + this.localL2Caching = localL2Caching; this.persistBatch = config.getPersistBatch(); this.persistBatchOnCascade = config.appliedPersistBatchOnCascade(); this.beanDescriptorManager = descMgr; @@ -402,7 +405,7 @@ public class TransactionManager { } PostCommitProcessing postCommit = new PostCommitProcessing(clusterManager, this, transaction); - postCommit.notifyLocalCache(viewInvalidation); + postCommit.notifyLocalCache(); backgroundExecutor.execute(postCommit.backgroundNotify()); for (TransactionEventListener listener : transactionEventListeners) { @@ -427,7 +430,7 @@ public class TransactionManager { event.add(tableEvents); PostCommitProcessing postCommit = new PostCommitProcessing(clusterManager, this, event); - postCommit.notifyLocalCache(viewInvalidation); + postCommit.notifyLocalCache(); backgroundExecutor.execute(postCommit.backgroundNotify()); } diff --git a/src/test/java/com/avaje/ebean/BaseTestCase.java b/src/test/java/com/avaje/ebean/BaseTestCase.java index b1d6026c7..ab5700445 100644 --- a/src/test/java/com/avaje/ebean/BaseTestCase.java +++ b/src/test/java/com/avaje/ebean/BaseTestCase.java @@ -41,11 +41,7 @@ public class BaseTestCase { * Wait for the L2 cache to propagate changes post-commit. */ protected void awaitL2Cache() { - try { - Thread.sleep(10); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } + // do nothing, used to thread sleep } protected BeanDescriptor getBeanDescriptor(Class cls) {