mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
WIP AutoTune refactor
This commit is contained in:
@@ -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.
|
||||
* <p>
|
||||
* After this amount of profiling has been obtained profiling is collected at
|
||||
* the Profiling Percentage rate.
|
||||
* </p>
|
||||
*/
|
||||
void setProfilingBase(int profilingBase);
|
||||
|
||||
/**
|
||||
* Return the minimum number of queries profiled before autoFetch will start
|
||||
* automatically tuning the queries.
|
||||
* <p>
|
||||
* This could be one which means start autoFetch tuning after the first
|
||||
* profiling information is collected.
|
||||
* </p>
|
||||
*/
|
||||
int getProfilingMin();
|
||||
|
||||
/**
|
||||
* Set the minimum number of queries profiled per query point before autoFetch
|
||||
* will automatically tune the queries.
|
||||
* <p>
|
||||
* Increasing this number will mean more profiling is collected before
|
||||
* autoFetch starts tuning the query.
|
||||
* </p>
|
||||
*/
|
||||
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 {
|
||||
* <p>
|
||||
* This is done periodically and can also be manually invoked.
|
||||
* </p>
|
||||
*
|
||||
* @return a summary of the updates that occurred
|
||||
*/
|
||||
String updateTunedQueryInfo();
|
||||
void updateTunedQueryInfo();
|
||||
|
||||
/**
|
||||
* Clear all the tuned query info.
|
||||
* <p>
|
||||
* Should only need do this for testing and playing around.
|
||||
* </p>
|
||||
*
|
||||
* @return the amount of tuned query information cleared.
|
||||
*/
|
||||
int clearTunedQueryInfo();
|
||||
|
||||
/**
|
||||
* Clear all the profiling information.
|
||||
* <p>
|
||||
* This means the profiling information will need to be re-gathered.
|
||||
* </p>
|
||||
* <p>
|
||||
* Should only need do this for testing and playing around.
|
||||
* </p>
|
||||
*
|
||||
* @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();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> used = new HashSet<String>();
|
||||
private final Set<String> used = new LinkedHashSet<String>();
|
||||
|
||||
/**
|
||||
* 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<String> getUsed() {
|
||||
public Set<String> getUsed() {
|
||||
return used;
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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<T> extends Query<T> {
|
||||
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.
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* The profile information is periodically converted into "tuned query details" -
|
||||
* which is used to automatically tune the queries that use autoFetch.
|
||||
* </p>
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
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.
|
||||
* <p>
|
||||
* Should only need do this for testing and playing around.
|
||||
* </p>
|
||||
*/
|
||||
int clearTunedQueryInfo();
|
||||
|
||||
/**
|
||||
* Clear all the profiling information.
|
||||
* <p>
|
||||
* This means the profiling information will need to be re-gathered.
|
||||
* </p>
|
||||
* <p>
|
||||
* Should only need do this for testing and playing around.
|
||||
* </p>
|
||||
*/
|
||||
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.
|
||||
* <p>
|
||||
* This should be a read only iteration.
|
||||
* </p>
|
||||
*/
|
||||
Iterator<TunedQueryInfo> iterateTunedQueryInfo();
|
||||
|
||||
/**
|
||||
* Iterate the node usage statistics.
|
||||
* <p>
|
||||
* This should be a read only iteration.
|
||||
* </p>
|
||||
*/
|
||||
Iterator<Statistics> iterateStatistics();
|
||||
|
||||
/**
|
||||
* Return true if profiling is enabled.
|
||||
*/
|
||||
boolean isProfiling();
|
||||
|
||||
/**
|
||||
* Set to true to enable profiling.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
* <p>
|
||||
* Due to this garbage collection delay, when turning off profiling while
|
||||
* the application is running you should consider calling
|
||||
* collectUsageViaGC() <em>BEFORE</em> setProfiling(false). This hints to
|
||||
* the JVM to perform garbage collection, and hopefully collects the
|
||||
* profiling information.
|
||||
* </p>
|
||||
*/
|
||||
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).
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
int getProfilingBase();
|
||||
|
||||
/**
|
||||
* Set a max number of queries to profile per query point.
|
||||
* <p>
|
||||
* This number should provide a level of confidence that no more profiling
|
||||
* is required for this query point.
|
||||
* </p>
|
||||
*/
|
||||
void setProfilingBase(int profilingMax);
|
||||
|
||||
/**
|
||||
* Return the minimum number of queries profiled before autoFetch will start
|
||||
* automatically tuning the queries.
|
||||
* <p>
|
||||
* This could be one which means start autoFetch tuning after the first
|
||||
* profiling information is collected.
|
||||
* </p>
|
||||
*/
|
||||
int getProfilingMin();
|
||||
|
||||
/**
|
||||
* Set the minimum number of queries profiled per query point before
|
||||
* autoFetch will automatically tune the queries.
|
||||
* <p>
|
||||
* Increasing this number will mean more profiling is collected before
|
||||
* autoFetch starts tuning the query.
|
||||
* </p>
|
||||
*/
|
||||
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".
|
||||
* <p>
|
||||
* This is done periodically and can also be manually invoked.
|
||||
* </p>
|
||||
* <p>
|
||||
* This returns a string summary of the updates that occurred.
|
||||
* </p>
|
||||
*/
|
||||
String updateTunedQueryInfo();
|
||||
|
||||
/**
|
||||
* Called when a query thinks it should be automatically tuned by autoFetch.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
* <p>
|
||||
* This will also determine if the query should be profiled.
|
||||
* </p>
|
||||
*/
|
||||
boolean tuneQuery(SpiQuery<?> query);
|
||||
|
||||
/**
|
||||
* Collect query profiling information.
|
||||
* <p>
|
||||
* This is for the original query as well as any subsequent lazy loading
|
||||
* queries that are required as the object graph is traversed.
|
||||
* </p>
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
@@ -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.
|
||||
* <p>
|
||||
* The profile information is periodically converted into "tuned query details" -
|
||||
* which is used to automatically tune the queries that use autoFetch.
|
||||
* </p>
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
public interface AutoTuneService extends AdminAutofetch {
|
||||
|
||||
/**
|
||||
* Load the query tuning information.
|
||||
*/
|
||||
void startup();
|
||||
|
||||
/**
|
||||
* Called when a query thinks it should be automatically tuned by autoFetch.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
* <p>
|
||||
* This will also determine if the query should be profiled.
|
||||
* </p>
|
||||
*/
|
||||
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".
|
||||
* <p>
|
||||
* This is done periodically and can also be manually invoked.
|
||||
* </p>
|
||||
*/
|
||||
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();
|
||||
|
||||
}
|
||||
@@ -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<String, Statistics> statisticsMap = new ConcurrentHashMap<String, Statistics>();
|
||||
|
||||
/**
|
||||
* Map of the tuned query details per profile query point.
|
||||
*/
|
||||
private final Map<String, TunedQueryInfo> tunedQueryInfoMap = new ConcurrentHashMap<String, TunedQueryInfo>();
|
||||
|
||||
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<TunedQueryInfo> iterateTunedQueryInfo() {
|
||||
return tunedQueryInfoMap.values().iterator();
|
||||
}
|
||||
|
||||
public Iterator<Statistics> 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.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
public void shutdown() {
|
||||
if (garbageCollectionOnShutdown) {
|
||||
collectUsageViaGC(-1);
|
||||
serialize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask for a System.gc() so that we gather node usage information.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
* <p>
|
||||
* If waitMillis is -1 then the defaultGarbageCollectionWait is used which
|
||||
* defaults to 100 milliseconds.
|
||||
* </p>
|
||||
*/
|
||||
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.
|
||||
* <p>
|
||||
* This is sent to use from a EntityBeanIntercept when the finalise method
|
||||
* is called on the bean.
|
||||
* </p>
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>AutoFetch Implementation</title>
|
||||
</head>
|
||||
<body>
|
||||
AutoFetch Implementation
|
||||
</body>
|
||||
</html>
|
||||
+13
-12
@@ -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() + "]");
|
||||
+207
@@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
public void shutdown() {
|
||||
if (garbageCollectionOnShutdown) {
|
||||
collectUsageViaGC(-1);
|
||||
saveProfiling();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask for a System.gc() so that we gather node usage information.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
* <p>
|
||||
* If waitMillis is -1 then the defaultGarbageCollectionWait is used which
|
||||
* defaults to 100 milliseconds.
|
||||
* </p>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<String, TunedQueryInfo> tunedQueryInfoMap = new ConcurrentHashMap<String, TunedQueryInfo>();
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -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();
|
||||
@@ -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<String, ProfileOrigin> profileMap = new ConcurrentHashMap<String, ProfileOrigin>();
|
||||
|
||||
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.
|
||||
* <p>
|
||||
* This is sent to use from a EntityBeanIntercept when the finalise method
|
||||
* is called on the bean.
|
||||
* </p>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
+59
-60
@@ -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<String, StatisticsQuery> queryStatsMap = new LinkedHashMap<String, StatisticsQuery>();
|
||||
private final Map<String, ProfileOriginQuery> queryStatsMap = new LinkedHashMap<String, ProfileOriginQuery>();
|
||||
|
||||
private final Map<String, StatisticsNodeUsage> nodeUsageMap = new LinkedHashMap<String, StatisticsNodeUsage>();
|
||||
private final Map<String, ProfileOriginNodeUsage> nodeUsageMap = new LinkedHashMap<String, ProfileOriginNodeUsage>();
|
||||
|
||||
@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();
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
+5
-5
@@ -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<String> aggregateUsed = new LinkedHashSet<String>();
|
||||
|
||||
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<String> used = profile.getUsed();
|
||||
Set<String> used = profile.getUsed();
|
||||
|
||||
profileCount++;
|
||||
if (!used.isEmpty()) {
|
||||
+3
-3
@@ -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;
|
||||
}
|
||||
|
||||
+1
-1
@@ -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;
|
||||
@@ -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
|
||||
|
||||
@@ -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 <T> SpiOrmQueryRequest<T> createQueryRequest(BeanDescriptor<T> desc, SpiQuery<T> 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();
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* This is accessible via {@link EbeanServer#getAdminAutofetch()} or via JMX
|
||||
* MBeans.
|
||||
* </p>
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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).
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
int getProfilingBase();
|
||||
|
||||
/**
|
||||
* Set a max number of queries to profile per query point.
|
||||
* <p>
|
||||
* This number should provide a level of confidence that no more profiling
|
||||
* is required for this query point.
|
||||
* </p>
|
||||
*/
|
||||
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.
|
||||
* <p>
|
||||
* This could be one which means start autoFetch tuning after the first
|
||||
* profiling information is collected.
|
||||
* </p>
|
||||
*/
|
||||
int getProfilingMin();
|
||||
|
||||
/**
|
||||
* Set the minimum number of queries profiled per query point before
|
||||
* autoFetch will automatically tune the queries.
|
||||
* <p>
|
||||
* Increasing this number will mean more profiling is collected before
|
||||
* autoFetch starts tuning the query.
|
||||
* </p>
|
||||
*/
|
||||
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".
|
||||
* <p>
|
||||
* This is done periodically and can also be manually invoked.
|
||||
* </p>
|
||||
*/
|
||||
String updateTunedQueryInfo();
|
||||
|
||||
/**
|
||||
* Clear all the tuned query info.
|
||||
* <p>
|
||||
* Should only need do this for testing and playing around.
|
||||
* </p>
|
||||
* @return the amount of tuned query information cleared.
|
||||
*/
|
||||
int clearTunedQueryInfo();
|
||||
|
||||
/**
|
||||
* Clear all the profiling information.
|
||||
* <p>
|
||||
* This means the profiling information will need to be re-gathered.
|
||||
* </p>
|
||||
* <p>
|
||||
* Should only need do this for testing and playing around.
|
||||
* </p>
|
||||
* @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();
|
||||
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.jmx;
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String, ObjectGraphNode> nodePathMap = new HashMap<String, ObjectGraphNode>();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -70,7 +70,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
|
||||
queryProps.configureBeanQuery(query);
|
||||
}
|
||||
|
||||
if (parent.isUseAutofetchManager()) {
|
||||
if (parent.isUseAutoTune()) {
|
||||
query.setAutofetch(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<T> 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<NodeUsageListener> autoFetchManagerRef;
|
||||
private final WeakReference<NodeUsageListener> profilingListenerRef;
|
||||
|
||||
private final Boolean readOnly;
|
||||
|
||||
@@ -176,11 +176,10 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
|
||||
|
||||
this.readOnly = request.isReadOnly();
|
||||
|
||||
this.autoFetchManager = query.getAutoFetchManager();
|
||||
this.autoFetchProfiling = autoFetchManager != null;
|
||||
this.objectGraphNode = query.getParentNode();
|
||||
this.autoFetchManagerRef = autoFetchProfiling ? new WeakReference<NodeUsageListener>(
|
||||
autoFetchManager) : null;
|
||||
this.profilingListener = query.getProfilingListener();
|
||||
this.autoTuneProfiling = profilingListener != null;
|
||||
this.profilingListenerRef = autoTuneProfiling ? new WeakReference<NodeUsageListener>(profilingListener) : null;
|
||||
|
||||
// set the generated sql back to the query
|
||||
// so its available to the user...
|
||||
@@ -518,8 +517,8 @@ public class CQuery<T> 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<T> 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<T> 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<String, String> currentPathMap) {
|
||||
|
||||
@@ -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<T> implements SpiQuery<T> {
|
||||
*/
|
||||
private transient TableJoin includeTableJoin;
|
||||
|
||||
private transient AutoFetchManager autoFetchManager;
|
||||
private transient ProfilingListener profilingListener;
|
||||
|
||||
private transient BeanDescriptor<?> beanDescriptor;
|
||||
|
||||
@@ -481,7 +480,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
DefaultOrmQuery<T> copy = new DefaultOrmQuery<T>(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<T> implements SpiQuery<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultOrmQuery<T> 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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user