WIP AutoTune refactor

This commit is contained in:
Robin Bygrave
2015-09-01 07:55:25 +12:00
parent f4ed813c08
commit 987fe38618
36 changed files with 740 additions and 1478 deletions
@@ -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>
@@ -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() + "]");
@@ -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);
}
}
}
}
@@ -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);
}
}
@@ -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();
// }
// }
}
@@ -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()) {
@@ -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,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;
}