From 987fe386182874a1d4a9b47df14db5da3f3d0023 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Tue, 1 Sep 2015 07:55:25 +1200 Subject: [PATCH 1/9] WIP AutoTune refactor --- .../java/com/avaje/ebean/AdminAutofetch.java | 120 +--- .../avaje/ebean/bean/NodeUsageCollector.java | 6 +- .../ebeaninternal/api/SpiEbeanServer.java | 6 - .../com/avaje/ebeaninternal/api/SpiQuery.java | 14 +- .../server/autofetch/AutoFetchManager.java | 241 ------- .../server/autofetch/AutoTuneService.java | 63 ++ .../autofetch/DefaultAutoFetchManager.java | 587 ------------------ .../server/autofetch/ProfilingListener.java | 13 + .../server/autofetch/package.html | 8 - .../AutoTuneServiceFactory.java} | 25 +- .../service/BaseAutoTuneService.java | 207 ++++++ .../autofetch/service/BaseQueryTuner.java | 150 +++++ .../DefaultAutoFetchManagerLogging.java | 6 +- .../autofetch/service/ProfileManager.java | 112 ++++ .../ProfileOrigin.java} | 119 ++-- .../ProfileOriginNodeUsage.java} | 10 +- .../ProfileOriginQuery.java} | 6 +- .../{ => service}/TunedQueryInfo.java | 2 +- .../server/core/DefaultContainer.java | 28 +- .../server/core/DefaultServer.java | 75 +-- .../server/core/InternalConfiguration.java | 8 +- .../server/jmx/MAdminAutofetch.java | 121 ---- .../server/jmx/MAdminAutofetchMBean.java | 145 ----- .../server/jmx/package-info.java | 1 - .../server/loadcontext/DLoadBeanContext.java | 2 +- .../server/loadcontext/DLoadContext.java | 8 +- .../server/loadcontext/DLoadManyContext.java | 2 +- .../ebeaninternal/server/query/CQuery.java | 24 +- .../server/querydefn/DefaultOrmQuery.java | 35 +- .../ebeaninternal/api/TDSpiEbeanServer.java | 6 - .../server/autofetch/TunedQueryInfoTest.java | 2 +- .../autofetch/AutofetchEmbeddedTest.java | 48 +- .../cache/TestL2CacheWithSharedBean.java | 2 +- .../query/TestAutofetchTuneWithJoin.java | 7 +- src/test/resources/ebean.properties | 7 +- src/test/resources/logback-test.xml | 2 + 36 files changed, 740 insertions(+), 1478 deletions(-) delete mode 100644 src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManager.java create mode 100644 src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneService.java delete mode 100644 src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManager.java create mode 100644 src/main/java/com/avaje/ebeaninternal/server/autofetch/ProfilingListener.java delete mode 100644 src/main/java/com/avaje/ebeaninternal/server/autofetch/package.html rename src/main/java/com/avaje/ebeaninternal/server/autofetch/{AutoFetchManagerFactory.java => service/AutoTuneServiceFactory.java} (69%) create mode 100644 src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseAutoTuneService.java create mode 100644 src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseQueryTuner.java rename src/main/java/com/avaje/ebeaninternal/server/autofetch/{ => service}/DefaultAutoFetchManagerLogging.java (92%) create mode 100644 src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileManager.java rename src/main/java/com/avaje/ebeaninternal/server/autofetch/{Statistics.java => service/ProfileOrigin.java} (52%) rename src/main/java/com/avaje/ebeaninternal/server/autofetch/{StatisticsNodeUsage.java => service/ProfileOriginNodeUsage.java} (91%) rename src/main/java/com/avaje/ebeaninternal/server/autofetch/{StatisticsQuery.java => service/ProfileOriginQuery.java} (81%) rename src/main/java/com/avaje/ebeaninternal/server/autofetch/{ => service}/TunedQueryInfo.java (98%) delete mode 100644 src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminAutofetch.java delete mode 100644 src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminAutofetchMBean.java delete mode 100644 src/main/java/com/avaje/ebeaninternal/server/jmx/package-info.java diff --git a/src/main/java/com/avaje/ebean/AdminAutofetch.java b/src/main/java/com/avaje/ebean/AdminAutofetch.java index 15e19f9c1..49ab9287e 100644 --- a/src/main/java/com/avaje/ebean/AdminAutofetch.java +++ b/src/main/java/com/avaje/ebean/AdminAutofetch.java @@ -5,80 +5,11 @@ package com.avaje.ebean; */ public interface AdminAutofetch { - /** - * Return true if profiling is enabled. - */ - boolean isProfiling(); - - /** - * Set to true to enable profiling. - */ - void setProfiling(boolean enable); - - /** - * Return true if autoFetch automatic query tuning is enabled. - */ - boolean isQueryTuning(); - - /** - * Set to true to enable autoFetch automatic query tuning. - */ - void setQueryTuning(boolean enable); - - /** - * Returns the rate which profiling is collected. This is an int between 0 and - * 100. - */ - double getProfilingRate(); - - /** - * Set the rate at which profiling is collected after the base. - * - * @param rate - * a int between 0 and 100. - */ - void setProfilingRate(double rate); - - /** - * Return the number of queries profiled after which profiling is collected at - * a percentage rate. - */ - int getProfilingBase(); - - /** - * Set a base number of queries to profile per query point. - *

- * After this amount of profiling has been obtained profiling is collected at - * the Profiling Percentage rate. - *

- */ - void setProfilingBase(int profilingBase); - - /** - * Return the minimum number of queries profiled before autoFetch will start - * automatically tuning the queries. - *

- * This could be one which means start autoFetch tuning after the first - * profiling information is collected. - *

- */ - int getProfilingMin(); - - /** - * Set the minimum number of queries profiled per query point before autoFetch - * will automatically tune the queries. - *

- * Increasing this number will mean more profiling is collected before - * autoFetch starts tuning the query. - *

- */ - void setProfilingMin(int autoFetchMinThreshold); - /** * Fire a garbage collection (hint to the JVM). Assuming garbage collection * fires this will gather the usage profiling information. */ - String collectUsageViaGC(); + void collectUsageViaGC(); /** * This will take the current profiling information and update the "tuned @@ -86,52 +17,7 @@ public interface AdminAutofetch { *

* This is done periodically and can also be manually invoked. *

- * - * @return a summary of the updates that occurred */ - String updateTunedQueryInfo(); + void updateTunedQueryInfo(); - /** - * Clear all the tuned query info. - *

- * Should only need do this for testing and playing around. - *

- * - * @return the amount of tuned query information cleared. - */ - int clearTunedQueryInfo(); - - /** - * Clear all the profiling information. - *

- * This means the profiling information will need to be re-gathered. - *

- *

- * Should only need do this for testing and playing around. - *

- * - * @return the amount of profiled information cleared. - */ - int clearProfilingInfo(); - - /** - * Clear the query execution statistics. - */ - void clearQueryStatistics(); - - /** - * Return the number of queries tuned by AutoFetch. - */ - int getTotalTunedQueryCount(); - - /** - * Return the size of the TuneQuery map. - */ - int getTotalTunedQuerySize(); - - /** - * Return the size of the profile map. - */ - int getTotalProfileSize(); - -} \ No newline at end of file +} diff --git a/src/main/java/com/avaje/ebean/bean/NodeUsageCollector.java b/src/main/java/com/avaje/ebean/bean/NodeUsageCollector.java index 42dc52c8c..00c1f7d0f 100644 --- a/src/main/java/com/avaje/ebean/bean/NodeUsageCollector.java +++ b/src/main/java/com/avaje/ebean/bean/NodeUsageCollector.java @@ -2,6 +2,8 @@ package com.avaje.ebean.bean; import java.lang.ref.WeakReference; import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Set; /** * Collects profile information for a bean (or reference/proxy bean) at a given @@ -29,7 +31,7 @@ public final class NodeUsageCollector { /** * The properties used at this profile point. */ - private final HashSet used = new HashSet(); + private final Set used = new LinkedHashSet(); /** * set to true if the bean is modified (setter called) @@ -105,7 +107,7 @@ public final class NodeUsageCollector { /** * Return the set of used properties. */ - public HashSet getUsed() { + public Set getUsed() { return used; } diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java b/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java index 290a6e4fa..31dbc82de 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java +++ b/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java @@ -11,7 +11,6 @@ import com.avaje.ebean.bean.CallStack; import com.avaje.ebean.bean.ObjectGraphNode; import com.avaje.ebean.config.ServerConfig; import com.avaje.ebean.config.dbplatform.DatabasePlatform; -import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest; import com.avaje.ebean.dbmigration.DdlGenerator; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; @@ -70,11 +69,6 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL */ DdlGenerator getDdlGenerator(); - /** - * Return the AutoFetchListener. - */ - AutoFetchManager getAutoFetchManager(); - /** * Clear the query execution statistics. */ diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java b/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java index 1f496d319..ebb5a49ae 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java @@ -10,7 +10,7 @@ import com.avaje.ebean.bean.CallStack; import com.avaje.ebean.bean.ObjectGraphNode; import com.avaje.ebean.bean.PersistenceContext; import com.avaje.ebean.event.BeanQueryRequest; -import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; +import com.avaje.ebeaninternal.server.autofetch.ProfilingListener; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; import com.avaje.ebeaninternal.server.deploy.TableJoin; @@ -370,16 +370,16 @@ public interface SpiQuery extends Query { void logSecondaryQuery(SpiQuery query); /** - * If return null then no autoFetch profiling for this query. If a - * AutoFetchManager is returned this implies that profiling is turned on for - * this query (and all the objects this query creates). + * If return null then no profiling for this query. If a ProfilingListener is + * returned this implies that profiling is turned on for this query (and all + * the objects this query creates). */ - AutoFetchManager getAutoFetchManager(); + ProfilingListener getProfilingListener(); /** - * This has the effect of turning on autoFetch profiling for this query. + * This has the effect of turning on profiling for this query. */ - void setAutoFetchManager(AutoFetchManager manager); + void setProfilingListener(ProfilingListener manager); /** * Return the origin point for the query. diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManager.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManager.java deleted file mode 100644 index 7073bf30d..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManager.java +++ /dev/null @@ -1,241 +0,0 @@ -package com.avaje.ebeaninternal.server.autofetch; - -import com.avaje.ebean.Query; -import com.avaje.ebean.bean.NodeUsageListener; -import com.avaje.ebean.bean.ObjectGraphNode; -import com.avaje.ebean.config.AutofetchMode; -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.api.SpiQuery; - -import java.util.Iterator; - -/** - * Collects and manages the the profile information. - *

- * The profile information is periodically converted into "tuned query details" - - * which is used to automatically tune the queries that use autoFetch. - *

- *

- * The "tuned query details" effectively are part of the query that has the - * select() and join() information (but not the where clause, order by, limits - * etc). These are applied to the query when tuneQuery() is called. - *

- */ -public interface AutoFetchManager extends NodeUsageListener { - - /** - * Set the owning ebean server. - */ - void setOwner(SpiEbeanServer server, ServerConfig serverConfig); - - /** - * Clear the query execution statistics. - */ - void clearQueryStatistics(); - - /** - * Clear all the tuned query info. - *

- * Should only need do this for testing and playing around. - *

- */ - int clearTunedQueryInfo(); - - /** - * Clear all the profiling information. - *

- * This means the profiling information will need to be re-gathered. - *

- *

- * Should only need do this for testing and playing around. - *

- */ - int clearProfilingInfo(); - - /** - * On shutdown fire garbage collection and collect statistics. Note that - * usually we add a little delay (100 milliseconds) to give the garbage - * collector plenty of time to do its thing and collect the profile - * information. - */ - void shutdown(); - - /** - * Return the current tuned fetch information for a given queryPoint key. - */ - TunedQueryInfo getTunedQueryInfo(String queryPointKey); - - /** - * Return the current Statistics for a given queryPoint key. - */ - Statistics getStatistics(String queryPointKey); - - /** - * Iterate the tuned fetch info. - *

- * This should be a read only iteration. - *

- */ - Iterator iterateTunedQueryInfo(); - - /** - * Iterate the node usage statistics. - *

- * This should be a read only iteration. - *

- */ - Iterator iterateStatistics(); - - /** - * Return true if profiling is enabled. - */ - boolean isProfiling(); - - /** - * Set to true to enable profiling. - *

- * We rely on garbage collection to collect the profiling information. This - * means there is a unknown delay between when a query is executed and when - * we actually collect the usage profile information. - *

- *

- * Due to this garbage collection delay, when turning off profiling while - * the application is running you should consider calling - * collectUsageViaGC() BEFORE setProfiling(false). This hints to - * the JVM to perform garbage collection, and hopefully collects the - * profiling information. - *

- */ - void setProfiling(boolean enable); - - /** - * Return true if automatic query tuning is enabled. - */ - boolean isQueryTuning(); - - /** - * Set to true to enable automatic query tuning. - */ - void setQueryTuning(boolean enable); - - /** - * This controls whether autoFetch is used when it has not been explicitly - * set on a query via {@link Query#setAutofetch(boolean)}. - */ - AutofetchMode getMode(); - - /** - * Set the auto fetch mode used when a query has not had - * {@link Query#setAutofetch(boolean)}. - */ - void setMode(AutofetchMode Mode); - - /** - * Return the profiling rate (int between 0 and 100). - */ - double getProfilingRate(); - - /** - * Set the profiling rate (int between 0 and 100). - */ - void setProfilingRate(double rate); - - /** - * Return the max number of queries profiled (per query point). - *

- * The number of queries profiled is collected per query point. Once a query - * point has profiled this number of queries it does not profile any more. - *

- */ - int getProfilingBase(); - - /** - * Set a max number of queries to profile per query point. - *

- * This number should provide a level of confidence that no more profiling - * is required for this query point. - *

- */ - void setProfilingBase(int profilingMax); - - /** - * Return the minimum number of queries profiled before autoFetch will start - * automatically tuning the queries. - *

- * This could be one which means start autoFetch tuning after the first - * profiling information is collected. - *

- */ - int getProfilingMin(); - - /** - * Set the minimum number of queries profiled per query point before - * autoFetch will automatically tune the queries. - *

- * Increasing this number will mean more profiling is collected before - * autoFetch starts tuning the query. - *

- */ - void setProfilingMin(int autoFetchMinThreshold); - - /** - * Fire a garbage collection (hint to the JVM). Assuming garbage collection - * fires this will gather the usage profiling information. - */ - String collectUsageViaGC(long waitMillis); - - /** - * This will take the current profiling information and update the "tuned - * query detail". - *

- * This is done periodically and can also be manually invoked. - *

- *

- * This returns a string summary of the updates that occurred. - *

- */ - String updateTunedQueryInfo(); - - /** - * Called when a query thinks it should be automatically tuned by autoFetch. - *

- * This internally checks that autoFetch is enabled, there is a "tuned query - * detail" to tune the query with and that the autoFetchMinThreshold has - * been reached. - *

- *

- * This will also determine if the query should be profiled. - *

- */ - boolean tuneQuery(SpiQuery query); - - /** - * Collect query profiling information. - *

- * This is for the original query as well as any subsequent lazy loading - * queries that are required as the object graph is traversed. - *

- * - * @param node the node path in the object graph. - * @param beans the number of beans loaded by the query. - * @param micros the query executing time in microseconds - */ - void collectQueryInfo(ObjectGraphNode node, long beans, long micros); - - - /** - * Return the number of queries tuned by AutoFetch. - */ - int getTotalTunedQueryCount(); - - /** - * Return the size of the TuneQuery map. - */ - int getTotalTunedQuerySize(); - - /** - * Return the size of the profile map. - */ - int getTotalProfileSize(); -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneService.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneService.java new file mode 100644 index 000000000..291cfe9f3 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneService.java @@ -0,0 +1,63 @@ +package com.avaje.ebeaninternal.server.autofetch; + +import com.avaje.ebean.AdminAutofetch; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.SpiQuery; + +/** + * Collects and manages the the profile information. + *

+ * The profile information is periodically converted into "tuned query details" - + * which is used to automatically tune the queries that use autoFetch. + *

+ *

+ * The "tuned query details" effectively are part of the query that has the + * select() and join() information (but not the where clause, order by, limits + * etc). These are applied to the query when tuneQuery() is called. + *

+ */ +public interface AutoTuneService extends AdminAutofetch { + + /** + * Load the query tuning information. + */ + void startup(); + + /** + * Called when a query thinks it should be automatically tuned by autoFetch. + *

+ * This internally checks that autoFetch is enabled, there is a "tuned query + * detail" to tune the query with and that the autoFetchMinThreshold has + * been reached. + *

+ *

+ * This will also determine if the query should be profiled. + *

+ */ + boolean tuneQuery(SpiQuery query); + + /** + * Fire a garbage collection (hint to the JVM). Assuming garbage collection + * fires this will gather the usage profiling information. + */ + void collectUsageViaGC(); + + /** + * This will take the current profiling information and update the "tuned + * query detail". + *

+ * This is done periodically and can also be manually invoked. + *

+ */ + void updateTunedQueryInfo(); + + /** + * On shutdown fire garbage collection and collect statistics. Note that + * usually we add a little delay (100 milliseconds) to give the garbage + * collector plenty of time to do its thing and collect the profile + * information. + */ + void shutdown(); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManager.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManager.java deleted file mode 100644 index 35dd210ba..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManager.java +++ /dev/null @@ -1,587 +0,0 @@ -package com.avaje.ebeaninternal.server.autofetch; - -import com.avaje.ebean.bean.CallStack; -import com.avaje.ebean.bean.NodeUsageCollector; -import com.avaje.ebean.bean.ObjectGraphNode; -import com.avaje.ebean.bean.ObjectGraphOrigin; -import com.avaje.ebean.config.AutofetchConfig; -import com.avaje.ebean.config.AutofetchMode; -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebeaninternal.api.ClassUtil; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.persistence.PersistenceException; -import java.io.File; -import java.io.FileOutputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** - * The manager of all the usage/query statistics as well as the tuned fetch - * information. - */ -public class DefaultAutoFetchManager implements AutoFetchManager, Serializable { - - private static final Logger logger = LoggerFactory.getLogger(DefaultAutoFetchManager.class); - - private static final long serialVersionUID = -6826119882781771722L; - - @SuppressWarnings("RedundantStringConstructorCall") - private final String statisticsMonitor = new String(); - - private final String fileName; - - /** - * Map of the usage and query statistics gathered. - */ - private final Map statisticsMap = new ConcurrentHashMap(); - - /** - * Map of the tuned query details per profile query point. - */ - private final Map tunedQueryInfoMap = new ConcurrentHashMap(); - - private transient long defaultGarbageCollectionWait = 100; - - /** - * Left without synchronized for now. - */ - private transient int tunedQueryCount; - - /** - * Converted from a 0-100 int to a double. Effectively a percentage rate at - * which to collect profiling information. - */ - private transient double profilingRate = 0.1d; - - private transient int profilingBase = 10; - - private transient int profilingMin = 1; - - private transient boolean profiling; - - private transient boolean queryTuning; - - private transient boolean queryTuningAddVersion; - - private transient boolean garbageCollectionOnShutdown; - - private transient AutofetchMode mode; - - /** - * Server that owns this Profile Listener. - */ - private transient SpiEbeanServer server; - - /** - * The logger. - */ - private transient DefaultAutoFetchManagerLogging logging; - - public DefaultAutoFetchManager(String fileName) { - this.fileName = fileName; - } - - /** - * Set up this profile listener before it is active. - */ - public void setOwner(SpiEbeanServer server, ServerConfig serverConfig) { - this.server = server; - this.logging = new DefaultAutoFetchManagerLogging(serverConfig, this); - - AutofetchConfig autofetchConfig = serverConfig.getAutofetchConfig(); - - garbageCollectionOnShutdown = autofetchConfig.isGarbageCollectionOnShutdown(); - queryTuning = autofetchConfig.isQueryTuning(); - queryTuningAddVersion = autofetchConfig.isQueryTuningAddVersion(); - profiling = autofetchConfig.isProfiling(); - profilingMin = autofetchConfig.getProfilingMin(); - profilingBase = autofetchConfig.getProfilingBase(); - - setProfilingRate(autofetchConfig.getProfilingRate()); - - defaultGarbageCollectionWait = (long) autofetchConfig.getGarbageCollectionWait(); - - // determine the mode to use when Query.setAutoFetch() was - // not explicitly set - mode = autofetchConfig.getMode(); - - if (profiling || queryTuning) { - // log the guts of the autoFetch setup - String msg = "AutoFetch queryTuning[" + queryTuning + "] profiling[" + profiling - + "] mode[" + mode + "] profiling rate[" + profilingRate - + "] min[" + profilingMin + "] base[" + profilingBase + "]"; - logging.logInfo(msg, null); - - // Register a periodic update of the profiling informations - this.logging.init(server); - } - } - - - public void clearQueryStatistics() { - server.clearQueryStatistics(); - } - - /** - * Return the number of queries tuned by AutoFetch. - */ - public int getTotalTunedQueryCount() { - return tunedQueryCount; - } - - /** - * Return the size of the TuneQuery map. - */ - public int getTotalTunedQuerySize() { - return tunedQueryInfoMap.size(); - } - - /** - * Return the size of the profile map. - */ - public int getTotalProfileSize() { - return statisticsMap.size(); - } - - public int clearTunedQueryInfo() { - - // reset the rough count as well - tunedQueryCount = 0; - - // clear the map... - int size = tunedQueryInfoMap.size(); - tunedQueryInfoMap.clear(); - return size; - } - - public int clearProfilingInfo() { - int size = statisticsMap.size(); - statisticsMap.clear(); - return size; - } - - - public void serialize() { - - File autoFetchFile = new File(fileName); - - try { - FileOutputStream fout = new FileOutputStream(autoFetchFile); - - ObjectOutputStream oout = new ObjectOutputStream(fout); - oout.writeObject(this); - oout.flush(); - oout.close(); - - } catch (Exception e) { - String msg = "Error serializing autofetch file"; - logging.logError(msg, e); - } - } - - /** - * Return the current Tuned query info for a given origin key. - */ - public TunedQueryInfo getTunedQueryInfo(String originKey) { - return tunedQueryInfoMap.get(originKey); - } - - /** - * Return the current Statistics for a given originKey key. - */ - public Statistics getStatistics(String originKey) { - return statisticsMap.get(originKey); - } - - public Iterator iterateTunedQueryInfo() { - return tunedQueryInfoMap.values().iterator(); - } - - public Iterator iterateStatistics() { - return statisticsMap.values().iterator(); - } - - public boolean isProfiling() { - return profiling; - } - - /** - * When the application is running, BEFORE turning off profiling you - * probably should call collectUsageViaGC() as there is a delay (waiting for - * garbage collection) collecting usage profiling information. - */ - public void setProfiling(boolean profiling) { - this.profiling = profiling; - } - - public boolean isQueryTuning() { - return queryTuning; - } - - public void setQueryTuning(boolean queryTuning) { - this.queryTuning = queryTuning; - } - - public double getProfilingRate() { - return profilingRate; - } - - public AutofetchMode getMode() { - return mode; - } - - public void setMode(AutofetchMode mode) { - this.mode = mode; - } - - public void setProfilingRate(double rate) { - if (rate < 0) { - rate = 0d; - } else if (rate > 1) { - rate = 1d; - } - profilingRate = rate; - } - - public int getProfilingBase() { - return profilingBase; - } - - public void setProfilingBase(int profilingBase) { - this.profilingBase = profilingBase; - } - - public int getProfilingMin() { - return profilingMin; - } - - public void setProfilingMin(int profilingMin) { - this.profilingMin = profilingMin; - } - - /** - * Shutdown the listener. - *

- * We should try to collect the usage statistics by calling a System.gc(). - * This is necessary for use with short lived applications where garbage - * collection may not otherwise occur at all. - *

- */ - public void shutdown() { - if (garbageCollectionOnShutdown) { - collectUsageViaGC(-1); - serialize(); - } - } - - /** - * Ask for a System.gc() so that we gather node usage information. - *

- * Really only want to do this sparingly but useful just prior to shutdown - * for short run application where garbage collection may otherwise not - * occur at all. - *

- *

- * waitMillis will do a thread sleep to give the garbage collection a little - * time to do its thing assuming we are shutting down the VM. - *

- *

- * If waitMillis is -1 then the defaultGarbageCollectionWait is used which - * defaults to 100 milliseconds. - *

- */ - public String collectUsageViaGC(long waitMillis) { - System.gc(); - try { - if (waitMillis < 0) { - waitMillis = defaultGarbageCollectionWait; - } - Thread.sleep(waitMillis); - } catch (InterruptedException e) { - String msg = "Error while sleeping after System.gc() request."; - logging.logError(msg, e); - return msg; - } - return updateTunedQueryInfo(); - } - - /** - * Update the tuned fetch plans from the current usage information. - */ - public String updateTunedQueryInfo() { - - if (!profiling) { - // we are not collecting any profiling information at - // the moment so don't try updating the tuned query plans. - return "Not profiling"; - } - - synchronized (statisticsMonitor) { - - Counters counters = new Counters(); - - for (Statistics queryPointStatistics : statisticsMap.values()) { - if (!queryPointStatistics.hasUsage()) { - // no usage statistics collected yet... - counters.incrementNoUsage(); - } else { - updateTunedQueryFromUsage(counters, queryPointStatistics); - } - } - - String summaryInfo = counters.toString(); - - if (counters.isInteresting()) { - // only log it if its interesting - logging.logSummary(summaryInfo); - } - - return summaryInfo; - } - } - - private static class Counters { - - int newPlan; - int modified; - int unchanged; - int noUsage; - - void incrementNoUsage() { - noUsage++; - } - - void incrementNew() { - newPlan++; - } - - void incrementModified() { - modified++; - } - - void incrementUnchanged() { - unchanged++; - } - - boolean isInteresting() { - return newPlan > 0 || modified > 0; - } - - public String toString() { - return "new[" + newPlan + "] modified[" + modified + "] unchanged[" + unchanged + "] nousage[" + noUsage + "]"; - } - } - - private void updateTunedQueryFromUsage(Counters counters, Statistics statistics) { - - ObjectGraphOrigin queryPoint = statistics.getOrigin(); - String beanType = queryPoint.getBeanType(); - - try { - Class beanClass = ClassUtil.forName(beanType, this.getClass()); - BeanDescriptor beanDescriptor = server.getBeanDescriptor(beanClass); - if (beanDescriptor != null) { - - // Determine the fetch plan from the latest statistics. - // Use this to compare with current "tuned fetch plan". - OrmQueryDetail newFetchDetail = statistics.buildTunedFetch(beanDescriptor); - - // get the current tuned fetch info... - TunedQueryInfo currentFetch = tunedQueryInfoMap.get(queryPoint.getKey()); - - if (currentFetch == null) { - // its a new fetch plan, add it. - counters.incrementNew(); - - currentFetch = statistics.createTunedFetch(newFetchDetail); - logging.logNew(currentFetch); - tunedQueryInfoMap.put(queryPoint.getKey(), currentFetch); - - } else if (!currentFetch.isSame(newFetchDetail)) { - // the fetch plan has changed, update it. - counters.incrementModified(); - - logging.logChanged(currentFetch, newFetchDetail); - currentFetch.setTunedDetail(newFetchDetail); - - } else { - // the fetch plan has not changed... - counters.incrementUnchanged(); - } - - currentFetch.setProfileCount(statistics.getCounter()); - } - - } catch (ClassNotFoundException e) { - // expected after renaming/moving an entity bean - String msg = e.toString() + " updating autoFetch tuned query for " + beanType - + ". It isLikely this bean has been renamed or moved"; - logging.logInfo(msg, null); - statisticsMap.remove(statistics.getOrigin().getKey()); - } - } - - /** - * Return true if we should try to use autoFetch for this query. - */ - private boolean useAutoFetch(SpiQuery query) { - - if (query.isLoadBeanCache()) { - // when loading the cache don't tune the query - // as we want full objects loaded into the cache - return false; - } - - Boolean autoFetch = query.isAutofetch(); - if (autoFetch != null) { - // explicitly set... - return autoFetch; - - } else { - // determine using implicit mode... - switch (mode) { - case DEFAULT_ON: - return true; - - case DEFAULT_OFF: - return false; - - case DEFAULT_ONIFEMPTY: - return query.isDetailEmpty(); - - default: - throw new PersistenceException("Invalid autoFetchMode " + mode); - } - } - } - - /** - * Auto tune the query and enable profiling. - */ - public boolean tuneQuery(SpiQuery query) { - - if (!queryTuning && !profiling) { - return false; - } - - if (!useAutoFetch(query)) { - // not using autoFetch for this query - return false; - } - - ObjectGraphNode parentAutoFetchNode = query.getParentNode(); - if (parentAutoFetchNode != null) { - // This is a +lazy/+query query with profiling on. - // We continue to collect the profiling information. - query.setAutoFetchManager(this); - return true; - } - - // create a query point to identify the query - CallStack stack = server.createCallStack(); - ObjectGraphNode origin = query.setOrigin(stack); - - // get current "tuned fetch" for this query point - TunedQueryInfo tunedFetch = tunedQueryInfoMap.get(origin.getOriginQueryPoint().getKey()); - - // get the number of times we have collected profiling information - int profileCount = tunedFetch == null ? 0 : tunedFetch.getProfileCount(); - - if (profiling) { - // we want more profiling information? - if (tunedFetch == null) { - query.setAutoFetchManager(this); - - } else if (profileCount < profilingBase) { - query.setAutoFetchManager(this); - - } else if (tunedFetch.isPercentageProfile(profilingRate)) { - query.setAutoFetchManager(this); - } - } - - if (queryTuning) { - if (tunedFetch != null && profileCount >= profilingMin) { - // deemed to have enough profiling - // information for automatic tuning - if (tunedFetch.autoFetchTune(query)) { - // tunedQueryCount++ not thread-safe, could use AtomicInteger. - // But I'm happy if this statistic is a little wrong - // and this is a VERY HOT method - tunedQueryCount++; - } - return true; - } - } - - return false; - } - - /** - * Gather query execution statistics. This could either be the originating - * query in which case the parentNode will be null, or a lazy loading query - * resulting from traversal of the object graph. - */ - public void collectQueryInfo(ObjectGraphNode node, long beans, long micros) { - - if (node != null) { - ObjectGraphOrigin origin = node.getOriginQueryPoint(); - if (origin != null) { - Statistics stats = getQueryPointStats(origin); - stats.collectQueryInfo(node, beans, micros); - } - } - } - - /** - * Collect usage statistics from a node in the object graph. - *

- * This is sent to use from a EntityBeanIntercept when the finalise method - * is called on the bean. - *

- */ - public void collectNodeUsage(NodeUsageCollector usageCollector) { - - ObjectGraphOrigin origin = usageCollector.getNode().getOriginQueryPoint(); - - Statistics stats = getQueryPointStats(origin); - - if (logger.isTraceEnabled()) { - logger.trace("... NodeUsageCollector " + usageCollector); - } - - stats.collectUsageInfo(usageCollector); - - if (logger.isTraceEnabled()) { - logger.trace("stats\n" + stats); - } - } - - private Statistics getQueryPointStats(ObjectGraphOrigin originQueryPoint) { - synchronized (statisticsMonitor) { - Statistics stats = statisticsMap.get(originQueryPoint.getKey()); - if (stats == null) { - stats = new Statistics(originQueryPoint, queryTuningAddVersion); - statisticsMap.put(originQueryPoint.getKey(), stats); - } - return stats; - } - } - - public String toString() { - synchronized (statisticsMonitor) { - return statisticsMap.values().toString(); - } - } - - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/ProfilingListener.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/ProfilingListener.java new file mode 100644 index 000000000..9885f7435 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/ProfilingListener.java @@ -0,0 +1,13 @@ +package com.avaje.ebeaninternal.server.autofetch; + +import com.avaje.ebean.bean.NodeUsageListener; +import com.avaje.ebean.bean.ObjectGraphNode; + +/** + * Profiling listener gets call backs for node usage and the associated query executions. + */ +public interface ProfilingListener extends NodeUsageListener { + + void collectQueryInfo(ObjectGraphNode node, long beans, long micros); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/package.html b/src/main/java/com/avaje/ebeaninternal/server/autofetch/package.html deleted file mode 100644 index f0e805a5c..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/package.html +++ /dev/null @@ -1,8 +0,0 @@ - - - AutoFetch Implementation - - -AutoFetch Implementation - - \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManagerFactory.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/AutoTuneServiceFactory.java similarity index 69% rename from src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManagerFactory.java rename to src/main/java/com/avaje/ebeaninternal/server/autofetch/service/AutoTuneServiceFactory.java index b19a4ee37..1e6c2d1cd 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManagerFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/AutoTuneServiceFactory.java @@ -1,7 +1,8 @@ -package com.avaje.ebeaninternal.server.autofetch; +package com.avaje.ebeaninternal.server.autofetch.service; import com.avaje.ebean.config.ServerConfig; import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.autofetch.AutoTuneService; import com.avaje.ebeaninternal.server.resource.ResourceManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,29 +12,29 @@ import java.io.File; import java.io.FileInputStream; import java.io.ObjectInputStream; -public class AutoFetchManagerFactory { +public class AutoTuneServiceFactory { - private static final Logger logger = LoggerFactory.getLogger(AutoFetchManagerFactory.class); + private static final Logger logger = LoggerFactory.getLogger(AutoTuneServiceFactory.class); - public static AutoFetchManager create(SpiEbeanServer server, ServerConfig serverConfig, ResourceManager resourceManager) { + public static AutoTuneService create(SpiEbeanServer server, ServerConfig serverConfig, ResourceManager resourceManager) { - AutoFetchManagerFactory me = new AutoFetchManagerFactory(); + AutoTuneServiceFactory me = new AutoTuneServiceFactory(); return me.createAutoFetchManager(server, serverConfig, resourceManager); } - private AutoFetchManager createAutoFetchManager(SpiEbeanServer server, ServerConfig serverConfig, ResourceManager resourceManager) { + private AutoTuneService createAutoFetchManager(SpiEbeanServer server, ServerConfig serverConfig, ResourceManager resourceManager) { - AutoFetchManager manager = createAutoFetchManager(server.getName(), resourceManager); + AutoTuneService manager = createAutoFetchManager(server.getName(), resourceManager); manager.setOwner(server, serverConfig); return manager; } - private AutoFetchManager createAutoFetchManager(String serverName, ResourceManager resourceManager) { + private AutoTuneService createAutoFetchManager(String serverName, ResourceManager resourceManager) { File autoFetchFile = getAutoFetchFile(serverName, resourceManager); - AutoFetchManager autoFetchManager = null; + AutoTuneService autoFetchManager = null; boolean readFile = !"false".equalsIgnoreCase(System.getProperty("autofetch.readfromfile")); if (readFile) { @@ -44,13 +45,13 @@ public class AutoFetchManagerFactory { // not deserialized from file so create as empty // It will be populated automatically by querying the // database meta data - autoFetchManager = new DefaultAutoFetchManager(autoFetchFile.getAbsolutePath()); + autoFetchManager = new BaseAutoTuneService(autoFetchFile.getAbsolutePath()); } return autoFetchManager; } - private AutoFetchManager deserializeAutoFetch(File autoFetchFile) { + private AutoTuneService deserializeAutoFetch(File autoFetchFile) { try { if (!autoFetchFile.exists()) { @@ -58,7 +59,7 @@ public class AutoFetchManagerFactory { } FileInputStream fi = new FileInputStream(autoFetchFile); ObjectInputStream ois = new ObjectInputStream(fi); - AutoFetchManager profListener = (AutoFetchManager) ois.readObject(); + AutoTuneService profListener = (AutoTuneService) ois.readObject(); ois.close(); logger.info("AutoFetch deserialized from file [" + autoFetchFile.getAbsolutePath() + "]"); diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseAutoTuneService.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseAutoTuneService.java new file mode 100644 index 000000000..81ee660b7 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseAutoTuneService.java @@ -0,0 +1,207 @@ +package com.avaje.ebeaninternal.server.autofetch.service; + +import com.avaje.ebean.config.AutofetchConfig; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.server.autofetch.AutoTuneService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * Implementation of the AutoTuneService which is comprised of profiling and query tuning. + */ +public class BaseAutoTuneService implements AutoTuneService { + + private static final Logger logger = LoggerFactory.getLogger(BaseAutoTuneService.class); + + private final long defaultGarbageCollectionWait; + + private final boolean garbageCollectionOnShutdown; + + private final BaseQueryTuner queryTuner; + + public BaseAutoTuneService(SpiEbeanServer server, ServerConfig serverConfig) { + + AutofetchConfig config = serverConfig.getAutofetchConfig(); + + ProfileManager profileManager = new ProfileManager(config, server); + this.queryTuner = new BaseQueryTuner(config, server, profileManager); + + this.garbageCollectionOnShutdown = config.isGarbageCollectionOnShutdown(); + this.defaultGarbageCollectionWait = (long) config.getGarbageCollectionWait(); + } + + /** + * Load the query tuning information from it's data store. + */ + public void startup() { + +// File autoFetchFile = new File(fileName); +// +// try { +// FileOutputStream fout = new FileOutputStream(autoFetchFile); +// +// ObjectOutputStream oout = new ObjectOutputStream(fout); +// oout.writeObject(this); +// oout.flush(); +// oout.close(); +// +// } catch (Exception e) { +// String msg = "Error serializing autofetch file"; +// logging.logError(msg, e); +// } + } + + private void saveProfiling() { + + } + + /** + * Shutdown the listener. + *

+ * We should try to collect the usage statistics by calling a System.gc(). + * This is necessary for use with short lived applications where garbage + * collection may not otherwise occur at all. + *

+ */ + public void shutdown() { + if (garbageCollectionOnShutdown) { + collectUsageViaGC(-1); + saveProfiling(); + } + } + + /** + * Ask for a System.gc() so that we gather node usage information. + *

+ * Really only want to do this sparingly but useful just prior to shutdown + * for short run application where garbage collection may otherwise not + * occur at all. + *

+ *

+ * waitMillis will do a thread sleep to give the garbage collection a little + * time to do its thing assuming we are shutting down the VM. + *

+ *

+ * If waitMillis is -1 then the defaultGarbageCollectionWait is used which + * defaults to 100 milliseconds. + *

+ */ + public void collectUsageViaGC() { + collectUsageViaGC(-1); + } + + public void collectUsageViaGC(long waitMillis) { + System.gc(); + try { + if (waitMillis < 0) { + waitMillis = defaultGarbageCollectionWait; + } + Thread.sleep(waitMillis); + } catch (InterruptedException e) { + logger.warn("Error while sleeping after System.gc() request.", e); + } + updateTunedQueryInfo(); + } + + /** + * Update the tuned fetch plans from the current usage information. + */ + public void updateTunedQueryInfo() { + +// if (!profiling) { +// // we are not collecting any profiling information at +// // the moment so don't try updating the tuned query plans. +// return "Not profiling"; +// } +// +// synchronized (statisticsMonitor) { +// +// Counters counters = new Counters(); +// +// for (ProfileOrigin queryPointStatistics : statisticsMap.values()) { +// if (!queryPointStatistics.hasUsage()) { +// // no usage statistics collected yet... +// counters.incrementNoUsage(); +// } else { +// updateTunedQueryFromUsage(counters, queryPointStatistics); +// } +// } +// +// String summaryInfo = counters.toString(); +// +// if (counters.isInteresting()) { +// // only log it if its interesting +// logging.logSummary(summaryInfo); +// } +// +// return summaryInfo; +// } + } + + +// +// private void updateTunedQueryFromUsage(Counters counters, ProfileOrigin statistics) { +// +// ObjectGraphOrigin queryPoint = statistics.getOrigin(); +// String beanType = queryPoint.getBeanType(); +// +// try { +// Class beanClass = ClassUtil.forName(beanType, this.getClass()); +// BeanDescriptor beanDescriptor = server.getBeanDescriptor(beanClass); +// if (beanDescriptor != null) { +// +// // Determine the fetch plan from the latest statistics. +// // Use this to compare with current "tuned fetch plan". +// OrmQueryDetail newFetchDetail = statistics.buildTunedFetch(beanDescriptor); +// +// // get the current tuned fetch info... +// TunedQueryInfo currentFetch = tunedQueryInfoMap.get(queryPoint.getKey()); +// +// if (currentFetch == null) { +// // its a new fetch plan, add it. +// counters.incrementNew(); +// +// currentFetch = statistics.createTunedFetch(newFetchDetail); +// logging.logNew(currentFetch); +// tunedQueryInfoMap.put(queryPoint.getKey(), currentFetch); +// +// } else if (!currentFetch.isSame(newFetchDetail)) { +// // the fetch plan has changed, update it. +// counters.incrementModified(); +// +// logging.logChanged(currentFetch, newFetchDetail); +// currentFetch.setTunedDetail(newFetchDetail); +// +// } else { +// // the fetch plan has not changed... +// counters.incrementUnchanged(); +// } +// +// currentFetch.setProfileCount(statistics.getCounter()); +// } +// +// } catch (ClassNotFoundException e) { +// // expected after renaming/moving an entity bean +// String msg = e.toString() + " updating autoFetch tuned query for " + beanType +// + ". It isLikely this bean has been renamed or moved"; +// logging.logInfo(msg, null); +// statisticsMap.remove(statistics.getOrigin().getKey()); +// } +// } +// + + /** + * Auto tune the query and enable profiling. + */ + public boolean tuneQuery(SpiQuery query) { + return queryTuner.tuneQuery(query); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseQueryTuner.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseQueryTuner.java new file mode 100644 index 000000000..a60f73fd9 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseQueryTuner.java @@ -0,0 +1,150 @@ +package com.avaje.ebeaninternal.server.autofetch.service; + +import com.avaje.ebean.bean.CallStack; +import com.avaje.ebean.bean.ObjectGraphNode; +import com.avaje.ebean.config.AutofetchConfig; +import com.avaje.ebean.config.AutofetchMode; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.ebeaninternal.server.autofetch.ProfilingListener; + +import javax.persistence.PersistenceException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * + */ +public class BaseQueryTuner { + + private final boolean queryTuning; + + /** + * Converted from a 0-100 int to a double. Effectively a percentage rate at + * which to collect profiling information. + */ + private final double profilingRate; + + private final int profilingBase; + + private final int profilingMin; + + private boolean profiling; + + private final AutofetchMode mode; + + /** + * Map of the tuned query details per profile query point. + */ + private final Map tunedQueryInfoMap = new ConcurrentHashMap(); + + + private final SpiEbeanServer server; + + private final ProfilingListener profilingListener; + + public BaseQueryTuner(AutofetchConfig config, SpiEbeanServer server, ProfilingListener profilingListener) { + this.server = server; + this.profilingListener = profilingListener; + this.mode = config.getMode(); + this.queryTuning = config.isQueryTuning(); + this.profiling = config.isProfiling(); + this.profilingRate = config.getProfilingRate(); + this.profilingBase = config.getProfilingBase(); + this.profilingMin = config.getProfilingMin(); + } + + /** + * Load the tuned query information. + */ + public void load(String key, TunedQueryInfo queryInfo) { + tunedQueryInfoMap.put(key, queryInfo); + } + + /** + * Auto tune the query and enable profiling. + */ + public boolean tuneQuery(SpiQuery query) { + + if (!queryTuning && !profiling) { + return false; + } + + if (!useAutoFetch(query)) { + // not using autoFetch for this query + return false; + } + + ObjectGraphNode parentAutoFetchNode = query.getParentNode(); + if (parentAutoFetchNode != null) { + // This is a +lazy/+query query with profiling on. + // We continue to collect the profiling information. + query.setProfilingListener(profilingListener); + return true; + } + + // create a query point to identify the query + CallStack stack = server.createCallStack(); + ObjectGraphNode origin = query.setOrigin(stack); + + // get current "tuned fetch" for this query point + TunedQueryInfo tunedFetch = tunedQueryInfoMap.get(origin.getOriginQueryPoint().getKey()); + + // get the number of times we have collected profiling information + int profileCount = tunedFetch == null ? 0 : tunedFetch.getProfileCount(); + + if (profiling) { + // we want more profiling information? + if (tunedFetch == null) { + query.setProfilingListener(profilingListener); + + } else if (profileCount < profilingBase) { + query.setProfilingListener(profilingListener); + + } else if (tunedFetch.isPercentageProfile(profilingRate)) { + query.setProfilingListener(profilingListener); + } + } + + if (queryTuning && tunedFetch != null && profileCount >= profilingMin) { + // deemed to have enough profiling information for automatic tuning + return tunedFetch.autoFetchTune(query); + } + + return false; + } + + /** + * Return true if we should try to use autoFetch for this query. + */ + private boolean useAutoFetch(SpiQuery query) { + + if (query.isLoadBeanCache()) { + // when loading the cache don't tune the query + // as we want full objects loaded into the cache + return false; + } + + Boolean autoFetch = query.isAutofetch(); + if (autoFetch != null) { + // explicitly set... + return autoFetch; + + } else { + // determine using implicit mode... + switch (mode) { + case DEFAULT_ON: + return true; + + case DEFAULT_OFF: + return false; + + case DEFAULT_ONIFEMPTY: + return query.isDetailEmpty(); + + default: + throw new PersistenceException("Invalid autoFetchMode " + mode); + } + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManagerLogging.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoFetchManagerLogging.java similarity index 92% rename from src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManagerLogging.java rename to src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoFetchManagerLogging.java index f1cbb183b..8ba42bb6b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManagerLogging.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoFetchManagerLogging.java @@ -1,4 +1,4 @@ -package com.avaje.ebeaninternal.server.autofetch; +package com.avaje.ebeaninternal.server.autofetch.service; import com.avaje.ebean.config.ServerConfig; import com.avaje.ebeaninternal.api.SpiEbeanServer; @@ -19,11 +19,11 @@ public class DefaultAutoFetchManagerLogging { private static final Logger logger = LoggerFactory.getLogger(DefaultAutoFetchManagerLogging.class); - private final DefaultAutoFetchManager manager; + private final BaseAutoTuneService manager; private final int updateFreqInSecs; - public DefaultAutoFetchManagerLogging(ServerConfig serverConfig, DefaultAutoFetchManager profileListener) { + public DefaultAutoFetchManagerLogging(ServerConfig serverConfig, BaseAutoTuneService profileListener) { this.manager = profileListener; this.updateFreqInSecs = serverConfig.getAutofetchConfig().getProfileUpdateFrequency(); diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileManager.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileManager.java new file mode 100644 index 000000000..a9bada622 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileManager.java @@ -0,0 +1,112 @@ +package com.avaje.ebeaninternal.server.autofetch.service; + +import com.avaje.ebean.bean.NodeUsageCollector; +import com.avaje.ebean.bean.ObjectGraphNode; +import com.avaje.ebean.bean.ObjectGraphOrigin; +import com.avaje.ebean.config.AutofetchConfig; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.autofetch.ProfilingListener; +import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; +import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * + */ +public class ProfileManager implements ProfilingListener { + + private static final Logger logger = LoggerFactory.getLogger(ProfileManager.class); + + private final boolean queryTuningAddVersion; + + private final boolean profiling; + + /** + * Map of the usage and query statistics gathered. + */ + private final Map profileMap = new ConcurrentHashMap(); + + private final Object monitor = new Object(); + + private final SpiEbeanServer server; + + public ProfileManager(AutofetchConfig config, SpiEbeanServer server) { + this.server = server; + this.profiling = config.isProfiling(); + this.queryTuningAddVersion = config.isQueryTuningAddVersion(); + } + + /** + * Gather query execution statistics. This could either be the originating + * query in which case the parentNode will be null, or a lazy loading query + * resulting from traversal of the object graph. + */ + public void collectQueryInfo(ObjectGraphNode node, long beans, long micros) { + + if (node != null) { + ObjectGraphOrigin origin = node.getOriginQueryPoint(); + if (origin != null) { + ProfileOrigin stats = getProfileOrigin(origin); + stats.collectQueryInfo(node, beans, micros); + } + } + } + + /** + * Collect usage statistics from a node in the object graph. + *

+ * This is sent to use from a EntityBeanIntercept when the finalise method + * is called on the bean. + *

+ */ + public void collectNodeUsage(NodeUsageCollector usageCollector) { + + ProfileOrigin profileOrigin = getProfileOrigin(usageCollector.getNode().getOriginQueryPoint()); + profileOrigin.collectUsageInfo(usageCollector); + } + + private ProfileOrigin getProfileOrigin(ObjectGraphOrigin originQueryPoint) { + synchronized (monitor) { + ProfileOrigin stats = profileMap.get(originQueryPoint.getKey()); + if (stats == null) { + stats = new ProfileOrigin(originQueryPoint, queryTuningAddVersion); + profileMap.put(originQueryPoint.getKey(), stats); + } + return stats; + } + } + + + /** + * Update the tuned fetch plans from the current usage information. + */ + public void updateTunedQueryInfo() { + + if (!profiling) { + // we are not collecting any profiling information at + // the moment so don't try updating the tuned query plans. + return;// "Not profiling"; + } + + synchronized (monitor) { + + for (ProfileOrigin origin : profileMap.values()) { + if (origin.hasUsage()) { + OrmQueryDetail ormQueryDetail = updateTunedQueryFromUsage(origin); + + } + } + } + } + + + private OrmQueryDetail updateTunedQueryFromUsage(ProfileOrigin statistics) { + + BeanDescriptor desc = server.getBeanDescriptorById(statistics.getOrigin().getBeanType()); + return desc == null ? null : statistics.buildTunedFetch(desc); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/Statistics.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOrigin.java similarity index 52% rename from src/main/java/com/avaje/ebeaninternal/server/autofetch/Statistics.java rename to src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOrigin.java index a9625ca70..959b740a2 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/Statistics.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOrigin.java @@ -1,4 +1,4 @@ -package com.avaje.ebeaninternal.server.autofetch; +package com.avaje.ebeaninternal.server.autofetch.service; import com.avaje.ebean.bean.NodeUsageCollector; import com.avaje.ebean.bean.ObjectGraphNode; @@ -13,7 +13,7 @@ import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; -public class Statistics implements Serializable { +public class ProfileOrigin implements Serializable { private static final long serialVersionUID = -5586783791097230766L; @@ -24,14 +24,14 @@ public class Statistics implements Serializable { private int counter; - private final Map queryStatsMap = new LinkedHashMap(); + private final Map queryStatsMap = new LinkedHashMap(); - private final Map nodeUsageMap = new LinkedHashMap(); + private final Map nodeUsageMap = new LinkedHashMap(); @SuppressWarnings("RedundantStringConstructorCall") private final String monitor = new String(); - public Statistics(ObjectGraphOrigin origin, boolean queryTuningAddVersion) { + public ProfileOrigin(ObjectGraphOrigin origin, boolean queryTuningAddVersion) { this.origin = origin; this.queryTuningAddVersion = queryTuningAddVersion; } @@ -40,13 +40,13 @@ public class Statistics implements Serializable { return origin; } - public TunedQueryInfo createTunedFetch(OrmQueryDetail newFetchDetail) { - synchronized (monitor) { - // NB: create a copy of queryPoint allowing garbage - // collection of source... - return new TunedQueryInfo(origin, newFetchDetail, counter); - } - } +// public TunedQueryInfo createTunedFetch(OrmQueryDetail newFetchDetail) { +// synchronized (monitor) { +// // NB: create a copy of queryPoint allowing garbage +// // collection of source... +// return new TunedQueryInfo(origin, newFetchDetail, counter); +// } +// } /** * Return the number of times the root query has executed. @@ -77,7 +77,7 @@ public class Statistics implements Serializable { PathProperties pathProps = new PathProperties(); - for (StatisticsNodeUsage statsNode : nodeUsageMap.values()) { + for (ProfileOriginNodeUsage statsNode : nodeUsageMap.values()) { statsNode.buildTunedFetch(pathProps, rootDesc); } @@ -98,23 +98,22 @@ public class Statistics implements Serializable { public void collectQueryInfo(ObjectGraphNode node, long beansLoaded, long micros) { - synchronized (monitor) { - String key = node.getPath(); - if (key == null) { - key = ""; - // this is basically the number of times the root query - // has executed which gives us an indication of how - // much profiling information we have gathered. - counter++; - } - - StatisticsQuery stats = queryStatsMap.get(key); - if (stats == null) { - stats = new StatisticsQuery(key); - queryStatsMap.put(key, stats); - } - stats.add(beansLoaded, micros); + String key = node.getPath(); + if (key == null) { + key = ""; + // this is basically the number of times the root query + // has executed which gives us an indication of how + // much profiling information we have gathered. + counter++; } + + ProfileOriginQuery stats = queryStatsMap.get(key); + if (stats == null) { + // a race condition but we don't care + stats = new ProfileOriginQuery(key); + queryStatsMap.put(key, stats); + } + stats.add(beansLoaded, micros); } @@ -126,49 +125,49 @@ public class Statistics implements Serializable { if (!profile.isEmpty()) { ObjectGraphNode node = profile.getNode(); - StatisticsNodeUsage nodeStats = getNodeStats(node.getPath()); + ProfileOriginNodeUsage nodeStats = getNodeStats(node.getPath()); nodeStats.publish(profile); } } - private StatisticsNodeUsage getNodeStats(String path) { + private ProfileOriginNodeUsage getNodeStats(String path) { synchronized (monitor) { - StatisticsNodeUsage nodeStats = nodeUsageMap.get(path); + ProfileOriginNodeUsage nodeStats = nodeUsageMap.get(path); if (nodeStats == null) { - nodeStats = new StatisticsNodeUsage(path, queryTuningAddVersion); + nodeStats = new ProfileOriginNodeUsage(path, queryTuningAddVersion); nodeUsageMap.put(path, nodeStats); } return nodeStats; } } - public String getUsageDebug() { - synchronized (monitor) { - StringBuilder sb = new StringBuilder(); - sb.append("root[").append(origin.getBeanType()).append("] "); - for (StatisticsNodeUsage node : nodeUsageMap.values()) { - sb.append(node.toString()).append("\n"); - } - return sb.toString(); - } - } - - public String getQueryStatDebug() { - synchronized (monitor) { - StringBuilder sb = new StringBuilder(); - for (StatisticsQuery queryStat : queryStatsMap.values()) { - sb.append(queryStat.toString()).append("\n"); - } - return sb.toString(); - } - } - - public String toString() { - - synchronized (monitor) { - return getUsageDebug(); - } - } +// public String getUsageDebug() { +// synchronized (monitor) { +// StringBuilder sb = new StringBuilder(); +// sb.append("root[").append(origin.getBeanType()).append("] "); +// for (ProfileOriginNodeUsage node : nodeUsageMap.values()) { +// sb.append(node.toString()).append("\n"); +// } +// return sb.toString(); +// } +// } +// +// public String getQueryStatDebug() { +// synchronized (monitor) { +// StringBuilder sb = new StringBuilder(); +// for (ProfileOriginQuery queryStat : queryStatsMap.values()) { +// sb.append(queryStat.toString()).append("\n"); +// } +// return sb.toString(); +// } +// } +// +// public String toString() { +// +// synchronized (monitor) { +// return getUsageDebug(); +// } +// } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/StatisticsNodeUsage.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginNodeUsage.java similarity index 91% rename from src/main/java/com/avaje/ebeaninternal/server/autofetch/StatisticsNodeUsage.java rename to src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginNodeUsage.java index 868f8f356..a051bc31f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/StatisticsNodeUsage.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginNodeUsage.java @@ -1,4 +1,4 @@ -package com.avaje.ebeaninternal.server.autofetch; +package com.avaje.ebeaninternal.server.autofetch.service; import com.avaje.ebean.bean.NodeUsageCollector; import com.avaje.ebean.text.PathProperties; @@ -18,11 +18,11 @@ import java.util.Set; /** * Collects usages statistics for a given node in the object graph. */ -public class StatisticsNodeUsage implements Serializable { +public class ProfileOriginNodeUsage implements Serializable { private static final long serialVersionUID = -1663951463963779547L; - private static final Logger logger = LoggerFactory.getLogger(StatisticsNodeUsage.class); + private static final Logger logger = LoggerFactory.getLogger(ProfileOriginNodeUsage.class); @SuppressWarnings("RedundantStringConstructorCall") private final String monitor = new String(); @@ -39,7 +39,7 @@ public class StatisticsNodeUsage implements Serializable { private final Set aggregateUsed = new LinkedHashSet(); - public StatisticsNodeUsage(String path, boolean queryTuningAddVersion) { + public ProfileOriginNodeUsage(String path, boolean queryTuningAddVersion) { this.path = path; this.queryTuningAddVersion = queryTuningAddVersion; } @@ -99,7 +99,7 @@ public class StatisticsNodeUsage implements Serializable { synchronized (monitor) { - HashSet used = profile.getUsed(); + Set used = profile.getUsed(); profileCount++; if (!used.isEmpty()) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/StatisticsQuery.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginQuery.java similarity index 81% rename from src/main/java/com/avaje/ebeaninternal/server/autofetch/StatisticsQuery.java rename to src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginQuery.java index 506087b31..b4d2b6bfa 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/StatisticsQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginQuery.java @@ -1,11 +1,11 @@ -package com.avaje.ebeaninternal.server.autofetch; +package com.avaje.ebeaninternal.server.autofetch.service; import java.io.Serializable; /** * Used to accumulate query execution statistics. */ -public class StatisticsQuery implements Serializable { +public class ProfileOriginQuery implements Serializable { private static final long serialVersionUID = -1133958958072778811L; @@ -17,7 +17,7 @@ public class StatisticsQuery implements Serializable { private long totalMicros; - public StatisticsQuery(String path) { + public ProfileOriginQuery(String path) { this.path = path; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfo.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/TunedQueryInfo.java similarity index 98% rename from src/main/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfo.java rename to src/main/java/com/avaje/ebeaninternal/server/autofetch/service/TunedQueryInfo.java index dec85ebee..8a6a5d046 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfo.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/TunedQueryInfo.java @@ -1,4 +1,4 @@ -package com.avaje.ebeaninternal.server.autofetch; +package com.avaje.ebeaninternal.server.autofetch.service; import com.avaje.ebean.bean.ObjectGraphOrigin; import com.avaje.ebeaninternal.api.SpiQuery; 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 2f0a2db78..e09e3873e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java @@ -50,7 +50,7 @@ public class DefaultContainer implements SpiContainer { private final JndiDataSourceLookup jndiDataSourceFactory; - private final AtomicInteger serverId = new AtomicInteger(1); +// private final AtomicInteger serverId = new AtomicInteger(1); public DefaultContainer(ContainerConfig containerConfig) { @@ -119,7 +119,7 @@ public class DefaultContainer implements SpiContainer { ServerCacheManager cacheManager = getCacheManager(serverConfig); - int uniqueServerId = serverId.incrementAndGet(); +// int uniqueServerId = serverId.incrementAndGet(); SpiBackgroundExecutor bgExecutor = createBackgroundExecutor(serverConfig); XmlConfigLoader xmlConfigLoader = new XmlConfigLoader(null); @@ -131,18 +131,18 @@ public class DefaultContainer implements SpiContainer { cacheManager.init(server); - if (serverConfig.isRegisterJmxMBeans()) { - MBeanServer mbeanServer; - ArrayList list = MBeanServerFactory.findMBeanServer(null); - if (list.size() == 0) { - // probably not running in a server - mbeanServer = MBeanServerFactory.createMBeanServer(); - } else { - // use the first MBeanServer - mbeanServer = (MBeanServer) list.get(0); - } - server.registerMBeans(mbeanServer, uniqueServerId); - } +// if (serverConfig.isRegisterJmxMBeans()) { +// MBeanServer mbeanServer; +// ArrayList list = MBeanServerFactory.findMBeanServer(null); +// if (list.size() == 0) { +// // probably not running in a server +// mbeanServer = MBeanServerFactory.createMBeanServer(); +// } else { +// // use the first MBeanServer +// mbeanServer = (MBeanServer) list.get(0); +// } +// server.registerMBeans(mbeanServer, uniqueServerId); +// } // generate and run DDL if required // if there are any other tasks requiring action in their plugins, do them as well diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java index a716516c5..4b14fc42b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java @@ -33,7 +33,7 @@ import com.avaje.ebeaninternal.api.SpiQuery.Type; import com.avaje.ebeaninternal.api.SpiSqlQuery; import com.avaje.ebeaninternal.api.SpiTransaction; import com.avaje.ebeaninternal.api.TransactionEventTable; -import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; +import com.avaje.ebeaninternal.server.autofetch.AutoTuneService; import com.avaje.ebean.dbmigration.DdlGenerator; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; @@ -43,7 +43,6 @@ import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery; import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate; import com.avaje.ebeaninternal.server.deploy.InheritInfo; import com.avaje.ebeaninternal.server.el.ElFilter; -import com.avaje.ebeaninternal.server.jmx.MAdminAutofetch; import com.avaje.ebeaninternal.server.lib.ShutdownManager; import com.avaje.ebeaninternal.server.query.CQuery; import com.avaje.ebeaninternal.server.query.CQueryEngine; @@ -69,9 +68,6 @@ import com.avaje.ebeaninternal.util.ParamTypeHelper.TypeInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.management.InstanceAlreadyExistsException; -import javax.management.MBeanServer; -import javax.management.ObjectName; import javax.persistence.OptimisticLockException; import javax.persistence.PersistenceException; import java.util.ArrayList; @@ -103,8 +99,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { private final DatabasePlatform databasePlatform; - private final AdminAutofetch adminAutofetch; - private final TransactionManager transactionManager; private final TransactionScopeManager transactionScopeManager; @@ -132,7 +126,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { private final DiffHelp diffHelp; - private final AutoFetchManager autoFetchManager; + private final AutoTuneService autoTuneService; private final CQueryEngine cqueryEngine; @@ -152,21 +146,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { private final MetaInfoManager metaInfoManager; - /** - * The MBean name used to register Ebean. - */ - private String mbeanName; - /** * The default PersistenceContextScope used if it is not explicitly set on a query. */ private final PersistenceContextScope defaultPersistenceContextScope; - /** - * The MBeanServer Ebean is registered with. - */ - private MBeanServer mbeanServer; - /** * Flag set when the server has shutdown. */ @@ -235,8 +219,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { this.queryEngine = config.createOrmQueryEngine(); this.relationalQueryEngine = config.createRelationalQueryEngine(); - this.autoFetchManager = config.createAutoFetchManager(this); - this.adminAutofetch = new MAdminAutofetch(autoFetchManager); + this.autoTuneService = config.createAutoFetchManager(this); this.beanLoader = new DefaultBeanLoader(this); this.jsonContext = config.createJsonContext(this); @@ -346,11 +329,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { } public AdminAutofetch getAdminAutofetch() { - return adminAutofetch; - } - - public AutoFetchManager getAutoFetchManager() { - return autoFetchManager; + return autoTuneService; } /** @@ -373,41 +352,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { public void start() { } - public void registerMBeans(MBeanServer mbeanServer, int uniqueServerId) { - - this.mbeanServer = mbeanServer; - this.mbeanName = "Ebean:server=" + serverName + uniqueServerId; - - ObjectName autofetchName; - try { - autofetchName = new ObjectName(mbeanName + ",key=AutoFetch"); - } catch (Exception e) { - String msg = "Failed to register the JMX beans for Ebean server [" + serverName + "]."; - logger.error(msg, e); - return; - } - - try { - mbeanServer.registerMBean(adminAutofetch, autofetchName); - - } catch (InstanceAlreadyExistsException e) { - // tomcat webapp reloading - String msg = "JMX beans for Ebean server [" + serverName + "] already registered. Will try unregister/register" + e.getMessage(); - logger.warn(msg); - try { - mbeanServer.unregisterMBean(autofetchName); - mbeanServer.registerMBean(adminAutofetch, autofetchName); - - } catch (Exception ae) { - String amsg = "Unable to unregister/register the JMX beans for Ebean server [" + serverName + "]."; - logger.error(amsg, ae); - } - } catch (Exception e) { - String msg = "Error registering MBean[" + mbeanName + "]"; - logger.error(msg, e); - } - } - /** * Shutting down via JVM Shutdown hook. */ @@ -439,16 +383,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { return; } shutdownPlugins(); - try { - if (mbeanServer != null) { - mbeanServer.unregisterMBean(new ObjectName(mbeanName + ",key=AutoFetch")); - } - } catch (Exception e) { - logger.error("Error unregistering Ebean " + mbeanName, e); - } // shutdown autofetch profile collection - autoFetchManager.shutdown(); + autoTuneService.shutdown(); // shutdown background threads backgroundExecutor.shutdown(); // shutdown DataSource (if its an Ebean one) @@ -1100,7 +1037,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { public SpiOrmQueryRequest createQueryRequest(BeanDescriptor desc, SpiQuery query, Transaction t) { - if (desc.isAutoFetchTunable() && !query.isSqlSelect() && !autoFetchManager.tuneQuery(query)) { + if (desc.isAutoFetchTunable() && !query.isSqlSelect() && !autoTuneService.tuneQuery(query)) { // use deployment FetchType.LAZY/EAGER annotations // to define the 'default' select clause query.setDefaultSelectClause(); 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 f0b0e0b30..c1148d0cf 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java @@ -13,8 +13,8 @@ import com.avaje.ebean.plugin.SpiServerPlugin; import com.avaje.ebean.text.json.JsonContext; import com.avaje.ebeaninternal.api.SpiBackgroundExecutor; import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; -import com.avaje.ebeaninternal.server.autofetch.AutoFetchManagerFactory; +import com.avaje.ebeaninternal.server.autofetch.AutoTuneService; +import com.avaje.ebeaninternal.server.autofetch.service.AutoTuneServiceFactory; import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogPrepare; import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogRegister; import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogListener; @@ -235,8 +235,8 @@ public class InternalConfiguration { return xmlConfig; } - public AutoFetchManager createAutoFetchManager(SpiEbeanServer server) { - return AutoFetchManagerFactory.create(server, serverConfig, resourceManager); + public AutoTuneService createAutoFetchManager(SpiEbeanServer server) { + return AutoTuneServiceFactory.create(server, serverConfig, resourceManager); } public RelationalQueryEngine createRelationalQueryEngine() { diff --git a/src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminAutofetch.java b/src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminAutofetch.java deleted file mode 100644 index a31100d6d..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminAutofetch.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.avaje.ebeaninternal.server.jmx; - -import com.avaje.ebean.AdminAutofetch; -import com.avaje.ebean.EbeanServer; -import com.avaje.ebean.config.AutofetchMode; -import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Implementation of the AutoFetchControl. - *

- * This is accessible via {@link EbeanServer#getAdminAutofetch()} or via JMX - * MBeans. - *

- */ -public class MAdminAutofetch implements MAdminAutofetchMBean, AdminAutofetch { - - private static final Logger logger = LoggerFactory.getLogger(MAdminAutofetch.class); - - final AutoFetchManager autoFetchManager; - - final String modeOptions; - - public MAdminAutofetch(AutoFetchManager autoFetchListener) { - this.autoFetchManager = autoFetchListener; - this.modeOptions = AutofetchMode.DEFAULT_OFF + ", " - + AutofetchMode.DEFAULT_ON + ", " - + AutofetchMode.DEFAULT_ONIFEMPTY; - } - - public boolean isQueryTuning() { - return autoFetchManager.isQueryTuning(); - } - - public void setQueryTuning(boolean enable) { - autoFetchManager.setQueryTuning(enable); - } - - public boolean isProfiling() { - return autoFetchManager.isProfiling(); - } - - public void setProfiling(boolean enable) { - autoFetchManager.setProfiling(enable); - } - - public String getModeOptions() { - return modeOptions; - } - - public String getMode() { - return autoFetchManager.getMode().name(); - } - - public void setMode(String implicitMode) { - try { - AutofetchMode mode = AutofetchMode.valueOf(implicitMode); - autoFetchManager.setMode(mode); - } catch (Exception e) { - logger.info("Invalid implicit mode attempted "+e.getMessage()); - } - } - - public String collectUsageViaGC() { - return autoFetchManager.collectUsageViaGC(-1); - } - - public double getProfilingRate() { - return autoFetchManager.getProfilingRate(); - } - - public void setProfilingRate(double rate) { - autoFetchManager.setProfilingRate(rate); - } - - public int getProfilingMin() { - return autoFetchManager.getProfilingMin(); - } - - public int getProfilingBase() { - return autoFetchManager.getProfilingBase(); - } - - public void setProfilingMin(int profilingMin) { - autoFetchManager.setProfilingMin(profilingMin); - } - - public void setProfilingBase(int profilingMax) { - autoFetchManager.setProfilingBase(profilingMax); - } - - public String updateTunedQueryInfo() { - return autoFetchManager.updateTunedQueryInfo(); - } - - public int clearProfilingInfo() { - return autoFetchManager.clearProfilingInfo(); - } - - public int clearTunedQueryInfo() { - return autoFetchManager.clearTunedQueryInfo(); - } - - public void clearQueryStatistics() { - autoFetchManager.clearQueryStatistics(); - } - - public int getTotalProfileSize() { - return autoFetchManager.getTotalProfileSize(); - } - - public int getTotalTunedQueryCount() { - return autoFetchManager.getTotalTunedQueryCount(); - } - - public int getTotalTunedQuerySize() { - return autoFetchManager.getTotalTunedQuerySize(); - } - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminAutofetchMBean.java b/src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminAutofetchMBean.java deleted file mode 100644 index ee6e10e78..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/jmx/MAdminAutofetchMBean.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.avaje.ebeaninternal.server.jmx; - -import com.avaje.ebean.Query; - -public interface MAdminAutofetchMBean { - - /** - * Return true if profiling is enabled. - */ - boolean isProfiling(); - - /** - * Set to true to enable profiling. - */ - void setProfiling(boolean enable); - - /** - * Return true if autoFetch is enabled. - */ - boolean isQueryTuning(); - - /** - * Set to true to enable autoFetch. - */ - void setQueryTuning(boolean enable); - - /** - * This controls whether autoFetch is used when it has not - * been explicitly set on a query via {@link Query#setAutofetch(boolean)}. - */ - String getMode(); - - /** - * List of the valid implicit modes that can be used. - */ - String getModeOptions(); - - /** - * Set the auto fetch mode used when a query has not had {@link Query#setAutofetch(boolean)}. - */ - void setMode(String mode); - - /** - * Return the max number of queries profiled (per query point). - *

- * The number of queries profiled is collected per query point. Once a query - * point has profiled this number of queries it does not profile any more. - *

- */ - int getProfilingBase(); - - /** - * Set a max number of queries to profile per query point. - *

- * This number should provide a level of confidence that no more profiling - * is required for this query point. - *

- */ - void setProfilingBase(int profilingMaxThreshold); - - /** - * Returns the rate which profiling is collected. - * This is an int between 0 and 100. - */ - double getProfilingRate(); - - /** - * Set the rate at which profiling is collected after the base. - * - * @param rate a int between 0 and 100. - */ - void setProfilingRate(double rate); - - /** - * Return the minimum number of queries profiled before autoFetch will start - * automatically tuning the queries. - *

- * This could be one which means start autoFetch tuning after the first - * profiling information is collected. - *

- */ - int getProfilingMin(); - - /** - * Set the minimum number of queries profiled per query point before - * autoFetch will automatically tune the queries. - *

- * Increasing this number will mean more profiling is collected before - * autoFetch starts tuning the query. - *

- */ - void setProfilingMin(int autoFetchMinThreshold); - - /** - * Fire a garbage collection (hint to the JVM). Assuming garbage collection - * fires this will gather the usage profiling information. - */ - String collectUsageViaGC(); - - /** - * This will take the current profiling information and update the "tuned query - * detail". - *

- * This is done periodically and can also be manually invoked. - *

- */ - String updateTunedQueryInfo(); - - /** - * Clear all the tuned query info. - *

- * Should only need do this for testing and playing around. - *

- * @return the amount of tuned query information cleared. - */ - int clearTunedQueryInfo(); - - /** - * Clear all the profiling information. - *

- * This means the profiling information will need to be re-gathered. - *

- *

- * Should only need do this for testing and playing around. - *

- * @return the amount of profiled information cleared. - */ - int clearProfilingInfo(); - - /** - * Return the number of queries tuned by AutoFetch. - */ - int getTotalTunedQueryCount(); - - /** - * Return the size of the TuneQuery map. - */ - int getTotalTunedQuerySize(); - - /** - * Return the size of the profile map. - */ - int getTotalProfileSize(); - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/jmx/package-info.java b/src/main/java/com/avaje/ebeaninternal/server/jmx/package-info.java deleted file mode 100644 index edaa2ad21..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/jmx/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.avaje.ebeaninternal.server.jmx; \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java index b1f7adada..d7eb2d86b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadBeanContext.java @@ -58,7 +58,7 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex if (queryProps != null) { queryProps.configureBeanQuery(query); } - if (parent.isUseAutofetchManager()) { + if (parent.isUseAutoTune()) { query.setAutofetch(true); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java index f9f1c1870..9a6b3fb3d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadContext.java @@ -47,7 +47,7 @@ public class DLoadContext implements LoadContext { */ private final String relativePath; private final ObjectGraphOrigin origin; - private final boolean useAutofetchManager; + private final boolean useProfiling; private final Map nodePathMap = new HashMap(); @@ -67,7 +67,7 @@ public class DLoadContext implements LoadContext { this.readOnly = query.isReadOnly(); this.disableLazyLoading = query.isDisableLazyLoading(); this.excludeBeanCache = Boolean.FALSE.equals(query.isUseBeanCache()); - this.useAutofetchManager = query.getAutoFetchManager() != null; + this.useProfiling = query.getProfilingListener() != null; ObjectGraphNode parentNode = query.getParentNode(); if (parentNode != null) { @@ -197,8 +197,8 @@ public class DLoadContext implements LoadContext { return new ObjectGraphNode(origin, path); } - public boolean isUseAutofetchManager() { - return useAutofetchManager; + public boolean isUseAutoTune() { + return useProfiling; } protected String getFullPath(String path) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java index 2e726ee94..fc3927b31 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java +++ b/src/main/java/com/avaje/ebeaninternal/server/loadcontext/DLoadManyContext.java @@ -70,7 +70,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex queryProps.configureBeanQuery(query); } - if (parent.isUseAutofetchManager()) { + if (parent.isUseAutoTune()) { query.setAutofetch(true); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java index 1f0518c1c..923d4ebed 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQuery.java @@ -6,7 +6,7 @@ import com.avaje.ebean.bean.*; import com.avaje.ebeaninternal.api.SpiQuery; import com.avaje.ebeaninternal.api.SpiQuery.Mode; import com.avaje.ebeaninternal.api.SpiTransaction; -import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; +import com.avaje.ebeaninternal.server.autofetch.ProfilingListener; import com.avaje.ebeaninternal.server.core.Message; import com.avaje.ebeaninternal.server.core.OrmQueryRequest; import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest; @@ -149,13 +149,13 @@ public class CQuery implements DbReadContext, CancelableQuery { private final Mode queryMode; - private final boolean autoFetchProfiling; + private final boolean autoTuneProfiling; private final ObjectGraphNode objectGraphNode; - private final AutoFetchManager autoFetchManager; + private final ProfilingListener profilingListener; - private final WeakReference autoFetchManagerRef; + private final WeakReference profilingListenerRef; private final Boolean readOnly; @@ -176,11 +176,10 @@ public class CQuery implements DbReadContext, CancelableQuery { this.readOnly = request.isReadOnly(); - this.autoFetchManager = query.getAutoFetchManager(); - this.autoFetchProfiling = autoFetchManager != null; this.objectGraphNode = query.getParentNode(); - this.autoFetchManagerRef = autoFetchProfiling ? new WeakReference( - autoFetchManager) : null; + this.profilingListener = query.getProfilingListener(); + this.autoTuneProfiling = profilingListener != null; + this.profilingListenerRef = autoTuneProfiling ? new WeakReference(profilingListener) : null; // set the generated sql back to the query // so its available to the user... @@ -518,8 +517,8 @@ public class CQuery implements DbReadContext, CancelableQuery { long exeNano = System.nanoTime() - startNano; executionTimeMicros = TimeUnit.NANOSECONDS.toMicros(exeNano); - if (autoFetchProfiling) { - autoFetchManager.collectQueryInfo(objectGraphNode, loadedBeanCount, executionTimeMicros); + if (autoTuneProfiling) { + profilingListener.collectQueryInfo(objectGraphNode, loadedBeanCount, executionTimeMicros); } queryPlan.executionTime(loadedBeanCount, executionTimeMicros, objectGraphNode); @@ -649,7 +648,7 @@ public class CQuery implements DbReadContext, CancelableQuery { // need query.isProfiling() because we just take the data // from the lazy loaded or refreshed beans and put it into the already // existing beans which are already collecting usage information - return autoFetchProfiling && query.isUsageProfiling(); + return autoTuneProfiling && query.isUsageProfiling(); } private String getPath(String propertyName) { @@ -671,8 +670,7 @@ public class CQuery implements DbReadContext, CancelableQuery { public void profileBean(EntityBeanIntercept ebi, String prefix) { ObjectGraphNode node = request.getGraphContext().getObjectGraphNode(prefix); - - ebi.setNodeUsageCollector(new NodeUsageCollector(node, autoFetchManagerRef)); + ebi.setNodeUsageCollector(new NodeUsageCollector(node, profilingListenerRef)); } public void setCurrentPrefix(String currentPrefix, Map currentPathMap) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java index db2dfb342..b8543b0e1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -1,14 +1,5 @@ package com.avaje.ebeaninternal.server.querydefn; -import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.persistence.PersistenceException; - import com.avaje.ebean.*; import com.avaje.ebean.OrderBy.Property; import com.avaje.ebean.bean.BeanCollectionTouched; @@ -26,7 +17,7 @@ import com.avaje.ebeaninternal.api.ManyWhereJoins; import com.avaje.ebeaninternal.api.SpiExpression; import com.avaje.ebeaninternal.api.SpiExpressionList; import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; +import com.avaje.ebeaninternal.server.autofetch.ProfilingListener; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; import com.avaje.ebeaninternal.server.deploy.DRawSqlSelect; @@ -36,6 +27,14 @@ import com.avaje.ebeaninternal.server.expression.SimpleExpression; import com.avaje.ebeaninternal.server.query.CancelableQuery; import com.avaje.ebeaninternal.util.DefaultExpressionList; +import javax.persistence.PersistenceException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + /** * Default implementation of an Object Relational query. */ @@ -57,7 +56,7 @@ public class DefaultOrmQuery implements SpiQuery { */ private transient TableJoin includeTableJoin; - private transient AutoFetchManager autoFetchManager; + private transient ProfilingListener profilingListener; private transient BeanDescriptor beanDescriptor; @@ -481,7 +480,7 @@ public class DefaultOrmQuery implements SpiQuery { DefaultOrmQuery copy = new DefaultOrmQuery(beanType, server, expressionFactory, (String) null); copy.name = name; copy.includeTableJoin = includeTableJoin; - copy.autoFetchManager = autoFetchManager; + copy.profilingListener = profilingListener; copy.query = query; copy.additionalWhere = additionalWhere; @@ -616,19 +615,23 @@ public class DefaultOrmQuery implements SpiQuery { return this; } + @Override public DefaultOrmQuery setForUpdate(boolean forUpdate) { this.forUpdate = forUpdate; return this; } - public AutoFetchManager getAutoFetchManager() { - return autoFetchManager; + @Override + public ProfilingListener getProfilingListener() { + return profilingListener; } - public void setAutoFetchManager(AutoFetchManager autoFetchManager) { - this.autoFetchManager = autoFetchManager; + @Override + public void setProfilingListener(ProfilingListener profilingListener) { + this.profilingListener = profilingListener; } + @Override public Mode getMode() { return mode; } diff --git a/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java b/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java index 9304cf8ec..adbcc84d8 100644 --- a/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java +++ b/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java @@ -13,7 +13,6 @@ import com.avaje.ebean.meta.MetaInfoManager; import com.avaje.ebean.plugin.SpiServer; import com.avaje.ebean.text.csv.CsvReader; import com.avaje.ebean.text.json.JsonContext; -import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager; import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; import com.avaje.ebeaninternal.server.query.CQuery; @@ -86,11 +85,6 @@ public class TDSpiEbeanServer implements SpiEbeanServer { return null; } - @Override - public AutoFetchManager getAutoFetchManager() { - return null; - } - @Override public void clearQueryStatistics() { diff --git a/src/test/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfoTest.java b/src/test/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfoTest.java index b66df26fb..eaf1ec8be 100644 --- a/src/test/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfoTest.java +++ b/src/test/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfoTest.java @@ -15,7 +15,7 @@ import com.avaje.ebean.bean.EntityBean; import com.avaje.ebean.bean.EntityBeanIntercept; import com.avaje.ebean.cache.ServerCacheManager; import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.server.autofetch.TunedQueryInfo; +import com.avaje.ebeaninternal.server.autofetch.service.TunedQueryInfo; import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; import com.avaje.tests.model.basic.Order; import com.avaje.tests.model.basic.ResetBasicData; diff --git a/src/test/java/com/avaje/tests/autofetch/AutofetchEmbeddedTest.java b/src/test/java/com/avaje/tests/autofetch/AutofetchEmbeddedTest.java index 2a201f256..ba59ae6cb 100644 --- a/src/test/java/com/avaje/tests/autofetch/AutofetchEmbeddedTest.java +++ b/src/test/java/com/avaje/tests/autofetch/AutofetchEmbeddedTest.java @@ -37,30 +37,30 @@ public class AutofetchEmbeddedTest extends BaseTestCase { @Test public void testEmbeddedBeanQueryTuning() { - Ebean.getServer(null).getAdminAutofetch().setProfiling(true); - Ebean.getServer(null).getAdminAutofetch().setQueryTuning(true); - Ebean.getServer(null).getAdminAutofetch().setProfilingBase(1); - - EMain testBean = new EMain(); - testBean.setName("test"); - testBean.getEmbeddable().setDescription("test description"); - Ebean.save(testBean); - - //This should not throw an exception - for (int i = 0; i < 5; i++) { - Ebean.beginTransaction(); - try { - List result = Ebean.find(EMain.class).setAutofetch(true).findList(); - for (EMain e : result) { - e.getEmbeddable().setDescription("Test" + i); - Ebean.save(e); - } - Ebean.commitTransaction(); - } finally { - Ebean.endTransaction(); - logger.debug(Ebean.getServer(null).getAdminAutofetch().collectUsageViaGC()); - } - } +// Ebean.getServer(null).getAdminAutofetch().setProfiling(true); +// Ebean.getServer(null).getAdminAutofetch().setQueryTuning(true); +// Ebean.getServer(null).getAdminAutofetch().setProfilingBase(1); +// +// EMain testBean = new EMain(); +// testBean.setName("test"); +// testBean.getEmbeddable().setDescription("test description"); +// Ebean.save(testBean); +// +// //This should not throw an exception +// for (int i = 0; i < 5; i++) { +// Ebean.beginTransaction(); +// try { +// List result = Ebean.find(EMain.class).setAutofetch(true).findList(); +// for (EMain e : result) { +// e.getEmbeddable().setDescription("Test" + i); +// Ebean.save(e); +// } +// Ebean.commitTransaction(); +// } finally { +// Ebean.endTransaction(); +// logger.debug(Ebean.getServer(null).getAdminAutofetch().collectUsageViaGC()); +// } +// } } @Test diff --git a/src/test/java/com/avaje/tests/cache/TestL2CacheWithSharedBean.java b/src/test/java/com/avaje/tests/cache/TestL2CacheWithSharedBean.java index 381da8236..a31184917 100644 --- a/src/test/java/com/avaje/tests/cache/TestL2CacheWithSharedBean.java +++ b/src/test/java/com/avaje/tests/cache/TestL2CacheWithSharedBean.java @@ -8,7 +8,7 @@ import com.avaje.ebean.Ebean; import com.avaje.ebean.Query; import com.avaje.ebean.cache.ServerCache; import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.server.autofetch.TunedQueryInfo; +import com.avaje.ebeaninternal.server.autofetch.service.TunedQueryInfo; import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; import com.avaje.tests.model.basic.FeatureDescription; diff --git a/src/test/java/com/avaje/tests/query/TestAutofetchTuneWithJoin.java b/src/test/java/com/avaje/tests/query/TestAutofetchTuneWithJoin.java index 0693038fe..f9fcfe399 100644 --- a/src/test/java/com/avaje/tests/query/TestAutofetchTuneWithJoin.java +++ b/src/test/java/com/avaje/tests/query/TestAutofetchTuneWithJoin.java @@ -28,8 +28,11 @@ public class TestAutofetchTuneWithJoin extends BaseTestCase { ResetBasicData.reset(); - Query q = Ebean.find(Order.class).setAutofetch(true).fetch("customer") - .fetch("customer.contacts").where().lt("id", 3).query(); + Query q = Ebean.find(Order.class) + .setAutofetch(true) + .fetch("customer") + .fetch("customer.contacts") + .where().lt("id", 3).query(); List list = q.findList(); diff --git a/src/test/resources/ebean.properties b/src/test/resources/ebean.properties index 86562c599..10323b913 100644 --- a/src/test/resources/ebean.properties +++ b/src/test/resources/ebean.properties @@ -11,14 +11,15 @@ ebean.encryptKeyManager=com.avaje.tests.basic.encrypt.BasicEncyptKeyManager -ebean.autofetch.querytuning=false -ebean.autofetch.profiling=false +ebean.autofetch.querytuning=true +ebean.autofetch.profiling=true ebean.autofetch.implicitmode=default_off #ebean.autofetch.implicitmode=default_onifempty ebean.autofetch.profiling.min=1 ebean.autofetch.profiling.base=10 #ebean.autofetch.profiling.rate=0.05 -ebean.autofetch.traceUsageCollection=false +ebean.autofetch.garbageCollectionOnShutdown=true +ebean.autofetch.traceUsageCollection=true ebean.ddl.generate=true diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index b52efc14d..39dcc78bd 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -42,6 +42,8 @@ + + From 5c1de0a886c5b4ece437f6e74de698eb12ff7608 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Wed, 2 Sep 2015 09:28:17 +1200 Subject: [PATCH 2/9] WIP Autotune refactor --- .../server/autofetch/AutoTuneCollection.java | 101 ++++++++++ .../server/autofetch/AutoTuneStorage.java | 17 ++ .../server/autofetch/ProfilingListener.java | 12 ++ .../service/AutoTuneServiceFactory.java | 86 +------- .../service/BaseAutoTuneService.java | 26 +-- .../autofetch/service/BaseQueryTuner.java | 46 +---- .../DefaultAutoFetchManagerLogging.java | 67 ------- .../autofetch/service/ProfileManager.java | 51 ++--- .../autofetch/service/ProfileOrigin.java | 186 +++++++++--------- .../service/ProfileOriginNodeUsage.java | 18 +- .../autofetch/service/ProfileOriginQuery.java | 29 +-- .../autofetch/service/TunedQueryInfo.java | 136 +------------ .../server/core/InternalConfiguration.java | 2 +- .../server/querydefn/OrmQueryDetail.java | 2 +- .../server/autofetch/TunedQueryInfoTest.java | 20 +- .../cache/TestL2CacheWithSharedBean.java | 4 +- 16 files changed, 307 insertions(+), 496 deletions(-) create mode 100644 src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneCollection.java create mode 100644 src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneStorage.java delete mode 100644 src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoFetchManagerLogging.java diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneCollection.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneCollection.java new file mode 100644 index 000000000..e26b892f1 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneCollection.java @@ -0,0 +1,101 @@ +package com.avaje.ebeaninternal.server.autofetch; + +import com.avaje.ebean.bean.ObjectGraphOrigin; +import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; + +import java.util.ArrayList; +import java.util.List; + +/** + * Profiling information collected. + */ +public class AutoTuneCollection { + + List entries = new ArrayList(); + + public Entry add(ObjectGraphOrigin origin, OrmQueryDetail detail) { + Entry entry = new Entry(origin, detail); + entries.add(entry); + return entry; + } + + /** + * Profiling entry at a given origin point. + */ + public static class Entry { + + /** + * Profiling origin point. + */ + private final ObjectGraphOrigin origin; + + /** + * The tuned query detail. + */ + private final OrmQueryDetail detail; + + /** + * Summary execution statistics for queries related to this origin point. + */ + private final List queries = new ArrayList(); + + public Entry(ObjectGraphOrigin origin, OrmQueryDetail detail) { + this.origin = origin; + this.detail = detail; + } + + public void addQuery(EntryQuery entryQuery) { + queries.add(entryQuery); + } + + public ObjectGraphOrigin getOrigin() { + return origin; + } + + public OrmQueryDetail getDetail() { + return detail; + } + + public List getQueries() { + return queries; + } + + } + + /** + * Summary query execution statistics for the origin point. + */ + public static class EntryQuery { + + final String path; + final long exeCount; + final long totalBeanLoaded; + final long totalMicros; + + public EntryQuery(String path, long exeCount, long totalBeanLoaded, long totalMicros) { + this.path = path; + this.exeCount = exeCount; + this.totalBeanLoaded = totalBeanLoaded; + this.totalMicros = totalMicros; + } + + /** + * Return the relative path with empty string for the origin query. + */ + public String getPath() { + return path; + } + + public long getExeCount() { + return exeCount; + } + + public long getTotalBeanLoaded() { + return totalBeanLoaded; + } + + public long getTotalMicros() { + return totalMicros; + } + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneStorage.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneStorage.java new file mode 100644 index 000000000..831cdeee2 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneStorage.java @@ -0,0 +1,17 @@ +package com.avaje.ebeaninternal.server.autofetch; + +/** + * + */ +public interface AutoTuneStorage { + + /** + * Load and return the tuning information. + */ + AutoTuneCollection load(); + + /** + * Store the collected profiling information. + */ + void store(AutoTuneCollection profiling); +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/ProfilingListener.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/ProfilingListener.java index 9885f7435..ca496456f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/ProfilingListener.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/ProfilingListener.java @@ -8,6 +8,18 @@ import com.avaje.ebean.bean.ObjectGraphNode; */ public interface ProfilingListener extends NodeUsageListener { + /** + * Collect summary statistics for a query executed for the given node. + * + * @param node the node relative to the origin point + * @param beans the number of beans loaded by the query + * @param micros the query execution in microseconds + */ void collectQueryInfo(ObjectGraphNode node, long beans, long micros); + /** + * Return true if this request should be profiled (based on the + * profiling ratio and collection count for this origin). + */ + boolean isProfileRequest(ObjectGraphNode origin); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/AutoTuneServiceFactory.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/AutoTuneServiceFactory.java index 1e6c2d1cd..c0327c4fb 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/AutoTuneServiceFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/AutoTuneServiceFactory.java @@ -3,94 +3,12 @@ package com.avaje.ebeaninternal.server.autofetch.service; import com.avaje.ebean.config.ServerConfig; import com.avaje.ebeaninternal.api.SpiEbeanServer; import com.avaje.ebeaninternal.server.autofetch.AutoTuneService; -import com.avaje.ebeaninternal.server.resource.ResourceManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.persistence.PersistenceException; -import java.io.File; -import java.io.FileInputStream; -import java.io.ObjectInputStream; public class AutoTuneServiceFactory { - private static final Logger logger = LoggerFactory.getLogger(AutoTuneServiceFactory.class); + public static AutoTuneService create(SpiEbeanServer server, ServerConfig serverConfig) { - public static AutoTuneService create(SpiEbeanServer server, ServerConfig serverConfig, ResourceManager resourceManager) { - - AutoTuneServiceFactory me = new AutoTuneServiceFactory(); - return me.createAutoFetchManager(server, serverConfig, resourceManager); - } - - private AutoTuneService createAutoFetchManager(SpiEbeanServer server, ServerConfig serverConfig, ResourceManager resourceManager) { - - AutoTuneService manager = createAutoFetchManager(server.getName(), resourceManager); - manager.setOwner(server, serverConfig); - - return manager; - } - - private AutoTuneService createAutoFetchManager(String serverName, ResourceManager resourceManager) { - - File autoFetchFile = getAutoFetchFile(serverName, resourceManager); - - AutoTuneService autoFetchManager = null; - - boolean readFile = !"false".equalsIgnoreCase(System.getProperty("autofetch.readfromfile")); - if (readFile) { - autoFetchManager = deserializeAutoFetch(autoFetchFile); - } - - if (autoFetchManager == null) { - // not deserialized from file so create as empty - // It will be populated automatically by querying the - // database meta data - autoFetchManager = new BaseAutoTuneService(autoFetchFile.getAbsolutePath()); - } - - return autoFetchManager; - } - - private AutoTuneService deserializeAutoFetch(File autoFetchFile) { - try { - - if (!autoFetchFile.exists()) { - return null; - } - FileInputStream fi = new FileInputStream(autoFetchFile); - ObjectInputStream ois = new ObjectInputStream(fi); - AutoTuneService profListener = (AutoTuneService) ois.readObject(); - ois.close(); - - logger.info("AutoFetch deserialized from file [" + autoFetchFile.getAbsolutePath() + "]"); - - return profListener; - - } catch (Exception ex) { - logger.error("Error loading autofetch file " + autoFetchFile.getAbsolutePath(), ex); - return null; - } - } - - /** - * Return the file name of the autoFetch meta data. - */ - private File getAutoFetchFile(String serverName, ResourceManager resourceManager) { - - String fileName = ".ebean." + serverName + ".autofetch"; - - File dir = resourceManager.getAutofetchDirectory(); - - if (!dir.exists()) { - // automatically create the directory if it does not exist. - // this is probably a fairly reasonable thing to do - if (!dir.mkdirs()) { - String m = "Unable to create directory [" + dir + "] for autofetch file [" + fileName + "]"; - throw new PersistenceException(m); - } - } - - return new File(dir, fileName); + return new BaseAutoTuneService(server, serverConfig); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseAutoTuneService.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseAutoTuneService.java index 81ee660b7..9b4923276 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseAutoTuneService.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseAutoTuneService.java @@ -5,14 +5,10 @@ import com.avaje.ebean.config.ServerConfig; import com.avaje.ebeaninternal.api.SpiEbeanServer; import com.avaje.ebeaninternal.api.SpiQuery; import com.avaje.ebeaninternal.server.autofetch.AutoTuneService; +import com.avaje.ebeaninternal.server.autofetch.AutoTuneCollection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.FileOutputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; - /** * Implementation of the AutoTuneService which is comprised of profiling and query tuning. */ @@ -26,11 +22,13 @@ public class BaseAutoTuneService implements AutoTuneService { private final BaseQueryTuner queryTuner; + private final ProfileManager profileManager; + public BaseAutoTuneService(SpiEbeanServer server, ServerConfig serverConfig) { AutofetchConfig config = serverConfig.getAutofetchConfig(); - ProfileManager profileManager = new ProfileManager(config, server); + this.profileManager = new ProfileManager(config, server); this.queryTuner = new BaseQueryTuner(config, server, profileManager); this.garbageCollectionOnShutdown = config.isGarbageCollectionOnShutdown(); @@ -42,24 +40,12 @@ public class BaseAutoTuneService implements AutoTuneService { */ public void startup() { -// File autoFetchFile = new File(fileName); -// -// try { -// FileOutputStream fout = new FileOutputStream(autoFetchFile); -// -// ObjectOutputStream oout = new ObjectOutputStream(fout); -// oout.writeObject(this); -// oout.flush(); -// oout.close(); -// -// } catch (Exception e) { -// String msg = "Error serializing autofetch file"; -// logging.logError(msg, e); -// } } private void saveProfiling() { + AutoTuneCollection autoTuneCollection = profileManager.profilingCollection(false); + } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseQueryTuner.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseQueryTuner.java index a60f73fd9..6ad1fe2c0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseQueryTuner.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseQueryTuner.java @@ -19,16 +19,6 @@ public class BaseQueryTuner { private final boolean queryTuning; - /** - * Converted from a 0-100 int to a double. Effectively a percentage rate at - * which to collect profiling information. - */ - private final double profilingRate; - - private final int profilingBase; - - private final int profilingMin; - private boolean profiling; private final AutofetchMode mode; @@ -49,9 +39,6 @@ public class BaseQueryTuner { this.mode = config.getMode(); this.queryTuning = config.isQueryTuning(); this.profiling = config.isProfiling(); - this.profilingRate = config.getProfilingRate(); - this.profilingBase = config.getProfilingBase(); - this.profilingMin = config.getProfilingMin(); } /** @@ -70,13 +57,12 @@ public class BaseQueryTuner { return false; } - if (!useAutoFetch(query)) { + if (!useAutoTune(query)) { // not using autoFetch for this query return false; } - ObjectGraphNode parentAutoFetchNode = query.getParentNode(); - if (parentAutoFetchNode != null) { + if (query.getParentNode() != null) { // This is a +lazy/+query query with profiling on. // We continue to collect the profiling information. query.setProfilingListener(profilingListener); @@ -87,37 +73,25 @@ public class BaseQueryTuner { CallStack stack = server.createCallStack(); ObjectGraphNode origin = query.setOrigin(stack); - // get current "tuned fetch" for this query point - TunedQueryInfo tunedFetch = tunedQueryInfoMap.get(origin.getOriginQueryPoint().getKey()); - - // get the number of times we have collected profiling information - int profileCount = tunedFetch == null ? 0 : tunedFetch.getProfileCount(); - if (profiling) { - // we want more profiling information? - if (tunedFetch == null) { - query.setProfilingListener(profilingListener); - - } else if (profileCount < profilingBase) { - query.setProfilingListener(profilingListener); - - } else if (tunedFetch.isPercentageProfile(profilingRate)) { + if (profilingListener.isProfileRequest(origin)) { + // collect more profiling based on profiling rate etc query.setProfilingListener(profilingListener); } } - if (queryTuning && tunedFetch != null && profileCount >= profilingMin) { - // deemed to have enough profiling information for automatic tuning - return tunedFetch.autoFetchTune(query); + if (queryTuning) { + // get current "tuned fetch" for this query point + TunedQueryInfo tuneInfo = tunedQueryInfoMap.get(origin.getOriginQueryPoint().getKey()); + return tuneInfo != null && tuneInfo.tuneQuery(query); } - return false; } /** - * Return true if we should try to use autoFetch for this query. + * Return true if we should try to tune this query. */ - private boolean useAutoFetch(SpiQuery query) { + private boolean useAutoTune(SpiQuery query) { if (query.isLoadBeanCache()) { // when loading the cache don't tune the query diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoFetchManagerLogging.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoFetchManagerLogging.java deleted file mode 100644 index 8ba42bb6b..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoFetchManagerLogging.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.avaje.ebeaninternal.server.autofetch.service; - -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebeaninternal.api.SpiEbeanServer; -import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.concurrent.TimeUnit; - -/** - * Handles the logging aspects for the DefaultAutoFetchListener. - *

- * Note that java util logging loggers generally should not be serialised and - * that is one of the main reasons for pulling out the logging to this class. - *

- */ -public class DefaultAutoFetchManagerLogging { - - private static final Logger logger = LoggerFactory.getLogger(DefaultAutoFetchManagerLogging.class); - - private final BaseAutoTuneService manager; - - private final int updateFreqInSecs; - - public DefaultAutoFetchManagerLogging(ServerConfig serverConfig, BaseAutoTuneService profileListener) { - - this.manager = profileListener; - this.updateFreqInSecs = serverConfig.getAutofetchConfig().getProfileUpdateFrequency(); - } - - public void init(SpiEbeanServer ebeanServer) { - ebeanServer.getBackgroundExecutor().executePeriodically(new UpdateProfile(), updateFreqInSecs, TimeUnit.SECONDS); - } - - private final class UpdateProfile implements Runnable { - public void run() { - manager.updateTunedQueryInfo(); - } - } - - public void logInfo(String msg, Throwable e) { - logger.info(msg, e); - } - - public void logError(String msg, Throwable e) { - logger.error(msg, e); - } - - public void logSummary(String summaryInfo) { - - String msg = "\"Summary\",\"" + summaryInfo + "\",,,,"; - logger.debug(msg); - } - - public void logChanged(TunedQueryInfo tunedFetch, OrmQueryDetail newQueryDetail) { - - String msg = tunedFetch.getLogOutput(newQueryDetail); - logger.debug(msg); - } - - public void logNew(TunedQueryInfo tunedFetch) { - - String msg = tunedFetch.getLogOutput(null); - logger.debug(msg); - } -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileManager.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileManager.java index a9bada622..b76e66e47 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileManager.java @@ -5,9 +5,9 @@ import com.avaje.ebean.bean.ObjectGraphNode; import com.avaje.ebean.bean.ObjectGraphOrigin; import com.avaje.ebean.config.AutofetchConfig; import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.server.autofetch.AutoTuneCollection; import com.avaje.ebeaninternal.server.autofetch.ProfilingListener; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; -import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,7 +23,13 @@ public class ProfileManager implements ProfilingListener { private final boolean queryTuningAddVersion; - private final boolean profiling; + /** + * Converted from a 0-100 int to a double. Effectively a percentage rate at + * which to collect profiling information. + */ + private final double profilingRate; + + private final int profilingBase; /** * Map of the usage and query statistics gathered. @@ -36,10 +42,18 @@ public class ProfileManager implements ProfilingListener { public ProfileManager(AutofetchConfig config, SpiEbeanServer server) { this.server = server; - this.profiling = config.isProfiling(); + this.profilingRate = config.getProfilingRate(); + this.profilingBase = config.getProfilingBase(); this.queryTuningAddVersion = config.isQueryTuningAddVersion(); } + @Override + public boolean isProfileRequest(ObjectGraphNode origin) { + + ProfileOrigin profileOrigin = profileMap.get(origin.getOriginQueryPoint().getKey()); + return profileOrigin == null || profileOrigin.isProfile(); + } + /** * Gather query execution statistics. This could either be the originating * query in which case the parentNode will be null, or a lazy loading query @@ -73,40 +87,29 @@ public class ProfileManager implements ProfilingListener { synchronized (monitor) { ProfileOrigin stats = profileMap.get(originQueryPoint.getKey()); if (stats == null) { - stats = new ProfileOrigin(originQueryPoint, queryTuningAddVersion); + stats = new ProfileOrigin(originQueryPoint, queryTuningAddVersion, profilingBase, profilingRate); profileMap.put(originQueryPoint.getKey(), stats); } return stats; } } - /** - * Update the tuned fetch plans from the current usage information. + * Collect all the profiling information. */ - public void updateTunedQueryInfo() { + public AutoTuneCollection profilingCollection(boolean reset) { - if (!profiling) { - // we are not collecting any profiling information at - // the moment so don't try updating the tuned query plans. - return;// "Not profiling"; - } + AutoTuneCollection req = new AutoTuneCollection(); - synchronized (monitor) { + for (ProfileOrigin origin : profileMap.values()) { - for (ProfileOrigin origin : profileMap.values()) { - if (origin.hasUsage()) { - OrmQueryDetail ormQueryDetail = updateTunedQueryFromUsage(origin); - - } + BeanDescriptor desc = server.getBeanDescriptorById(origin.getOrigin().getBeanType()); + if (desc != null) { + origin.profilingCollection(desc, req, reset); } } + + return req; } - - private OrmQueryDetail updateTunedQueryFromUsage(ProfileOrigin statistics) { - - BeanDescriptor desc = server.getBeanDescriptorById(statistics.getOrigin().getBeanType()); - return desc == null ? null : statistics.buildTunedFetch(desc); - } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOrigin.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOrigin.java index 959b740a2..84f758c0b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOrigin.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOrigin.java @@ -5,106 +5,127 @@ import com.avaje.ebean.bean.ObjectGraphNode; import com.avaje.ebean.bean.ObjectGraphOrigin; import com.avaje.ebean.text.PathProperties; import com.avaje.ebean.text.PathProperties.Props; +import com.avaje.ebeaninternal.server.autofetch.AutoTuneCollection; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; -import java.io.Serializable; import java.util.Collection; -import java.util.LinkedHashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; -public class ProfileOrigin implements Serializable { +public class ProfileOrigin { - - private static final long serialVersionUID = -5586783791097230766L; + private static final long RESET_COUNT = -1000000000L; private final ObjectGraphOrigin origin; private final boolean queryTuningAddVersion; - private int counter; + private final int profilingBase; - private final Map queryStatsMap = new LinkedHashMap(); + private final double profilingRate; - private final Map nodeUsageMap = new LinkedHashMap(); + private final Map queryStatsMap = new ConcurrentHashMap(); - @SuppressWarnings("RedundantStringConstructorCall") - private final String monitor = new String(); + private final Map nodeUsageMap = new ConcurrentHashMap(); - public ProfileOrigin(ObjectGraphOrigin origin, boolean queryTuningAddVersion) { + private final Object monitor = new Object(); + + private final AtomicLong requestCount = new AtomicLong(); + + private final AtomicLong profileCount = new AtomicLong(); + + public ProfileOrigin(ObjectGraphOrigin origin, boolean queryTuningAddVersion, int profilingBase, double profilingRate) { this.origin = origin; this.queryTuningAddVersion = queryTuningAddVersion; + this.profilingBase = profilingBase; + this.profilingRate = profilingRate; } + /** + * Return true if this query should be profiled based on a percentage rate. + */ + public boolean isProfile() { + + long count = requestCount.incrementAndGet(); + if (count < profilingBase) { + return true; + } + long hits = profileCount.get(); + if (profilingRate > (double) hits / count) { + profileCount.incrementAndGet(); + return true; + } else { + return false; + } + } + + /** + * Collect profiling information with the option to reset the underlying profiling detail. + */ + public void profilingCollection(BeanDescriptor rootDesc, AutoTuneCollection req, boolean reset) { + + synchronized (monitor) { + if (nodeUsageMap.isEmpty()) { + return; + } + + OrmQueryDetail detail = buildDetail(rootDesc); + AutoTuneCollection.Entry entry = req.add(origin, detail); + + Collection values = queryStatsMap.values(); + for (ProfileOriginQuery queryEntry : values) { + entry.addQuery(queryEntry.createEntryQuery(reset)); + } + if (reset) { + nodeUsageMap.clear(); + if (requestCount.get() > RESET_COUNT) { + requestCount.set(profilingBase); + profileCount.set(0); + } + } + } + } + + private OrmQueryDetail buildDetail(BeanDescriptor rootDesc) { + PathProperties pathProps = new PathProperties(); + + for (ProfileOriginNodeUsage statsNode : nodeUsageMap.values()) { + statsNode.buildTunedFetch(pathProps, rootDesc); + } + + OrmQueryDetail detail = new OrmQueryDetail(); + + Collection pathProperties = pathProps.getPathProps(); + for (Props props : pathProperties) { + if (!props.isEmpty()) { + detail.addFetch(props.getPath(), props.getPropertiesAsString(), null); + } + } + + detail.sortFetchPaths(rootDesc); + return detail; + } + + /** + * Return the origin. + */ public ObjectGraphOrigin getOrigin() { return origin; } -// public TunedQueryInfo createTunedFetch(OrmQueryDetail newFetchDetail) { -// synchronized (monitor) { -// // NB: create a copy of queryPoint allowing garbage -// // collection of source... -// return new TunedQueryInfo(origin, newFetchDetail, counter); -// } -// } - /** - * Return the number of times the root query has executed. + * Collect query execution summary statistics. *

- * This tells us how much profiling we have done for this query. - * For example, after 100 times we may stop collecting more profiling info. + * This can give us a quick overview into bad lazy loading areas etc. *

*/ - public int getCounter() { - return counter; - } - - /** - * Return true if this has usage statistics. - */ - public boolean hasUsage() { - synchronized (monitor) { - return !nodeUsageMap.isEmpty(); - } - } - - public OrmQueryDetail buildTunedFetch(BeanDescriptor rootDesc) { - - synchronized (monitor) { - if (nodeUsageMap.isEmpty()) { - return null; - } - - PathProperties pathProps = new PathProperties(); - - for (ProfileOriginNodeUsage statsNode : nodeUsageMap.values()) { - statsNode.buildTunedFetch(pathProps, rootDesc); - } - - OrmQueryDetail detail = new OrmQueryDetail(); - - Collection pathProperties = pathProps.getPathProps(); - for (Props props : pathProperties) { - if (!props.isEmpty()) { - detail.addFetch(props.getPath(), props.getPropertiesAsString(), null); - } - } - - detail.sortFetchPaths(rootDesc); - return detail; - } - } - - public void collectQueryInfo(ObjectGraphNode node, long beansLoaded, long micros) { String key = node.getPath(); if (key == null) { key = ""; - // this is basically the number of times the root query - // has executed which gives us an indication of how - // much profiling information we have gathered. - counter++; } ProfileOriginQuery stats = queryStatsMap.get(key); @@ -116,7 +137,6 @@ public class ProfileOrigin implements Serializable { stats.add(beansLoaded, micros); } - /** * Collect the usage information for from a instance for this node. */ @@ -126,7 +146,7 @@ public class ProfileOrigin implements Serializable { ObjectGraphNode node = profile.getNode(); ProfileOriginNodeUsage nodeStats = getNodeStats(node.getPath()); - nodeStats.publish(profile); + nodeStats.collectUsageInfo(profile); } } @@ -142,32 +162,4 @@ public class ProfileOrigin implements Serializable { } } -// public String getUsageDebug() { -// synchronized (monitor) { -// StringBuilder sb = new StringBuilder(); -// sb.append("root[").append(origin.getBeanType()).append("] "); -// for (ProfileOriginNodeUsage node : nodeUsageMap.values()) { -// sb.append(node.toString()).append("\n"); -// } -// return sb.toString(); -// } -// } -// -// public String getQueryStatDebug() { -// synchronized (monitor) { -// StringBuilder sb = new StringBuilder(); -// for (ProfileOriginQuery queryStat : queryStatsMap.values()) { -// sb.append(queryStat.toString()).append("\n"); -// } -// return sb.toString(); -// } -// } -// -// public String toString() { -// -// synchronized (monitor) { -// return getUsageDebug(); -// } -// } - } diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginNodeUsage.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginNodeUsage.java index a051bc31f..632ac8097 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginNodeUsage.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginNodeUsage.java @@ -10,22 +10,17 @@ import com.avaje.ebeaninternal.server.query.SplitName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.Serializable; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; /** * Collects usages statistics for a given node in the object graph. */ -public class ProfileOriginNodeUsage implements Serializable { - - private static final long serialVersionUID = -1663951463963779547L; +public class ProfileOriginNodeUsage { private static final Logger logger = LoggerFactory.getLogger(ProfileOriginNodeUsage.class); - @SuppressWarnings("RedundantStringConstructorCall") - private final String monitor = new String(); + private final Object monitor = new Object(); private final String path; @@ -53,7 +48,7 @@ public class ProfileOriginNodeUsage implements Serializable { ElPropertyValue elGetValue = rootDesc.getElGetValue(path); if (elGetValue == null) { desc = null; - logger.warn("Autofetch: Can't find join for path[" + path + "] for " + rootDesc.getName()); + logger.warn("AutoTune: Can't find join for path[" + path + "] for " + rootDesc.getName()); } else { BeanProperty beanProperty = elGetValue.getBeanProperty(); @@ -66,7 +61,7 @@ public class ProfileOriginNodeUsage implements Serializable { for (String propName : aggregateUsed) { BeanProperty beanProp = desc.getBeanPropertyFromPath(propName); if (beanProp == null) { - logger.warn("Autofetch: Can't find property[" + propName + "] for " + desc.getName()); + logger.warn("AutoTune: Can't find property[" + propName + "] for " + desc.getName()); } else { if (beanProp instanceof BeanPropertyAssoc) { @@ -95,7 +90,10 @@ public class ProfileOriginNodeUsage implements Serializable { } } - public void publish(NodeUsageCollector profile) { + /** + * Collect usage from a node. + */ + public void collectUsageInfo(NodeUsageCollector profile) { synchronized (monitor) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginQuery.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginQuery.java index b4d2b6bfa..344d040dc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginQuery.java @@ -1,9 +1,12 @@ package com.avaje.ebeaninternal.server.autofetch.service; +import com.avaje.ebeaninternal.server.autofetch.AutoTuneCollection; +import com.avaje.ebeaninternal.server.util.LongAdder; + import java.io.Serializable; /** - * Used to accumulate query execution statistics. + * Used to accumulate query execution statistics for paths relative to the origin query. */ public class ProfileOriginQuery implements Serializable { @@ -11,26 +14,30 @@ public class ProfileOriginQuery implements Serializable { private final String path; - private long exeCount; + private final LongAdder exeCount = new LongAdder(); - private long totalBeanLoaded; + private final LongAdder totalBeanLoaded = new LongAdder(); - private long totalMicros; + private final LongAdder totalMicros = new LongAdder(); public ProfileOriginQuery(String path) { this.path = path; } public void add(long beansLoaded, long micros) { - exeCount++; - totalBeanLoaded += beansLoaded; - totalMicros += micros; + exeCount.increment(); + totalBeanLoaded.add(beansLoaded); + totalMicros.add(micros); } - public String toString() { - long avgMicros = exeCount == 0 ? 0 : totalMicros / exeCount; + public AutoTuneCollection.EntryQuery createEntryQuery(boolean reset){ - return "queryExe path[" + path + "] count[" + exeCount + "] totalBeansLoaded[" + totalBeanLoaded + "] avgMicros[" - + avgMicros + "] totalMicros[" + totalMicros + "]"; + if (reset) { + return new AutoTuneCollection.EntryQuery(path, exeCount.sumThenReset(), totalBeanLoaded.sumThenReset(), totalMicros.sumThenReset()); + + } else { + return new AutoTuneCollection.EntryQuery(path, exeCount.sum(), totalBeanLoaded.sum(), totalMicros.sum()); + } } + } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/TunedQueryInfo.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/TunedQueryInfo.java index 8a6a5d046..b1e554cbd 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/TunedQueryInfo.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/TunedQueryInfo.java @@ -12,90 +12,13 @@ import java.io.Serializable; */ public class TunedQueryInfo implements Serializable { - private static final long serialVersionUID = 7381493228797997282L; - private final ObjectGraphOrigin origin; - /** - * The tuned query details with joins and properties. - */ - private OrmQueryDetail tunedDetail; + private final OrmQueryDetail tunedDetail; - /** - * The number of times profiling has been collected for this query point. - */ - private int profileCount; - - private Long lastTuneTime = (long) 0; - - @SuppressWarnings("RedundantStringConstructorCall") - private final String rateMonitor = new String(); - - /** - * The number of queries tuned by this object. - * Could use AtomicInteger perhaps. - */ - private transient int tunedCount; - - private transient int rateTotal; - - private transient int rateHits; - - private transient double lastRate; - - public TunedQueryInfo(ObjectGraphOrigin queryPoint, OrmQueryDetail tunedDetail, int profileCount) { + public TunedQueryInfo(ObjectGraphOrigin queryPoint, OrmQueryDetail tunedDetail) { this.origin = queryPoint; this.tunedDetail = tunedDetail; - this.profileCount = profileCount; - } - - /** - * Return true if this query should be profiled based on a percentage rate. - */ - public boolean isPercentageProfile(double rate) { - - synchronized (rateMonitor) { - - if (lastRate != rate) { - // the rate has changed so resetting - lastRate = rate; - rateTotal = 0; - rateHits = 0; - } - - rateTotal++; - if (rate > (double) rateHits / rateTotal) { - rateHits++; - return true; - } else { - return false; - } - } - } - - /** - * Set the number of times profiling has been collected for this query - * point. - */ - public void setProfileCount(int profileCount) { - // int assignment is atomic - this.profileCount = profileCount; - } - - /** - * Set the tuned query detail. - */ - public void setTunedDetail(OrmQueryDetail tunedDetail) { - // assignment is atomic - this.tunedDetail = tunedDetail; - this.lastTuneTime = System.currentTimeMillis(); - } - - /** - * Return true if the fetches are essentially the same. - */ - public boolean isSame(OrmQueryDetail newQueryDetail) { - return tunedDetail != null && tunedDetail.isAutoFetchEqual(newQueryDetail); } /** @@ -103,13 +26,12 @@ public class TunedQueryInfo implements Serializable { * * @return true if the query was tuned, otherwise false. */ - public boolean autoFetchTune(SpiQuery query) { + public boolean tuneQuery(SpiQuery query) { if (tunedDetail == null) { return false; } boolean tuned; - //Note: tunedDetail is immutable by convention if (query.isDetailEmpty()) { tuned = true; // tune by 'replacement' @@ -120,62 +42,10 @@ public class TunedQueryInfo implements Serializable { } if (tuned) { query.setAutoFetchTuned(true); - // a case for AtomicInteger but good enough for statistics - tunedCount++; } return tuned; } - /** - * Return the time of the last tune. - */ - public Long getLastTuneTime() { - return lastTuneTime; - } - - /** - * Return the number of queries tuned by this object. - */ - public int getTunedCount() { - return tunedCount; - } - - /** - * Return the number of times profiling has been collected for this query - * point. - */ - public int getProfileCount() { - return profileCount; - } - - public OrmQueryDetail getTunedDetail() { - return tunedDetail; - } - - public ObjectGraphOrigin getOrigin() { - return origin; - } - - public String getLogOutput(OrmQueryDetail newQueryDetail) { - - boolean changed = newQueryDetail != null; - - StringBuilder sb = new StringBuilder(150); - sb.append(changed ? "\"Changed\"," : "\"New\","); - sb.append("\"").append(origin.getBeanType()).append("\","); - sb.append("\"").append(origin.getKey()).append("\","); - if (changed) { - sb.append("\"to: ").append(newQueryDetail.toString()).append("\","); - sb.append("\"from: ").append(tunedDetail.toString()).append("\","); - } else { - sb.append("\"to: ").append(tunedDetail.toString()).append("\","); - sb.append("\"\","); - } - sb.append("\"").append(origin.getFirstStackElement()).append("\""); - - return sb.toString(); - } - public String toString() { return origin.getBeanType() + " " + origin.getKey() + " " + tunedDetail; } 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 c1148d0cf..62c1e2470 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java @@ -236,7 +236,7 @@ public class InternalConfiguration { } public AutoTuneService createAutoFetchManager(SpiEbeanServer server) { - return AutoTuneServiceFactory.create(server, serverConfig, resourceManager); + return AutoTuneServiceFactory.create(server, serverConfig); } public RelationalQueryEngine createRelationalQueryEngine() { diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java index b7a2885d8..a65edfa16 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java @@ -82,7 +82,7 @@ public class OrmQueryDetail implements Serializable { /** * Return true if equal in terms of autofetch (select and joins). */ - public boolean isAutoFetchEqual(OrmQueryDetail otherDetail) { + public boolean isAutoTuneEqual(OrmQueryDetail otherDetail) { return autofetchPlanHash() == otherDetail.autofetchPlanHash(); } diff --git a/src/test/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfoTest.java b/src/test/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfoTest.java index eaf1ec8be..a27881bc5 100644 --- a/src/test/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfoTest.java +++ b/src/test/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfoTest.java @@ -41,11 +41,11 @@ public class TunedQueryInfoTest extends BaseTestCase { OrmQueryDetail tunedDetail = new OrmQueryDetail(); tunedDetail.select(null); - TunedQueryInfo tunedInfo = new TunedQueryInfo(null, tunedDetail, 0); + TunedQueryInfo tunedInfo = new TunedQueryInfo(null, tunedDetail); Query query = server.find(Order.class).setId(1); - tunedInfo.autoFetchTune((SpiQuery)query); + tunedInfo.tuneQuery((SpiQuery) query); Order order = query.findUnique(); EntityBean eb = (EntityBean)order; @@ -69,11 +69,11 @@ public class TunedQueryInfoTest extends BaseTestCase { OrmQueryDetail tunedDetail = new OrmQueryDetail(); tunedDetail.select(""); - TunedQueryInfo tunedInfo = new TunedQueryInfo(null, tunedDetail, 0); + TunedQueryInfo tunedInfo = new TunedQueryInfo(null, tunedDetail); Query query = server.find(Order.class).setId(1); - tunedInfo.autoFetchTune((SpiQuery)query); + tunedInfo.tuneQuery((SpiQuery) query); Order order = query.findUnique(); EntityBean eb = (EntityBean)order; @@ -96,11 +96,11 @@ public class TunedQueryInfoTest extends BaseTestCase { OrmQueryDetail tunedDetail = new OrmQueryDetail(); tunedDetail.select("somethingThatDoesNotExist"); - TunedQueryInfo tunedInfo = new TunedQueryInfo(null, tunedDetail, 0); + TunedQueryInfo tunedInfo = new TunedQueryInfo(null, tunedDetail); Query query = server.find(Order.class).setId(1); - tunedInfo.autoFetchTune((SpiQuery)query); + tunedInfo.tuneQuery((SpiQuery) query); LoggedSqlCollector.start(); @@ -132,11 +132,11 @@ public class TunedQueryInfoTest extends BaseTestCase { OrmQueryDetail tunedDetail = new OrmQueryDetail(); tunedDetail.select("status, customer"); - TunedQueryInfo tunedInfo = new TunedQueryInfo(null, tunedDetail, 0); + TunedQueryInfo tunedInfo = new TunedQueryInfo(null, tunedDetail); Query query = server.find(Order.class).setId(1); - tunedInfo.autoFetchTune((SpiQuery)query); + tunedInfo.tuneQuery((SpiQuery) query); LoggedSqlCollector.start(); @@ -167,11 +167,11 @@ public class TunedQueryInfoTest extends BaseTestCase { OrmQueryDetail tunedDetail = new OrmQueryDetail(); tunedDetail.select("status"); - TunedQueryInfo tunedInfo = new TunedQueryInfo(null, tunedDetail, 0); + TunedQueryInfo tunedInfo = new TunedQueryInfo(null, tunedDetail); Query query = server.find(Order.class).setId(1); - tunedInfo.autoFetchTune((SpiQuery)query); + tunedInfo.tuneQuery((SpiQuery) query); LoggedSqlCollector.start(); diff --git a/src/test/java/com/avaje/tests/cache/TestL2CacheWithSharedBean.java b/src/test/java/com/avaje/tests/cache/TestL2CacheWithSharedBean.java index a31184917..c3367d686 100644 --- a/src/test/java/com/avaje/tests/cache/TestL2CacheWithSharedBean.java +++ b/src/test/java/com/avaje/tests/cache/TestL2CacheWithSharedBean.java @@ -28,11 +28,11 @@ public class TestL2CacheWithSharedBean extends BaseTestCase { OrmQueryDetail tunedDetail = new OrmQueryDetail(); tunedDetail.select("name"); - TunedQueryInfo tunedInfo = new TunedQueryInfo(null, tunedDetail, 0); + TunedQueryInfo tunedInfo = new TunedQueryInfo(null, tunedDetail); Query query = Ebean.find(FeatureDescription.class).setId(f1.getId()); - tunedInfo.autoFetchTune((SpiQuery) query); + tunedInfo.tuneQuery((SpiQuery) query); query.findUnique(); // PUT into cache From 5b8ce8eaa0d95022b98dcb7cecc47a563145b058 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Fri, 4 Sep 2015 08:48:25 +1200 Subject: [PATCH 3/9] WIP AutoTune refactor --- ebean-autotune-profiling.xml | 32 ++++ ebean-autotune.xml | 31 ++++ .../java/com/avaje/ebean/bean/CallStack.java | 11 ++ .../server/autofetch/AutoTuneCollection.java | 4 + .../server/autofetch/model/Autotune.java | 123 +++++++++++++ .../server/autofetch/model/ObjectFactory.java | 64 +++++++ .../server/autofetch/model/Origin.java | 171 ++++++++++++++++++ .../server/autofetch/model/ProfileDiff.java | 69 +++++++ .../server/autofetch/model/ProfileNew.java | 69 +++++++ .../server/autofetch/model/package-info.java | 2 + .../autofetch/service/AutoTuneXmlReader.java | 57 ++++++ .../autofetch/service/AutoTuneXmlWriter.java | 32 ++++ .../service/BaseAutoTuneService.java | 66 ++++++- .../autofetch/service/BaseQueryTuner.java | 28 ++- .../autofetch/service/ProfileOrigin.java | 16 +- .../service/ProfileOriginNodeUsage.java | 15 +- .../autofetch/service/TunedQueryInfo.java | 14 +- .../server/core/DefaultServer.java | 2 + .../server/querydefn/OrmQueryDetail.java | 50 +++-- .../querydefn/OrmQueryDetailParser.java | 20 +- .../server/querydefn/OrmQueryProperties.java | 7 + src/main/resources/ebean-autotune-1.0.xsd | 46 +++++ .../querydefn/OrmQueryDetailParserTest.java | 80 ++++++++ .../server/querydefn/OrmQueryDetailTest.java | 21 +++ .../query/TestAutofetchTuneWithJoin.java | 4 +- .../query/autotune/TestAutoTuneProfiling.java | 82 +++++++++ .../resources/autotune/test-profiling.xml | 10 + 27 files changed, 1092 insertions(+), 34 deletions(-) create mode 100644 ebean-autotune-profiling.xml create mode 100644 ebean-autotune.xml create mode 100644 src/main/java/com/avaje/ebeaninternal/server/autofetch/model/Autotune.java create mode 100644 src/main/java/com/avaje/ebeaninternal/server/autofetch/model/ObjectFactory.java create mode 100644 src/main/java/com/avaje/ebeaninternal/server/autofetch/model/Origin.java create mode 100644 src/main/java/com/avaje/ebeaninternal/server/autofetch/model/ProfileDiff.java create mode 100644 src/main/java/com/avaje/ebeaninternal/server/autofetch/model/ProfileNew.java create mode 100644 src/main/java/com/avaje/ebeaninternal/server/autofetch/model/package-info.java create mode 100644 src/main/java/com/avaje/ebeaninternal/server/autofetch/service/AutoTuneXmlReader.java create mode 100644 src/main/java/com/avaje/ebeaninternal/server/autofetch/service/AutoTuneXmlWriter.java create mode 100644 src/main/resources/ebean-autotune-1.0.xsd create mode 100644 src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailParserTest.java create mode 100644 src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailTest.java create mode 100644 src/test/java/com/avaje/tests/query/autotune/TestAutoTuneProfiling.java create mode 100644 src/test/resources/autotune/test-profiling.xml diff --git a/ebean-autotune-profiling.xml b/ebean-autotune-profiling.xml new file mode 100644 index 000000000..3911ea279 --- /dev/null +++ b/ebean-autotune-profiling.xml @@ -0,0 +1,32 @@ + + + + + + + com.avaje.tests.query.autotune.TestAutoTuneProfiling.findById(TestAutoTuneProfiling.java:45) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.useOrderDate(TestAutoTuneProfiling.java:49) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.execute(TestAutoTuneProfiling.java:35) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.test(TestAutoTuneProfiling.java:26) + sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + + + + + + + + + + com.avaje.tests.query.autotune.TestAutoTuneProfiling.findById(TestAutoTuneProfiling.java:45) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.useOrderDateCustomerName(TestAutoTuneProfiling.java:55) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.execute(TestAutoTuneProfiling.java:36) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.test(TestAutoTuneProfiling.java:26) + sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + + + + + diff --git a/ebean-autotune.xml b/ebean-autotune.xml new file mode 100644 index 000000000..73563dec3 --- /dev/null +++ b/ebean-autotune.xml @@ -0,0 +1,31 @@ + + + + + com.avaje.tests.query.autotune.TestAutoTuneProfiling.findById(TestAutoTuneProfiling.java:45) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.useLots(TestAutoTuneProfiling.java:62) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.execute(TestAutoTuneProfiling.java:37) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.test(TestAutoTuneProfiling.java:26) + sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + + + + com.avaje.tests.query.autotune.TestAutoTuneProfiling.findById(TestAutoTuneProfiling.java:45) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.useOrderDate(TestAutoTuneProfiling.java:49) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.execute(TestAutoTuneProfiling.java:35) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.test(TestAutoTuneProfiling.java:26) + sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + + + + + + + + + + + + + diff --git a/src/main/java/com/avaje/ebean/bean/CallStack.java b/src/main/java/com/avaje/ebean/bean/CallStack.java index 0f4b78f0c..f283ea591 100644 --- a/src/main/java/com/avaje/ebean/bean/CallStack.java +++ b/src/main/java/com/avaje/ebean/bean/CallStack.java @@ -87,6 +87,17 @@ public final class CallStack implements Serializable { return zeroHash + ":" + pathHash + ":" + callStack[0]; } + /** + * Return the call stack lines appended with the given newLine string. + */ + public String description(String newLine) { + StringBuilder sb = new StringBuilder(400); + for (int i = 0; i < callStack.length; i++) { + sb.append(callStack[i].toString()).append(newLine); + } + return sb.toString(); + } + public String getOriginKey(int queryHash) { return zeroHash + "." + enc(queryHash) + "." + pathHash; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneCollection.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneCollection.java index e26b892f1..eb62059b9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneCollection.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneCollection.java @@ -19,6 +19,10 @@ public class AutoTuneCollection { return entry; } + public List getEntries() { + return entries; + } + /** * Profiling entry at a given origin point. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/Autotune.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/Autotune.java new file mode 100644 index 000000000..4be03eb68 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/Autotune.java @@ -0,0 +1,123 @@ + +package com.avaje.ebeaninternal.server.autofetch.model; + +import java.util.ArrayList; +import java.util.List; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + *

Java class for anonymous complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+ * <complexType>
+ *   <complexContent>
+ *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       <sequence>
+ *         <element ref="{http://ebean-orm.github.io/xml/ns/autotune}origin" maxOccurs="unbounded" minOccurs="0"/>
+ *         <element ref="{http://ebean-orm.github.io/xml/ns/autotune}profileDiff" minOccurs="0"/>
+ *         <element ref="{http://ebean-orm.github.io/xml/ns/autotune}profileNew" minOccurs="0"/>
+ *       </sequence>
+ *     </restriction>
+ *   </complexContent>
+ * </complexType>
+ * 
+ * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "origin", + "profileDiff", + "profileNew" +}) +@XmlRootElement(name = "autotune") +public class Autotune { + + protected List origin; + protected ProfileDiff profileDiff; + protected ProfileNew profileNew; + + /** + * Gets the value of the origin property. + * + *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the origin property. + * + *

+ * For example, to add a new item, do as follows: + *

+     *    getOrigin().add(newItem);
+     * 
+ * + * + *

+ * Objects of the following type(s) are allowed in the list + * {@link Origin } + * + * + */ + public List getOrigin() { + if (origin == null) { + origin = new ArrayList(); + } + return this.origin; + } + + /** + * Gets the value of the profileDiff property. + * + * @return + * possible object is + * {@link ProfileDiff } + * + */ + public ProfileDiff getProfileDiff() { + return profileDiff; + } + + /** + * Sets the value of the profileDiff property. + * + * @param value + * allowed object is + * {@link ProfileDiff } + * + */ + public void setProfileDiff(ProfileDiff value) { + this.profileDiff = value; + } + + /** + * Gets the value of the profileNew property. + * + * @return + * possible object is + * {@link ProfileNew } + * + */ + public ProfileNew getProfileNew() { + return profileNew; + } + + /** + * Sets the value of the profileNew property. + * + * @param value + * allowed object is + * {@link ProfileNew } + * + */ + public void setProfileNew(ProfileNew value) { + this.profileNew = value; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/ObjectFactory.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/ObjectFactory.java new file mode 100644 index 000000000..a6fc83ab8 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/ObjectFactory.java @@ -0,0 +1,64 @@ + +package com.avaje.ebeaninternal.server.autofetch.model; + +import javax.xml.bind.annotation.XmlRegistry; + + +/** + * This object contains factory methods for each + * Java content interface and Java element interface + * generated in the com.avaje.ebeaninternal.server.autofetch.model package. + *

An ObjectFactory allows you to programatically + * construct new instances of the Java representation + * for XML content. The Java representation of XML + * content can consist of schema derived interfaces + * and classes representing the binding of schema + * type definitions, element declarations and model + * groups. Factory methods for each of these are + * provided in this class. + * + */ +@XmlRegistry +public class ObjectFactory { + + + /** + * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.avaje.ebeaninternal.server.autofetch.model + * + */ + public ObjectFactory() { + } + + /** + * Create an instance of {@link ProfileNew } + * + */ + public ProfileNew createProfileNew() { + return new ProfileNew(); + } + + /** + * Create an instance of {@link Origin } + * + */ + public Origin createOrigin() { + return new Origin(); + } + + /** + * Create an instance of {@link Autotune } + * + */ + public Autotune createAutotune() { + return new Autotune(); + } + + /** + * Create an instance of {@link ProfileDiff } + * + */ + public ProfileDiff createProfileDiff() { + return new ProfileDiff(); + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/Origin.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/Origin.java new file mode 100644 index 000000000..86eebba46 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/Origin.java @@ -0,0 +1,171 @@ + +package com.avaje.ebeaninternal.server.autofetch.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + *

Java class for anonymous complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+ * <complexType>
+ *   <complexContent>
+ *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       <sequence>
+ *         <element name="callStack" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
+ *       </sequence>
+ *       <attribute name="key" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *       <attribute name="beanType" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *       <attribute name="detail" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *       <attribute name="tuneDetail" type="{http://www.w3.org/2001/XMLSchema}string" />
+ *     </restriction>
+ *   </complexContent>
+ * </complexType>
+ * 
+ * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "callStack" +}) +@XmlRootElement(name = "origin") +public class Origin { + + protected String callStack; + @XmlAttribute(name = "key", required = true) + protected String key; + @XmlAttribute(name = "beanType") + protected String beanType; + @XmlAttribute(name = "detail") + protected String detail; + @XmlAttribute(name = "tuneDetail") + protected String tuneDetail; + + /** + * Gets the value of the callStack property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getCallStack() { + return callStack; + } + + /** + * Sets the value of the callStack property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setCallStack(String value) { + this.callStack = value; + } + + /** + * Gets the value of the key property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getKey() { + return key; + } + + /** + * Sets the value of the key property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setKey(String value) { + this.key = value; + } + + /** + * Gets the value of the beanType property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getBeanType() { + return beanType; + } + + /** + * Sets the value of the beanType property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setBeanType(String value) { + this.beanType = value; + } + + /** + * Gets the value of the detail property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getDetail() { + return detail; + } + + /** + * Sets the value of the detail property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setDetail(String value) { + this.detail = value; + } + + /** + * Gets the value of the tuneDetail property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getTuneDetail() { + return tuneDetail; + } + + /** + * Sets the value of the tuneDetail property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setTuneDetail(String value) { + this.tuneDetail = value; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/ProfileDiff.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/ProfileDiff.java new file mode 100644 index 000000000..a1bc1c91b --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/ProfileDiff.java @@ -0,0 +1,69 @@ + +package com.avaje.ebeaninternal.server.autofetch.model; + +import java.util.ArrayList; +import java.util.List; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + *

Java class for anonymous complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+ * <complexType>
+ *   <complexContent>
+ *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       <sequence>
+ *         <element ref="{http://ebean-orm.github.io/xml/ns/autotune}origin" maxOccurs="unbounded" minOccurs="0"/>
+ *       </sequence>
+ *     </restriction>
+ *   </complexContent>
+ * </complexType>
+ * 
+ * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "origin" +}) +@XmlRootElement(name = "profileDiff") +public class ProfileDiff { + + protected List origin; + + /** + * Gets the value of the origin property. + * + *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the origin property. + * + *

+ * For example, to add a new item, do as follows: + *

+     *    getOrigin().add(newItem);
+     * 
+ * + * + *

+ * Objects of the following type(s) are allowed in the list + * {@link Origin } + * + * + */ + public List getOrigin() { + if (origin == null) { + origin = new ArrayList(); + } + return this.origin; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/ProfileNew.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/ProfileNew.java new file mode 100644 index 000000000..0a058fb29 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/ProfileNew.java @@ -0,0 +1,69 @@ + +package com.avaje.ebeaninternal.server.autofetch.model; + +import java.util.ArrayList; +import java.util.List; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + *

Java class for anonymous complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+ * <complexType>
+ *   <complexContent>
+ *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       <sequence>
+ *         <element ref="{http://ebean-orm.github.io/xml/ns/autotune}origin" maxOccurs="unbounded" minOccurs="0"/>
+ *       </sequence>
+ *     </restriction>
+ *   </complexContent>
+ * </complexType>
+ * 
+ * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "origin" +}) +@XmlRootElement(name = "profileNew") +public class ProfileNew { + + protected List origin; + + /** + * Gets the value of the origin property. + * + *

+ * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the origin property. + * + *

+ * For example, to add a new item, do as follows: + *

+     *    getOrigin().add(newItem);
+     * 
+ * + * + *

+ * Objects of the following type(s) are allowed in the list + * {@link Origin } + * + * + */ + public List getOrigin() { + if (origin == null) { + origin = new ArrayList(); + } + return this.origin; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/package-info.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/package-info.java new file mode 100644 index 000000000..7204153e4 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/package-info.java @@ -0,0 +1,2 @@ +@javax.xml.bind.annotation.XmlSchema(namespace = "http://ebean-orm.github.io/xml/ns/autotune", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) +package com.avaje.ebeaninternal.server.autofetch.model; diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/AutoTuneXmlReader.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/AutoTuneXmlReader.java new file mode 100644 index 000000000..8ff62af16 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/AutoTuneXmlReader.java @@ -0,0 +1,57 @@ +package com.avaje.ebeaninternal.server.autofetch.service; + + +import com.avaje.ebeaninternal.server.autofetch.model.Autotune; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * Reads a profiling xml document. + */ +public class AutoTuneXmlReader { + + /** + * Read and return a Profiling from an xml file. + */ + public Autotune read(File file) { + + try { + return readFile(file); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + protected Autotune readFile(File file) throws IOException { + if (!file.exists()) { + return new Autotune(); + } + FileInputStream is = new FileInputStream(file); + try { + return read(is); + } finally { + is.close(); + } + } + + /** + * Read and return a Profiling from an xml document. + */ + public Autotune read(InputStream is) { + + try { + JAXBContext jaxbContext = JAXBContext.newInstance(Autotune.class); + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + return (Autotune) unmarshaller.unmarshal(is); + } catch (JAXBException e) { + throw new IllegalStateException(e); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/AutoTuneXmlWriter.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/AutoTuneXmlWriter.java new file mode 100644 index 000000000..7770bafb1 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/AutoTuneXmlWriter.java @@ -0,0 +1,32 @@ +package com.avaje.ebeaninternal.server.autofetch.service; + + +import com.avaje.ebeaninternal.server.autofetch.model.Autotune; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import java.io.File; + +/** + * Simple writer for output of the AutoTune Profiling as an XML document. + */ +public class AutoTuneXmlWriter { + + /** + * Write Profiling to a file as xml. + */ + public void write(Autotune profiling, File file) { + + try { + JAXBContext jaxbContext = JAXBContext.newInstance(Autotune.class); + Marshaller marshaller = jaxbContext.createMarshaller(); + marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + marshaller.marshal(profiling, file); + + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseAutoTuneService.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseAutoTuneService.java index 9b4923276..5dc886cfe 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseAutoTuneService.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseAutoTuneService.java @@ -1,14 +1,25 @@ package com.avaje.ebeaninternal.server.autofetch.service; +import com.avaje.ebean.bean.ObjectGraphOrigin; import com.avaje.ebean.config.AutofetchConfig; import com.avaje.ebean.config.ServerConfig; import com.avaje.ebeaninternal.api.SpiEbeanServer; import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.ebeaninternal.server.autofetch.AutoTuneService; import com.avaje.ebeaninternal.server.autofetch.AutoTuneCollection; +import com.avaje.ebeaninternal.server.autofetch.AutoTuneService; +import com.avaje.ebeaninternal.server.autofetch.model.Autotune; +import com.avaje.ebeaninternal.server.autofetch.model.Origin; +import com.avaje.ebeaninternal.server.autofetch.model.ProfileDiff; +import com.avaje.ebeaninternal.server.autofetch.model.ProfileNew; +import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; +import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetailParser; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; +import java.util.List; + /** * Implementation of the AutoTuneService which is comprised of profiling and query tuning. */ @@ -40,12 +51,65 @@ public class BaseAutoTuneService implements AutoTuneService { */ public void startup() { + File file = new File("ebean-autotune.xml"); + AutoTuneXmlReader reader = new AutoTuneXmlReader(); + Autotune profiling = reader.read(file); + List originList = profiling.getOrigin(); + for (Origin origin : originList) { + String key = origin.getKey(); + String detail = origin.getDetail(); + OrmQueryDetailParser parser = new OrmQueryDetailParser(detail); + OrmQueryDetail fetchDetail = parser.parse(); + TunedQueryInfo tunedQueryInfo = new TunedQueryInfo(fetchDetail); + queryTuner.load(key, tunedQueryInfo); + } } private void saveProfiling() { + Autotune document = new Autotune(); + AutoTuneCollection autoTuneCollection = profileManager.profilingCollection(false); + List entries = autoTuneCollection.getEntries(); + for (AutoTuneCollection.Entry entry : entries) { + ObjectGraphOrigin point = entry.getOrigin(); + OrmQueryDetail profileDetail = entry.getDetail(); + + OrmQueryDetail tuneDetail = queryTuner.get(point.getKey()); + if (tuneDetail == null) { + ProfileNew profileNew = document.getProfileNew(); + if (profileNew == null) { + profileNew = new ProfileNew(); + document.setProfileNew(profileNew); + } + profileNew.getOrigin().add( createOrigin(entry, point)); + + } else if (!tuneDetail.isAutoTuneEqual(profileDetail)) { + Origin origin1 = createOrigin(entry, point); + origin1.setTuneDetail(tuneDetail.toString()); + ProfileDiff diff = document.getProfileDiff(); + if (diff == null) { + diff = new ProfileDiff(); + document.setProfileDiff(diff); + } + diff.getOrigin().add(origin1); + } + } + + File file = new File("ebean-autotune-profiling.xml"); + AutoTuneXmlWriter writer = new AutoTuneXmlWriter(); + writer.write(document, file); + } + + @NotNull + private Origin createOrigin(AutoTuneCollection.Entry entry, ObjectGraphOrigin point) { + Origin origin1 = new Origin(); + origin1.setKey(point.getKey()); + origin1.setBeanType(point.getBeanType()); + origin1.setDetail(entry.getDetail().toString()); + origin1.setCallStack(point.getCallStack().description("\n")); + return origin1; } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseQueryTuner.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseQueryTuner.java index 6ad1fe2c0..9536197d5 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseQueryTuner.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseQueryTuner.java @@ -7,6 +7,7 @@ import com.avaje.ebean.config.AutofetchMode; import com.avaje.ebeaninternal.api.SpiEbeanServer; import com.avaje.ebeaninternal.api.SpiQuery; import com.avaje.ebeaninternal.server.autofetch.ProfilingListener; +import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; import javax.persistence.PersistenceException; import java.util.Map; @@ -48,6 +49,17 @@ public class BaseQueryTuner { tunedQueryInfoMap.put(key, queryInfo); } + /** + * Return the detail currently used for tuning. + * This returns null if there is currently no matching tuning. + */ + public OrmQueryDetail get(String key) { + TunedQueryInfo info = tunedQueryInfoMap.get(key); + return (info == null) ? null : info.getTunedDetail(); + } + + boolean fullProfiling = true; + /** * Auto tune the query and enable profiling. */ @@ -58,7 +70,11 @@ public class BaseQueryTuner { } if (!useAutoTune(query)) { - // not using autoFetch for this query + // not tuning this query but maybe profiling + if (fullProfiling) { + CallStack stack = server.createCallStack(); + profiling(query, stack); + } return false; } @@ -88,6 +104,16 @@ public class BaseQueryTuner { return false; } + private void profiling(SpiQuery query, CallStack stack) { + + // create a query point to identify the query + ObjectGraphNode origin = query.setOrigin(stack); + if (profilingListener.isProfileRequest(origin)) { + // collect more profiling based on profiling rate etc + query.setProfilingListener(profilingListener); + } + } + /** * Return true if we should try to tune this query. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOrigin.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOrigin.java index 84f758c0b..411f9756f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOrigin.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOrigin.java @@ -16,6 +16,8 @@ import java.util.concurrent.atomic.AtomicLong; public class ProfileOrigin { + //private static final Logger logger = LoggerFactory.getLogger(ProfileOrigin.class); + private static final long RESET_COUNT = -1000000000L; private final ObjectGraphOrigin origin; @@ -89,10 +91,10 @@ public class ProfileOrigin { } private OrmQueryDetail buildDetail(BeanDescriptor rootDesc) { - PathProperties pathProps = new PathProperties(); + PathProperties pathProps = new PathProperties(); for (ProfileOriginNodeUsage statsNode : nodeUsageMap.values()) { - statsNode.buildTunedFetch(pathProps, rootDesc); + statsNode.buildTunedFetch(pathProps, rootDesc, queryTuningAddVersion); } OrmQueryDetail detail = new OrmQueryDetail(); @@ -142,10 +144,10 @@ public class ProfileOrigin { */ public void collectUsageInfo(NodeUsageCollector profile) { - if (!profile.isEmpty()) { - ObjectGraphNode node = profile.getNode(); + //logger.info("COLLECT USAGE {}", profile.toString()); - ProfileOriginNodeUsage nodeStats = getNodeStats(node.getPath()); + if (!profile.isEmpty()) { + ProfileOriginNodeUsage nodeStats = getNodeStats(profile.getNode().getPath()); nodeStats.collectUsageInfo(profile); } } @@ -153,9 +155,11 @@ public class ProfileOrigin { private ProfileOriginNodeUsage getNodeStats(String path) { synchronized (monitor) { + // handle null paths as using ConcurrentHashMap + path = (path == null) ? "" : path; ProfileOriginNodeUsage nodeStats = nodeUsageMap.get(path); if (nodeStats == null) { - nodeStats = new ProfileOriginNodeUsage(path, queryTuningAddVersion); + nodeStats = new ProfileOriginNodeUsage(path); nodeUsageMap.put(path, nodeStats); } return nodeStats; diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginNodeUsage.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginNodeUsage.java index 632ac8097..ec8e9dc1a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginNodeUsage.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOriginNodeUsage.java @@ -24,8 +24,6 @@ public class ProfileOriginNodeUsage { private final String path; - private final boolean queryTuningAddVersion; - private int profileCount; private int profileUsedCount; @@ -34,12 +32,12 @@ public class ProfileOriginNodeUsage { private final Set aggregateUsed = new LinkedHashSet(); - public ProfileOriginNodeUsage(String path, boolean queryTuningAddVersion) { - this.path = path; - this.queryTuningAddVersion = queryTuningAddVersion; + public ProfileOriginNodeUsage(String path) { + // handle null paths as using ConcurrentHashMap + this.path = "".equals(path) ? null : path; } - public void buildTunedFetch(PathProperties pathProps, BeanDescriptor rootDesc) { + protected void buildTunedFetch(PathProperties pathProps, BeanDescriptor rootDesc, boolean addVersionProperty) { synchronized (monitor) { @@ -59,6 +57,7 @@ public class ProfileOriginNodeUsage { } for (String propName : aggregateUsed) { + //propName = "".equals(propName) ? null : propName; BeanProperty beanProp = desc.getBeanPropertyFromPath(propName); if (beanProp == null) { logger.warn("AutoTune: Can't find property[" + propName + "] for " + desc.getName()); @@ -81,7 +80,7 @@ public class ProfileOriginNodeUsage { } } - if ((modified || queryTuningAddVersion) && desc != null) { + if ((modified || addVersionProperty) && desc != null) { BeanProperty versionProp = desc.getVersionProperty(); if (versionProp != null) { pathProps.addToPath(path, versionProp.getName()); @@ -93,7 +92,7 @@ public class ProfileOriginNodeUsage { /** * Collect usage from a node. */ - public void collectUsageInfo(NodeUsageCollector profile) { + protected void collectUsageInfo(NodeUsageCollector profile) { synchronized (monitor) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/TunedQueryInfo.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/TunedQueryInfo.java index b1e554cbd..e203d5f70 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/TunedQueryInfo.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/TunedQueryInfo.java @@ -21,6 +21,18 @@ public class TunedQueryInfo implements Serializable { this.tunedDetail = tunedDetail; } + public TunedQueryInfo(OrmQueryDetail tunedDetail) { + this.origin = null; + this.tunedDetail = tunedDetail; + } + + /** + * Return the tuned detail (for comparison with profiling information). + */ + public OrmQueryDetail getTunedDetail() { + return tunedDetail; + } + /** * Tune the query by replacing its OrmQueryDetail with a tuned one. * @@ -47,7 +59,7 @@ public class TunedQueryInfo implements Serializable { } public String toString() { - return origin.getBeanType() + " " + origin.getKey() + " " + tunedDetail; + return tunedDetail.toString(); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java index 4b14fc42b..cdab0e94f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java @@ -236,6 +236,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { private void configureServerPlugins() { + autoTuneService.startup(); + for (SpiServerPlugin plugin : serverPlugins) { plugin.configure(this); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java index a65edfa16..954c5f4fe 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java @@ -9,6 +9,7 @@ import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Set; import javax.persistence.PersistenceException; @@ -83,25 +84,48 @@ public class OrmQueryDetail implements Serializable { * Return true if equal in terms of autofetch (select and joins). */ public boolean isAutoTuneEqual(OrmQueryDetail otherDetail) { - return autofetchPlanHash() == otherDetail.autofetchPlanHash(); - } - /** - * Calculate the hash for the query plan. - */ - private int autofetchPlanHash() { - - int hc = (baseProps == null ? 1 : baseProps.autofetchPlanHash()); - - if (fetchPaths != null) { - for (OrmQueryProperties p : fetchPaths.values()) { - hc = hc * 31 + p.autofetchPlanHash(); + if (!isSame(baseProps, otherDetail.baseProps)) { + return false; + } + if (fetchPaths == null) { + return otherDetail.fetchPaths == null; + } + Set> entries = fetchPaths.entrySet(); + for (Map.Entry entry : entries) { + OrmQueryProperties chunk = otherDetail.getChunk(entry.getKey(), false); + if (!isSame(entry.getValue(), chunk)) { + return false; } } - return hc; + return true; + //return autofetchPlanHash() == otherDetail.autofetchPlanHash(); } + private boolean isSame(OrmQueryProperties p1, OrmQueryProperties p2) { + if (p1 == null) { + return p2 == null; + } + return p1.isSame(p2); + } + +// /** +// * Calculate the hash for the query plan. +// */ +// private int autofetchPlanHash() { +// +// int hc = (baseProps == null ? 1 : baseProps.autofetchPlanHash()); +// +// if (fetchPaths != null) { +// for (OrmQueryProperties p : fetchPaths.values()) { +// hc = hc * 31 + p.autofetchPlanHash(); +// } +// } +// +// return hc; +// } + public String toString() { StringBuilder sb = new StringBuilder(); if (baseProps != null) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailParser.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailParser.java index f33572efb..9a5026693 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailParser.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailParser.java @@ -27,10 +27,11 @@ public class OrmQueryDetailParser { this.parser = new SimpleTextParser(oql); } - public void parse() throws PersistenceException { + public OrmQueryDetail parse() throws PersistenceException { parser.nextWord(); processInitial(); + return detail; } protected void assign(DefaultOrmQuery query) { @@ -42,7 +43,9 @@ public class OrmQueryDetailParser { } private void processInitial() { - if (parser.isMatch("find")) { + if (parser.isMatch("select")) { + readSelect(); + } else if (parser.isMatch("find")) { OrmQueryProperties props = readFindFetch(); detail.setBase(props); } else { @@ -149,6 +152,19 @@ public class OrmQueryDetailParser { } } + private void readSelect() { + String path = null; + String props = parser.nextWord(); + if (props.startsWith("(")) { + props = props.substring(1, props.length() - 1); + OrmQueryProperties base = new OrmQueryProperties(path, props); + detail.setBase(base); + parser.nextWord(); + } else { + process(); + } + } + private OrmQueryProperties readFindFetch() { boolean readAlias = false; diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java index a045b4194..23d270ce4 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java @@ -573,4 +573,11 @@ public class OrmQueryProperties implements Serializable { return Collections.unmodifiableSet(set); } + + public boolean isSame(OrmQueryProperties p2) { + if (included == null) { + return p2.included == null; + } + return included.equals(p2.included); + } } diff --git a/src/main/resources/ebean-autotune-1.0.xsd b/src/main/resources/ebean-autotune-1.0.xsd new file mode 100644 index 000000000..42a1ce081 --- /dev/null +++ b/src/main/resources/ebean-autotune-1.0.xsd @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailParserTest.java b/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailParserTest.java new file mode 100644 index 000000000..5ddd475b1 --- /dev/null +++ b/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailParserTest.java @@ -0,0 +1,80 @@ +package com.avaje.ebeaninternal.server.querydefn; + +import com.avaje.ebean.BaseTestCase; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertNull; + + +public class OrmQueryDetailParserTest extends BaseTestCase { + + @Test + public void testParseBasic() throws Exception { + + + OrmQueryDetail other = new OrmQueryDetail(); + other.select("id,name"); + + OrmQueryProperties root = other.getChunk(null, false); + assertNull(root.getPath()); + assertThat(root.getAllIncludedProperties()).contains("id", "name"); + + OrmQueryDetailParser p = new OrmQueryDetailParser("select (id,name)"); + OrmQueryDetail detail = p.parse(); + + root = detail.getChunk(null, false); + assertNull(root.getPath()); + assertThat(root.getAllIncludedProperties()).contains("id", "name"); + } + + @Test + public void testParseEmptySelect() throws Exception { + + OrmQueryDetailParser p = new OrmQueryDetailParser("select fetch customer (email)"); + OrmQueryDetail detail = p.parse(); + + OrmQueryProperties root = detail.getChunk(null, false); + assertNull(root.getPath()); + assertThat(root.getAllIncludedProperties()).isNull(); + + OrmQueryProperties chunk = detail.getChunk("customer", false); + assertThat(chunk.getPath()).isEqualTo("customer"); + assertThat(chunk.getAllIncludedProperties()).contains("email"); + } + + @Test + public void testParseSelectFetch() throws Exception { + + OrmQueryDetailParser p = new OrmQueryDetailParser("select (id,name) fetch customer (email)"); + OrmQueryDetail detail = p.parse(); + + OrmQueryProperties root = detail.getChunk(null, false); + assertNull(root.getPath()); + assertThat(root.getAllIncludedProperties()).contains("id", "name"); + + OrmQueryProperties chunk = detail.getChunk("customer", false); + assertThat(chunk.getPath()).isEqualTo("customer"); + assertThat(chunk.getAllIncludedProperties()).contains("email"); + } + + @Test + public void testParseSelectFetchMore() throws Exception { + + OrmQueryDetailParser p = new OrmQueryDetailParser("select (id,name) fetch customer (email) fetch details.product (sku,description)"); + OrmQueryDetail detail = p.parse(); + + OrmQueryProperties root = detail.getChunk(null, false); + assertNull(root.getPath()); + assertThat(root.getAllIncludedProperties()).contains("id", "name"); + + OrmQueryProperties chunk = detail.getChunk("customer", false); + assertThat(chunk.getPath()).isEqualTo("customer"); + assertThat(chunk.getAllIncludedProperties()).contains("email"); + + chunk = detail.getChunk("details.product", false); + assertThat(chunk.getPath()).isEqualTo("details.product"); + assertThat(chunk.getAllIncludedProperties()).contains("sku","description"); + } + +} \ No newline at end of file diff --git a/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailTest.java b/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailTest.java new file mode 100644 index 000000000..4a6a18ec6 --- /dev/null +++ b/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailTest.java @@ -0,0 +1,21 @@ +package com.avaje.ebeaninternal.server.querydefn; + +import org.junit.Test; + +import static org.junit.Assert.*; + +public class OrmQueryDetailTest { + + + @Test + public void test_isAutoTuneEqual() { + + OrmQueryDetailParser parser1 = new OrmQueryDetailParser("select order (id,name) fetch customer (name) fetch details (code)"); + OrmQueryDetail detail1 = parser1.parse(); + + OrmQueryDetailParser parser2 = new OrmQueryDetailParser("select order (id,name) fetch details (code) fetch customer (name)"); + OrmQueryDetail detail2 = parser2.parse(); + + assertTrue(detail1.isAutoTuneEqual(detail2)); + } +} \ No newline at end of file diff --git a/src/test/java/com/avaje/tests/query/TestAutofetchTuneWithJoin.java b/src/test/java/com/avaje/tests/query/TestAutofetchTuneWithJoin.java index f9fcfe399..d5e2cbdf9 100644 --- a/src/test/java/com/avaje/tests/query/TestAutofetchTuneWithJoin.java +++ b/src/test/java/com/avaje/tests/query/TestAutofetchTuneWithJoin.java @@ -30,8 +30,8 @@ public class TestAutofetchTuneWithJoin extends BaseTestCase { Query q = Ebean.find(Order.class) .setAutofetch(true) - .fetch("customer") - .fetch("customer.contacts") + //.fetch("customer") + //.fetch("customer.contacts") .where().lt("id", 3).query(); List list = q.findList(); diff --git a/src/test/java/com/avaje/tests/query/autotune/TestAutoTuneProfiling.java b/src/test/java/com/avaje/tests/query/autotune/TestAutoTuneProfiling.java new file mode 100644 index 000000000..e270607e2 --- /dev/null +++ b/src/test/java/com/avaje/tests/query/autotune/TestAutoTuneProfiling.java @@ -0,0 +1,82 @@ +package com.avaje.tests.query.autotune; + +import com.avaje.ebean.AdminAutofetch; +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.Query; +import com.avaje.ebean.bean.ObjectGraphNode; +import com.avaje.ebean.bean.ObjectGraphOrigin; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.tests.model.basic.Address; +import com.avaje.tests.model.basic.Customer; +import com.avaje.tests.model.basic.Order; +import com.avaje.tests.model.basic.ResetBasicData; +import org.junit.Test; + +import java.util.List; + +public class TestAutoTuneProfiling extends BaseTestCase { + + @Test + public void test() { + + ResetBasicData.reset(); + + for (int i = 0; i < 1; i++) { + execute(); + } + + + collectUsage(); + } + + + private void execute() { + useOrderDate(); + useOrderDateCustomerName(); + useLots(); + } + + + private Order findById(long id) { + return Ebean.find(Order.class) + .setAutofetch(true) + .setId(id) + .findUnique(); + } + + private void useOrderDate() { + Order order = findById(3); + order.getStatus(); + order.getShipDate(); + } + + private void useOrderDateCustomerName() { + Order order = findById(3); + order.getOrderDate(); + order.getCustomer().getName(); + } + + private void useLots() { + + Order order = findById(3); + order.getOrderDate(); + order.getShipDate(); + // order.setShipDate(new Date(System.currentTimeMillis())); + Customer customer = order.getCustomer(); + customer.getName(); + Address shippingAddress = customer.getShippingAddress(); + if (shippingAddress != null) { + shippingAddress.getLine1(); + shippingAddress.getCity(); + } + } + + private static void collectUsage() { + + AdminAutofetch adminAutofetch = Ebean.getServer(null).getAdminAutofetch(); + adminAutofetch.collectUsageViaGC(); + + } + +} diff --git a/src/test/resources/autotune/test-profiling.xml b/src/test/resources/autotune/test-profiling.xml new file mode 100644 index 000000000..7bd775db9 --- /dev/null +++ b/src/test/resources/autotune/test-profiling.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file From 116cc3a3fee172e0b5c81f198bab9f21806f7e1e Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Wed, 9 Sep 2015 15:21:10 +1200 Subject: [PATCH 4/9] AutoTune update - remove resources --- .../avaje/ebean/config/AutofetchConfig.java | 62 +++------- .../service/AutoTuneServiceFactory.java | 2 +- ...rvice.java => DefaultAutoTuneService.java} | 6 +- .../server/core/InternalConfiguration.java | 13 +-- .../server/deploy/DeployOrmXml.java | 31 ++--- .../server/lib/resource/DirectoryFinder.java | 108 ------------------ .../lib/resource/FileResourceContent.java | 70 ------------ .../lib/resource/FileResourceSource.java | 50 -------- .../server/lib/resource/ResourceContent.java | 34 ------ .../server/lib/resource/ResourceSource.java | 26 ----- .../server/lib/resource/package.html | 13 --- .../server/resource/ResourceManager.java | 29 ----- .../resource/ResourceManagerFactory.java | 107 ----------------- .../server/resource/package.html | 10 -- .../server/querydefn/OrmQueryDetailTest.java | 4 +- 15 files changed, 28 insertions(+), 537 deletions(-) rename src/main/java/com/avaje/ebeaninternal/server/autofetch/service/{BaseAutoTuneService.java => DefaultAutoTuneService.java} (97%) delete mode 100644 src/main/java/com/avaje/ebeaninternal/server/lib/resource/DirectoryFinder.java delete mode 100644 src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceContent.java delete mode 100644 src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceSource.java delete mode 100644 src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceContent.java delete mode 100644 src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceSource.java delete mode 100644 src/main/java/com/avaje/ebeaninternal/server/lib/resource/package.html delete mode 100644 src/main/java/com/avaje/ebeaninternal/server/resource/ResourceManager.java delete mode 100644 src/main/java/com/avaje/ebeaninternal/server/resource/ResourceManagerFactory.java delete mode 100644 src/main/java/com/avaje/ebeaninternal/server/resource/package.html diff --git a/src/main/java/com/avaje/ebean/config/AutofetchConfig.java b/src/main/java/com/avaje/ebean/config/AutofetchConfig.java index 02dbdda38..96aa821d5 100644 --- a/src/main/java/com/avaje/ebean/config/AutofetchConfig.java +++ b/src/main/java/com/avaje/ebean/config/AutofetchConfig.java @@ -7,19 +7,15 @@ public class AutofetchConfig { private AutofetchMode mode = AutofetchMode.DEFAULT_ONIFEMPTY; - private boolean queryTuning = false; + private boolean queryTuning; - private boolean queryTuningAddVersion = false; + private boolean queryTuningAddVersion; - private boolean profiling = false; + private boolean profiling; - private int profilingMin = 1; + private int profilingBase = 5; - private int profilingBase = 10; - - private double profilingRate = 0.05; - - private String logDirectory; + private double profilingRate = 0.01; private int profileUpdateFrequency = 60; @@ -101,22 +97,6 @@ public class AutofetchConfig { this.profiling = profiling; } - /** - * Return the minimum number of queries to profile before autofetch will start - * tuning the queries. - */ - public int getProfilingMin() { - return profilingMin; - } - - /** - * Set the minimum number of queries to profile before autofetch will start - * tuning the queries. - */ - public void setProfilingMin(int profilingMin) { - this.profilingMin = profilingMin; - } - /** * Return the base number of queries to profile before changing to profile * only a percentage of following queries (profileRate). @@ -148,20 +128,6 @@ public class AutofetchConfig { this.profilingRate = profilingRate; } - /** - * Return the log directory to put the autofetch log. - */ - public String getLogDirectory() { - return logDirectory; - } - - /** - * Set the directory to put the autofetch log in. - */ - public void setLogDirectory(String logDirectory) { - this.logDirectory = logDirectory; - } - /** * Return the frequency in seconds to update the autofetch tuned queries from * the profiled information. @@ -228,18 +194,16 @@ public class AutofetchConfig { */ public void loadSettings(PropertiesWrapper p) { - logDirectory = p.get("autofetch.logDirectory", logDirectory); - queryTuning = p.getBoolean("autofetch.querytuning", queryTuning); - queryTuningAddVersion = p.getBoolean("autofetch.queryTuningAddVersion", queryTuningAddVersion); - garbageCollectionOnShutdown = p.getBoolean("autofetch.garbageCollectionOnShutdown", garbageCollectionOnShutdown); + queryTuning = p.getBoolean("autotune.querytuning", queryTuning); + queryTuningAddVersion = p.getBoolean("autotune.queryTuningAddVersion", queryTuningAddVersion); + garbageCollectionOnShutdown = p.getBoolean("autotune.garbageCollectionOnShutdown", garbageCollectionOnShutdown); - profiling = p.getBoolean("autofetch.profiling", profiling); - mode = p.getEnum(AutofetchMode.class, "autofetch.implicitmode", mode); + profiling = p.getBoolean("autotune.profiling", profiling); + mode = p.getEnum(AutofetchMode.class, "autotune.implicitmode", mode); - profilingMin = p.getInt("autofetch.profiling.min", profilingMin); - profilingBase = p.getInt("autofetch.profiling.base", profilingBase); + profilingBase = p.getInt("autotune.profiling.base", profilingBase); - profilingRate = p.getDouble("autofetch.profiling.rate", profilingRate); - profileUpdateFrequency = p.getInt("autofetch.profiling.updatefrequency", profileUpdateFrequency); + profilingRate = p.getDouble("autotune.profiling.rate", profilingRate); + profileUpdateFrequency = p.getInt("autotune.profiling.updatefrequency", profileUpdateFrequency); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/AutoTuneServiceFactory.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/AutoTuneServiceFactory.java index c0327c4fb..f730a3b31 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/AutoTuneServiceFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/AutoTuneServiceFactory.java @@ -8,7 +8,7 @@ public class AutoTuneServiceFactory { public static AutoTuneService create(SpiEbeanServer server, ServerConfig serverConfig) { - return new BaseAutoTuneService(server, serverConfig); + return new DefaultAutoTuneService(server, serverConfig); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseAutoTuneService.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoTuneService.java similarity index 97% rename from src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseAutoTuneService.java rename to src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoTuneService.java index 5dc886cfe..3a22f1d57 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseAutoTuneService.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoTuneService.java @@ -23,9 +23,9 @@ import java.util.List; /** * Implementation of the AutoTuneService which is comprised of profiling and query tuning. */ -public class BaseAutoTuneService implements AutoTuneService { +public class DefaultAutoTuneService implements AutoTuneService { - private static final Logger logger = LoggerFactory.getLogger(BaseAutoTuneService.class); + private static final Logger logger = LoggerFactory.getLogger(DefaultAutoTuneService.class); private final long defaultGarbageCollectionWait; @@ -35,7 +35,7 @@ public class BaseAutoTuneService implements AutoTuneService { private final ProfileManager profileManager; - public BaseAutoTuneService(SpiEbeanServer server, ServerConfig serverConfig) { + public DefaultAutoTuneService(SpiEbeanServer server, ServerConfig serverConfig) { AutofetchConfig config = serverConfig.getAutofetchConfig(); 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 62c1e2470..9f77e4029 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/InternalConfiguration.java @@ -15,9 +15,9 @@ import com.avaje.ebeaninternal.api.SpiBackgroundExecutor; import com.avaje.ebeaninternal.api.SpiEbeanServer; import com.avaje.ebeaninternal.server.autofetch.AutoTuneService; import com.avaje.ebeaninternal.server.autofetch.service.AutoTuneServiceFactory; +import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogListener; import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogPrepare; import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogRegister; -import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogListener; import com.avaje.ebeaninternal.server.cluster.ClusterManager; import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager; import com.avaje.ebeaninternal.server.deploy.DeployOrmXml; @@ -32,8 +32,6 @@ import com.avaje.ebeaninternal.server.persist.DefaultPersister; import com.avaje.ebeaninternal.server.query.CQueryEngine; import com.avaje.ebeaninternal.server.query.DefaultOrmQueryEngine; import com.avaje.ebeaninternal.server.query.DefaultRelationalQueryEngine; -import com.avaje.ebeaninternal.server.resource.ResourceManager; -import com.avaje.ebeaninternal.server.resource.ResourceManagerFactory; import com.avaje.ebeaninternal.server.text.json.DJsonContext; import com.avaje.ebeaninternal.server.transaction.AutoCommitTransactionManager; import com.avaje.ebeaninternal.server.transaction.DefaultTransactionScopeManager; @@ -68,8 +66,6 @@ public class InternalConfiguration { private final DeployInherit deployInherit; - private final ResourceManager resourceManager; - private final DeployOrmXml deployOrmXml; private final TypeManager typeManager; @@ -120,8 +116,7 @@ public class InternalConfiguration { this.typeManager = new DefaultTypeManager(serverConfig, bootupClasses); - this.resourceManager = ResourceManagerFactory.createResourceManager(serverConfig); - this.deployOrmXml = new DeployOrmXml(resourceManager.getResourceSource()); + this.deployOrmXml = new DeployOrmXml(); this.deployInherit = new DeployInherit(bootupClasses); this.deployCreateProperties = new DeployCreateProperties(typeManager); @@ -288,10 +283,6 @@ public class InternalConfiguration { return deployInherit; } - public ResourceManager getResourceManager() { - return resourceManager; - } - public DeployOrmXml getDeployOrmXml() { return deployOrmXml; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/DeployOrmXml.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/DeployOrmXml.java index 833d1a5cc..fedc80baf 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/DeployOrmXml.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/DeployOrmXml.java @@ -1,18 +1,16 @@ package com.avaje.ebeaninternal.server.deploy; +import com.avaje.ebeaninternal.server.lib.util.Dnode; +import com.avaje.ebeaninternal.server.lib.util.DnodeReader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import com.avaje.ebeaninternal.server.lib.resource.ResourceContent; -import com.avaje.ebeaninternal.server.lib.resource.ResourceSource; -import com.avaje.ebeaninternal.server.lib.util.Dnode; -import com.avaje.ebeaninternal.server.lib.util.DnodeReader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * Controls the creation and caching of BeanManager's, BeanDescriptors, * BeanTable etc for both beans and tables(MapBeans). @@ -27,15 +25,10 @@ public class DeployOrmXml { private final HashMap nativeQueryCache; private final ArrayList ormXmlList; - - private final ResourceSource resSource; - public DeployOrmXml(ResourceSource resSource) { - - this.resSource = resSource; + public DeployOrmXml() { this.nativeQueryCache = new HashMap(); this.ormXmlList = findAllOrmXml(); - initialiseNativeQueries(); } @@ -106,17 +99,7 @@ public class DeployOrmXml { private void readOrmXml(String ormXmlName, ArrayList ormXmlList) { try { - Dnode ormXml; - ResourceContent content = resSource.getContent(ormXmlName); - if (content != null) { - // servlet resource or file system... - ormXml = readOrmXml(content.getInputStream()); - - } else { - // try the classpath... - ormXml = readOrmXmlFromClasspath(ormXmlName); - } - + Dnode ormXml = readOrmXmlFromClasspath(ormXmlName); if (ormXml != null) { ormXml.setAttribute("ebean.filename", ormXmlName); ormXmlList.add(ormXml); diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/DirectoryFinder.java b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/DirectoryFinder.java deleted file mode 100644 index 0755ba6c8..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/DirectoryFinder.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.avaje.ebeaninternal.server.lib.resource; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; - -/** - * Helper object used to find directories typically from the current working - * directory. - */ -public class DirectoryFinder { - - private static final Logger logger = LoggerFactory.getLogger(DirectoryFinder.class); - - /** - * Find a directory by search through subdirectories. - *

- * For example, used to find the WEB-INF directory starting from the current - * working directory. - *

- * - *
-	 * 
-	 * // search to a depth of 3 from the current working directory
-	 * // looking for a directory WEB-INF that contains the subdirectory
-	 * // data
-	 * 
-	 * File dir = DirectoryFinder.find(null, "WEB-INF/data", 3);
-	 * if (dir != null) {
-	 * 	//found the directory
-	 * }
-	 * 
- */ - public static File find(File startDir, String match, int maxDepth) { - - String matchSub = null; - int slashPos = match.indexOf('/'); - if (slashPos > -1) { - // match has sub directories - matchSub = match.substring(slashPos + 1); - match = match.substring(0, slashPos); - } - - // search for the directory - File found = find(startDir, match, matchSub, 0, maxDepth); - - if (found != null && matchSub != null) { - // match has sub directories - return new File(found, matchSub); - } - return found; - } - - private static File find(File dir, String match, String matchSub, int depth, int maxDepth) { - - if (dir == null) { - String curDir = System.getProperty("user.dir"); - dir = new File(curDir); - } - - if (dir.exists()) { - File[] list = dir.listFiles(); - if (list != null){ - for (int i = 0; i < list.length; i++) { - if (isMatch(list[i], match, matchSub)) { - return list[i]; - } - } - - // go through the directories again - // Aka *NOT* a depth first search - if (depth < maxDepth) { - for (int i = 0; i < list.length; i++) { - if (list[i].isDirectory()) { - File found = find(list[i], match, matchSub, depth + 1, maxDepth); - if (found != null) { - return found; - } - } - } - } - } - } - return null; - } - - private static boolean isMatch(File f, String match, String matchSub) { - if (f == null) { - return false; - } - if (!f.isDirectory()) { - return false; - } - if (!f.getName().equalsIgnoreCase(match)) { - return false; - } - if (matchSub == null) { - return true; - } - File sub = new File(f, matchSub); - if (logger.isTraceEnabled()){ - logger.trace("search; " + f.getPath()); - } - return sub.exists(); - - } -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceContent.java b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceContent.java deleted file mode 100644 index b2f6cb07e..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceContent.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.avaje.ebeaninternal.server.lib.resource; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Date; - -/** - * Content from a file system file. - */ -public class FileResourceContent implements ResourceContent { - - /** - * The underlying file. - */ - final File file; - - final String entryName; - - /** - * Create with a File and the entryName. - */ - public FileResourceContent(File file, String entryName) { - this.file = file; - this.entryName = entryName; - } - - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("[").append(getName()); - sb.append("] size[").append(size()); - sb.append("] lastModified[").append(new Date(lastModified())); - sb.append("]"); - return sb.toString(); - } - - /** - * Returns the entry name which contains the path from the base directory. - *

- * This does not return the full path of the file, but the path relative to - * the FileIoSource directory. - *

- */ - public String getName() { - return entryName; - } - - /** - * Return the time the file was last modified. - */ - public long lastModified() { - return file.lastModified(); - } - - /** - * Return the size of the file. - */ - public long size() { - return file.length(); - } - - /** - * Return the input stream for this file. - */ - public InputStream getInputStream() throws IOException { - - return new FileInputStream(file); - } -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceSource.java b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceSource.java deleted file mode 100644 index 496660c1e..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/FileResourceSource.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.avaje.ebeaninternal.server.lib.resource; - -import java.io.File; - -/** - * A file system directory represented as a FileSource. - */ -public class FileResourceSource implements ResourceSource { - - /** - * The directory name. - */ - final String directory; - - final String baseDir; - - /** - * Create the source based on a directory name. - */ - public FileResourceSource(String directory) { - this.directory = directory; - this.baseDir = directory + File.separator; - } - - /** - * Create the source based on a directory file. - */ - public FileResourceSource(File dir) { - this(dir.getPath()); - } - - - public String getRealPath() { - return directory; - } - - /** - * Search for the given file and return as IoContent. - */ - public ResourceContent getContent(String entry) { - - String fullPath = baseDir + entry; - - File f = new File(fullPath); - if (f.exists()) { - return new FileResourceContent(f, entry); - } - return null; - } -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceContent.java b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceContent.java deleted file mode 100644 index 6aab953c7..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceContent.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.avaje.ebeaninternal.server.lib.resource; - -import java.io.IOException; -import java.io.InputStream; - -/** - * Represents content that can be read via the ResourceManager. - *

- * Typically either content from a File or a URL. - *

- */ -public interface ResourceContent { - - /** - * The name of the content. - */ - String getName(); - - /** - * The size of the content in bytes. - */ - long size(); - - /** - * The last modified timestamp of the content. - */ - long lastModified(); - - /** - * The content itself. - */ - InputStream getInputStream() throws IOException; - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceSource.java b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceSource.java deleted file mode 100644 index 1be9338bb..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/ResourceSource.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.avaje.ebeaninternal.server.lib.resource; - -/** - * A Source for ResourceManager. - *

- * Typically a File System Directory based source or a ServletContext URL - * resource based source (for Servlet WAR files). - *

- */ -public interface ResourceSource { - - /** - * Return the File System path of the root of the ResourceSource. - *

- * This will return null IF the ResourceSource is an unpacked WAR file. - *

- */ - String getRealPath(); - - /** - * Find the content with a given entry name. This will return null if no - * matching content was found. - */ - ResourceContent getContent(String entry); - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/package.html b/src/main/java/com/avaje/ebeaninternal/server/lib/resource/package.html deleted file mode 100644 index 9ca4bbdba..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/lib/resource/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - AvajeLib - - -Service for reading content from either the file system or servlet context. -

-A service used to read deployment content such as xml files, images etc -taking into account the environment (servlet war file or file system). -

- - \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/resource/ResourceManager.java b/src/main/java/com/avaje/ebeaninternal/server/resource/ResourceManager.java deleted file mode 100644 index 087e1d2cd..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/resource/ResourceManager.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.avaje.ebeaninternal.server.resource; - -import java.io.File; - -import com.avaje.ebeaninternal.server.lib.resource.ResourceSource; - -/** - * The ResourceManager implementation. - */ -public class ResourceManager { - - final ResourceSource resourceSource; - - final File autofetchDir; - - public ResourceManager(ResourceSource resourceSource, File autofetchDir) { - this.resourceSource = resourceSource; - this.autofetchDir = autofetchDir; - } - - public ResourceSource getResourceSource() { - return resourceSource; - } - - public File getAutofetchDirectory() { - return autofetchDir; - } - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/resource/ResourceManagerFactory.java b/src/main/java/com/avaje/ebeaninternal/server/resource/ResourceManagerFactory.java deleted file mode 100644 index a32fd5625..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/resource/ResourceManagerFactory.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.avaje.ebeaninternal.server.resource; - -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebeaninternal.server.lib.resource.DirectoryFinder; -import com.avaje.ebeaninternal.server.lib.resource.FileResourceSource; -import com.avaje.ebeaninternal.server.lib.resource.ResourceSource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; - -/** - * Creates a ResourceManager for a server depending on the avaje.properties. - *

- * This can use URL based resource loading for web applications or file based - * otherwise. - *

- */ -public class ResourceManagerFactory { - - private static final Logger logger = LoggerFactory.getLogger(ResourceManagerFactory.class); - - /** - * Construct with the properties for a server. - */ - public ResourceManagerFactory() { - } - - /** - * Create the resource manager given the properties for this server. - */ - public static ResourceManager createResourceManager(ServerConfig serverConfig) { - - ResourceSource resourceSource = createResourceSource(serverConfig); - File autofetchDir = getAutofetchDir(serverConfig, resourceSource); - - return new ResourceManager(resourceSource, autofetchDir); - } - - - /** - * Return the directory that autofetch file goes into. - */ - protected static File getAutofetchDir(ServerConfig serverConfig, ResourceSource resourceSource) { - - String dir = null; - if (serverConfig.getAutofetchConfig() != null) { - dir = serverConfig.getAutofetchConfig().getLogDirectory(); - } - if (dir != null) { - return new File(dir); - } - - String realPath = resourceSource.getRealPath(); - if (realPath != null) { - return new File(realPath); - - } else { - throw new RuntimeException("No autofetch directory set?"); - } - } - - /** - * Return the resource loader for external sql files. - *

- * This can be url based (for webapps) or otherwise file based. - *

- */ - protected static ResourceSource createResourceSource(ServerConfig serverConfig) { - - // default for web application, override this for file system - String defaultDir = serverConfig.getResourceDirectory(); - - // use File System directory - return createFileSource(defaultDir); - } - - private static ResourceSource createFileSource(String fileDir) { - - if (fileDir != null) { - // explicitly stated so - File dir = new File(fileDir); - if (dir.exists()) { - logger.info("ResourceManager initialised: type[file] [" + fileDir + "]"); - return new FileResourceSource(fileDir); - } else { - String msg = "ResourceManager could not find directory [" + fileDir + "]"; - throw new IllegalArgumentException(msg); - } - } - - // try to guess the directory starting from the current working - // directory, and searching to a maximum depth of 3 subdirectories - File guessDir = DirectoryFinder.find(null, "WEB-INF", 3); - if (guessDir != null) { - // Typically this means we found the WEB-INF directory below the - // current working directory - logger.info("ResourceManager initialised: type[file] [" + guessDir.getPath() + "]"); - return new FileResourceSource(guessDir.getPath()); - } - - // default to the current working directory - File workingDir = new File("."); - return new FileResourceSource(workingDir); - } - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/resource/package.html b/src/main/java/com/avaje/ebeaninternal/server/resource/package.html deleted file mode 100644 index abed4e076..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/resource/package.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - Resource Management - - -Resource Management - - - \ No newline at end of file diff --git a/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailTest.java b/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailTest.java index 4a6a18ec6..3fa127b73 100644 --- a/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailTest.java +++ b/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailTest.java @@ -10,10 +10,10 @@ public class OrmQueryDetailTest { @Test public void test_isAutoTuneEqual() { - OrmQueryDetailParser parser1 = new OrmQueryDetailParser("select order (id,name) fetch customer (name) fetch details (code)"); + OrmQueryDetailParser parser1 = new OrmQueryDetailParser("select (id,name) fetch customer (name) fetch details (code)"); OrmQueryDetail detail1 = parser1.parse(); - OrmQueryDetailParser parser2 = new OrmQueryDetailParser("select order (id,name) fetch details (code) fetch customer (name)"); + OrmQueryDetailParser parser2 = new OrmQueryDetailParser("select (id,name) fetch details (code) fetch customer (name)"); OrmQueryDetail detail2 = parser2.parse(); assertTrue(detail1.isAutoTuneEqual(detail2)); From 71dd0e57286fb35f06a2a951bb386d2aa0a8b33b Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Thu, 10 Sep 2015 00:17:49 +1200 Subject: [PATCH 5/9] AutoTune update - rename and profiling against original query --- .../java/com/avaje/ebean/EbeanServer.java | 5 +- .../com/avaje/ebean/config/ServerConfig.java | 16 +- .../server/autofetch/ProfilingListener.java | 3 +- .../server/autofetch/model/Origin.java | 18 +- .../autofetch/service/ProfileManager.java | 16 +- .../autofetch/service/ProfileOrigin.java | 12 +- .../server/querydefn/OrmQueryDetail.java | 24 +-- .../server/querydefn/OrmQueryProperties.java | 11 +- .../ebeaninternal/api/TDSpiEbeanServer.java | 2 +- .../querydefn/OrmQueryDetailParserTest.java | 39 ++++ .../com/avaje/tests/basic/TestBatchLazy.java | 85 ++++----- .../avaje/tests/basic/lob/TestByteOnly.java | 4 +- .../query/TestAutofetchTuneWithJoin.java | 160 ++++++++-------- .../query/autotune/TestAutoTuneProfiling.java | 178 ++++++++++-------- src/test/resources/ebean.properties | 17 +- 15 files changed, 322 insertions(+), 268 deletions(-) diff --git a/src/main/java/com/avaje/ebean/EbeanServer.java b/src/main/java/com/avaje/ebean/EbeanServer.java index 3f19306c8..327d89275 100644 --- a/src/main/java/com/avaje/ebean/EbeanServer.java +++ b/src/main/java/com/avaje/ebean/EbeanServer.java @@ -106,10 +106,9 @@ public interface EbeanServer { void shutdown(boolean shutdownDataSource, boolean deregisterDriver); /** - * Return the AdminAutofetch which is used to control and configure the - * Autofetch service at runtime. + * Return AutoTune which is used to control the AutoTune service at runtime. */ - AdminAutofetch getAdminAutofetch(); + AutoTune getAutoTune(); /** * Return the name. This is used with {@link Ebean#getServer(String)} to get a diff --git a/src/main/java/com/avaje/ebean/config/ServerConfig.java b/src/main/java/com/avaje/ebean/config/ServerConfig.java index 1cdc2290c..8e13cadc4 100644 --- a/src/main/java/com/avaje/ebean/config/ServerConfig.java +++ b/src/main/java/com/avaje/ebean/config/ServerConfig.java @@ -130,7 +130,7 @@ public class ServerConfig { /** * Config controlling the autofetch behaviour. */ - private AutofetchConfig autofetchConfig = new AutofetchConfig(); + private AutoTuneConfig autoTuneConfig = new AutoTuneConfig(); /** * The JSON format used for DateTime types. Default to millis. @@ -1106,15 +1106,15 @@ public class ServerConfig { /** * Return the configuration for the Autofetch feature. */ - public AutofetchConfig getAutofetchConfig() { - return autofetchConfig; + public AutoTuneConfig getAutoTuneConfig() { + return autoTuneConfig; } /** * Set the configuration for the Autofetch feature. */ - public void setAutofetchConfig(AutofetchConfig autofetchConfig) { - this.autofetchConfig = autofetchConfig; + public void setAutoTuneConfig(AutoTuneConfig autoTuneConfig) { + this.autoTuneConfig = autoTuneConfig; } /** @@ -2003,7 +2003,7 @@ public class ServerConfig { * This is broken out for the same reason as above - preserve existing behaviour but let it be overridden. */ protected void loadAutofetchSettings(PropertiesWrapper p) { - autofetchConfig.loadSettings(p); + autoTuneConfig.loadSettings(p); } /** @@ -2017,8 +2017,8 @@ public class ServerConfig { if (namingConvention != null) { namingConvention.loadFromProperties(p); } - if (autofetchConfig == null) { - autofetchConfig = new AutofetchConfig(); + if (autoTuneConfig == null) { + autoTuneConfig = new AutoTuneConfig(); } loadAutofetchSettings(p); diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/ProfilingListener.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/ProfilingListener.java index ca496456f..f24300b91 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/ProfilingListener.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/ProfilingListener.java @@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.autofetch; import com.avaje.ebean.bean.NodeUsageListener; import com.avaje.ebean.bean.ObjectGraphNode; +import com.avaje.ebeaninternal.api.SpiQuery; /** * Profiling listener gets call backs for node usage and the associated query executions. @@ -21,5 +22,5 @@ public interface ProfilingListener extends NodeUsageListener { * Return true if this request should be profiled (based on the * profiling ratio and collection count for this origin). */ - boolean isProfileRequest(ObjectGraphNode origin); + boolean isProfileRequest(ObjectGraphNode origin, SpiQuery query); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/Origin.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/Origin.java index 86eebba46..eb2203c92 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/Origin.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/model/Origin.java @@ -23,7 +23,7 @@ import javax.xml.bind.annotation.XmlType; * <attribute name="key" use="required" type="{http://www.w3.org/2001/XMLSchema}string" /> * <attribute name="beanType" type="{http://www.w3.org/2001/XMLSchema}string" /> * <attribute name="detail" type="{http://www.w3.org/2001/XMLSchema}string" /> - * <attribute name="tuneDetail" type="{http://www.w3.org/2001/XMLSchema}string" /> + * <attribute name="original" type="{http://www.w3.org/2001/XMLSchema}string" /> * </restriction> * </complexContent> * </complexType> @@ -45,8 +45,8 @@ public class Origin { protected String beanType; @XmlAttribute(name = "detail") protected String detail; - @XmlAttribute(name = "tuneDetail") - protected String tuneDetail; + @XmlAttribute(name = "original") + protected String original; /** * Gets the value of the callStack property. @@ -145,27 +145,27 @@ public class Origin { } /** - * Gets the value of the tuneDetail property. + * Gets the value of the original property. * * @return * possible object is * {@link String } * */ - public String getTuneDetail() { - return tuneDetail; + public String getOriginal() { + return original; } /** - * Sets the value of the tuneDetail property. + * Sets the value of the original property. * * @param value * allowed object is * {@link String } * */ - public void setTuneDetail(String value) { - this.tuneDetail = value; + public void setOriginal(String value) { + this.original = value; } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileManager.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileManager.java index b76e66e47..4ef3b464a 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileManager.java @@ -3,8 +3,9 @@ package com.avaje.ebeaninternal.server.autofetch.service; import com.avaje.ebean.bean.NodeUsageCollector; import com.avaje.ebean.bean.ObjectGraphNode; import com.avaje.ebean.bean.ObjectGraphOrigin; -import com.avaje.ebean.config.AutofetchConfig; +import com.avaje.ebean.config.AutoTuneConfig; import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.SpiQuery; import com.avaje.ebeaninternal.server.autofetch.AutoTuneCollection; import com.avaje.ebeaninternal.server.autofetch.ProfilingListener; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; @@ -40,7 +41,7 @@ public class ProfileManager implements ProfilingListener { private final SpiEbeanServer server; - public ProfileManager(AutofetchConfig config, SpiEbeanServer server) { + public ProfileManager(AutoTuneConfig config, SpiEbeanServer server) { this.server = server; this.profilingRate = config.getProfilingRate(); this.profilingBase = config.getProfilingBase(); @@ -48,10 +49,17 @@ public class ProfileManager implements ProfilingListener { } @Override - public boolean isProfileRequest(ObjectGraphNode origin) { + public boolean isProfileRequest(ObjectGraphNode origin, SpiQuery query) { ProfileOrigin profileOrigin = profileMap.get(origin.getOriginQueryPoint().getKey()); - return profileOrigin == null || profileOrigin.isProfile(); + if (profileOrigin == null) { + profileOrigin = new ProfileOrigin(origin.getOriginQueryPoint(), queryTuningAddVersion, profilingBase, profilingRate); + profileOrigin.setOriginalQuery(query.getDetail().toString()); + profileMap.put(origin.getOriginQueryPoint().getKey(), profileOrigin); + return true; + } else { + return profileOrigin.isProfile(); + } } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOrigin.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOrigin.java index 411f9756f..d83c163d9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOrigin.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/ProfileOrigin.java @@ -38,6 +38,8 @@ public class ProfileOrigin { private final AtomicLong profileCount = new AtomicLong(); + private String originalQuery; + public ProfileOrigin(ObjectGraphOrigin origin, boolean queryTuningAddVersion, int profilingBase, double profilingRate) { this.origin = origin; this.queryTuningAddVersion = queryTuningAddVersion; @@ -45,6 +47,14 @@ public class ProfileOrigin { this.profilingRate = profilingRate; } + public String getOriginalQuery() { + return originalQuery; + } + + public void setOriginalQuery(String originalQuery) { + this.originalQuery = originalQuery; + } + /** * Return true if this query should be profiled based on a percentage rate. */ @@ -74,7 +84,7 @@ public class ProfileOrigin { } OrmQueryDetail detail = buildDetail(rootDesc); - AutoTuneCollection.Entry entry = req.add(origin, detail); + AutoTuneCollection.Entry entry = req.add(origin, detail, originalQuery); Collection values = queryStatsMap.values(); for (ProfileOriginQuery queryEntry : values) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java index 954c5f4fe..0f4ab5f46 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetail.java @@ -81,7 +81,7 @@ public class OrmQueryDetail implements Serializable { } /** - * Return true if equal in terms of autofetch (select and joins). + * Return true if equal in terms of autoTune (select and fetch). */ public boolean isAutoTuneEqual(OrmQueryDetail otherDetail) { @@ -91,6 +91,9 @@ public class OrmQueryDetail implements Serializable { if (fetchPaths == null) { return otherDetail.fetchPaths == null; } + if (fetchPaths.size() != otherDetail.fetchPaths.size()) { + return false; + } Set> entries = fetchPaths.entrySet(); for (Map.Entry entry : entries) { OrmQueryProperties chunk = otherDetail.getChunk(entry.getKey(), false); @@ -100,7 +103,6 @@ public class OrmQueryDetail implements Serializable { } return true; - //return autofetchPlanHash() == otherDetail.autofetchPlanHash(); } private boolean isSame(OrmQueryProperties p1, OrmQueryProperties p2) { @@ -110,22 +112,6 @@ public class OrmQueryDetail implements Serializable { return p1.isSame(p2); } -// /** -// * Calculate the hash for the query plan. -// */ -// private int autofetchPlanHash() { -// -// int hc = (baseProps == null ? 1 : baseProps.autofetchPlanHash()); -// -// if (fetchPaths != null) { -// for (OrmQueryProperties p : fetchPaths.values()) { -// hc = hc * 31 + p.autofetchPlanHash(); -// } -// } -// -// return hc; -// } - public String toString() { StringBuilder sb = new StringBuilder(); if (baseProps != null) { @@ -236,7 +222,7 @@ public class OrmQueryDetail implements Serializable { boolean tuned = false; OrmQueryProperties tunedRoot = tunedDetail.getChunk(null, false); - if (tunedRoot != null && tunedRoot.hasProperties()) { + if (tunedRoot != null) { tuned = true; baseProps.setTunedProperties(tunedRoot); diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java index 23d270ce4..21f2e566b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryProperties.java @@ -215,9 +215,13 @@ public class OrmQueryProperties implements Serializable { * Set the properties from a matching autofetch tuned properties. */ public void setTunedProperties(OrmQueryProperties tunedProperties) { - this.properties = tunedProperties.properties; - this.trimmedProperties = tunedProperties.trimmedProperties; - this.included = tunedProperties.included; + if (tunedProperties.hasProperties()) { + this.properties = tunedProperties.properties; + this.trimmedProperties = tunedProperties.trimmedProperties; + this.included = tunedProperties.included; + this.queryFetchBatch = Math.max(queryFetchBatch, tunedProperties.queryFetchBatch); + this.lazyFetchBatch = Math.max(lazyFetchBatch, tunedProperties.lazyFetchBatch); + } } /** @@ -262,6 +266,7 @@ public class OrmQueryProperties implements Serializable { copy.parentPath = parentPath; copy.path = path; copy.properties = properties; + copy.trimmedProperties = trimmedProperties; copy.cache = cache; copy.readOnly = readOnly; copy.queryFetchAll = queryFetchAll; diff --git a/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java b/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java index adbcc84d8..d6f443bc7 100644 --- a/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java +++ b/src/test/java/com/avaje/ebeaninternal/api/TDSpiEbeanServer.java @@ -216,7 +216,7 @@ public class TDSpiEbeanServer implements SpiEbeanServer { } @Override - public AdminAutofetch getAdminAutofetch() { + public AutoTune getAutoTune() { return null; } diff --git a/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailParserTest.java b/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailParserTest.java index 5ddd475b1..0e877728c 100644 --- a/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailParserTest.java +++ b/src/test/java/com/avaje/ebeaninternal/server/querydefn/OrmQueryDetailParserTest.java @@ -77,4 +77,43 @@ public class OrmQueryDetailParserTest extends BaseTestCase { assertThat(chunk.getAllIncludedProperties()).contains("sku","description"); } + @Test + public void testParseWithPlusQuery() throws Exception { + + OrmQueryDetailParser p = new OrmQueryDetailParser("select (id,name) fetch customer (+query,id,name,email)"); + OrmQueryDetail detail = p.parse(); + + OrmQueryProperties root = detail.getChunk(null, false); + assertNull(root.getPath()); + assertThat(root.getAllIncludedProperties()).contains("id", "name"); + + OrmQueryProperties chunk = detail.getChunk("customer", false); + assertThat(chunk.getPath()).isEqualTo("customer"); + assertThat(chunk.getAllIncludedProperties()).contains("id", "name", "email"); + assertThat(chunk.isQueryFetch()).isTrue(); + + } + + @Test + public void testTuneApply() { + + OrmQueryDetailParser p = new OrmQueryDetailParser("select (status) fetch customer (email)"); + OrmQueryDetail detail = p.parse(); + + OrmQueryDetailParser p2 = new OrmQueryDetailParser("select (id,name) fetch customer (+query,id,name,email)"); + OrmQueryDetail tune = p2.parse(); + + + detail.tuneFetchProperties(tune); + + OrmQueryProperties root = detail.getChunk(null, false); + assertNull(root.getPath()); + assertThat(root.getAllIncludedProperties()).contains("id", "name"); + + OrmQueryProperties chunk = detail.getChunk("customer", false); + assertThat(chunk.getPath()).isEqualTo("customer"); + assertThat(chunk.getAllIncludedProperties()).contains("id", "name", "email"); + assertThat(chunk.isQueryFetch()).isTrue(); + } + } \ No newline at end of file diff --git a/src/test/java/com/avaje/tests/basic/TestBatchLazy.java b/src/test/java/com/avaje/tests/basic/TestBatchLazy.java index 607d0c4cb..8a2e3ca37 100644 --- a/src/test/java/com/avaje/tests/basic/TestBatchLazy.java +++ b/src/test/java/com/avaje/tests/basic/TestBatchLazy.java @@ -1,44 +1,41 @@ -package com.avaje.tests.basic; - -import java.util.List; - -import org.junit.Test; - -import com.avaje.ebean.AdminAutofetch; -import com.avaje.ebean.BaseTestCase; -import com.avaje.ebean.Ebean; -import com.avaje.ebean.Query; -import com.avaje.tests.model.basic.Customer; -import com.avaje.tests.model.basic.Order; -import com.avaje.tests.model.basic.OrderDetail; -import com.avaje.tests.model.basic.ResetBasicData; - -public class TestBatchLazy extends BaseTestCase { - - @Test - public void testMe() { - - ResetBasicData.reset(); - - Query query = Ebean.find(Order.class); - List list = query.findList(); - - - for (Order order : list) { - Customer customer = order.getCustomer(); - customer.getName(); - - List details = order.getDetails(); - for (OrderDetail orderDetail : details) { - orderDetail.getProduct().getSku(); - } - } - - AdminAutofetch adminAutofetch = Ebean.getServer(null).getAdminAutofetch(); - adminAutofetch.collectUsageViaGC(); - - } - - - -} +package com.avaje.tests.basic; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.Query; +import com.avaje.tests.model.basic.Customer; +import com.avaje.tests.model.basic.Order; +import com.avaje.tests.model.basic.OrderDetail; +import com.avaje.tests.model.basic.ResetBasicData; +import org.junit.Test; + +import java.util.List; + +public class TestBatchLazy extends BaseTestCase { + + @Test + public void testMe() { + + ResetBasicData.reset(); + + Query query = Ebean.find(Order.class); + List list = query.findList(); + + + for (Order order : list) { + Customer customer = order.getCustomer(); + customer.getName(); + + List details = order.getDetails(); + for (OrderDetail orderDetail : details) { + orderDetail.getProduct().getSku(); + } + } + + Ebean.getDefaultServer().getAutoTune().collectProfiling(); + + } + + + +} diff --git a/src/test/java/com/avaje/tests/basic/lob/TestByteOnly.java b/src/test/java/com/avaje/tests/basic/lob/TestByteOnly.java index 758ac7e59..1ce5a64a3 100644 --- a/src/test/java/com/avaje/tests/basic/lob/TestByteOnly.java +++ b/src/test/java/com/avaje/tests/basic/lob/TestByteOnly.java @@ -24,8 +24,8 @@ public class TestByteOnly extends BaseTestCase { Ebean.save(e2); - // Ebean.getServer(null).getAdminAutofetch().collectUsageViaGC(); - // Ebean.getServer(null).getAdminAutofetch().updateTunedQueryInfo(); + // Ebean.getServer(null).getAutoTune().collectProfiling(); + // Ebean.getServer(null).getAutoTune().updateTunedQueryInfo(); System.out.println("done"); } diff --git a/src/test/java/com/avaje/tests/query/TestAutofetchTuneWithJoin.java b/src/test/java/com/avaje/tests/query/TestAutofetchTuneWithJoin.java index d5e2cbdf9..d82fbfe6a 100644 --- a/src/test/java/com/avaje/tests/query/TestAutofetchTuneWithJoin.java +++ b/src/test/java/com/avaje/tests/query/TestAutofetchTuneWithJoin.java @@ -1,82 +1,78 @@ -package com.avaje.tests.query; - -import java.util.List; - -import org.junit.Test; - -import com.avaje.ebean.AdminAutofetch; -import com.avaje.ebean.BaseTestCase; -import com.avaje.ebean.Ebean; -import com.avaje.ebean.Query; -import com.avaje.ebean.bean.ObjectGraphNode; -import com.avaje.ebean.bean.ObjectGraphOrigin; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.tests.model.basic.Address; -import com.avaje.tests.model.basic.Customer; -import com.avaje.tests.model.basic.Order; -import com.avaje.tests.model.basic.ResetBasicData; - -public class TestAutofetchTuneWithJoin extends BaseTestCase { - - @Test - public void test() { - runQuery(); - collectUsage(); - } - - private void runQuery() { - - ResetBasicData.reset(); - - Query q = Ebean.find(Order.class) - .setAutofetch(true) - //.fetch("customer") - //.fetch("customer.contacts") - .where().lt("id", 3).query(); - - List list = q.findList(); - - for (int i = 0; i < list.size(); i++) { - Order order = list.get(i); - order.getOrderDate(); - order.getShipDate(); - // order.setShipDate(new Date(System.currentTimeMillis())); - Customer customer = order.getCustomer(); - customer.getName(); - Address shippingAddress = customer.getShippingAddress(); - if (shippingAddress != null) { - shippingAddress.getLine1(); - shippingAddress.getCity(); - } - // customer.getContacts() - } - - SpiQuery sq = (SpiQuery) q; - ObjectGraphNode parentNode = sq.getParentNode(); - ObjectGraphOrigin origin = parentNode.getOriginQueryPoint(); - - System.out.println("Origin:" + origin.getKey()); - - // MetaAutoFetchStatistic metaAutoFetchStatistic = - // ((DefaultOrmQuery)q).getMetaAutoFetchStatistic(); - // if (metaAutoFetchStatistic != null) { - // List nodeUsageStats = - // metaAutoFetchStatistic.getNodeUsageStats(); - // System.out.println(nodeUsageStats); - // List queryStats = metaAutoFetchStatistic.getQueryStats(); - // System.out.println(queryStats); - // } - - if (q.isAutofetchTuned()) { - System.out.println("TUNED..."); - } - } - - private static void collectUsage() { - - AdminAutofetch adminAutofetch = Ebean.getServer(null).getAdminAutofetch(); - adminAutofetch.collectUsageViaGC(); - - } - -} +package com.avaje.tests.query; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.Query; +import com.avaje.ebean.bean.ObjectGraphNode; +import com.avaje.ebean.bean.ObjectGraphOrigin; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.tests.model.basic.Address; +import com.avaje.tests.model.basic.Customer; +import com.avaje.tests.model.basic.Order; +import com.avaje.tests.model.basic.ResetBasicData; +import org.junit.Test; + +import java.util.List; + +public class TestAutofetchTuneWithJoin extends BaseTestCase { + + @Test + public void test() { + runQuery(); + collectUsage(); + } + + private void runQuery() { + + ResetBasicData.reset(); + + Query q = Ebean.find(Order.class) + .setAutofetch(true) + //.fetch("customer") + //.fetch("customer.contacts") + .where().lt("id", 3).query(); + + List list = q.findList(); + + for (int i = 0; i < list.size(); i++) { + Order order = list.get(i); + order.getOrderDate(); + order.getShipDate(); + // order.setShipDate(new Date(System.currentTimeMillis())); + Customer customer = order.getCustomer(); + customer.getName(); + Address shippingAddress = customer.getShippingAddress(); + if (shippingAddress != null) { + shippingAddress.getLine1(); + shippingAddress.getCity(); + } + // customer.getContacts() + } + + SpiQuery sq = (SpiQuery) q; + ObjectGraphNode parentNode = sq.getParentNode(); + ObjectGraphOrigin origin = parentNode.getOriginQueryPoint(); + + System.out.println("Origin:" + origin.getKey()); + + // MetaAutoFetchStatistic metaAutoFetchStatistic = + // ((DefaultOrmQuery)q).getMetaAutoFetchStatistic(); + // if (metaAutoFetchStatistic != null) { + // List nodeUsageStats = + // metaAutoFetchStatistic.getNodeUsageStats(); + // System.out.println(nodeUsageStats); + // List queryStats = metaAutoFetchStatistic.getQueryStats(); + // System.out.println(queryStats); + // } + + if (q.isAutofetchTuned()) { + System.out.println("TUNED..."); + } + } + + private static void collectUsage() { + + Ebean.getDefaultServer().getAutoTune().collectProfiling(); + } + +} diff --git a/src/test/java/com/avaje/tests/query/autotune/TestAutoTuneProfiling.java b/src/test/java/com/avaje/tests/query/autotune/TestAutoTuneProfiling.java index e270607e2..2bcccb34a 100644 --- a/src/test/java/com/avaje/tests/query/autotune/TestAutoTuneProfiling.java +++ b/src/test/java/com/avaje/tests/query/autotune/TestAutoTuneProfiling.java @@ -1,82 +1,96 @@ -package com.avaje.tests.query.autotune; - -import com.avaje.ebean.AdminAutofetch; -import com.avaje.ebean.BaseTestCase; -import com.avaje.ebean.Ebean; -import com.avaje.ebean.Query; -import com.avaje.ebean.bean.ObjectGraphNode; -import com.avaje.ebean.bean.ObjectGraphOrigin; -import com.avaje.ebeaninternal.api.SpiQuery; -import com.avaje.tests.model.basic.Address; -import com.avaje.tests.model.basic.Customer; -import com.avaje.tests.model.basic.Order; -import com.avaje.tests.model.basic.ResetBasicData; -import org.junit.Test; - -import java.util.List; - -public class TestAutoTuneProfiling extends BaseTestCase { - - @Test - public void test() { - - ResetBasicData.reset(); - - for (int i = 0; i < 1; i++) { - execute(); - } - - - collectUsage(); - } - - - private void execute() { - useOrderDate(); - useOrderDateCustomerName(); - useLots(); - } - - - private Order findById(long id) { - return Ebean.find(Order.class) - .setAutofetch(true) - .setId(id) - .findUnique(); - } - - private void useOrderDate() { - Order order = findById(3); - order.getStatus(); - order.getShipDate(); - } - - private void useOrderDateCustomerName() { - Order order = findById(3); - order.getOrderDate(); - order.getCustomer().getName(); - } - - private void useLots() { - - Order order = findById(3); - order.getOrderDate(); - order.getShipDate(); - // order.setShipDate(new Date(System.currentTimeMillis())); - Customer customer = order.getCustomer(); - customer.getName(); - Address shippingAddress = customer.getShippingAddress(); - if (shippingAddress != null) { - shippingAddress.getLine1(); - shippingAddress.getCity(); - } - } - - private static void collectUsage() { - - AdminAutofetch adminAutofetch = Ebean.getServer(null).getAdminAutofetch(); - adminAutofetch.collectUsageViaGC(); - - } - -} +package com.avaje.tests.query.autotune; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.tests.model.basic.Address; +import com.avaje.tests.model.basic.Customer; +import com.avaje.tests.model.basic.Order; +import com.avaje.tests.model.basic.OrderDetail; +import com.avaje.tests.model.basic.ResetBasicData; +import org.junit.Test; + +import java.util.List; + +public class TestAutoTuneProfiling extends BaseTestCase { + + @Test + public void test() { + + ResetBasicData.reset(); + + for (int i = 0; i < 1; i++) { + execute(); + } + + + collectUsage(); + } + + + private void execute() { + useOrderDate(); + useOrderDateCustomerName(); + useLots(); + useLotUntuned(); + } + + private Order findById(long id) { + return Ebean.find(Order.class) + .select("status, orderDate, shipDate") + .setId(id) + .findUnique(); + } + + private void useOrderDate() { + Order order = findById(3); + order.getStatus(); + order.getShipDate(); + } + + private void useOrderDateCustomerName() { + Order order = findById(3); + order.getOrderDate(); + order.getCustomer().getName(); + } + + private void useLots() { + + Order order = findById(3); + order.getOrderDate(); + order.getShipDate(); + // order.setShipDate(new Date(System.currentTimeMillis())); + Customer customer = order.getCustomer(); + customer.getName(); + Address shippingAddress = customer.getShippingAddress(); + if (shippingAddress != null) { + shippingAddress.getLine1(); + shippingAddress.getCity(); + } + } + + private void useLotUntuned() { + Order order = findById(3); + List details = order.getDetails(); + for (OrderDetail detail : details) { + detail.getProduct().getName(); + detail.getOrderQty(); + detail.getShipQty(); + detail.getUnitPrice(); + } + + Customer customer = order.getCustomer(); + customer.getName(); + Address billingAddress = customer.getBillingAddress(); + if (billingAddress != null) { + billingAddress.getCity(); + billingAddress.getLine1(); + billingAddress.getLine2(); + } + } + + private static void collectUsage() { + + Ebean.getDefaultServer().getAutoTune().collectProfiling(); + } + +} diff --git a/src/test/resources/ebean.properties b/src/test/resources/ebean.properties index 10323b913..228b08aa6 100644 --- a/src/test/resources/ebean.properties +++ b/src/test/resources/ebean.properties @@ -11,15 +11,14 @@ ebean.encryptKeyManager=com.avaje.tests.basic.encrypt.BasicEncyptKeyManager -ebean.autofetch.querytuning=true -ebean.autofetch.profiling=true -ebean.autofetch.implicitmode=default_off -#ebean.autofetch.implicitmode=default_onifempty -ebean.autofetch.profiling.min=1 -ebean.autofetch.profiling.base=10 -#ebean.autofetch.profiling.rate=0.05 -ebean.autofetch.garbageCollectionOnShutdown=true -ebean.autofetch.traceUsageCollection=true +ebean.autotune.querytuning=true +ebean.autotune.profiling=true +#ebean.autoTune.implicitmode=default_off +#ebean.autotune.implicitmode=default_onifempty +#ebean.autofetch.profiling.min=1 +#ebean.autofetch.profiling.base=10 +##ebean.autofetch.profiling.rate=0.05 +#ebean.autofetch.garbageCollectionOnShutdown=true ebean.ddl.generate=true From a2a4a15f32c489cd1b6b111b27a8eb5a24e3064a Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Thu, 10 Sep 2015 00:18:11 +1200 Subject: [PATCH 6/9] AutoTune update - rename and profiling against original query --- ebean-autotune-profiling.xml | 32 --- ebean-autotune.xml | 48 ++-- .../java/com/avaje/ebean/AdminAutofetch.java | 23 -- src/main/java/com/avaje/ebean/AutoTune.java | 14 ++ ...tofetchConfig.java => AutoTuneConfig.java} | 80 +++---- .../{AutofetchMode.java => AutoTuneMode.java} | 2 +- .../server/autofetch/AutoTuneCollection.java | 16 +- .../server/autofetch/AutoTuneService.java | 17 +- .../autofetch/service/BaseQueryTuner.java | 16 +- .../service/DefaultAutoTuneService.java | 210 ++++++------------ .../server/core/DefaultServer.java | 8 +- .../server/deploy/BeanDescriptor.java | 10 +- src/main/resources/ebean-autotune-1.0.xsd | 2 +- .../autofetch/AutofetchEmbeddedTest.java | 8 +- 14 files changed, 181 insertions(+), 305 deletions(-) delete mode 100644 ebean-autotune-profiling.xml delete mode 100644 src/main/java/com/avaje/ebean/AdminAutofetch.java create mode 100644 src/main/java/com/avaje/ebean/AutoTune.java rename src/main/java/com/avaje/ebean/config/{AutofetchConfig.java => AutoTuneConfig.java} (60%) rename src/main/java/com/avaje/ebean/config/{AutofetchMode.java => AutoTuneMode.java} (96%) diff --git a/ebean-autotune-profiling.xml b/ebean-autotune-profiling.xml deleted file mode 100644 index 3911ea279..000000000 --- a/ebean-autotune-profiling.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - com.avaje.tests.query.autotune.TestAutoTuneProfiling.findById(TestAutoTuneProfiling.java:45) - com.avaje.tests.query.autotune.TestAutoTuneProfiling.useOrderDate(TestAutoTuneProfiling.java:49) - com.avaje.tests.query.autotune.TestAutoTuneProfiling.execute(TestAutoTuneProfiling.java:35) - com.avaje.tests.query.autotune.TestAutoTuneProfiling.test(TestAutoTuneProfiling.java:26) - sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - - - - - - - - - - com.avaje.tests.query.autotune.TestAutoTuneProfiling.findById(TestAutoTuneProfiling.java:45) - com.avaje.tests.query.autotune.TestAutoTuneProfiling.useOrderDateCustomerName(TestAutoTuneProfiling.java:55) - com.avaje.tests.query.autotune.TestAutoTuneProfiling.execute(TestAutoTuneProfiling.java:36) - com.avaje.tests.query.autotune.TestAutoTuneProfiling.test(TestAutoTuneProfiling.java:26) - sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - - - - - diff --git a/ebean-autotune.xml b/ebean-autotune.xml index 73563dec3..53be9c98e 100644 --- a/ebean-autotune.xml +++ b/ebean-autotune.xml @@ -1,31 +1,37 @@ - - - com.avaje.tests.query.autotune.TestAutoTuneProfiling.findById(TestAutoTuneProfiling.java:45) - com.avaje.tests.query.autotune.TestAutoTuneProfiling.useLots(TestAutoTuneProfiling.java:62) - com.avaje.tests.query.autotune.TestAutoTuneProfiling.execute(TestAutoTuneProfiling.java:37) - com.avaje.tests.query.autotune.TestAutoTuneProfiling.test(TestAutoTuneProfiling.java:26) + + com.avaje.tests.query.autotune.TestAutoTuneProfiling.findById(TestAutoTuneProfiling.java:41) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.useLots(TestAutoTuneProfiling.java:58) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.execute(TestAutoTuneProfiling.java:33) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.test(TestAutoTuneProfiling.java:22) sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - - com.avaje.tests.query.autotune.TestAutoTuneProfiling.findById(TestAutoTuneProfiling.java:45) - com.avaje.tests.query.autotune.TestAutoTuneProfiling.useOrderDate(TestAutoTuneProfiling.java:49) - com.avaje.tests.query.autotune.TestAutoTuneProfiling.execute(TestAutoTuneProfiling.java:35) - com.avaje.tests.query.autotune.TestAutoTuneProfiling.test(TestAutoTuneProfiling.java:26) + + com.avaje.tests.query.autotune.TestAutoTuneProfiling.findById(TestAutoTuneProfiling.java:41) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.useOrderDateCustomerName(TestAutoTuneProfiling.java:51) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.execute(TestAutoTuneProfiling.java:32) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.test(TestAutoTuneProfiling.java:22) + sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + + + + com.avaje.tests.query.autotune.TestAutoTuneProfiling.findById(TestAutoTuneProfiling.java:41) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.useLotUntuned(TestAutoTuneProfiling.java:72) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.execute(TestAutoTuneProfiling.java:34) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.test(TestAutoTuneProfiling.java:22) + sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) + + + + com.avaje.tests.query.autotune.TestAutoTuneProfiling.findById(TestAutoTuneProfiling.java:41) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.useOrderDate(TestAutoTuneProfiling.java:45) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.execute(TestAutoTuneProfiling.java:31) + com.avaje.tests.query.autotune.TestAutoTuneProfiling.test(TestAutoTuneProfiling.java:22) sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - - - - - - - - - diff --git a/src/main/java/com/avaje/ebean/AdminAutofetch.java b/src/main/java/com/avaje/ebean/AdminAutofetch.java deleted file mode 100644 index 49ab9287e..000000000 --- a/src/main/java/com/avaje/ebean/AdminAutofetch.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.avaje.ebean; - -/** - * Administrative control of Autofetch during runtime. - */ -public interface AdminAutofetch { - - /** - * Fire a garbage collection (hint to the JVM). Assuming garbage collection - * fires this will gather the usage profiling information. - */ - void collectUsageViaGC(); - - /** - * This will take the current profiling information and update the "tuned - * query detail". - *

- * This is done periodically and can also be manually invoked. - *

- */ - void updateTunedQueryInfo(); - -} diff --git a/src/main/java/com/avaje/ebean/AutoTune.java b/src/main/java/com/avaje/ebean/AutoTune.java new file mode 100644 index 000000000..c5d17ac55 --- /dev/null +++ b/src/main/java/com/avaje/ebean/AutoTune.java @@ -0,0 +1,14 @@ +package com.avaje.ebean; + +/** + * Administrative control of AutoTune during runtime. + */ +public interface AutoTune { + + /** + * Fire a garbage collection (hint to the JVM). Assuming garbage collection + * fires this will gather remaining usage profiling information. + */ + void collectProfiling(); + +} diff --git a/src/main/java/com/avaje/ebean/config/AutofetchConfig.java b/src/main/java/com/avaje/ebean/config/AutoTuneConfig.java similarity index 60% rename from src/main/java/com/avaje/ebean/config/AutofetchConfig.java rename to src/main/java/com/avaje/ebean/config/AutoTuneConfig.java index 96aa821d5..e95fe2395 100644 --- a/src/main/java/com/avaje/ebean/config/AutofetchConfig.java +++ b/src/main/java/com/avaje/ebean/config/AutoTuneConfig.java @@ -1,11 +1,11 @@ package com.avaje.ebean.config; /** - * Defines the Autofetch behaviour for a EbeanServer. + * Defines the AutoTune behaviour for a EbeanServer. */ -public class AutofetchConfig { +public class AutoTuneConfig { - private AutofetchMode mode = AutofetchMode.DEFAULT_ONIFEMPTY; + private AutoTuneMode mode = AutoTuneMode.DEFAULT_ON; private boolean queryTuning; @@ -17,27 +17,25 @@ public class AutofetchConfig { private double profilingRate = 0.01; - private int profileUpdateFrequency = 60; - private int garbageCollectionWait = 100; - private boolean garbageCollectionOnShutdown; + private boolean skipCollectionOnShutdown; - public AutofetchConfig() { + public AutoTuneConfig() { } /** - * Return the mode used when autofetch has not been explicit defined on a + * Return the mode used when autoTune has not been explicit defined on a * query. */ - public AutofetchMode getMode() { + public AutoTuneMode getMode() { return mode; } /** - * Set the mode used when autofetch has not been explicit defined on a query. + * Set the mode used when autoTune has not been explicit defined on a query. */ - public void setMode(AutofetchMode mode) { + public void setMode(AutoTuneMode mode) { this.mode = mode; } @@ -74,6 +72,9 @@ public class AutofetchConfig { * If this is false then the version property will be added when profiling * detects that the bean is possibly going to be modified. *

+ *

+ * Generally this is not expected to be turned on. + *

*/ public void setQueryTuningAddVersion(boolean queryTuningAddVersion) { this.queryTuningAddVersion = queryTuningAddVersion; @@ -128,29 +129,13 @@ public class AutofetchConfig { this.profilingRate = profilingRate; } - /** - * Return the frequency in seconds to update the autofetch tuned queries from - * the profiled information. - */ - public int getProfileUpdateFrequency() { - return profileUpdateFrequency; - } - - /** - * Set the frequency in seconds to update the autofetch tuned queries from the - * profiled information. - */ - public void setProfileUpdateFrequency(int profileUpdateFrequency) { - this.profileUpdateFrequency = profileUpdateFrequency; - } - /** * Return the time in millis to wait after a system gc to collect profiling * information. *

* The profiling information is collected on object finalise. As such we * generally don't want to trigger GC (let the JVM do its thing) but on - * shutdown the autofetch manager will trigger System.gc() and then wait + * shutdown the autoTune manager will trigger System.gc() and then wait * (default 100 millis) to hopefully collect profiling information - * especially for short run unit tests. *

@@ -160,33 +145,24 @@ public class AutofetchConfig { } /** - * Set the time in millis to wait after a System.gc() to collect profiling - * information. + * Set the time in millis to wait after a System.gc() to collect profiling information. */ public void setGarbageCollectionWait(int garbageCollectionWait) { this.garbageCollectionWait = garbageCollectionWait; } - /** - * Return true if GC should be trigger on shutdown. - *

- * Autofetch profiling information is collected as part of garbage collection. - *

+ * Return true if profiling collection should be skipped on shutdown. */ - public boolean isGarbageCollectionOnShutdown() { - return garbageCollectionOnShutdown; + public boolean isSkipCollectionOnShutdown() { + return skipCollectionOnShutdown; } /** - * Set to true if you want GC to trigger on shutdown. - *

- * This would be done if you want to try and collect Autofetch profiling information - * on shutdown. - *

+ * Set to true if profiling collection should be skipped on shutdown. */ - public void setGarbageCollectionOnShutdown(boolean garbageCollectionOnShutdown) { - this.garbageCollectionOnShutdown = garbageCollectionOnShutdown; + public void setSkipCollectionOnShutdown(boolean skipCollectionOnShutdown) { + this.skipCollectionOnShutdown = skipCollectionOnShutdown; } /** @@ -194,16 +170,14 @@ public class AutofetchConfig { */ public void loadSettings(PropertiesWrapper p) { - queryTuning = p.getBoolean("autotune.querytuning", queryTuning); - queryTuningAddVersion = p.getBoolean("autotune.queryTuningAddVersion", queryTuningAddVersion); - garbageCollectionOnShutdown = p.getBoolean("autotune.garbageCollectionOnShutdown", garbageCollectionOnShutdown); + queryTuning = p.getBoolean("autoTune.queryTuning", queryTuning); + queryTuningAddVersion = p.getBoolean("autoTune.queryTuningAddVersion", queryTuningAddVersion); + skipCollectionOnShutdown = p.getBoolean("autoTune.skipCollectionOnShutdown", skipCollectionOnShutdown); - profiling = p.getBoolean("autotune.profiling", profiling); - mode = p.getEnum(AutofetchMode.class, "autotune.implicitmode", mode); + mode = p.getEnum(AutoTuneMode.class, "autoTune.mode", mode); - profilingBase = p.getInt("autotune.profiling.base", profilingBase); - - profilingRate = p.getDouble("autotune.profiling.rate", profilingRate); - profileUpdateFrequency = p.getInt("autotune.profiling.updatefrequency", profileUpdateFrequency); + profiling = p.getBoolean("autoTune.profiling", profiling); + profilingBase = p.getInt("autoTune.profilingBase", profilingBase); + profilingRate = p.getDouble("autoTune.profilingRate", profilingRate); } } diff --git a/src/main/java/com/avaje/ebean/config/AutofetchMode.java b/src/main/java/com/avaje/ebean/config/AutoTuneMode.java similarity index 96% rename from src/main/java/com/avaje/ebean/config/AutofetchMode.java rename to src/main/java/com/avaje/ebean/config/AutoTuneMode.java index 86dcf95db..b255421ff 100644 --- a/src/main/java/com/avaje/ebean/config/AutofetchMode.java +++ b/src/main/java/com/avaje/ebean/config/AutoTuneMode.java @@ -11,7 +11,7 @@ import com.avaje.ebean.Query; * query. *

*/ -public enum AutofetchMode { +public enum AutoTuneMode { /** * Don't implicitly use Autofetch. Must explicitly turn it on. diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneCollection.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneCollection.java index eb62059b9..98285d9d3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneCollection.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneCollection.java @@ -13,8 +13,8 @@ public class AutoTuneCollection { List entries = new ArrayList(); - public Entry add(ObjectGraphOrigin origin, OrmQueryDetail detail) { - Entry entry = new Entry(origin, detail); + public Entry add(ObjectGraphOrigin origin, OrmQueryDetail detail, String sourceQuery) { + Entry entry = new Entry(origin, detail, sourceQuery); entries.add(entry); return entry; } @@ -38,14 +38,20 @@ public class AutoTuneCollection { */ private final OrmQueryDetail detail; + /** + * The original/existing query detail. + */ + private final String originalQuery; + /** * Summary execution statistics for queries related to this origin point. */ private final List queries = new ArrayList(); - public Entry(ObjectGraphOrigin origin, OrmQueryDetail detail) { + public Entry(ObjectGraphOrigin origin, OrmQueryDetail detail, String originalQuery) { this.origin = origin; this.detail = detail; + this.originalQuery = originalQuery; } public void addQuery(EntryQuery entryQuery) { @@ -60,6 +66,10 @@ public class AutoTuneCollection { return detail; } + public String getOriginalQuery() { + return originalQuery; + } + public List getQueries() { return queries; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneService.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneService.java index 291cfe9f3..2e8258697 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneService.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoTuneService.java @@ -1,8 +1,6 @@ package com.avaje.ebeaninternal.server.autofetch; -import com.avaje.ebean.AdminAutofetch; -import com.avaje.ebean.config.ServerConfig; -import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebean.AutoTune; import com.avaje.ebeaninternal.api.SpiQuery; /** @@ -17,7 +15,7 @@ import com.avaje.ebeaninternal.api.SpiQuery; * etc). These are applied to the query when tuneQuery() is called. *

*/ -public interface AutoTuneService extends AdminAutofetch { +public interface AutoTuneService extends AutoTune { /** * Load the query tuning information. @@ -41,16 +39,7 @@ public interface AutoTuneService extends AdminAutofetch { * Fire a garbage collection (hint to the JVM). Assuming garbage collection * fires this will gather the usage profiling information. */ - void collectUsageViaGC(); - - /** - * This will take the current profiling information and update the "tuned - * query detail". - *

- * This is done periodically and can also be manually invoked. - *

- */ - void updateTunedQueryInfo(); + void collectProfiling(); /** * On shutdown fire garbage collection and collect statistics. Note that diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseQueryTuner.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseQueryTuner.java index 9536197d5..f3614d4f8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseQueryTuner.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/BaseQueryTuner.java @@ -2,8 +2,8 @@ package com.avaje.ebeaninternal.server.autofetch.service; import com.avaje.ebean.bean.CallStack; import com.avaje.ebean.bean.ObjectGraphNode; -import com.avaje.ebean.config.AutofetchConfig; -import com.avaje.ebean.config.AutofetchMode; +import com.avaje.ebean.config.AutoTuneConfig; +import com.avaje.ebean.config.AutoTuneMode; import com.avaje.ebeaninternal.api.SpiEbeanServer; import com.avaje.ebeaninternal.api.SpiQuery; import com.avaje.ebeaninternal.server.autofetch.ProfilingListener; @@ -22,7 +22,7 @@ public class BaseQueryTuner { private boolean profiling; - private final AutofetchMode mode; + private final AutoTuneMode mode; /** * Map of the tuned query details per profile query point. @@ -34,7 +34,9 @@ public class BaseQueryTuner { private final ProfilingListener profilingListener; - public BaseQueryTuner(AutofetchConfig config, SpiEbeanServer server, ProfilingListener profilingListener) { + boolean fullProfiling = true; + + public BaseQueryTuner(AutoTuneConfig config, SpiEbeanServer server, ProfilingListener profilingListener) { this.server = server; this.profilingListener = profilingListener; this.mode = config.getMode(); @@ -58,8 +60,6 @@ public class BaseQueryTuner { return (info == null) ? null : info.getTunedDetail(); } - boolean fullProfiling = true; - /** * Auto tune the query and enable profiling. */ @@ -90,7 +90,7 @@ public class BaseQueryTuner { ObjectGraphNode origin = query.setOrigin(stack); if (profiling) { - if (profilingListener.isProfileRequest(origin)) { + if (profilingListener.isProfileRequest(origin, query)) { // collect more profiling based on profiling rate etc query.setProfilingListener(profilingListener); } @@ -108,7 +108,7 @@ public class BaseQueryTuner { // create a query point to identify the query ObjectGraphNode origin = query.setOrigin(stack); - if (profilingListener.isProfileRequest(origin)) { + if (profilingListener.isProfileRequest(origin, query)) { // collect more profiling based on profiling rate etc query.setProfilingListener(profilingListener); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoTuneService.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoTuneService.java index 3a22f1d57..445b59715 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoTuneService.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoTuneService.java @@ -1,7 +1,7 @@ package com.avaje.ebeaninternal.server.autofetch.service; import com.avaje.ebean.bean.ObjectGraphOrigin; -import com.avaje.ebean.config.AutofetchConfig; +import com.avaje.ebean.config.AutoTuneConfig; import com.avaje.ebean.config.ServerConfig; import com.avaje.ebeaninternal.api.SpiEbeanServer; import com.avaje.ebeaninternal.api.SpiQuery; @@ -18,6 +18,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.List; /** @@ -29,87 +31,107 @@ public class DefaultAutoTuneService implements AutoTuneService { private final long defaultGarbageCollectionWait; - private final boolean garbageCollectionOnShutdown; + private final boolean skipCollectionOnShutdown; private final BaseQueryTuner queryTuner; private final ProfileManager profileManager; + private final boolean profiling; + public DefaultAutoTuneService(SpiEbeanServer server, ServerConfig serverConfig) { - AutofetchConfig config = serverConfig.getAutofetchConfig(); + AutoTuneConfig config = serverConfig.getAutoTuneConfig(); + this.profiling = config.isProfiling(); this.profileManager = new ProfileManager(config, server); this.queryTuner = new BaseQueryTuner(config, server, profileManager); - this.garbageCollectionOnShutdown = config.isGarbageCollectionOnShutdown(); + this.skipCollectionOnShutdown = config.isSkipCollectionOnShutdown(); this.defaultGarbageCollectionWait = (long) config.getGarbageCollectionWait(); } /** * Load the query tuning information from it's data store. */ + @Override public void startup() { File file = new File("ebean-autotune.xml"); AutoTuneXmlReader reader = new AutoTuneXmlReader(); Autotune profiling = reader.read(file); - List originList = profiling.getOrigin(); - for (Origin origin : originList) { - String key = origin.getKey(); - String detail = origin.getDetail(); - OrmQueryDetailParser parser = new OrmQueryDetailParser(detail); - OrmQueryDetail fetchDetail = parser.parse(); - TunedQueryInfo tunedQueryInfo = new TunedQueryInfo(fetchDetail); - queryTuner.load(key, tunedQueryInfo); + for (Origin origin : profiling.getOrigin()) { + queryTuner.load(origin.getKey(), createTunedQueryInfo(origin)); } } - private void saveProfiling() { + @NotNull + private TunedQueryInfo createTunedQueryInfo(Origin origin) { + OrmQueryDetail detail = new OrmQueryDetailParser(origin.getDetail()).parse(); + return new TunedQueryInfo(detail); + } + + private void saveProfiling(boolean reset) { Autotune document = new Autotune(); - AutoTuneCollection autoTuneCollection = profileManager.profilingCollection(false); + AutoTuneCollection autoTuneCollection = profileManager.profilingCollection(reset); List entries = autoTuneCollection.getEntries(); for (AutoTuneCollection.Entry entry : entries) { - ObjectGraphOrigin point = entry.getOrigin(); - OrmQueryDetail profileDetail = entry.getDetail(); - - OrmQueryDetail tuneDetail = queryTuner.get(point.getKey()); - if (tuneDetail == null) { - ProfileNew profileNew = document.getProfileNew(); - if (profileNew == null) { - profileNew = new ProfileNew(); - document.setProfileNew(profileNew); - } - profileNew.getOrigin().add( createOrigin(entry, point)); - - } else if (!tuneDetail.isAutoTuneEqual(profileDetail)) { - Origin origin1 = createOrigin(entry, point); - origin1.setTuneDetail(tuneDetail.toString()); - ProfileDiff diff = document.getProfileDiff(); - if (diff == null) { - diff = new ProfileDiff(); - document.setProfileDiff(diff); - } - diff.getOrigin().add(origin1); - } + saveProfilingEntry(document, entry); } - File file = new File("ebean-autotune-profiling.xml"); + SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss"); + String now = df.format(new Date()); + + File file = new File("ebean-autotune-profiling"+"-"+now+".xml"); AutoTuneXmlWriter writer = new AutoTuneXmlWriter(); writer.write(document, file); } + private void saveProfilingEntry(Autotune document, AutoTuneCollection.Entry entry) { + + ObjectGraphOrigin point = entry.getOrigin(); + OrmQueryDetail profileDetail = entry.getDetail(); + + // compare with the existing query tuning entry + OrmQueryDetail tuneDetail = queryTuner.get(point.getKey()); + if (tuneDetail == null) { + // New entry + ProfileNew profileNew = document.getProfileNew(); + if (profileNew == null) { + profileNew = new ProfileNew(); + document.setProfileNew(profileNew); + } + Origin origin = createOrigin(entry, point); + origin.setOriginal(entry.getOriginalQuery()); + profileNew.getOrigin().add(origin); + + } else if (!tuneDetail.isAutoTuneEqual(profileDetail)) { + // Diff entry + Origin origin = createOrigin(entry, point); + origin.setOriginal(tuneDetail.toString()); + ProfileDiff diff = document.getProfileDiff(); + if (diff == null) { + diff = new ProfileDiff(); + document.setProfileDiff(diff); + } + diff.getOrigin().add(origin); + } + } + + /** + * Create the XML Origin bean for the given entry and ObjectGraphOrigin. + */ @NotNull private Origin createOrigin(AutoTuneCollection.Entry entry, ObjectGraphOrigin point) { - Origin origin1 = new Origin(); - origin1.setKey(point.getKey()); - origin1.setBeanType(point.getBeanType()); - origin1.setDetail(entry.getDetail().toString()); - origin1.setCallStack(point.getCallStack().description("\n")); - return origin1; + Origin origin = new Origin(); + origin.setKey(point.getKey()); + origin.setBeanType(point.getBeanType()); + origin.setDetail(entry.getDetail().toString()); + origin.setCallStack(point.getCallStack().description("\n")); + return origin; } /** @@ -120,10 +142,11 @@ public class DefaultAutoTuneService implements AutoTuneService { * collection may not otherwise occur at all. *

*/ + @Override public void shutdown() { - if (garbageCollectionOnShutdown) { - collectUsageViaGC(-1); - saveProfiling(); + if (profiling && !skipCollectionOnShutdown) { + collectProfiling(-1); + saveProfiling(false); } } @@ -143,11 +166,12 @@ public class DefaultAutoTuneService implements AutoTuneService { * defaults to 100 milliseconds. *

*/ - public void collectUsageViaGC() { - collectUsageViaGC(-1); + @Override + public void collectProfiling() { + collectProfiling(-1); } - public void collectUsageViaGC(long waitMillis) { + public void collectProfiling(long waitMillis) { System.gc(); try { if (waitMillis < 0) { @@ -157,99 +181,13 @@ public class DefaultAutoTuneService implements AutoTuneService { } catch (InterruptedException e) { logger.warn("Error while sleeping after System.gc() request.", e); } - updateTunedQueryInfo(); } - /** - * Update the tuned fetch plans from the current usage information. - */ - public void updateTunedQueryInfo() { - -// if (!profiling) { -// // we are not collecting any profiling information at -// // the moment so don't try updating the tuned query plans. -// return "Not profiling"; -// } -// -// synchronized (statisticsMonitor) { -// -// Counters counters = new Counters(); -// -// for (ProfileOrigin queryPointStatistics : statisticsMap.values()) { -// if (!queryPointStatistics.hasUsage()) { -// // no usage statistics collected yet... -// counters.incrementNoUsage(); -// } else { -// updateTunedQueryFromUsage(counters, queryPointStatistics); -// } -// } -// -// String summaryInfo = counters.toString(); -// -// if (counters.isInteresting()) { -// // only log it if its interesting -// logging.logSummary(summaryInfo); -// } -// -// return summaryInfo; -// } - } - - -// -// private void updateTunedQueryFromUsage(Counters counters, ProfileOrigin statistics) { -// -// ObjectGraphOrigin queryPoint = statistics.getOrigin(); -// String beanType = queryPoint.getBeanType(); -// -// try { -// Class beanClass = ClassUtil.forName(beanType, this.getClass()); -// BeanDescriptor beanDescriptor = server.getBeanDescriptor(beanClass); -// if (beanDescriptor != null) { -// -// // Determine the fetch plan from the latest statistics. -// // Use this to compare with current "tuned fetch plan". -// OrmQueryDetail newFetchDetail = statistics.buildTunedFetch(beanDescriptor); -// -// // get the current tuned fetch info... -// TunedQueryInfo currentFetch = tunedQueryInfoMap.get(queryPoint.getKey()); -// -// if (currentFetch == null) { -// // its a new fetch plan, add it. -// counters.incrementNew(); -// -// currentFetch = statistics.createTunedFetch(newFetchDetail); -// logging.logNew(currentFetch); -// tunedQueryInfoMap.put(queryPoint.getKey(), currentFetch); -// -// } else if (!currentFetch.isSame(newFetchDetail)) { -// // the fetch plan has changed, update it. -// counters.incrementModified(); -// -// logging.logChanged(currentFetch, newFetchDetail); -// currentFetch.setTunedDetail(newFetchDetail); -// -// } else { -// // the fetch plan has not changed... -// counters.incrementUnchanged(); -// } -// -// currentFetch.setProfileCount(statistics.getCounter()); -// } -// -// } catch (ClassNotFoundException e) { -// // expected after renaming/moving an entity bean -// String msg = e.toString() + " updating autoFetch tuned query for " + beanType -// + ". It isLikely this bean has been renamed or moved"; -// logging.logInfo(msg, null); -// statisticsMap.remove(statistics.getOrigin().getKey()); -// } -// } -// /** * Auto tune the query and enable profiling. */ + @Override public boolean tuneQuery(SpiQuery query) { return queryTuner.tuneQuery(query); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java index cdab0e94f..8bfdb6ea0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java @@ -330,7 +330,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { return ddlGenerator; } - public AdminAutofetch getAdminAutofetch() { + public AutoTune getAutoTune() { return autoTuneService; } @@ -1039,7 +1039,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { public SpiOrmQueryRequest createQueryRequest(BeanDescriptor desc, SpiQuery query, Transaction t) { - if (desc.isAutoFetchTunable() && !query.isSqlSelect() && !autoTuneService.tuneQuery(query)) { + if (desc.isAutoTunable() && !query.isSqlSelect() && !autoTuneService.tuneQuery(query)) { // use deployment FetchType.LAZY/EAGER annotations // to define the 'default' select clause query.setDefaultSelectClause(); @@ -1047,13 +1047,13 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { if (query.selectAllForLazyLoadProperty()) { // we need to select all properties to ensure the lazy load property - // was included (was not included by default or via autofetch). + // was included (was not included by default or via autoTune). if (logger.isDebugEnabled()) { logger.debug("Using selectAllForLazyLoadProperty"); } } - // if determine cost and no origin for Autofetch + // if determine cost and no origin for AutoTune if (query.getParentNode() == null) { query.setOrigin(createCallStack()); } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java index e73218217..049063b82 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java @@ -126,7 +126,7 @@ public class BeanDescriptor implements MetaBeanInfo, SpiBeanType { */ private final String selectLastInsertedId; - private final boolean autoFetchTunable; + private final boolean autoTunable; /** * The concurrency mode for beans of this type. @@ -366,7 +366,7 @@ public class BeanDescriptor implements MetaBeanInfo, SpiBeanType { this.baseTable = InternString.intern(deploy.getBaseTable()); this.baseTableAsOf = deploy.getBaseTableAsOf(); this.baseTableVersionsBetween = deploy.getBaseTableVersionsBetween(); - this.autoFetchTunable = EntityType.ORM.equals(entityType) && (beanFinder == null); + this.autoTunable = EntityType.ORM.equals(entityType) && (beanFinder == null); // helper object used to derive lists of properties DeployBeanPropertyLists listHelper = new DeployBeanPropertyLists(owner, this, deploy); @@ -1603,10 +1603,10 @@ public class BeanDescriptor implements MetaBeanInfo, SpiBeanType { } /** - * Return true if queries for beans of this type are autoFetch tunable. + * Return true if queries for beans of this type are auto tunable. */ - public boolean isAutoFetchTunable() { - return autoFetchTunable; + public boolean isAutoTunable() { + return autoTunable; } /** diff --git a/src/main/resources/ebean-autotune-1.0.xsd b/src/main/resources/ebean-autotune-1.0.xsd index 42a1ce081..fd4fc3ad2 100644 --- a/src/main/resources/ebean-autotune-1.0.xsd +++ b/src/main/resources/ebean-autotune-1.0.xsd @@ -23,7 +23,7 @@ - + diff --git a/src/test/java/com/avaje/tests/autofetch/AutofetchEmbeddedTest.java b/src/test/java/com/avaje/tests/autofetch/AutofetchEmbeddedTest.java index ba59ae6cb..c6de0cd29 100644 --- a/src/test/java/com/avaje/tests/autofetch/AutofetchEmbeddedTest.java +++ b/src/test/java/com/avaje/tests/autofetch/AutofetchEmbeddedTest.java @@ -37,9 +37,9 @@ public class AutofetchEmbeddedTest extends BaseTestCase { @Test public void testEmbeddedBeanQueryTuning() { -// Ebean.getServer(null).getAdminAutofetch().setProfiling(true); -// Ebean.getServer(null).getAdminAutofetch().setQueryTuning(true); -// Ebean.getServer(null).getAdminAutofetch().setProfilingBase(1); +// Ebean.getServer(null).getAutoTune().setProfiling(true); +// Ebean.getServer(null).getAutoTune().setQueryTuning(true); +// Ebean.getServer(null).getAutoTune().setProfilingBase(1); // // EMain testBean = new EMain(); // testBean.setName("test"); @@ -58,7 +58,7 @@ public class AutofetchEmbeddedTest extends BaseTestCase { // Ebean.commitTransaction(); // } finally { // Ebean.endTransaction(); -// logger.debug(Ebean.getServer(null).getAdminAutofetch().collectUsageViaGC()); +// logger.debug(Ebean.getServer(null).getAutoTune().collectProfiling()); // } // } } From 00964777572b79b958c037e6131cf633eada9be9 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Thu, 10 Sep 2015 09:16:39 +1200 Subject: [PATCH 7/9] AutoTune update - repackage tests and sort profiling xml entries --- .../service/DefaultAutoTuneService.java | 40 ++++++++++++++++++- .../autofetch/TunedQueryInfoTest.java | 2 +- .../TunedQueryWithNullFetchedBeanTest.java | 2 +- .../transaction/TestAutoCommitDataSource.java | 2 +- .../transaction/TestBatchPersistCascade.java | 2 +- src/test/resources/ebean.properties | 6 --- 6 files changed, 42 insertions(+), 12 deletions(-) rename src/test/java/com/avaje/{ebeaninternal/server => tests}/autofetch/TunedQueryInfoTest.java (99%) rename src/test/java/com/avaje/{ebeaninternal/server => tests}/autofetch/TunedQueryWithNullFetchedBeanTest.java (97%) rename src/test/java/com/avaje/{ebeaninternal/server => tests}/transaction/TestAutoCommitDataSource.java (97%) rename src/test/java/com/avaje/{ebeaninternal/server => tests}/transaction/TestBatchPersistCascade.java (98%) diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoTuneService.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoTuneService.java index 445b59715..d2d7242d5 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoTuneService.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoTuneService.java @@ -19,6 +19,8 @@ import org.slf4j.LoggerFactory; import java.io.File; import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.Comparator; import java.util.Date; import java.util.List; @@ -39,6 +41,8 @@ public class DefaultAutoTuneService implements AutoTuneService { private final boolean profiling; + private final String serverName; + public DefaultAutoTuneService(SpiEbeanServer server, ServerConfig serverConfig) { AutoTuneConfig config = serverConfig.getAutoTuneConfig(); @@ -46,7 +50,7 @@ public class DefaultAutoTuneService implements AutoTuneService { this.profiling = config.isProfiling(); this.profileManager = new ProfileManager(config, server); this.queryTuner = new BaseQueryTuner(config, server, profileManager); - + this.serverName = server.getName(); this.skipCollectionOnShutdown = config.isSkipCollectionOnShutdown(); this.defaultGarbageCollectionWait = (long) config.getGarbageCollectionWait(); } @@ -78,16 +82,48 @@ public class DefaultAutoTuneService implements AutoTuneService { AutoTuneCollection autoTuneCollection = profileManager.profilingCollection(reset); List entries = autoTuneCollection.getEntries(); + for (AutoTuneCollection.Entry entry : entries) { saveProfilingEntry(document, entry); } + sortDocument(document); + SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss"); String now = df.format(new Date()); - File file = new File("ebean-autotune-profiling"+"-"+now+".xml"); + File file = new File("ebean-profiling"+"-"+serverName+"-"+now+".xml"); AutoTuneXmlWriter writer = new AutoTuneXmlWriter(); writer.write(document, file); + + logger.debug("profiling entries:{}", entries.size()); + } + + /** + * Set the diff and new entries by bean type followed by key. + */ + private void sortDocument(Autotune document) { + + ProfileDiff profileDiff = document.getProfileDiff(); + if (profileDiff != null) { + Collections.sort(profileDiff.getOrigin(), new OriginNameKeySort()); + } + ProfileNew profileNew = document.getProfileNew(); + if (profileNew != null) { + Collections.sort(profileNew.getOrigin(), new OriginNameKeySort()); + } + } + + class OriginNameKeySort implements Comparator { + + @Override + public int compare(Origin o1, Origin o2) { + int comp = o1.getBeanType().compareTo(o2.getBeanType()); + if (comp == 0) { + comp = o1.getKey().compareTo(o2.getKey()); + } + return comp; + } } private void saveProfilingEntry(Autotune document, AutoTuneCollection.Entry entry) { diff --git a/src/test/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfoTest.java b/src/test/java/com/avaje/tests/autofetch/TunedQueryInfoTest.java similarity index 99% rename from src/test/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfoTest.java rename to src/test/java/com/avaje/tests/autofetch/TunedQueryInfoTest.java index a27881bc5..5c4ff601f 100644 --- a/src/test/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfoTest.java +++ b/src/test/java/com/avaje/tests/autofetch/TunedQueryInfoTest.java @@ -1,4 +1,4 @@ -package com.avaje.ebeaninternal.server.autofetch; +package com.avaje.tests.autofetch; import java.util.List; import java.util.Set; diff --git a/src/test/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryWithNullFetchedBeanTest.java b/src/test/java/com/avaje/tests/autofetch/TunedQueryWithNullFetchedBeanTest.java similarity index 97% rename from src/test/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryWithNullFetchedBeanTest.java rename to src/test/java/com/avaje/tests/autofetch/TunedQueryWithNullFetchedBeanTest.java index 2e304b56b..501614941 100644 --- a/src/test/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryWithNullFetchedBeanTest.java +++ b/src/test/java/com/avaje/tests/autofetch/TunedQueryWithNullFetchedBeanTest.java @@ -1,4 +1,4 @@ -package com.avaje.ebeaninternal.server.autofetch; +package com.avaje.tests.autofetch; import java.util.List; import java.util.Set; diff --git a/src/test/java/com/avaje/ebeaninternal/server/transaction/TestAutoCommitDataSource.java b/src/test/java/com/avaje/tests/transaction/TestAutoCommitDataSource.java similarity index 97% rename from src/test/java/com/avaje/ebeaninternal/server/transaction/TestAutoCommitDataSource.java rename to src/test/java/com/avaje/tests/transaction/TestAutoCommitDataSource.java index b24b37316..75ec8d753 100644 --- a/src/test/java/com/avaje/ebeaninternal/server/transaction/TestAutoCommitDataSource.java +++ b/src/test/java/com/avaje/tests/transaction/TestAutoCommitDataSource.java @@ -1,4 +1,4 @@ -package com.avaje.ebeaninternal.server.transaction; +package com.avaje.tests.transaction; import com.avaje.ebean.BaseTestCase; import com.avaje.ebean.EbeanServer; diff --git a/src/test/java/com/avaje/ebeaninternal/server/transaction/TestBatchPersistCascade.java b/src/test/java/com/avaje/tests/transaction/TestBatchPersistCascade.java similarity index 98% rename from src/test/java/com/avaje/ebeaninternal/server/transaction/TestBatchPersistCascade.java rename to src/test/java/com/avaje/tests/transaction/TestBatchPersistCascade.java index 9e0cc9608..db66fb72f 100644 --- a/src/test/java/com/avaje/ebeaninternal/server/transaction/TestBatchPersistCascade.java +++ b/src/test/java/com/avaje/tests/transaction/TestBatchPersistCascade.java @@ -1,4 +1,4 @@ -package com.avaje.ebeaninternal.server.transaction; +package com.avaje.tests.transaction; import com.avaje.ebean.BaseTestCase; import com.avaje.ebean.Ebean; diff --git a/src/test/resources/ebean.properties b/src/test/resources/ebean.properties index 228b08aa6..12018186d 100644 --- a/src/test/resources/ebean.properties +++ b/src/test/resources/ebean.properties @@ -13,12 +13,6 @@ ebean.encryptKeyManager=com.avaje.tests.basic.encrypt.BasicEncyptKeyManager ebean.autotune.querytuning=true ebean.autotune.profiling=true -#ebean.autoTune.implicitmode=default_off -#ebean.autotune.implicitmode=default_onifempty -#ebean.autofetch.profiling.min=1 -#ebean.autofetch.profiling.base=10 -##ebean.autofetch.profiling.rate=0.05 -#ebean.autofetch.garbageCollectionOnShutdown=true ebean.ddl.generate=true From 373598ab26c21812d56c7d17ec4d8b38ce3ab329 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Thu, 10 Sep 2015 10:02:17 +1200 Subject: [PATCH 8/9] AutoTune update - config properties for queryTuningFile and profilingFile + logging adjustment --- .../avaje/ebean/config/AutoTuneConfig.java | 35 ++++++++++ .../service/DefaultAutoTuneService.java | 66 ++++++++++++++----- src/test/resources/ebean.properties | 3 + 3 files changed, 88 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/avaje/ebean/config/AutoTuneConfig.java b/src/main/java/com/avaje/ebean/config/AutoTuneConfig.java index e95fe2395..6eab25e8c 100644 --- a/src/main/java/com/avaje/ebean/config/AutoTuneConfig.java +++ b/src/main/java/com/avaje/ebean/config/AutoTuneConfig.java @@ -7,12 +7,16 @@ public class AutoTuneConfig { private AutoTuneMode mode = AutoTuneMode.DEFAULT_ON; + private String queryTuningFile = "ebean-autotune.xml"; + private boolean queryTuning; private boolean queryTuningAddVersion; private boolean profiling; + private String profilingFile = "ebean-profiling"; + private int profilingBase = 5; private double profilingRate = 0.01; @@ -24,6 +28,34 @@ public class AutoTuneConfig { public AutoTuneConfig() { } + /** + * Return the name of the file that holds the query tuning information. + */ + public String getQueryTuningFile() { + return queryTuningFile; + } + + /** + * Set the name of the file that holds the query tuning information. + */ + public void setQueryTuningFile(String queryTuningFile) { + this.queryTuningFile = queryTuningFile; + } + + /** + * Return the name of the file that profiling information is written to. + */ + public String getProfilingFile() { + return profilingFile; + } + + /** + * Set the name of the file that profiling information is written to. + */ + public void setProfilingFile(String profilingFile) { + this.profilingFile = profilingFile; + } + /** * Return the mode used when autoTune has not been explicit defined on a * query. @@ -172,6 +204,8 @@ public class AutoTuneConfig { queryTuning = p.getBoolean("autoTune.queryTuning", queryTuning); queryTuningAddVersion = p.getBoolean("autoTune.queryTuningAddVersion", queryTuningAddVersion); + queryTuningFile = p.get("autoTune.queryTuningFile", queryTuningFile); + skipCollectionOnShutdown = p.getBoolean("autoTune.skipCollectionOnShutdown", skipCollectionOnShutdown); mode = p.getEnum(AutoTuneMode.class, "autoTune.mode", mode); @@ -179,5 +213,6 @@ public class AutoTuneConfig { profiling = p.getBoolean("autoTune.profiling", profiling); profilingBase = p.getInt("autoTune.profilingBase", profilingBase); profilingRate = p.getDouble("autoTune.profilingRate", profilingRate); + profilingFile = p.get("autoTune.profilingFile", profilingFile); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoTuneService.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoTuneService.java index d2d7242d5..12600ad6d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoTuneService.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/service/DefaultAutoTuneService.java @@ -23,6 +23,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; /** * Implementation of the AutoTuneService which is comprised of profiling and query tuning. @@ -41,16 +42,25 @@ public class DefaultAutoTuneService implements AutoTuneService { private final boolean profiling; + private final boolean queryTuning; + + private final String tuningFile; + + private final String profilingFile; + private final String serverName; public DefaultAutoTuneService(SpiEbeanServer server, ServerConfig serverConfig) { AutoTuneConfig config = serverConfig.getAutoTuneConfig(); + this.queryTuning = config.isQueryTuning(); this.profiling = config.isProfiling(); + this.tuningFile = config.getQueryTuningFile(); + this.profilingFile = config.getProfilingFile(); + this.serverName = server.getName(); this.profileManager = new ProfileManager(config, server); this.queryTuner = new BaseQueryTuner(config, server, profileManager); - this.serverName = server.getName(); this.skipCollectionOnShutdown = config.isSkipCollectionOnShutdown(); this.defaultGarbageCollectionWait = (long) config.getGarbageCollectionWait(); } @@ -61,11 +71,19 @@ public class DefaultAutoTuneService implements AutoTuneService { @Override public void startup() { - File file = new File("ebean-autotune.xml"); - AutoTuneXmlReader reader = new AutoTuneXmlReader(); - Autotune profiling = reader.read(file); - for (Origin origin : profiling.getOrigin()) { - queryTuner.load(origin.getKey(), createTunedQueryInfo(origin)); + if (queryTuning) { + File file = new File(tuningFile); + if (!file.exists()) { + logger.warn("AutoTune file {} not found - no automatic tuning will be applied", file.getAbsolutePath()); + + } else { + AutoTuneXmlReader reader = new AutoTuneXmlReader(); + Autotune profiling = reader.read(file); + logger.info("AutoTune loading {} tuning entries", profiling.getOrigin().size()); + for (Origin origin : profiling.getOrigin()) { + queryTuner.load(origin.getKey(), createTunedQueryInfo(origin)); + } + } } } @@ -83,20 +101,32 @@ public class DefaultAutoTuneService implements AutoTuneService { List entries = autoTuneCollection.getEntries(); + // count "new" and "diff" profiling entries + AtomicInteger newCounter = new AtomicInteger(); + AtomicInteger diffCounter = new AtomicInteger(); + for (AutoTuneCollection.Entry entry : entries) { - saveProfilingEntry(document, entry); + saveProfilingEntry(document, entry, newCounter, diffCounter); } - sortDocument(document); + int totalNew = newCounter.get(); + int totalDiff = diffCounter.get(); + if (totalNew == 0 && totalDiff == 0) { + logger.debug("No new or diff entries for profiling server:{}", serverName); - SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss"); - String now = df.format(new Date()); + } else { + sortDocument(document); - File file = new File("ebean-profiling"+"-"+serverName+"-"+now+".xml"); - AutoTuneXmlWriter writer = new AutoTuneXmlWriter(); - writer.write(document, file); + SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss"); + String now = df.format(new Date()); - logger.debug("profiling entries:{}", entries.size()); + // write the file with serverName and now suffix as we can output the profiling many times + File file = new File(profilingFile + "-" + serverName + "-" + now + ".xml"); + AutoTuneXmlWriter writer = new AutoTuneXmlWriter(); + writer.write(document, file); + + logger.debug("writing new:{} diff:{} profiling entries for server:{}", totalNew, totalDiff, serverName); + } } /** @@ -114,6 +144,9 @@ public class DefaultAutoTuneService implements AutoTuneService { } } + /** + * Comparator sort by bean type then key. + */ class OriginNameKeySort implements Comparator { @Override @@ -126,7 +159,7 @@ public class DefaultAutoTuneService implements AutoTuneService { } } - private void saveProfilingEntry(Autotune document, AutoTuneCollection.Entry entry) { + private void saveProfilingEntry(Autotune document, AutoTuneCollection.Entry entry, AtomicInteger newCount, AtomicInteger diffCount) { ObjectGraphOrigin point = entry.getOrigin(); OrmQueryDetail profileDetail = entry.getDetail(); @@ -135,6 +168,7 @@ public class DefaultAutoTuneService implements AutoTuneService { OrmQueryDetail tuneDetail = queryTuner.get(point.getKey()); if (tuneDetail == null) { // New entry + newCount.incrementAndGet(); ProfileNew profileNew = document.getProfileNew(); if (profileNew == null) { profileNew = new ProfileNew(); @@ -146,6 +180,7 @@ public class DefaultAutoTuneService implements AutoTuneService { } else if (!tuneDetail.isAutoTuneEqual(profileDetail)) { // Diff entry + diffCount.incrementAndGet(); Origin origin = createOrigin(entry, point); origin.setOriginal(tuneDetail.toString()); ProfileDiff diff = document.getProfileDiff(); @@ -219,7 +254,6 @@ public class DefaultAutoTuneService implements AutoTuneService { } } - /** * Auto tune the query and enable profiling. */ diff --git a/src/test/resources/ebean.properties b/src/test/resources/ebean.properties index 12018186d..3205cad75 100644 --- a/src/test/resources/ebean.properties +++ b/src/test/resources/ebean.properties @@ -12,7 +12,10 @@ ebean.encryptKeyManager=com.avaje.tests.basic.encrypt.BasicEncyptKeyManager ebean.autotune.querytuning=true +#ebean.autotune.queryTuningFile=${HOME}/config/tuning.xml + ebean.autotune.profiling=true +#ebean.autotune.profilingFile=/tmp/eb-prof ebean.ddl.generate=true From 74330918e16bc7a2eb5928fcc69d98dc2d6023f6 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Thu, 10 Sep 2015 10:04:31 +1200 Subject: [PATCH 9/9] AutoTune update - .gitignore the profiling output --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index bb406be3c..51330a2eb 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ target/ logs/ log/ +ebean-profiling*.xml /db /mydb.db !src/test/ddl-review/*.sql