From f4ed813c08748bbdf2ece6481cd4fe15614606b9 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Mon, 31 Aug 2015 16:16:10 +1200 Subject: [PATCH] No effective change - code format only --- .../server/autofetch/AutoFetchManager.java | 387 ++++--- .../autofetch/AutoFetchManagerFactory.java | 136 +-- .../autofetch/DefaultAutoFetchManager.java | 951 +++++++++--------- .../DefaultAutoFetchManagerLogging.java | 62 +- .../server/autofetch/Statistics.java | 256 ++--- .../server/autofetch/StatisticsNodeUsage.java | 191 ++-- .../server/autofetch/StatisticsQuery.java | 52 +- .../server/autofetch/TunedQueryInfo.java | 290 +++--- .../server/autofetch/package.html | 2 +- 9 files changed, 1163 insertions(+), 1164 deletions(-) diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManager.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManager.java index bab59e857..7073bf30d 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManager.java @@ -1,7 +1,5 @@ package com.avaje.ebeaninternal.server.autofetch; -import java.util.Iterator; - import com.avaje.ebean.Query; import com.avaje.ebean.bean.NodeUsageListener; import com.avaje.ebean.bean.ObjectGraphNode; @@ -10,6 +8,8 @@ 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. *

@@ -24,221 +24,218 @@ import com.avaje.ebeaninternal.api.SpiQuery; */ public interface AutoFetchManager extends NodeUsageListener { - /** - * Set the owning ebean server. - */ - void setOwner(SpiEbeanServer server, ServerConfig serverConfig); + /** + * 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 the query execution statistics. + */ + void clearQueryStatistics(); - /** - * 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(); + /** + * Clear all the tuned query info. + *

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

+ */ + int clearTunedQueryInfo(); - /** - * 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(); + /** + * 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(); - /** - * Return the current tuned fetch information for a given queryPoint key. - */ - TunedQueryInfo getTunedQueryInfo(String queryPointKey); + /** + * 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 Statistics for a given queryPoint key. - */ - Statistics getStatistics(String queryPointKey); + /** + * Return the current tuned fetch information for a given queryPoint key. + */ + TunedQueryInfo getTunedQueryInfo(String queryPointKey); - /** - * Iterate the tuned fetch info. - *

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

- */ - Iterator iterateTunedQueryInfo(); + /** + * Return the current Statistics for a given queryPoint key. + */ + Statistics getStatistics(String queryPointKey); - /** - * Iterate the node usage statistics. - *

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

- */ - Iterator iterateStatistics(); + /** + * Iterate the tuned fetch info. + *

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

+ */ + Iterator iterateTunedQueryInfo(); - /** - * Return true if profiling is enabled. - */ - boolean isProfiling(); + /** + * Iterate the node usage statistics. + *

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

+ */ + Iterator iterateStatistics(); - /** - * 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 profiling is enabled. + */ + boolean isProfiling(); - /** - * Return true if automatic query tuning is enabled. - */ - boolean isQueryTuning(); + /** + * 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); - /** - * Set to true to enable automatic query tuning. - */ - void setQueryTuning(boolean enable); + /** + * Return true if automatic query tuning is enabled. + */ + boolean isQueryTuning(); - /** - * This controls whether autoFetch is used when it has not been explicitly - * set on a query via {@link Query#setAutofetch(boolean)}. - */ - AutofetchMode getMode(); + /** + * Set to true to enable automatic query tuning. + */ + void setQueryTuning(boolean enable); - /** - * Set the auto fetch mode used when a query has not had - * {@link Query#setAutofetch(boolean)}. - */ - void setMode(AutofetchMode Mode); + /** + * This controls whether autoFetch is used when it has not been explicitly + * set on a query via {@link Query#setAutofetch(boolean)}. + */ + AutofetchMode getMode(); - /** - * Return the profiling rate (int between 0 and 100). - */ - double getProfilingRate(); + /** + * Set the auto fetch mode used when a query has not had + * {@link Query#setAutofetch(boolean)}. + */ + void setMode(AutofetchMode Mode); - /** - * Set the profiling rate (int between 0 and 100). - */ - void setProfilingRate(double rate); + /** + * Return the profiling rate (int between 0 and 100). + */ + double getProfilingRate(); - /** - * 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 the profiling rate (int between 0 and 100). + */ + void setProfilingRate(double rate); - /** - * 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 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(); - /** - * 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 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); - /** - * 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); + /** + * 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(); - /** - * Fire a garbage collection (hint to the JVM). Assuming garbage collection - * fires this will gather the usage profiling information. - */ - String collectUsageViaGC(long waitMillis); + /** + * 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); - /** - * 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(); + /** + * Fire a garbage collection (hint to the JVM). Assuming garbage collection + * fires this will gather the usage profiling information. + */ + String collectUsageViaGC(long waitMillis); - /** - * 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); + /** + * 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(); - /** - * 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); + /** + * 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); - - /** - * 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(); + /** + * 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/AutoFetchManagerFactory.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManagerFactory.java index 8cb8b2e79..b19a4ee37 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManagerFactory.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/AutoFetchManagerFactory.java @@ -13,83 +13,83 @@ import java.io.ObjectInputStream; public class AutoFetchManagerFactory { - private static final Logger logger = LoggerFactory.getLogger(AutoFetchManagerFactory.class); + private static final Logger logger = LoggerFactory.getLogger(AutoFetchManagerFactory.class); - public static AutoFetchManager create(SpiEbeanServer server, ServerConfig serverConfig, ResourceManager resourceManager) { - - AutoFetchManagerFactory me = new AutoFetchManagerFactory(); - return me.createAutoFetchManager(server, serverConfig, resourceManager); - } - - private AutoFetchManager createAutoFetchManager(SpiEbeanServer server, ServerConfig serverConfig, ResourceManager resourceManager){ - - AutoFetchManager manager = createAutoFetchManager(server.getName(), resourceManager); - manager.setOwner(server, serverConfig); - - return manager; - } - - private AutoFetchManager createAutoFetchManager(String serverName, ResourceManager resourceManager) { + public static AutoFetchManager create(SpiEbeanServer server, ServerConfig serverConfig, ResourceManager resourceManager) { - File autoFetchFile = getAutoFetchFile(serverName, resourceManager); + AutoFetchManagerFactory me = new AutoFetchManagerFactory(); + return me.createAutoFetchManager(server, serverConfig, resourceManager); + } - AutoFetchManager autoFetchManager = null; + private AutoFetchManager createAutoFetchManager(SpiEbeanServer server, ServerConfig serverConfig, ResourceManager resourceManager) { - boolean readFile = !"false".equalsIgnoreCase(System.getProperty("autofetch.readfromfile")); - if (readFile) { - autoFetchManager = deserializeAutoFetch(autoFetchFile); - } + AutoFetchManager manager = createAutoFetchManager(server.getName(), resourceManager); + manager.setOwner(server, serverConfig); - if (autoFetchManager == null) { - // not deserialized from file so create as empty - // It will be populated automatically by querying the - // database meta data - autoFetchManager = new DefaultAutoFetchManager(autoFetchFile.getAbsolutePath()); - } - - return autoFetchManager; - } - - private AutoFetchManager deserializeAutoFetch(File autoFetchFile) { - try { - - if (!autoFetchFile.exists()) { - return null; - } - FileInputStream fi = new FileInputStream(autoFetchFile); - ObjectInputStream ois = new ObjectInputStream(fi); - AutoFetchManager profListener = (AutoFetchManager) ois.readObject(); - ois.close(); - - logger.info("AutoFetch deserialized from file ["+autoFetchFile.getAbsolutePath()+"]"); - - return profListener; + return manager; + } - } 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) { + private AutoFetchManager createAutoFetchManager(String serverName, ResourceManager resourceManager) { - String fileName = ".ebean."+serverName+".autofetch"; + File autoFetchFile = getAutoFetchFile(serverName, resourceManager); - File dir = resourceManager.getAutofetchDirectory(); + AutoFetchManager autoFetchManager = null; - 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); - } - } + boolean readFile = !"false".equalsIgnoreCase(System.getProperty("autofetch.readfromfile")); + if (readFile) { + autoFetchManager = deserializeAutoFetch(autoFetchFile); + } - return new File(dir, fileName); - } + if (autoFetchManager == null) { + // not deserialized from file so create as empty + // It will be populated automatically by querying the + // database meta data + autoFetchManager = new DefaultAutoFetchManager(autoFetchFile.getAbsolutePath()); + } + + return autoFetchManager; + } + + private AutoFetchManager deserializeAutoFetch(File autoFetchFile) { + try { + + if (!autoFetchFile.exists()) { + return null; + } + FileInputStream fi = new FileInputStream(autoFetchFile); + ObjectInputStream ois = new ObjectInputStream(fi); + AutoFetchManager profListener = (AutoFetchManager) 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); + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManager.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManager.java index a2ba4dbec..35dd210ba 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManager.java @@ -1,15 +1,5 @@ package com.avaje.ebeaninternal.server.autofetch; -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; - -import javax.persistence.PersistenceException; - import com.avaje.ebean.bean.CallStack; import com.avaje.ebean.bean.NodeUsageCollector; import com.avaje.ebean.bean.ObjectGraphNode; @@ -25,6 +15,15 @@ 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. @@ -33,303 +32,302 @@ public class DefaultAutoFetchManager implements AutoFetchManager, Serializable { private static final Logger logger = LoggerFactory.getLogger(DefaultAutoFetchManager.class); - private static final long serialVersionUID = -6826119882781771722L; + private static final long serialVersionUID = -6826119882781771722L; - @SuppressWarnings("RedundantStringConstructorCall") + @SuppressWarnings("RedundantStringConstructorCall") private final String statisticsMonitor = new String(); - private final String fileName; + private final String fileName; - /** - * Map of the usage and query statistics gathered. - */ - private final Map statisticsMap = new ConcurrentHashMap(); + /** + * 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(); + /** + * Map of the tuned query details per profile query point. + */ + private final Map tunedQueryInfoMap = new ConcurrentHashMap(); - private transient long defaultGarbageCollectionWait = 100; + 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; + /** + * Left without synchronized for now. + */ + private transient int tunedQueryCount; - private transient int profilingBase = 10; + /** + * 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 profilingMin = 1; + private transient int profilingBase = 10; - private transient boolean profiling; + private transient int profilingMin = 1; - private transient boolean queryTuning; + private transient boolean profiling; - private transient boolean queryTuningAddVersion; + private transient boolean queryTuning; - private transient boolean garbageCollectionOnShutdown; - - private transient AutofetchMode mode; - - /** - * Server that owns this Profile Listener. - */ - private transient SpiEbeanServer server; + private transient boolean queryTuningAddVersion; - /** - * The logger. - */ - private transient DefaultAutoFetchManagerLogging logging; + private transient boolean garbageCollectionOnShutdown; - public DefaultAutoFetchManager(String fileName) { - this.fileName = fileName; - } + private transient AutofetchMode mode; - /** - * 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(); + /** + * Server that owns this Profile Listener. + */ + private transient SpiEbeanServer server; - setProfilingRate(autofetchConfig.getProfilingRate()); - - defaultGarbageCollectionWait = (long) autofetchConfig.getGarbageCollectionWait(); + /** + * The logger. + */ + private transient DefaultAutoFetchManagerLogging logging; - // determine the mode to use when Query.setAutoFetch() was - // not explicitly set - mode = autofetchConfig.getMode(); + public DefaultAutoFetchManager(String fileName) { + this.fileName = fileName; + } - 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); + /** + * 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); - // Register a periodic update of the profiling informations - this.logging.init(server); - } - } + AutofetchConfig autofetchConfig = serverConfig.getAutofetchConfig(); - - - public void clearQueryStatistics() { - server.clearQueryStatistics(); - } + garbageCollectionOnShutdown = autofetchConfig.isGarbageCollectionOnShutdown(); + queryTuning = autofetchConfig.isQueryTuning(); + queryTuningAddVersion = autofetchConfig.isQueryTuningAddVersion(); + profiling = autofetchConfig.isProfiling(); + profilingMin = autofetchConfig.getProfilingMin(); + profilingBase = autofetchConfig.getProfilingBase(); - /** - * 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; - } + setProfilingRate(autofetchConfig.getProfilingRate()); - public int clearProfilingInfo() { - int size = statisticsMap.size(); - statisticsMap.clear(); - return size; - } + defaultGarbageCollectionWait = (long) autofetchConfig.getGarbageCollectionWait(); - - public void serialize() { + // determine the mode to use when Query.setAutoFetch() was + // not explicitly set + mode = autofetchConfig.getMode(); - File autoFetchFile = new File(fileName); + 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); - try { - FileOutputStream fout = new FileOutputStream(autoFetchFile); + // Register a periodic update of the profiling informations + this.logging.init(server); + } + } - 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); - } - } + public void clearQueryStatistics() { + server.clearQueryStatistics(); + } - /** - * Return the current Tuned query info for a given origin key. - */ - public TunedQueryInfo getTunedQueryInfo(String originKey) { - return tunedQueryInfoMap.get(originKey); - } + /** + * Return the number of queries tuned by AutoFetch. + */ + public int getTotalTunedQueryCount() { + return tunedQueryCount; + } - /** - * Return the current Statistics for a given originKey key. - */ - public Statistics getStatistics(String originKey) { - return statisticsMap.get(originKey); - } + /** + * Return the size of the TuneQuery map. + */ + public int getTotalTunedQuerySize() { + return tunedQueryInfoMap.size(); + } - public Iterator iterateTunedQueryInfo() { - return tunedQueryInfoMap.values().iterator(); - } + /** + * Return the size of the profile map. + */ + public int getTotalProfileSize() { + return statisticsMap.size(); + } - public Iterator iterateStatistics() { - return statisticsMap.values().iterator(); - } + public int clearTunedQueryInfo() { - public boolean isProfiling() { - return profiling; - } + // reset the rough count as well + tunedQueryCount = 0; - /** - * 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; - } + // clear the map... + int size = tunedQueryInfoMap.size(); + tunedQueryInfoMap.clear(); + return size; + } - public boolean isQueryTuning() { - return queryTuning; - } + public int clearProfilingInfo() { + int size = statisticsMap.size(); + statisticsMap.clear(); + return size; + } - public void setQueryTuning(boolean queryTuning) { - this.queryTuning = queryTuning; - } - - public double getProfilingRate() { - return profilingRate; - } - public AutofetchMode getMode() { - return mode; - } + public void serialize() { - public void setMode(AutofetchMode mode) { - this.mode = mode; - } + File autoFetchFile = new File(fileName); - public void setProfilingRate(double rate) { - if (rate < 0) { - rate = 0d; - } else if (rate > 1) { - rate = 1d; - } - profilingRate = rate; - } + try { + FileOutputStream fout = new FileOutputStream(autoFetchFile); - public int getProfilingBase() { - return profilingBase; - } + ObjectOutputStream oout = new ObjectOutputStream(fout); + oout.writeObject(this); + oout.flush(); + oout.close(); - public void setProfilingBase(int profilingBase) { - this.profilingBase = profilingBase; - } + } catch (Exception e) { + String msg = "Error serializing autofetch file"; + logging.logError(msg, e); + } + } - public int getProfilingMin() { - return profilingMin; - } + /** + * Return the current Tuned query info for a given origin key. + */ + public TunedQueryInfo getTunedQueryInfo(String originKey) { + return tunedQueryInfoMap.get(originKey); + } - public void setProfilingMin(int profilingMin) { - this.profilingMin = profilingMin; - } + /** + * Return the current Statistics for a given originKey key. + */ + public Statistics getStatistics(String originKey) { + return statisticsMap.get(originKey); + } - /** - * 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(); - } - } + public Iterator iterateTunedQueryInfo() { + return tunedQueryInfoMap.values().iterator(); + } - /** - * 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(); - } + public Iterator iterateStatistics() { + return statisticsMap.values().iterator(); + } - /** - * Update the tuned fetch plans from the current usage information. - */ - public String updateTunedQueryInfo() { + public boolean isProfiling() { + return profiling; + } - if (!profiling) { - // we are not collecting any profiling information at - // the moment so don't try updating the tuned query plans. - return "Not 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; + } - synchronized (statisticsMonitor) { + public boolean isQueryTuning() { + return queryTuning; + } - Counters counters = new Counters(); + 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()) { @@ -340,217 +338,222 @@ public class DefaultAutoFetchManager implements AutoFetchManager, Serializable { } } - String summaryInfo = counters.toString(); + String summaryInfo = counters.toString(); - if (counters.isInteresting()){ - // only log it if its interesting - logging.logSummary(summaryInfo); - } - - return summaryInfo; - } - } + if (counters.isInteresting()) { + // only log it if its interesting + logging.logSummary(summaryInfo); + } - private static class Counters { - - int newPlan; - int modified; - int unchanged; - int noUsage; - - void incrementNoUsage(){ - noUsage++; + 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(); } - void incrementNew(){ - newPlan++; + + 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++; } - 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(); + return true; + } + } - try { - Class beanClass = ClassUtil.forName(beanType, this.getClass()); - BeanDescriptor beanDescriptor = server.getBeanDescriptor(beanClass); - if (beanDescriptor != null){ + return false; + } - // Determine the fetch plan from the latest statistics. - // Use this to compare with current "tuned fetch plan". - OrmQueryDetail newFetchDetail = statistics.buildTunedFetch(beanDescriptor); + /** + * 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) { - // get the current tuned fetch info... - TunedQueryInfo currentFetch = tunedQueryInfoMap.get(queryPoint.getKey()); + if (node != null) { + ObjectGraphOrigin origin = node.getOriginQueryPoint(); + if (origin != null) { + Statistics stats = getQueryPointStats(origin); + stats.collectQueryInfo(node, beans, micros); + } + } + } - if (currentFetch == null) { - // its a new fetch plan, add it. - counters.incrementNew(); + /** + * 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) { - currentFetch = statistics.createTunedFetch(newFetchDetail); - logging.logNew(currentFetch); - tunedQueryInfoMap.put(queryPoint.getKey(), currentFetch); + ObjectGraphOrigin origin = usageCollector.getNode().getOriginQueryPoint(); - } 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); + Statistics stats = getQueryPointStats(origin); if (logger.isTraceEnabled()) { logger.trace("... NodeUsageCollector " + usageCollector); @@ -563,22 +566,22 @@ public class DefaultAutoFetchManager implements AutoFetchManager, Serializable { } } - 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; - } - } + 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(); - } - } + public String toString() { + synchronized (statisticsMonitor) { + return statisticsMap.values().toString(); + } + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManagerLogging.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManagerLogging.java index 6823b33ef..f1cbb183b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManagerLogging.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/DefaultAutoFetchManagerLogging.java @@ -17,27 +17,27 @@ import java.util.concurrent.TimeUnit; */ public class DefaultAutoFetchManagerLogging { - private static final Logger logger = LoggerFactory.getLogger(DefaultAutoFetchManagerLogging.class); + private static final Logger logger = LoggerFactory.getLogger(DefaultAutoFetchManagerLogging.class); - private final DefaultAutoFetchManager manager; + private final DefaultAutoFetchManager manager; - private final int updateFreqInSecs; - - public DefaultAutoFetchManagerLogging(ServerConfig serverConfig, DefaultAutoFetchManager profileListener) { + private final int updateFreqInSecs; - this.manager = profileListener; - this.updateFreqInSecs = serverConfig.getAutofetchConfig().getProfileUpdateFrequency(); - } - - public void init(SpiEbeanServer ebeanServer) { - ebeanServer.getBackgroundExecutor().executePeriodically(new UpdateProfile(), updateFreqInSecs, TimeUnit.SECONDS); - } + public DefaultAutoFetchManagerLogging(ServerConfig serverConfig, DefaultAutoFetchManager profileListener) { - private final class UpdateProfile implements Runnable { - public void run() { - manager.updateTunedQueryInfo(); - } - } + 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); @@ -47,21 +47,21 @@ public class DefaultAutoFetchManagerLogging { logger.error(msg, e); } - public void logSummary(String summaryInfo) { - - String msg = "\"Summary\",\""+summaryInfo+"\",,,,"; - logger.debug(msg); - } + public void logSummary(String summaryInfo) { - public void logChanged(TunedQueryInfo tunedFetch, OrmQueryDetail newQueryDetail) { - - String msg = tunedFetch.getLogOutput(newQueryDetail); - logger.debug(msg); - } + String msg = "\"Summary\",\"" + summaryInfo + "\",,,,"; + logger.debug(msg); + } - public void logNew(TunedQueryInfo tunedFetch) { + public void logChanged(TunedQueryInfo tunedFetch, OrmQueryDetail newQueryDetail) { - String msg = tunedFetch.getLogOutput(null); - logger.debug(msg); - } + 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/Statistics.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/Statistics.java index eb6f7780f..a9625ca70 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/Statistics.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/Statistics.java @@ -1,10 +1,5 @@ package com.avaje.ebeaninternal.server.autofetch; -import java.io.Serializable; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; - import com.avaje.ebean.bean.NodeUsageCollector; import com.avaje.ebean.bean.ObjectGraphNode; import com.avaje.ebean.bean.ObjectGraphOrigin; @@ -13,115 +8,120 @@ import com.avaje.ebean.text.PathProperties.Props; 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; + public class Statistics implements Serializable { - private static final long serialVersionUID = -5586783791097230766L; + private static final long serialVersionUID = -5586783791097230766L; - private final ObjectGraphOrigin origin; + private final ObjectGraphOrigin origin; - private final boolean queryTuningAddVersion; - - private int counter; - - private final Map queryStatsMap = new LinkedHashMap(); + private final boolean queryTuningAddVersion; - private final Map nodeUsageMap = new LinkedHashMap(); + private int counter; - @SuppressWarnings("RedundantStringConstructorCall") + private final Map queryStatsMap = new LinkedHashMap(); + + private final Map nodeUsageMap = new LinkedHashMap(); + + @SuppressWarnings("RedundantStringConstructorCall") private final String monitor = new String(); - public Statistics(ObjectGraphOrigin origin, boolean queryTuningAddVersion) { - this.origin = origin; - this.queryTuningAddVersion = queryTuningAddVersion; - } - - public ObjectGraphOrigin getOrigin() { - return origin; - } + public Statistics(ObjectGraphOrigin origin, boolean queryTuningAddVersion) { + this.origin = origin; + this.queryTuningAddVersion = queryTuningAddVersion; + } - 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. - *

- * This tells us how much profiling we have done for this query. - * For example, after 100 times we may stop collecting more profiling info. - *

- */ - 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(); + 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. + *

+ * This tells us how much profiling we have done for this query. + * For example, after 100 times we may stop collecting more profiling info. + *

+ */ + 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 (StatisticsNodeUsage statsNode : nodeUsageMap.values()) { statsNode.buildTunedFetch(pathProps, rootDesc); } - OrmQueryDetail detail = new OrmQueryDetail(); + 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; - } - } + Collection pathProperties = pathProps.getPathProps(); + for (Props props : pathProperties) { + if (!props.isEmpty()) { + detail.addFetch(props.getPath(), props.getPropertiesAsString(), null); + } + } - - 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); - } - } + detail.sortFetchPaths(rootDesc); + return detail; + } + } - /** - * Collect the usage information for from a instance for this node. - */ - public void collectUsageInfo(NodeUsageCollector profile) { + 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); + } + } + + + /** + * Collect the usage information for from a instance for this node. + */ + public void collectUsageInfo(NodeUsageCollector profile) { if (!profile.isEmpty()) { ObjectGraphNode node = profile.getNode(); @@ -131,44 +131,44 @@ public class Statistics implements Serializable { } } - private StatisticsNodeUsage getNodeStats(String path) { - - synchronized (monitor) { - StatisticsNodeUsage nodeStats = nodeUsageMap.get(path); - if (nodeStats == null) { - nodeStats = new StatisticsNodeUsage(path, queryTuningAddVersion); - nodeUsageMap.put(path, nodeStats); - } - return nodeStats; - } - } + private StatisticsNodeUsage getNodeStats(String path) { - 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(); - } + synchronized (monitor) { + StatisticsNodeUsage nodeStats = nodeUsageMap.get(path); + if (nodeStats == null) { + nodeStats = new StatisticsNodeUsage(path, queryTuningAddVersion); + nodeUsageMap.put(path, nodeStats); + } + return nodeStats; } + } - 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 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 toString() { + public String getQueryStatDebug() { + synchronized (monitor) { + StringBuilder sb = new StringBuilder(); + for (StatisticsQuery queryStat : queryStatsMap.values()) { + sb.append(queryStat.toString()).append("\n"); + } + return sb.toString(); + } + } - synchronized (monitor) { - return getUsageDebug(); - } - } + 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/StatisticsNodeUsage.java index 5010d5dde..868f8f356 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/StatisticsNodeUsage.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/StatisticsNodeUsage.java @@ -1,13 +1,5 @@ package com.avaje.ebeaninternal.server.autofetch; -import java.io.Serializable; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Set; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.avaje.ebean.bean.NodeUsageCollector; import com.avaje.ebean.text.PathProperties; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor; @@ -15,105 +7,112 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty; import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc; import com.avaje.ebeaninternal.server.el.ElPropertyValue; 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 StatisticsNodeUsage implements Serializable { - private static final long serialVersionUID = -1663951463963779547L; + private static final long serialVersionUID = -1663951463963779547L; - private static final Logger logger = LoggerFactory.getLogger(StatisticsNodeUsage.class); + private static final Logger logger = LoggerFactory.getLogger(StatisticsNodeUsage.class); - @SuppressWarnings("RedundantStringConstructorCall") + @SuppressWarnings("RedundantStringConstructorCall") private final String monitor = new String(); - - private final String path; - - private final boolean queryTuningAddVersion; - - private int profileCount; - - private int profileUsedCount; - - private boolean modified; - - private final Set aggregateUsed = new LinkedHashSet(); - public StatisticsNodeUsage(String path, boolean queryTuningAddVersion) { - this.path = path; - this.queryTuningAddVersion = queryTuningAddVersion; - } - - public void buildTunedFetch(PathProperties pathProps, BeanDescriptor rootDesc) { - - synchronized(monitor){ - - BeanDescriptor desc = rootDesc; - if (path != null){ - ElPropertyValue elGetValue = rootDesc.getElGetValue(path); - if (elGetValue == null){ - desc = null; - logger.warn("Autofetch: Can't find join for path["+path+"] for "+rootDesc.getName()); - - } else { - BeanProperty beanProperty = elGetValue.getBeanProperty(); - if (beanProperty instanceof BeanPropertyAssoc){ - desc = ((BeanPropertyAssoc) beanProperty).getTargetDescriptor(); - } - } - } + private final String path; - for (String propName : aggregateUsed) { - BeanProperty beanProp = desc.getBeanPropertyFromPath(propName); - if (beanProp == null){ - logger.warn("Autofetch: Can't find property["+propName+"] for "+desc.getName()); - - } else { - if (beanProp instanceof BeanPropertyAssoc){ - BeanPropertyAssoc assocProp = (BeanPropertyAssoc)beanProp; - String targetIdProp = assocProp.getTargetIdProperty(); - String manyPath = SplitName.add(path, assocProp.getName()); - pathProps.addToPath(manyPath, targetIdProp); - } else { - //noinspection StatementWithEmptyBody - if (beanProp.isLob() && !beanProp.isFetchEager()) { - // AutoFetch will not include Lob's marked FetchLazy - // (which is the default for Lob's so typical). - } else { - pathProps.addToPath(path, beanProp.getName()); - } - } - } + private final boolean queryTuningAddVersion; + + private int profileCount; + + private int profileUsedCount; + + private boolean modified; + + private final Set aggregateUsed = new LinkedHashSet(); + + public StatisticsNodeUsage(String path, boolean queryTuningAddVersion) { + this.path = path; + this.queryTuningAddVersion = queryTuningAddVersion; + } + + public void buildTunedFetch(PathProperties pathProps, BeanDescriptor rootDesc) { + + synchronized (monitor) { + + BeanDescriptor desc = rootDesc; + if (path != null) { + ElPropertyValue elGetValue = rootDesc.getElGetValue(path); + if (elGetValue == null) { + desc = null; + logger.warn("Autofetch: Can't find join for path[" + path + "] for " + rootDesc.getName()); + + } else { + BeanProperty beanProperty = elGetValue.getBeanProperty(); + if (beanProperty instanceof BeanPropertyAssoc) { + desc = ((BeanPropertyAssoc) beanProperty).getTargetDescriptor(); + } + } + } + + for (String propName : aggregateUsed) { + BeanProperty beanProp = desc.getBeanPropertyFromPath(propName); + if (beanProp == null) { + logger.warn("Autofetch: Can't find property[" + propName + "] for " + desc.getName()); + + } else { + if (beanProp instanceof BeanPropertyAssoc) { + BeanPropertyAssoc assocProp = (BeanPropertyAssoc) beanProp; + String targetIdProp = assocProp.getTargetIdProperty(); + String manyPath = SplitName.add(path, assocProp.getName()); + pathProps.addToPath(manyPath, targetIdProp); + } else { + //noinspection StatementWithEmptyBody + if (beanProp.isLob() && !beanProp.isFetchEager()) { + // AutoFetch will not include Lob's marked FetchLazy + // (which is the default for Lob's so typical). + } else { + pathProps.addToPath(path, beanProp.getName()); } + } + } + } - if ((modified || queryTuningAddVersion) && desc != null) { - BeanProperty versionProp = desc.getVersionProperty(); - if (versionProp != null) { - pathProps.addToPath(path, versionProp.getName()); - } - } - } - } - - public void publish(NodeUsageCollector profile) { - - synchronized(monitor){ - - HashSet used = profile.getUsed(); - - profileCount++; - if (!used.isEmpty()){ - profileUsedCount++; - aggregateUsed.addAll(used); - } - if (profile.isModified()){ - modified = true; - } - } - } - - public String toString() { - return "path["+path+"] profileCount["+profileCount+"] used["+profileUsedCount+"] props"+aggregateUsed; - } + if ((modified || queryTuningAddVersion) && desc != null) { + BeanProperty versionProp = desc.getVersionProperty(); + if (versionProp != null) { + pathProps.addToPath(path, versionProp.getName()); + } + } + } + } + + public void publish(NodeUsageCollector profile) { + + synchronized (monitor) { + + HashSet used = profile.getUsed(); + + profileCount++; + if (!used.isEmpty()) { + profileUsedCount++; + aggregateUsed.addAll(used); + } + if (profile.isModified()) { + modified = true; + } + } + } + + public String toString() { + return "path[" + path + "] profileCount[" + profileCount + "] used[" + profileUsedCount + "] props" + aggregateUsed; + } } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/StatisticsQuery.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/StatisticsQuery.java index 3670990db..506087b31 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/StatisticsQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/StatisticsQuery.java @@ -6,31 +6,31 @@ import java.io.Serializable; * Used to accumulate query execution statistics. */ public class StatisticsQuery implements Serializable { - - private static final long serialVersionUID = -1133958958072778811L; - private final String path; - - private long exeCount; - - private long totalBeanLoaded; - - private long totalMicros; - - public StatisticsQuery(String path){ - this.path = path; - } - - public void add(long beansLoaded, long micros) { - exeCount++; - totalBeanLoaded += beansLoaded; - totalMicros += micros; - } - - public String toString() { - long avgMicros = exeCount == 0 ? 0 : totalMicros / exeCount; - - return "queryExe path["+path+"] count[" + exeCount + "] totalBeansLoaded[" + totalBeanLoaded + "] avgMicros[" - + avgMicros + "] totalMicros[" + totalMicros + "]"; - } + private static final long serialVersionUID = -1133958958072778811L; + + private final String path; + + private long exeCount; + + private long totalBeanLoaded; + + private long totalMicros; + + public StatisticsQuery(String path) { + this.path = path; + } + + public void add(long beansLoaded, long micros) { + exeCount++; + totalBeanLoaded += beansLoaded; + totalMicros += micros; + } + + public String toString() { + long avgMicros = exeCount == 0 ? 0 : totalMicros / exeCount; + + return "queryExe path[" + path + "] count[" + exeCount + "] totalBeansLoaded[" + totalBeanLoaded + "] avgMicros[" + + avgMicros + "] totalMicros[" + totalMicros + "]"; + } } \ No newline at end of file diff --git a/src/main/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfo.java b/src/main/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfo.java index d5eae4870..dec85ebee 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfo.java +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/TunedQueryInfo.java @@ -1,183 +1,183 @@ package com.avaje.ebeaninternal.server.autofetch; -import java.io.Serializable; - import com.avaje.ebean.bean.ObjectGraphOrigin; import com.avaje.ebeaninternal.api.SpiQuery; import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail; +import java.io.Serializable; + /** * Holds tuned query information. Is immutable so this represents the tuning at * a given point in time. */ public class TunedQueryInfo implements Serializable { - private static final long serialVersionUID = 7381493228797997282L; + private static final long serialVersionUID = 7381493228797997282L; - private final ObjectGraphOrigin origin; + private final ObjectGraphOrigin origin; - /** - * The tuned query details with joins and properties. - */ - private OrmQueryDetail tunedDetail; + /** + * The tuned query details with joins and properties. + */ + private OrmQueryDetail tunedDetail; - /** - * The number of times profiling has been collected for this query point. - */ - private int profileCount; - - private Long lastTuneTime = (long) 0; + /** + * The number of times profiling has been collected for this query point. + */ + private int profileCount; - @SuppressWarnings("RedundantStringConstructorCall") + 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; + /** + * The number of queries tuned by this object. + * Could use AtomicInteger perhaps. + */ + private transient int tunedCount; - private transient int rateTotal; + private transient int rateTotal; - private transient int rateHits; + private transient int rateHits; - private transient double lastRate; + private transient double lastRate; - public TunedQueryInfo(ObjectGraphOrigin queryPoint, OrmQueryDetail tunedDetail, int profileCount) { - this.origin = queryPoint; - this.tunedDetail = tunedDetail; - this.profileCount = profileCount; - } + public TunedQueryInfo(ObjectGraphOrigin queryPoint, OrmQueryDetail tunedDetail, int profileCount) { + 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) { + /** + * Return true if this query should be profiled based on a percentage rate. + */ + public boolean isPercentageProfile(double rate) { - if (lastRate != rate) { - // the rate has changed so resetting - lastRate = rate; - rateTotal = 0; - rateHits = 0; - } + synchronized (rateMonitor) { - rateTotal++; - if (rate > (double) rateHits / rateTotal) { - rateHits++; - return true; - } else { - return false; - } - } - } + if (lastRate != rate) { + // the rate has changed so resetting + lastRate = rate; + rateTotal = 0; + rateHits = 0; + } - /** - * 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; - } + rateTotal++; + if (rate > (double) rateHits / rateTotal) { + rateHits++; + return true; + } else { + return false; + } + } + } - /** - * Set the tuned query detail. - */ - public void setTunedDetail(OrmQueryDetail tunedDetail) { - // assignment is atomic - this.tunedDetail = tunedDetail; - this.lastTuneTime = System.currentTimeMillis(); - } + /** + * 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; + } - /** - * Return true if the fetches are essentially the same. - */ - public boolean isSame(OrmQueryDetail newQueryDetail) { + /** + * 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); } - /** - * Tune the query by replacing its OrmQueryDetail with a tuned one. - * - * @return true if the query was tuned, otherwise false. - */ - public boolean autoFetchTune(SpiQuery query) { - if (tunedDetail == null) { - return false; - } - - boolean tuned; - //Note: tunedDetail is immutable by convention - if (query.isDetailEmpty()) { - tuned = true; - // tune by 'replacement' - query.setDetail(tunedDetail.copy()); - } else { - // tune by 'addition' - tuned = query.tuneFetchProperties(tunedDetail); - } - 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; - } + /** + * Tune the query by replacing its OrmQueryDetail with a tuned one. + * + * @return true if the query was tuned, otherwise false. + */ + public boolean autoFetchTune(SpiQuery query) { + if (tunedDetail == null) { + return false; + } - /** - * Return the number of queries tuned by this object. - */ - public int getTunedCount() { - return tunedCount; - } + boolean tuned; + //Note: tunedDetail is immutable by convention + if (query.isDetailEmpty()) { + tuned = true; + // tune by 'replacement' + query.setDetail(tunedDetail.copy()); + } else { + // tune by 'addition' + tuned = query.tuneFetchProperties(tunedDetail); + } + if (tuned) { + query.setAutoFetchTuned(true); + // a case for AtomicInteger but good enough for statistics + tunedCount++; + } + return tuned; + } - /** - * Return the number of times profiling has been collected for this query - * point. - */ - public int getProfileCount() { - return profileCount; - } + /** + * Return the time of the last tune. + */ + public Long getLastTuneTime() { + return lastTuneTime; + } - public OrmQueryDetail getTunedDetail() { - return tunedDetail; - } + /** + * Return the number of queries tuned by this object. + */ + public int getTunedCount() { + return tunedCount; + } - public ObjectGraphOrigin getOrigin() { - return origin; - } + /** + * Return the number of times profiling has been collected for this query + * point. + */ + public int getProfileCount() { + return profileCount; + } - 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; - } + 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/autofetch/package.html b/src/main/java/com/avaje/ebeaninternal/server/autofetch/package.html index 3b9b20417..f0e805a5c 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/autofetch/package.html +++ b/src/main/java/com/avaje/ebeaninternal/server/autofetch/package.html @@ -1,6 +1,6 @@ -AutoFetch Implementation + AutoFetch Implementation AutoFetch Implementation