mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
22
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6627195fc2 | ||
|
|
b76c4a83ec | ||
|
|
3dcb82b1f5 | ||
|
|
ae55a40ebd | ||
|
|
8ba07419fa | ||
|
|
5f8f735bbe | ||
|
|
47829f3985 | ||
|
|
ec4f655312 | ||
|
|
04fda0a6b4 | ||
|
|
0a8c902790 | ||
|
|
9632279c2f | ||
|
|
262d52ed44 | ||
|
|
74330918e1 | ||
|
|
373598ab26 | ||
|
|
0096477757 | ||
|
|
a2a4a15f32 | ||
|
|
71dd0e5728 | ||
|
|
116cc3a3fe | ||
|
|
92132d6613 | ||
|
|
5b8ce8eaa0 | ||
|
|
5c1de0a886 | ||
|
|
987fe38618 |
@@ -7,6 +7,8 @@
|
||||
target/
|
||||
logs/
|
||||
log/
|
||||
ebean-autotune.xml
|
||||
ebean-profiling*.xml
|
||||
/db
|
||||
/mydb.db
|
||||
!src/test/ddl-review/*.sql
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>org.avaje.ebeanorm</groupId>
|
||||
<artifactId>avaje-ebeanorm</artifactId>
|
||||
<version>6.6.1</version>
|
||||
<version>6.7.1</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>avaje-ebeanorm</name>
|
||||
@@ -113,6 +113,20 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!--<dependency>-->
|
||||
<!--<groupId>oracle</groupId>-->
|
||||
<!--<artifactId>oracle-jdbc</artifactId>-->
|
||||
<!--<version>7.0</version>-->
|
||||
<!--<scope>test</scope>-->
|
||||
<!--</dependency>-->
|
||||
|
||||
<!--<dependency>-->
|
||||
<!--<groupId>microsoft</groupId>-->
|
||||
<!--<artifactId>sqlserver-jdbc</artifactId>-->
|
||||
<!--<version>4.2</version>-->
|
||||
<!--<scope>test</scope>-->
|
||||
<!--</dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
/**
|
||||
* Administrative control of Autofetch during runtime.
|
||||
*/
|
||||
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();
|
||||
|
||||
/**
|
||||
* 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>
|
||||
*
|
||||
* @return a summary of the updates that occurred
|
||||
*/
|
||||
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();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.avaje.ebean;
|
||||
|
||||
/**
|
||||
* Administrative control of AutoTune during runtime.
|
||||
*/
|
||||
public interface AutoTune {
|
||||
|
||||
/**
|
||||
* Fire a garbage collection (hint to the JVM). Assuming garbage collection
|
||||
* fires this will gather remaining usage profiling information.
|
||||
*/
|
||||
void collectProfiling();
|
||||
|
||||
}
|
||||
@@ -106,10 +106,9 @@ public interface EbeanServer {
|
||||
void shutdown(boolean shutdownDataSource, boolean deregisterDriver);
|
||||
|
||||
/**
|
||||
* Return the AdminAutofetch which is used to control and configure the
|
||||
* Autofetch service at runtime.
|
||||
* Return AutoTune which is used to control the AutoTune service at runtime.
|
||||
*/
|
||||
AdminAutofetch getAdminAutofetch();
|
||||
AutoTune getAutoTune();
|
||||
|
||||
/**
|
||||
* Return the name. This is used with {@link Ebean#getServer(String)} to get a
|
||||
|
||||
@@ -36,7 +36,7 @@ import java.util.Set;
|
||||
* .sort("avgTimeMicros desc")
|
||||
* .gt("executionCount", 0)
|
||||
* .gt("lastQueryTime", nowMinus24Hrs)
|
||||
* .eq("autofetchTuned", true)
|
||||
* .eq("autoTuned", true)
|
||||
* .maxRows(10)
|
||||
* .filter(list);
|
||||
*
|
||||
|
||||
@@ -836,10 +836,10 @@ public abstract class Model {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a query with explicit 'Autofetch' use.
|
||||
* Create a query with explicit 'AutoTune' use.
|
||||
*/
|
||||
public Query<T> setAutofetch(boolean autofetch) {
|
||||
return query().setAutofetch(autofetch);
|
||||
public Query<T> setAutoTune(boolean autoTune) {
|
||||
return query().setAutoTune(autoTune);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,7 +28,7 @@ public final class OrderBy<T> implements Serializable {
|
||||
* Create an empty OrderBy with no associated query.
|
||||
*/
|
||||
public OrderBy() {
|
||||
this.list = new ArrayList<Property>(2);
|
||||
this.list = new ArrayList<Property>(3);
|
||||
}
|
||||
|
||||
private OrderBy(List<Property> list) {
|
||||
@@ -52,7 +52,7 @@ public final class OrderBy<T> implements Serializable {
|
||||
*/
|
||||
public OrderBy(Query<T> query, String orderByClause) {
|
||||
this.query = query;
|
||||
this.list = new ArrayList<Property>(2);
|
||||
this.list = new ArrayList<Property>(3);
|
||||
parse(orderByClause);
|
||||
}
|
||||
|
||||
@@ -83,6 +83,19 @@ public final class OrderBy<T> implements Serializable {
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the property is known to be contained in the order by clause.
|
||||
*/
|
||||
public boolean containsProperty(String propertyName) {
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (propertyName.equals(list.get(i).getProperty())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of this OrderBy with the path trimmed.
|
||||
*/
|
||||
|
||||
@@ -67,9 +67,9 @@ import java.util.Set;
|
||||
* ...
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Autofetch</h3>
|
||||
* <h3>AutoTune</h3>
|
||||
* <p>
|
||||
* Ebean has built in support for "Autofetch". This is a mechanism where a query
|
||||
* Ebean has built in support for "AutoTune". This is a mechanism where a query
|
||||
* can be automatically tuned based on profiling information that is collected.
|
||||
* </p>
|
||||
* <p>
|
||||
@@ -78,7 +78,7 @@ import java.util.Set;
|
||||
* more.
|
||||
* </p>
|
||||
* <p>
|
||||
* It is expected that Autofetch will be the default approach for many queries
|
||||
* It is expected that AutoTune will be the default approach for many queries
|
||||
* in a system. It is possibly not as useful where the result of a query is sent
|
||||
* to a remote client or where there is some requirement for "Read Consistency"
|
||||
* guarantees.
|
||||
@@ -338,23 +338,23 @@ public interface Query<T> extends Serializable {
|
||||
ExpressionFactory getExpressionFactory();
|
||||
|
||||
/**
|
||||
* Returns true if this query was tuned by autoFetch.
|
||||
* Returns true if this query was tuned by autoTune.
|
||||
*/
|
||||
boolean isAutofetchTuned();
|
||||
boolean isAutoTuned();
|
||||
|
||||
/**
|
||||
* Explicitly specify whether to use Autofetch for this query.
|
||||
* Explicitly specify whether to use AutoTune for this query.
|
||||
* <p>
|
||||
* If you do not call this method on a query the "Implicit Autofetch mode" is
|
||||
* used to determine if Autofetch should be used for a given query.
|
||||
* If you do not call this method on a query the "Implicit AutoTune mode" is
|
||||
* used to determine if AutoTune should be used for a given query.
|
||||
* </p>
|
||||
* <p>
|
||||
* Autofetch can add additional fetch paths to the query and specify which
|
||||
* AutoTune can add additional fetch paths to the query and specify which
|
||||
* properties are included for each path. If you have explicitly defined some
|
||||
* fetch paths Autofetch will not remove.
|
||||
* fetch paths AutoTune will not remove them.
|
||||
* </p>
|
||||
*/
|
||||
Query<T> setAutofetch(boolean autofetch);
|
||||
Query<T> setAutoTune(boolean autoTune);
|
||||
|
||||
/**
|
||||
* Set the default lazy loading batch size to use.
|
||||
|
||||
@@ -6,7 +6,7 @@ import java.util.Arrays;
|
||||
/**
|
||||
* Represent the call stack (stack trace elements).
|
||||
* <p>
|
||||
* Used with a query to identify a CallStackQuery for AutoFetch automatic query
|
||||
* Used with a query to identify a CallStackQuery for AutoTune automatic query
|
||||
* tuning.
|
||||
* </p>
|
||||
* <p>
|
||||
@@ -26,16 +26,12 @@ public final class CallStack implements Serializable {
|
||||
|
||||
private final StackTraceElement[] callStack;
|
||||
|
||||
public CallStack(StackTraceElement[] callStack) {
|
||||
public CallStack(StackTraceElement[] callStack, int zeroHash, int pathHash) {
|
||||
this.callStack = callStack;
|
||||
this.zeroHash = enc(callStack[0].hashCode());
|
||||
int hc = 0;
|
||||
for (int i = 1; i < callStack.length; i++) {
|
||||
hc = 31 * hc + callStack[i].hashCode();
|
||||
}
|
||||
this.pathHash = enc(hc);
|
||||
this.zeroHash = enc(zeroHash);
|
||||
this.pathHash = enc(pathHash);
|
||||
}
|
||||
|
||||
|
||||
public int hashCode() {
|
||||
int hc = 0;
|
||||
for (int i = 0; i < callStack.length; i++) {
|
||||
@@ -87,8 +83,19 @@ public final class CallStack implements Serializable {
|
||||
return zeroHash + ":" + pathHash + ":" + callStack[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the call stack lines appended with the given newLine string.
|
||||
*/
|
||||
public String description(String newLine) {
|
||||
StringBuilder sb = new StringBuilder(400);
|
||||
for (int i = 0; i < callStack.length; i++) {
|
||||
sb.append(callStack[i].toString()).append(newLine);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String getOriginKey(int queryHash) {
|
||||
return zeroHash + "." + enc(queryHash) + "." + pathHash;
|
||||
return enc(queryHash)+ "." + zeroHash + "." + pathHash;
|
||||
}
|
||||
|
||||
private static final int radix = 1 << 6;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
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
|
||||
* AutoFetchNode.
|
||||
* Collects profile information for a bean (or reference/proxy bean) at a given node.
|
||||
* <p>
|
||||
* The AutoFetchNode identifies the location of the bean in the object graph.
|
||||
* The node identifies the location of the bean in the object graph.
|
||||
* </p>
|
||||
* <p>
|
||||
* It has to use a weak reference so as to ensure that it does not stop the
|
||||
@@ -29,7 +29,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 +105,7 @@ public final class NodeUsageCollector {
|
||||
/**
|
||||
* Return the set of used properties.
|
||||
*/
|
||||
public HashSet<String> getUsed() {
|
||||
public Set<String> getUsed() {
|
||||
return used;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ public interface NodeUsageListener {
|
||||
* Collect node usage "profiling" information.
|
||||
* <p>
|
||||
* This is the properties that are used for a given bean in the object graph.
|
||||
* This information is used by autoFetch to tune queries.
|
||||
* This information is used by autoTune to tune queries.
|
||||
* </p>
|
||||
*/
|
||||
void collectNodeUsage(NodeUsageCollector collector);
|
||||
|
||||
@@ -9,7 +9,7 @@ import java.io.Serializable;
|
||||
* The call stack is included so that the query can have different tuned fetches
|
||||
* for each unique call stack. For example, a query to fetch a customer could be
|
||||
* called by three different methods and each can be treated as a separate
|
||||
* origin point (and autoFetch can tune each one separately).
|
||||
* origin point (and autoTune can tune each one separately).
|
||||
* </p>
|
||||
*/
|
||||
public final class ObjectGraphOrigin implements Serializable {
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
/**
|
||||
* Defines the AutoTune behaviour for a EbeanServer.
|
||||
*/
|
||||
public class AutoTuneConfig {
|
||||
|
||||
private AutoTuneMode mode = AutoTuneMode.DEFAULT_ON;
|
||||
|
||||
private String queryTuningFile = "ebean-autotune.xml";
|
||||
|
||||
private boolean queryTuning;
|
||||
|
||||
private boolean queryTuningAddVersion;
|
||||
|
||||
private boolean profiling;
|
||||
|
||||
private String profilingFile = "ebean-profiling";
|
||||
|
||||
private int profilingBase = 5;
|
||||
|
||||
private double profilingRate = 0.01;
|
||||
|
||||
private int garbageCollectionWait = 100;
|
||||
|
||||
private boolean skipCollectionOnShutdown;
|
||||
|
||||
public AutoTuneConfig() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the file that holds the query tuning information.
|
||||
*/
|
||||
public String getQueryTuningFile() {
|
||||
return queryTuningFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the file that holds the query tuning information.
|
||||
*/
|
||||
public void setQueryTuningFile(String queryTuningFile) {
|
||||
this.queryTuningFile = queryTuningFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the file that profiling information is written to.
|
||||
*/
|
||||
public String getProfilingFile() {
|
||||
return profilingFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the file that profiling information is written to.
|
||||
*/
|
||||
public void setProfilingFile(String profilingFile) {
|
||||
this.profilingFile = profilingFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mode used when autoTune has not been explicit defined on a
|
||||
* query.
|
||||
*/
|
||||
public AutoTuneMode getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mode used when autoTune has not been explicit defined on a query.
|
||||
*/
|
||||
public void setMode(AutoTuneMode mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the queries are being tuned.
|
||||
*/
|
||||
public boolean isQueryTuning() {
|
||||
return queryTuning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if the queries should be tuned by autoTune.
|
||||
*/
|
||||
public void setQueryTuning(boolean queryTuning) {
|
||||
this.queryTuning = queryTuning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the version property should be added when the query is
|
||||
* tuned.
|
||||
* <p>
|
||||
* If this is false then the version property will be added when profiling
|
||||
* detects that the bean is possibly going to be modified.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isQueryTuningAddVersion() {
|
||||
return queryTuningAddVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to force the version property to be always added by the query
|
||||
* tuning.
|
||||
* <p>
|
||||
* If this is false then the version property will be added when profiling
|
||||
* detects that the bean is possibly going to be modified.
|
||||
* </p>
|
||||
* <p>
|
||||
* Generally this is not expected to be turned on.
|
||||
* </p>
|
||||
*/
|
||||
public void setQueryTuningAddVersion(boolean queryTuningAddVersion) {
|
||||
this.queryTuningAddVersion = queryTuningAddVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if profiling information should be collected.
|
||||
*/
|
||||
public boolean isProfiling() {
|
||||
return profiling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if profiling information should be collected.
|
||||
* <p>
|
||||
* The profiling information is collected and then used to generate the tuned
|
||||
* queries for autoTune.
|
||||
* </p>
|
||||
*/
|
||||
public void setProfiling(boolean profiling) {
|
||||
this.profiling = profiling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the base number of queries to profile before changing to profile
|
||||
* only a percentage of following queries (profileRate).
|
||||
*/
|
||||
public int getProfilingBase() {
|
||||
return profilingBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the based number of queries to profile.
|
||||
*/
|
||||
public void setProfilingBase(int profilingBase) {
|
||||
this.profilingBase = profilingBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the rate (%) of queries to be profiled after the 'base' amount of
|
||||
* profiling.
|
||||
*/
|
||||
public double getProfilingRate() {
|
||||
return profilingRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the rate (%) of queries to be profiled after the 'base' amount of
|
||||
* profiling.
|
||||
*/
|
||||
public void setProfilingRate(double profilingRate) {
|
||||
this.profilingRate = profilingRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time in millis to wait after a system gc to collect profiling
|
||||
* information.
|
||||
* <p>
|
||||
* The profiling information is collected on object finalise. As such we
|
||||
* generally don't want to trigger GC (let the JVM do its thing) but on
|
||||
* shutdown the autoTune manager will trigger System.gc() and then wait
|
||||
* (default 100 millis) to hopefully collect profiling information -
|
||||
* especially for short run unit tests.
|
||||
* </p>
|
||||
*/
|
||||
public int getGarbageCollectionWait() {
|
||||
return garbageCollectionWait;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time in millis to wait after a System.gc() to collect profiling information.
|
||||
*/
|
||||
public void setGarbageCollectionWait(int garbageCollectionWait) {
|
||||
this.garbageCollectionWait = garbageCollectionWait;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if profiling collection should be skipped on shutdown.
|
||||
*/
|
||||
public boolean isSkipCollectionOnShutdown() {
|
||||
return skipCollectionOnShutdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if profiling collection should be skipped on shutdown.
|
||||
*/
|
||||
public void setSkipCollectionOnShutdown(boolean skipCollectionOnShutdown) {
|
||||
this.skipCollectionOnShutdown = skipCollectionOnShutdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the settings from the properties file.
|
||||
*/
|
||||
public void loadSettings(PropertiesWrapper p) {
|
||||
|
||||
queryTuning = p.getBoolean("autoTune.queryTuning", queryTuning);
|
||||
queryTuningAddVersion = p.getBoolean("autoTune.queryTuningAddVersion", queryTuningAddVersion);
|
||||
queryTuningFile = p.get("autoTune.queryTuningFile", queryTuningFile);
|
||||
|
||||
skipCollectionOnShutdown = p.getBoolean("autoTune.skipCollectionOnShutdown", skipCollectionOnShutdown);
|
||||
|
||||
mode = p.getEnum(AutoTuneMode.class, "autoTune.mode", mode);
|
||||
|
||||
profiling = p.getBoolean("autoTune.profiling", profiling);
|
||||
profilingBase = p.getInt("autoTune.profilingBase", profilingBase);
|
||||
profilingRate = p.getDouble("autoTune.profilingRate", profilingRate);
|
||||
profilingFile = p.get("autoTune.profilingFile", profilingFile);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
import com.avaje.ebean.Query;
|
||||
|
||||
/**
|
||||
* The mode for determining if AutoTune will be used for a given query when
|
||||
* {@link Query#setAutoTune(boolean)} has not been explicitly set on a query.
|
||||
* <p>
|
||||
* The explicit control of {@link Query#setAutoTune(boolean)} will always take
|
||||
* precedence. This mode is used when this has not been explicitly set on a
|
||||
* query.
|
||||
* </p>
|
||||
*/
|
||||
public enum AutoTuneMode {
|
||||
|
||||
/**
|
||||
* Don't implicitly use AutoTune. Must explicitly turn it on.
|
||||
*/
|
||||
DEFAULT_OFF,
|
||||
|
||||
/**
|
||||
* Use AutoTune implicitly. Must explicitly turn it off.
|
||||
*/
|
||||
DEFAULT_ON,
|
||||
|
||||
/**
|
||||
* Implicitly use AutoTune if the query has not got either select() or join()
|
||||
* defined.
|
||||
*/
|
||||
DEFAULT_ONIFEMPTY
|
||||
|
||||
}
|
||||
@@ -1,245 +0,0 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
/**
|
||||
* Defines the Autofetch behaviour for a EbeanServer.
|
||||
*/
|
||||
public class AutofetchConfig {
|
||||
|
||||
private AutofetchMode mode = AutofetchMode.DEFAULT_ONIFEMPTY;
|
||||
|
||||
private boolean queryTuning = false;
|
||||
|
||||
private boolean queryTuningAddVersion = false;
|
||||
|
||||
private boolean profiling = false;
|
||||
|
||||
private int profilingMin = 1;
|
||||
|
||||
private int profilingBase = 10;
|
||||
|
||||
private double profilingRate = 0.05;
|
||||
|
||||
private String logDirectory;
|
||||
|
||||
private int profileUpdateFrequency = 60;
|
||||
|
||||
private int garbageCollectionWait = 100;
|
||||
|
||||
private boolean garbageCollectionOnShutdown;
|
||||
|
||||
public AutofetchConfig() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mode used when autofetch has not been explicit defined on a
|
||||
* query.
|
||||
*/
|
||||
public AutofetchMode getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mode used when autofetch has not been explicit defined on a query.
|
||||
*/
|
||||
public void setMode(AutofetchMode mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the queries are being tuned.
|
||||
*/
|
||||
public boolean isQueryTuning() {
|
||||
return queryTuning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if the queries should be tuned by autofetch.
|
||||
*/
|
||||
public void setQueryTuning(boolean queryTuning) {
|
||||
this.queryTuning = queryTuning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the version property should be added when the query is
|
||||
* tuned.
|
||||
* <p>
|
||||
* If this is false then the version property will be added when profiling
|
||||
* detects that the bean is possibly going to be modified.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isQueryTuningAddVersion() {
|
||||
return queryTuningAddVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to force the version property to be always added by the query
|
||||
* tuning.
|
||||
* <p>
|
||||
* If this is false then the version property will be added when profiling
|
||||
* detects that the bean is possibly going to be modified.
|
||||
* </p>
|
||||
*/
|
||||
public void setQueryTuningAddVersion(boolean queryTuningAddVersion) {
|
||||
this.queryTuningAddVersion = queryTuningAddVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if profiling information should be collected.
|
||||
*/
|
||||
public boolean isProfiling() {
|
||||
return profiling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if profiling information should be collected.
|
||||
* <p>
|
||||
* The profiling information is collected and then used to generate the tuned
|
||||
* queries for autofetch.
|
||||
* </p>
|
||||
*/
|
||||
public void setProfiling(boolean profiling) {
|
||||
this.profiling = profiling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum number of queries to profile before autofetch will start
|
||||
* tuning the queries.
|
||||
*/
|
||||
public int getProfilingMin() {
|
||||
return profilingMin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minimum number of queries to profile before autofetch will start
|
||||
* tuning the queries.
|
||||
*/
|
||||
public void setProfilingMin(int profilingMin) {
|
||||
this.profilingMin = profilingMin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the base number of queries to profile before changing to profile
|
||||
* only a percentage of following queries (profileRate).
|
||||
*/
|
||||
public int getProfilingBase() {
|
||||
return profilingBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the based number of queries to profile.
|
||||
*/
|
||||
public void setProfilingBase(int profilingBase) {
|
||||
this.profilingBase = profilingBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the rate (%) of queries to be profiled after the 'base' amount of
|
||||
* profiling.
|
||||
*/
|
||||
public double getProfilingRate() {
|
||||
return profilingRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the rate (%) of queries to be profiled after the 'base' amount of
|
||||
* profiling.
|
||||
*/
|
||||
public void setProfilingRate(double profilingRate) {
|
||||
this.profilingRate = profilingRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the log directory to put the autofetch log.
|
||||
*/
|
||||
public String getLogDirectory() {
|
||||
return logDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the directory to put the autofetch log in.
|
||||
*/
|
||||
public void setLogDirectory(String logDirectory) {
|
||||
this.logDirectory = logDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the frequency in seconds to update the autofetch tuned queries from
|
||||
* the profiled information.
|
||||
*/
|
||||
public int getProfileUpdateFrequency() {
|
||||
return profileUpdateFrequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the frequency in seconds to update the autofetch tuned queries from the
|
||||
* profiled information.
|
||||
*/
|
||||
public void setProfileUpdateFrequency(int profileUpdateFrequency) {
|
||||
this.profileUpdateFrequency = profileUpdateFrequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time in millis to wait after a system gc to collect profiling
|
||||
* information.
|
||||
* <p>
|
||||
* The profiling information is collected on object finalise. As such we
|
||||
* generally don't want to trigger GC (let the JVM do its thing) but on
|
||||
* shutdown the autofetch manager will trigger System.gc() and then wait
|
||||
* (default 100 millis) to hopefully collect profiling information -
|
||||
* especially for short run unit tests.
|
||||
* </p>
|
||||
*/
|
||||
public int getGarbageCollectionWait() {
|
||||
return garbageCollectionWait;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time in millis to wait after a System.gc() to collect profiling
|
||||
* information.
|
||||
*/
|
||||
public void setGarbageCollectionWait(int garbageCollectionWait) {
|
||||
this.garbageCollectionWait = garbageCollectionWait;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if GC should be trigger on shutdown.
|
||||
* <p>
|
||||
* Autofetch profiling information is collected as part of garbage collection.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isGarbageCollectionOnShutdown() {
|
||||
return garbageCollectionOnShutdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true if you want GC to trigger on shutdown.
|
||||
* <p>
|
||||
* This would be done if you want to try and collect Autofetch profiling information
|
||||
* on shutdown.
|
||||
* </p>
|
||||
*/
|
||||
public void setGarbageCollectionOnShutdown(boolean garbageCollectionOnShutdown) {
|
||||
this.garbageCollectionOnShutdown = garbageCollectionOnShutdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the settings from the properties file.
|
||||
*/
|
||||
public void loadSettings(PropertiesWrapper p) {
|
||||
|
||||
logDirectory = p.get("autofetch.logDirectory", logDirectory);
|
||||
queryTuning = p.getBoolean("autofetch.querytuning", queryTuning);
|
||||
queryTuningAddVersion = p.getBoolean("autofetch.queryTuningAddVersion", queryTuningAddVersion);
|
||||
garbageCollectionOnShutdown = p.getBoolean("autofetch.garbageCollectionOnShutdown", garbageCollectionOnShutdown);
|
||||
|
||||
profiling = p.getBoolean("autofetch.profiling", profiling);
|
||||
mode = p.getEnum(AutofetchMode.class, "autofetch.implicitmode", mode);
|
||||
|
||||
profilingMin = p.getInt("autofetch.profiling.min", profilingMin);
|
||||
profilingBase = p.getInt("autofetch.profiling.base", profilingBase);
|
||||
|
||||
profilingRate = p.getDouble("autofetch.profiling.rate", profilingRate);
|
||||
profileUpdateFrequency = p.getInt("autofetch.profiling.updatefrequency", profileUpdateFrequency);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.avaje.ebean.config;
|
||||
|
||||
import com.avaje.ebean.Query;
|
||||
|
||||
/**
|
||||
* The mode for determining if Autofetch will be used for a given query when
|
||||
* {@link Query#setAutofetch(boolean)} has not been explicitly set on a query.
|
||||
* <p>
|
||||
* The explicit control of {@link Query#setAutofetch(boolean)} will always take
|
||||
* precedence. This mode is used when this has not been explicitly set on a
|
||||
* query.
|
||||
* </p>
|
||||
*/
|
||||
public enum AutofetchMode {
|
||||
|
||||
/**
|
||||
* Don't implicitly use Autofetch. Must explicitly turn it on.
|
||||
*/
|
||||
DEFAULT_OFF,
|
||||
|
||||
/**
|
||||
* Use Autofetch implicitly. Must explicitly turn it off.
|
||||
*/
|
||||
DEFAULT_ON,
|
||||
|
||||
/**
|
||||
* Implicitly use Autofetch if the query has not got either select() or join()
|
||||
* defined.
|
||||
*/
|
||||
DEFAULT_ONIFEMPTY
|
||||
|
||||
}
|
||||
@@ -130,9 +130,9 @@ public class ServerConfig {
|
||||
private CurrentUserProvider currentUserProvider;
|
||||
|
||||
/**
|
||||
* Config controlling the autofetch behaviour.
|
||||
* Config controlling the AutoTune behaviour.
|
||||
*/
|
||||
private AutofetchConfig autofetchConfig = new AutofetchConfig();
|
||||
private AutoTuneConfig autoTuneConfig = new AutoTuneConfig();
|
||||
|
||||
/**
|
||||
* The JSON format used for DateTime types. Default to millis.
|
||||
@@ -1144,17 +1144,17 @@ public class ServerConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configuration for the Autofetch feature.
|
||||
* Return the configuration for AutoTune.
|
||||
*/
|
||||
public AutofetchConfig getAutofetchConfig() {
|
||||
return autofetchConfig;
|
||||
public AutoTuneConfig getAutoTuneConfig() {
|
||||
return autoTuneConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the configuration for the Autofetch feature.
|
||||
* Set the configuration for AutoTune.
|
||||
*/
|
||||
public void setAutofetchConfig(AutofetchConfig autofetchConfig) {
|
||||
this.autofetchConfig = autofetchConfig;
|
||||
public void setAutoTuneConfig(AutoTuneConfig autoTuneConfig) {
|
||||
this.autoTuneConfig = autoTuneConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2042,8 +2042,8 @@ public class ServerConfig {
|
||||
/**
|
||||
* This is broken out for the same reason as above - preserve existing behaviour but let it be overridden.
|
||||
*/
|
||||
protected void loadAutofetchSettings(PropertiesWrapper p) {
|
||||
autofetchConfig.loadSettings(p);
|
||||
protected void loadAutoTuneSettings(PropertiesWrapper p) {
|
||||
autoTuneConfig.loadSettings(p);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2057,10 +2057,10 @@ public class ServerConfig {
|
||||
if (namingConvention != null) {
|
||||
namingConvention.loadFromProperties(p);
|
||||
}
|
||||
if (autofetchConfig == null) {
|
||||
autofetchConfig = new AutofetchConfig();
|
||||
if (autoTuneConfig == null) {
|
||||
autoTuneConfig = new AutoTuneConfig();
|
||||
}
|
||||
loadAutofetchSettings(p);
|
||||
loadAutoTuneSettings(p);
|
||||
|
||||
if (dataSourceConfig == null) {
|
||||
dataSourceConfig = new DataSourceConfig();
|
||||
|
||||
@@ -15,9 +15,9 @@ public interface MetaQueryPlanStatistic {
|
||||
Class<?> getBeanType();
|
||||
|
||||
/**
|
||||
* Return true if this query plan was tuned by Autofetch.
|
||||
* Return true if this query plan was tuned by AutoTune.
|
||||
*/
|
||||
boolean isAutofetchTuned();
|
||||
boolean isAutoTuned();
|
||||
|
||||
/**
|
||||
* Return a string representation of the query plan hash.
|
||||
|
||||
@@ -33,7 +33,7 @@ public interface LoadContext {
|
||||
void registerSecondaryQueries(SpiQuery<?> query);
|
||||
|
||||
/**
|
||||
* Return the node for a given path which is used by autofetch profiling.
|
||||
* Return the node for a given path which is used by AutoTune profiling.
|
||||
*/
|
||||
ObjectGraphNode getObjectGraphNode(String path);
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
|
||||
import com.avaje.ebean.event.readaudit.ReadAuditLogger;
|
||||
import com.avaje.ebean.event.readaudit.ReadAuditPrepare;
|
||||
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;
|
||||
@@ -56,7 +55,7 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
|
||||
/**
|
||||
* Create an object to represent the current CallStack.
|
||||
* <p>
|
||||
* Typically used to identify the origin of queries for Autofetch and object
|
||||
* Typically used to identify the origin of queries for AutoTune and object
|
||||
* graph costing.
|
||||
* </p>
|
||||
*/
|
||||
@@ -72,11 +71,6 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
|
||||
*/
|
||||
DdlGenerator getDdlGenerator();
|
||||
|
||||
/**
|
||||
* Return the AutoFetchListener.
|
||||
*/
|
||||
AutoFetchManager getAutoFetchManager();
|
||||
|
||||
/**
|
||||
* Clear the query execution statistics.
|
||||
*/
|
||||
|
||||
@@ -19,13 +19,13 @@ public interface SpiExpression extends Expression {
|
||||
void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins);
|
||||
|
||||
/**
|
||||
* Calculate a hash value used to identify a query for AutoFetch tuning.
|
||||
* Calculate a hash value used to identify a query for AutoTune tuning.
|
||||
* <p>
|
||||
* That is, if the hash changes then the query will be considered different
|
||||
* from an AutoFetch perspective and get different tuning.
|
||||
* from an AutoTune perspective and get different tuning.
|
||||
* </p>
|
||||
*/
|
||||
void queryAutoFetchHash(HashQueryPlanBuilder builder);
|
||||
void queryAutoTuneHash(HashQueryPlanBuilder builder);
|
||||
|
||||
/**
|
||||
* Calculate a hash value for the expression.
|
||||
|
||||
@@ -11,7 +11,7 @@ import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.bean.PersistenceContext;
|
||||
import com.avaje.ebean.event.BeanQueryRequest;
|
||||
import com.avaje.ebean.event.readaudit.ReadEvent;
|
||||
import com.avaje.ebeaninternal.server.autofetch.AutoFetchManager;
|
||||
import com.avaje.ebeaninternal.server.autotune.ProfilingListener;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.TableJoin;
|
||||
@@ -345,10 +345,10 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
boolean isDetailEmpty();
|
||||
|
||||
/**
|
||||
* Return explicit autoFetch setting or null. If null then not explicitly
|
||||
* Return explicit AutoTune setting or null. If null then not explicitly
|
||||
* set so we use the default behaviour.
|
||||
*/
|
||||
Boolean isAutofetch();
|
||||
Boolean isAutoTune();
|
||||
|
||||
/**
|
||||
* Set to true if you want to capture executed secondary queries.
|
||||
@@ -371,16 +371,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.
|
||||
@@ -423,14 +423,14 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
/**
|
||||
* Return false when this is a lazy load or refresh query for a bean.
|
||||
* <p>
|
||||
* We just take/copy the data from those beans and don't collect autoFetch
|
||||
* We just take/copy the data from those beans and don't collect AutoTune
|
||||
* usage profiling on those lazy load or refresh beans.
|
||||
* </p>
|
||||
*/
|
||||
boolean isUsageProfiling();
|
||||
|
||||
/**
|
||||
* Set to false if this query should not be included in the autoFetch usage
|
||||
* Set to false if this query should not be included in the AutoTune usage
|
||||
* profiling information.
|
||||
*/
|
||||
void setUsageProfiling(boolean usageProfiling);
|
||||
@@ -441,19 +441,19 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Calculate a hash used by AutoFetch to identify when a query has changed
|
||||
* Calculate a hash used by AutoTune to identify when a query has changed
|
||||
* (and hence potentially needs a new tuned query plan to be developed).
|
||||
* <p>
|
||||
* Excludes bind values and occurs prior to AutoFetch potentially
|
||||
* Excludes bind values and occurs prior to AutoTune potentially
|
||||
* tuning/modifying the query.
|
||||
* </p>
|
||||
*/
|
||||
HashQueryPlan queryAutofetchHash(HashQueryPlanBuilder builder);
|
||||
HashQueryPlan queryAutoTuneHash(HashQueryPlanBuilder builder);
|
||||
|
||||
/**
|
||||
* Identifies queries that are the same bar the bind variables.
|
||||
* <p>
|
||||
* This is used AFTER AutoFetch has potentially tuned the query. This is
|
||||
* This is used AFTER AutoTune has potentially tuned the query. This is
|
||||
* used to identify and reused query plans (the final SQL string and
|
||||
* associated SqlTree object).
|
||||
* </p>
|
||||
@@ -558,7 +558,7 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
String getQuery();
|
||||
|
||||
/**
|
||||
* Replace the query detail. This is used by the autoFetch feature to as a
|
||||
* Replace the query detail. This is used by the AutoTune feature to as a
|
||||
* fast way to set the query properties and joins.
|
||||
* <p>
|
||||
* Note care must be taken to keep the where, orderBy, firstRows and maxRows
|
||||
@@ -568,15 +568,15 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
void setDetail(OrmQueryDetail detail);
|
||||
|
||||
/**
|
||||
* Autofetch tune the detail specifying properties to select on already defined joins
|
||||
* AutoTune tune the detail specifying properties to select on already defined joins
|
||||
* and adding extra joins where they are missing.
|
||||
*/
|
||||
boolean tuneFetchProperties(OrmQueryDetail detail);
|
||||
|
||||
/**
|
||||
* Set to true if this query has been tuned by autoFetch.
|
||||
* Set to true if this query has been tuned by autoTune.
|
||||
*/
|
||||
void setAutoFetchTuned(boolean autoFetchTuned);
|
||||
void setAutoTuned(boolean autoTuned);
|
||||
|
||||
/**
|
||||
* Return the query detail.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.autofetch;
|
||||
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.server.resource.ResourceManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
|
||||
public class AutoFetchManagerFactory {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AutoFetchManagerFactory.class);
|
||||
|
||||
public static AutoFetchManager create(SpiEbeanServer server, ServerConfig serverConfig, ResourceManager resourceManager) {
|
||||
|
||||
AutoFetchManagerFactory me = new AutoFetchManagerFactory();
|
||||
return me.createAutoFetchManager(server, serverConfig, resourceManager);
|
||||
}
|
||||
|
||||
private AutoFetchManager createAutoFetchManager(SpiEbeanServer server, ServerConfig serverConfig, ResourceManager resourceManager) {
|
||||
|
||||
AutoFetchManager manager = createAutoFetchManager(server.getName(), resourceManager);
|
||||
manager.setOwner(server, serverConfig);
|
||||
|
||||
return manager;
|
||||
}
|
||||
|
||||
private AutoFetchManager createAutoFetchManager(String serverName, ResourceManager resourceManager) {
|
||||
|
||||
File autoFetchFile = getAutoFetchFile(serverName, resourceManager);
|
||||
|
||||
AutoFetchManager autoFetchManager = null;
|
||||
|
||||
boolean readFile = !"false".equalsIgnoreCase(System.getProperty("autofetch.readfromfile"));
|
||||
if (readFile) {
|
||||
autoFetchManager = deserializeAutoFetch(autoFetchFile);
|
||||
}
|
||||
|
||||
if (autoFetchManager == null) {
|
||||
// not deserialized from file so create as empty
|
||||
// It will be populated automatically by querying the
|
||||
// database meta data
|
||||
autoFetchManager = new DefaultAutoFetchManager(autoFetchFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
return autoFetchManager;
|
||||
}
|
||||
|
||||
private AutoFetchManager deserializeAutoFetch(File autoFetchFile) {
|
||||
try {
|
||||
|
||||
if (!autoFetchFile.exists()) {
|
||||
return null;
|
||||
}
|
||||
FileInputStream fi = new FileInputStream(autoFetchFile);
|
||||
ObjectInputStream ois = new ObjectInputStream(fi);
|
||||
AutoFetchManager profListener = (AutoFetchManager) ois.readObject();
|
||||
ois.close();
|
||||
|
||||
logger.info("AutoFetch deserialized from file [" + autoFetchFile.getAbsolutePath() + "]");
|
||||
|
||||
return profListener;
|
||||
|
||||
} catch (Exception ex) {
|
||||
logger.error("Error loading autofetch file " + autoFetchFile.getAbsolutePath(), ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the file name of the autoFetch meta data.
|
||||
*/
|
||||
private File getAutoFetchFile(String serverName, ResourceManager resourceManager) {
|
||||
|
||||
String fileName = ".ebean." + serverName + ".autofetch";
|
||||
|
||||
File dir = resourceManager.getAutofetchDirectory();
|
||||
|
||||
if (!dir.exists()) {
|
||||
// automatically create the directory if it does not exist.
|
||||
// this is probably a fairly reasonable thing to do
|
||||
if (!dir.mkdirs()) {
|
||||
String m = "Unable to create directory [" + dir + "] for autofetch file [" + fileName + "]";
|
||||
throw new PersistenceException(m);
|
||||
}
|
||||
}
|
||||
|
||||
return new File(dir, fileName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.autofetch;
|
||||
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Handles the logging aspects for the DefaultAutoFetchListener.
|
||||
* <p>
|
||||
* Note that java util logging loggers generally should not be serialised and
|
||||
* that is one of the main reasons for pulling out the logging to this class.
|
||||
* </p>
|
||||
*/
|
||||
public class DefaultAutoFetchManagerLogging {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DefaultAutoFetchManagerLogging.class);
|
||||
|
||||
private final DefaultAutoFetchManager manager;
|
||||
|
||||
private final int updateFreqInSecs;
|
||||
|
||||
public DefaultAutoFetchManagerLogging(ServerConfig serverConfig, DefaultAutoFetchManager profileListener) {
|
||||
|
||||
this.manager = profileListener;
|
||||
this.updateFreqInSecs = serverConfig.getAutofetchConfig().getProfileUpdateFrequency();
|
||||
}
|
||||
|
||||
public void init(SpiEbeanServer ebeanServer) {
|
||||
ebeanServer.getBackgroundExecutor().executePeriodically(new UpdateProfile(), updateFreqInSecs, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private final class UpdateProfile implements Runnable {
|
||||
public void run() {
|
||||
manager.updateTunedQueryInfo();
|
||||
}
|
||||
}
|
||||
|
||||
public void logInfo(String msg, Throwable e) {
|
||||
logger.info(msg, e);
|
||||
}
|
||||
|
||||
public void logError(String msg, Throwable e) {
|
||||
logger.error(msg, e);
|
||||
}
|
||||
|
||||
public void logSummary(String summaryInfo) {
|
||||
|
||||
String msg = "\"Summary\",\"" + summaryInfo + "\",,,,";
|
||||
logger.debug(msg);
|
||||
}
|
||||
|
||||
public void logChanged(TunedQueryInfo tunedFetch, OrmQueryDetail newQueryDetail) {
|
||||
|
||||
String msg = tunedFetch.getLogOutput(newQueryDetail);
|
||||
logger.debug(msg);
|
||||
}
|
||||
|
||||
public void logNew(TunedQueryInfo tunedFetch) {
|
||||
|
||||
String msg = tunedFetch.getLogOutput(null);
|
||||
logger.debug(msg);
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.autofetch;
|
||||
|
||||
import com.avaje.ebean.bean.NodeUsageCollector;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.bean.ObjectGraphOrigin;
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
import com.avaje.ebean.text.PathProperties.Props;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class Statistics implements Serializable {
|
||||
|
||||
|
||||
private static final long serialVersionUID = -5586783791097230766L;
|
||||
|
||||
private final ObjectGraphOrigin origin;
|
||||
|
||||
private final boolean queryTuningAddVersion;
|
||||
|
||||
private int counter;
|
||||
|
||||
private final Map<String, StatisticsQuery> queryStatsMap = new LinkedHashMap<String, StatisticsQuery>();
|
||||
|
||||
private final Map<String, StatisticsNodeUsage> nodeUsageMap = new LinkedHashMap<String, StatisticsNodeUsage>();
|
||||
|
||||
@SuppressWarnings("RedundantStringConstructorCall")
|
||||
private final String monitor = new String();
|
||||
|
||||
public Statistics(ObjectGraphOrigin origin, boolean queryTuningAddVersion) {
|
||||
this.origin = origin;
|
||||
this.queryTuningAddVersion = queryTuningAddVersion;
|
||||
}
|
||||
|
||||
public ObjectGraphOrigin getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
public 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.
|
||||
* <p>
|
||||
* This tells us how much profiling we have done for this query.
|
||||
* For example, after 100 times we may stop collecting more profiling info.
|
||||
* </p>
|
||||
*/
|
||||
public int getCounter() {
|
||||
return counter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this has usage statistics.
|
||||
*/
|
||||
public boolean hasUsage() {
|
||||
synchronized (monitor) {
|
||||
return !nodeUsageMap.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
public OrmQueryDetail buildTunedFetch(BeanDescriptor<?> rootDesc) {
|
||||
|
||||
synchronized (monitor) {
|
||||
if (nodeUsageMap.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PathProperties pathProps = new PathProperties();
|
||||
|
||||
for (StatisticsNodeUsage statsNode : nodeUsageMap.values()) {
|
||||
statsNode.buildTunedFetch(pathProps, rootDesc);
|
||||
}
|
||||
|
||||
OrmQueryDetail detail = new OrmQueryDetail();
|
||||
|
||||
Collection<Props> pathProperties = pathProps.getPathProps();
|
||||
for (Props props : pathProperties) {
|
||||
if (!props.isEmpty()) {
|
||||
detail.addFetch(props.getPath(), props.getPropertiesAsString(), null);
|
||||
}
|
||||
}
|
||||
|
||||
detail.sortFetchPaths(rootDesc);
|
||||
return detail;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void collectQueryInfo(ObjectGraphNode node, long beansLoaded, long micros) {
|
||||
|
||||
synchronized (monitor) {
|
||||
String key = node.getPath();
|
||||
if (key == null) {
|
||||
key = "";
|
||||
// this is basically the number of times the root query
|
||||
// has executed which gives us an indication of how
|
||||
// much profiling information we have gathered.
|
||||
counter++;
|
||||
}
|
||||
|
||||
StatisticsQuery stats = queryStatsMap.get(key);
|
||||
if (stats == null) {
|
||||
stats = new StatisticsQuery(key);
|
||||
queryStatsMap.put(key, stats);
|
||||
}
|
||||
stats.add(beansLoaded, micros);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Collect the usage information for from a instance for this node.
|
||||
*/
|
||||
public void collectUsageInfo(NodeUsageCollector profile) {
|
||||
|
||||
if (!profile.isEmpty()) {
|
||||
ObjectGraphNode node = profile.getNode();
|
||||
|
||||
StatisticsNodeUsage nodeStats = getNodeStats(node.getPath());
|
||||
nodeStats.publish(profile);
|
||||
}
|
||||
}
|
||||
|
||||
private StatisticsNodeUsage getNodeStats(String path) {
|
||||
|
||||
synchronized (monitor) {
|
||||
StatisticsNodeUsage nodeStats = nodeUsageMap.get(path);
|
||||
if (nodeStats == null) {
|
||||
nodeStats = new StatisticsNodeUsage(path, queryTuningAddVersion);
|
||||
nodeUsageMap.put(path, nodeStats);
|
||||
}
|
||||
return nodeStats;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.autofetch;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Used to accumulate query execution statistics.
|
||||
*/
|
||||
public class StatisticsQuery implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -1133958958072778811L;
|
||||
|
||||
private final String path;
|
||||
|
||||
private long exeCount;
|
||||
|
||||
private long totalBeanLoaded;
|
||||
|
||||
private long totalMicros;
|
||||
|
||||
public StatisticsQuery(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public void add(long beansLoaded, long micros) {
|
||||
exeCount++;
|
||||
totalBeanLoaded += beansLoaded;
|
||||
totalMicros += micros;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
long avgMicros = exeCount == 0 ? 0 : totalMicros / exeCount;
|
||||
|
||||
return "queryExe path[" + path + "] count[" + exeCount + "] totalBeansLoaded[" + totalBeanLoaded + "] avgMicros["
|
||||
+ avgMicros + "] totalMicros[" + totalMicros + "]";
|
||||
}
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.autofetch;
|
||||
|
||||
import com.avaje.ebean.bean.ObjectGraphOrigin;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Holds tuned query information. Is immutable so this represents the tuning at
|
||||
* a given point in time.
|
||||
*/
|
||||
public class TunedQueryInfo implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 7381493228797997282L;
|
||||
|
||||
private final ObjectGraphOrigin origin;
|
||||
|
||||
/**
|
||||
* The tuned query details with joins and properties.
|
||||
*/
|
||||
private OrmQueryDetail tunedDetail;
|
||||
|
||||
/**
|
||||
* The number of times profiling has been collected for this query point.
|
||||
*/
|
||||
private int profileCount;
|
||||
|
||||
private Long lastTuneTime = (long) 0;
|
||||
|
||||
@SuppressWarnings("RedundantStringConstructorCall")
|
||||
private final String rateMonitor = new String();
|
||||
|
||||
/**
|
||||
* The number of queries tuned by this object.
|
||||
* Could use AtomicInteger perhaps.
|
||||
*/
|
||||
private transient int tunedCount;
|
||||
|
||||
private transient int rateTotal;
|
||||
|
||||
private transient int rateHits;
|
||||
|
||||
private transient double lastRate;
|
||||
|
||||
public TunedQueryInfo(ObjectGraphOrigin queryPoint, OrmQueryDetail tunedDetail, int profileCount) {
|
||||
this.origin = queryPoint;
|
||||
this.tunedDetail = tunedDetail;
|
||||
this.profileCount = profileCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this query should be profiled based on a percentage rate.
|
||||
*/
|
||||
public boolean isPercentageProfile(double rate) {
|
||||
|
||||
synchronized (rateMonitor) {
|
||||
|
||||
if (lastRate != rate) {
|
||||
// the rate has changed so resetting
|
||||
lastRate = rate;
|
||||
rateTotal = 0;
|
||||
rateHits = 0;
|
||||
}
|
||||
|
||||
rateTotal++;
|
||||
if (rate > (double) rateHits / rateTotal) {
|
||||
rateHits++;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of times profiling has been collected for this query
|
||||
* point.
|
||||
*/
|
||||
public void setProfileCount(int profileCount) {
|
||||
// int assignment is atomic
|
||||
this.profileCount = profileCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the tuned query detail.
|
||||
*/
|
||||
public void setTunedDetail(OrmQueryDetail tunedDetail) {
|
||||
// assignment is atomic
|
||||
this.tunedDetail = tunedDetail;
|
||||
this.lastTuneTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the fetches are essentially the same.
|
||||
*/
|
||||
public boolean isSame(OrmQueryDetail newQueryDetail) {
|
||||
return tunedDetail != null && tunedDetail.isAutoFetchEqual(newQueryDetail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tune the query by replacing its OrmQueryDetail with a tuned one.
|
||||
*
|
||||
* @return true if the query was tuned, otherwise false.
|
||||
*/
|
||||
public boolean autoFetchTune(SpiQuery<?> query) {
|
||||
if (tunedDetail == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean tuned;
|
||||
//Note: tunedDetail is immutable by convention
|
||||
if (query.isDetailEmpty()) {
|
||||
tuned = true;
|
||||
// tune by 'replacement'
|
||||
query.setDetail(tunedDetail.copy());
|
||||
} else {
|
||||
// tune by 'addition'
|
||||
tuned = query.tuneFetchProperties(tunedDetail);
|
||||
}
|
||||
if (tuned) {
|
||||
query.setAutoFetchTuned(true);
|
||||
// a case for AtomicInteger but good enough for statistics
|
||||
tunedCount++;
|
||||
}
|
||||
return tuned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time of the last tune.
|
||||
*/
|
||||
public Long getLastTuneTime() {
|
||||
return lastTuneTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of queries tuned by this object.
|
||||
*/
|
||||
public int getTunedCount() {
|
||||
return tunedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of times profiling has been collected for this query
|
||||
* point.
|
||||
*/
|
||||
public int getProfileCount() {
|
||||
return profileCount;
|
||||
}
|
||||
|
||||
public OrmQueryDetail getTunedDetail() {
|
||||
return tunedDetail;
|
||||
}
|
||||
|
||||
public ObjectGraphOrigin getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
public String getLogOutput(OrmQueryDetail newQueryDetail) {
|
||||
|
||||
boolean changed = newQueryDetail != null;
|
||||
|
||||
StringBuilder sb = new StringBuilder(150);
|
||||
sb.append(changed ? "\"Changed\"," : "\"New\",");
|
||||
sb.append("\"").append(origin.getBeanType()).append("\",");
|
||||
sb.append("\"").append(origin.getKey()).append("\",");
|
||||
if (changed) {
|
||||
sb.append("\"to: ").append(newQueryDetail.toString()).append("\",");
|
||||
sb.append("\"from: ").append(tunedDetail.toString()).append("\",");
|
||||
} else {
|
||||
sb.append("\"to: ").append(tunedDetail.toString()).append("\",");
|
||||
sb.append("\"\",");
|
||||
}
|
||||
sb.append("\"").append(origin.getFirstStackElement()).append("\"");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return origin.getBeanType() + " " + origin.getKey() + " " + tunedDetail;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>AutoFetch Implementation</title>
|
||||
</head>
|
||||
<body>
|
||||
AutoFetch Implementation
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.avaje.ebeaninternal.server.autotune;
|
||||
|
||||
import com.avaje.ebean.bean.ObjectGraphOrigin;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Profiling information collected.
|
||||
*/
|
||||
public class AutoTuneCollection {
|
||||
|
||||
List<Entry> entries = new ArrayList<Entry>();
|
||||
|
||||
public Entry add(ObjectGraphOrigin origin, OrmQueryDetail detail, String sourceQuery) {
|
||||
Entry entry = new Entry(origin, detail, sourceQuery);
|
||||
entries.add(entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
public List<Entry> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Profiling entry at a given origin point.
|
||||
*/
|
||||
public static class Entry {
|
||||
|
||||
/**
|
||||
* Profiling origin point.
|
||||
*/
|
||||
private final ObjectGraphOrigin origin;
|
||||
|
||||
/**
|
||||
* The tuned query detail.
|
||||
*/
|
||||
private final OrmQueryDetail detail;
|
||||
|
||||
/**
|
||||
* The original/existing query detail.
|
||||
*/
|
||||
private final String originalQuery;
|
||||
|
||||
/**
|
||||
* Summary execution statistics for queries related to this origin point.
|
||||
*/
|
||||
private final List<EntryQuery> queries = new ArrayList<EntryQuery>();
|
||||
|
||||
public Entry(ObjectGraphOrigin origin, OrmQueryDetail detail, String originalQuery) {
|
||||
this.origin = origin;
|
||||
this.detail = detail;
|
||||
this.originalQuery = originalQuery;
|
||||
}
|
||||
|
||||
public void addQuery(EntryQuery entryQuery) {
|
||||
queries.add(entryQuery);
|
||||
}
|
||||
|
||||
public ObjectGraphOrigin getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
public OrmQueryDetail getDetail() {
|
||||
return detail;
|
||||
}
|
||||
|
||||
public String getOriginalQuery() {
|
||||
return originalQuery;
|
||||
}
|
||||
|
||||
public List<EntryQuery> getQueries() {
|
||||
return queries;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Summary query execution statistics for the origin point.
|
||||
*/
|
||||
public static class EntryQuery {
|
||||
|
||||
final String path;
|
||||
final long exeCount;
|
||||
final long totalBeanLoaded;
|
||||
final long totalMicros;
|
||||
|
||||
public EntryQuery(String path, long exeCount, long totalBeanLoaded, long totalMicros) {
|
||||
this.path = path;
|
||||
this.exeCount = exeCount;
|
||||
this.totalBeanLoaded = totalBeanLoaded;
|
||||
this.totalMicros = totalMicros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the relative path with empty string for the origin query.
|
||||
*/
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public long getExeCount() {
|
||||
return exeCount;
|
||||
}
|
||||
|
||||
public long getTotalBeanLoaded() {
|
||||
return totalBeanLoaded;
|
||||
}
|
||||
|
||||
public long getTotalMicros() {
|
||||
return totalMicros;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.avaje.ebeaninternal.server.autotune;
|
||||
|
||||
import com.avaje.ebean.AutoTune;
|
||||
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 AutoTune.
|
||||
* </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 AutoTune {
|
||||
|
||||
/**
|
||||
* Load the query tuning information.
|
||||
*/
|
||||
void startup();
|
||||
|
||||
/**
|
||||
* Called when a query thinks it should be automatically tuned by AutoTune.
|
||||
* <p>
|
||||
* Returns true if the query was tuned.
|
||||
* </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 collectProfiling();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.avaje.ebeaninternal.server.autotune;
|
||||
|
||||
import com.avaje.ebean.bean.NodeUsageListener;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
|
||||
/**
|
||||
* Profiling listener gets call backs for node usage and the associated query executions.
|
||||
*/
|
||||
public interface ProfilingListener extends NodeUsageListener {
|
||||
|
||||
/**
|
||||
* Collect summary statistics for a query executed for the given node.
|
||||
*
|
||||
* @param node the node relative to the origin point
|
||||
* @param beans the number of beans loaded by the query
|
||||
* @param micros the query execution in microseconds
|
||||
*/
|
||||
void collectQueryInfo(ObjectGraphNode node, long beans, long micros);
|
||||
|
||||
/**
|
||||
* Return true if this request should be profiled (based on the
|
||||
* profiling ratio and collection count for this origin).
|
||||
*/
|
||||
boolean isProfileRequest(ObjectGraphNode origin, SpiQuery<?> query);
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
|
||||
package com.avaje.ebeaninternal.server.autotune.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||
* <sequence>
|
||||
* <element ref="{http://ebean-orm.github.io/xml/ns/autotune}origin" maxOccurs="unbounded" minOccurs="0"/>
|
||||
* <element ref="{http://ebean-orm.github.io/xml/ns/autotune}profileDiff" minOccurs="0"/>
|
||||
* <element ref="{http://ebean-orm.github.io/xml/ns/autotune}profileNew" minOccurs="0"/>
|
||||
* <element ref="{http://ebean-orm.github.io/xml/ns/autotune}profileEmpty" minOccurs="0"/>
|
||||
* </sequence>
|
||||
* </restriction>
|
||||
* </complexContent>
|
||||
* </complexType>
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
*/
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlType(name = "", propOrder = {
|
||||
"origin",
|
||||
"profileDiff",
|
||||
"profileNew",
|
||||
"profileEmpty"
|
||||
})
|
||||
@XmlRootElement(name = "autotune")
|
||||
public class Autotune {
|
||||
|
||||
protected List<Origin> origin;
|
||||
protected ProfileDiff profileDiff;
|
||||
protected ProfileNew profileNew;
|
||||
protected ProfileEmpty profileEmpty;
|
||||
|
||||
/**
|
||||
* Gets the value of the origin property.
|
||||
*
|
||||
* <p>
|
||||
* This accessor method returns a reference to the live list,
|
||||
* not a snapshot. Therefore any modification you make to the
|
||||
* returned list will be present inside the JAXB object.
|
||||
* This is why there is not a <CODE>set</CODE> method for the origin property.
|
||||
*
|
||||
* <p>
|
||||
* For example, to add a new item, do as follows:
|
||||
* <pre>
|
||||
* getOrigin().add(newItem);
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Objects of the following type(s) are allowed in the list
|
||||
* {@link Origin }
|
||||
*
|
||||
*
|
||||
*/
|
||||
public List<Origin> getOrigin() {
|
||||
if (origin == null) {
|
||||
origin = new ArrayList<Origin>();
|
||||
}
|
||||
return this.origin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the profileDiff property.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link ProfileDiff }
|
||||
*
|
||||
*/
|
||||
public ProfileDiff getProfileDiff() {
|
||||
return profileDiff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the profileDiff property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link ProfileDiff }
|
||||
*
|
||||
*/
|
||||
public void setProfileDiff(ProfileDiff value) {
|
||||
this.profileDiff = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the profileNew property.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link ProfileNew }
|
||||
*
|
||||
*/
|
||||
public ProfileNew getProfileNew() {
|
||||
return profileNew;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the profileNew property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link ProfileNew }
|
||||
*
|
||||
*/
|
||||
public void setProfileNew(ProfileNew value) {
|
||||
this.profileNew = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the profileEmpty property.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link ProfileEmpty }
|
||||
*
|
||||
*/
|
||||
public ProfileEmpty getProfileEmpty() {
|
||||
return profileEmpty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the profileEmpty property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link ProfileEmpty }
|
||||
*
|
||||
*/
|
||||
public void setProfileEmpty(ProfileEmpty value) {
|
||||
this.profileEmpty = value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
|
||||
package com.avaje.ebeaninternal.server.autotune.model;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRegistry;
|
||||
|
||||
|
||||
/**
|
||||
* This object contains factory methods for each
|
||||
* Java content interface and Java element interface
|
||||
* generated in the com.avaje.ebeaninternal.server.autotune.model package.
|
||||
* <p>An ObjectFactory allows you to programatically
|
||||
* construct new instances of the Java representation
|
||||
* for XML content. The Java representation of XML
|
||||
* content can consist of schema derived interfaces
|
||||
* and classes representing the binding of schema
|
||||
* type definitions, element declarations and model
|
||||
* groups. Factory methods for each of these are
|
||||
* provided in this class.
|
||||
*
|
||||
*/
|
||||
@XmlRegistry
|
||||
public class ObjectFactory {
|
||||
|
||||
|
||||
/**
|
||||
* Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.avaje.ebeaninternal.server.autotune.model
|
||||
*
|
||||
*/
|
||||
public ObjectFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link ProfileNew }
|
||||
*
|
||||
*/
|
||||
public ProfileNew createProfileNew() {
|
||||
return new ProfileNew();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link Origin }
|
||||
*
|
||||
*/
|
||||
public Origin createOrigin() {
|
||||
return new Origin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link ProfileEmpty }
|
||||
*
|
||||
*/
|
||||
public ProfileEmpty createProfileEmpty() {
|
||||
return new ProfileEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link Autotune }
|
||||
*
|
||||
*/
|
||||
public Autotune createAutotune() {
|
||||
return new Autotune();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link ProfileDiff }
|
||||
*
|
||||
*/
|
||||
public ProfileDiff createProfileDiff() {
|
||||
return new ProfileDiff();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
|
||||
package com.avaje.ebeaninternal.server.autotune.model;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||
* <sequence>
|
||||
* <element name="callStack" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
|
||||
* </sequence>
|
||||
* <attribute name="key" use="required" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="beanType" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="detail" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* <attribute name="original" type="{http://www.w3.org/2001/XMLSchema}string" />
|
||||
* </restriction>
|
||||
* </complexContent>
|
||||
* </complexType>
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
*/
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlType(name = "", propOrder = {
|
||||
"callStack"
|
||||
})
|
||||
@XmlRootElement(name = "origin")
|
||||
public class Origin {
|
||||
|
||||
protected String callStack;
|
||||
@XmlAttribute(name = "key", required = true)
|
||||
protected String key;
|
||||
@XmlAttribute(name = "beanType")
|
||||
protected String beanType;
|
||||
@XmlAttribute(name = "detail")
|
||||
protected String detail;
|
||||
@XmlAttribute(name = "original")
|
||||
protected String original;
|
||||
|
||||
/**
|
||||
* Gets the value of the callStack property.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public String getCallStack() {
|
||||
return callStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the callStack property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public void setCallStack(String value) {
|
||||
this.callStack = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the key property.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the key property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public void setKey(String value) {
|
||||
this.key = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the beanType property.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public String getBeanType() {
|
||||
return beanType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the beanType property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public void setBeanType(String value) {
|
||||
this.beanType = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the detail property.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public String getDetail() {
|
||||
return detail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the detail property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public void setDetail(String value) {
|
||||
this.detail = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the original property.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public String getOriginal() {
|
||||
return original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the original property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public void setOriginal(String value) {
|
||||
this.original = value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
|
||||
package com.avaje.ebeaninternal.server.autotune.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||
* <sequence>
|
||||
* <element ref="{http://ebean-orm.github.io/xml/ns/autotune}origin" maxOccurs="unbounded" minOccurs="0"/>
|
||||
* </sequence>
|
||||
* </restriction>
|
||||
* </complexContent>
|
||||
* </complexType>
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
*/
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlType(name = "", propOrder = {
|
||||
"origin"
|
||||
})
|
||||
@XmlRootElement(name = "profileDiff")
|
||||
public class ProfileDiff {
|
||||
|
||||
protected List<Origin> origin;
|
||||
|
||||
/**
|
||||
* Gets the value of the origin property.
|
||||
*
|
||||
* <p>
|
||||
* This accessor method returns a reference to the live list,
|
||||
* not a snapshot. Therefore any modification you make to the
|
||||
* returned list will be present inside the JAXB object.
|
||||
* This is why there is not a <CODE>set</CODE> method for the origin property.
|
||||
*
|
||||
* <p>
|
||||
* For example, to add a new item, do as follows:
|
||||
* <pre>
|
||||
* getOrigin().add(newItem);
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Objects of the following type(s) are allowed in the list
|
||||
* {@link Origin }
|
||||
*
|
||||
*
|
||||
*/
|
||||
public List<Origin> getOrigin() {
|
||||
if (origin == null) {
|
||||
origin = new ArrayList<Origin>();
|
||||
}
|
||||
return this.origin;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
|
||||
package com.avaje.ebeaninternal.server.autotune.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||
* <sequence>
|
||||
* <element ref="{http://ebean-orm.github.io/xml/ns/autotune}origin" maxOccurs="unbounded" minOccurs="0"/>
|
||||
* </sequence>
|
||||
* </restriction>
|
||||
* </complexContent>
|
||||
* </complexType>
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
*/
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlType(name = "", propOrder = {
|
||||
"origin"
|
||||
})
|
||||
@XmlRootElement(name = "profileEmpty")
|
||||
public class ProfileEmpty {
|
||||
|
||||
protected List<Origin> origin;
|
||||
|
||||
/**
|
||||
* Gets the value of the origin property.
|
||||
*
|
||||
* <p>
|
||||
* This accessor method returns a reference to the live list,
|
||||
* not a snapshot. Therefore any modification you make to the
|
||||
* returned list will be present inside the JAXB object.
|
||||
* This is why there is not a <CODE>set</CODE> method for the origin property.
|
||||
*
|
||||
* <p>
|
||||
* For example, to add a new item, do as follows:
|
||||
* <pre>
|
||||
* getOrigin().add(newItem);
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Objects of the following type(s) are allowed in the list
|
||||
* {@link Origin }
|
||||
*
|
||||
*
|
||||
*/
|
||||
public List<Origin> getOrigin() {
|
||||
if (origin == null) {
|
||||
origin = new ArrayList<Origin>();
|
||||
}
|
||||
return this.origin;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
|
||||
package com.avaje.ebeaninternal.server.autotune.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Java class for anonymous complex type.
|
||||
*
|
||||
* <p>The following schema fragment specifies the expected content contained within this class.
|
||||
*
|
||||
* <pre>
|
||||
* <complexType>
|
||||
* <complexContent>
|
||||
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
|
||||
* <sequence>
|
||||
* <element ref="{http://ebean-orm.github.io/xml/ns/autotune}origin" maxOccurs="unbounded" minOccurs="0"/>
|
||||
* </sequence>
|
||||
* </restriction>
|
||||
* </complexContent>
|
||||
* </complexType>
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
*/
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlType(name = "", propOrder = {
|
||||
"origin"
|
||||
})
|
||||
@XmlRootElement(name = "profileNew")
|
||||
public class ProfileNew {
|
||||
|
||||
protected List<Origin> origin;
|
||||
|
||||
/**
|
||||
* Gets the value of the origin property.
|
||||
*
|
||||
* <p>
|
||||
* This accessor method returns a reference to the live list,
|
||||
* not a snapshot. Therefore any modification you make to the
|
||||
* returned list will be present inside the JAXB object.
|
||||
* This is why there is not a <CODE>set</CODE> method for the origin property.
|
||||
*
|
||||
* <p>
|
||||
* For example, to add a new item, do as follows:
|
||||
* <pre>
|
||||
* getOrigin().add(newItem);
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* Objects of the following type(s) are allowed in the list
|
||||
* {@link Origin }
|
||||
*
|
||||
*
|
||||
*/
|
||||
public List<Origin> getOrigin() {
|
||||
if (origin == null) {
|
||||
origin = new ArrayList<Origin>();
|
||||
}
|
||||
return this.origin;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
@javax.xml.bind.annotation.XmlSchema(namespace = "http://ebean-orm.github.io/xml/ns/autotune", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
|
||||
package com.avaje.ebeaninternal.server.autotune.model;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.avaje.ebeaninternal.server.autotune.service;
|
||||
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.server.autotune.AutoTuneService;
|
||||
|
||||
public class AutoTuneServiceFactory {
|
||||
|
||||
public static AutoTuneService create(SpiEbeanServer server, ServerConfig serverConfig) {
|
||||
|
||||
return new DefaultAutoTuneService(server, serverConfig);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.avaje.ebeaninternal.server.autotune.service;
|
||||
|
||||
|
||||
import com.avaje.ebeaninternal.server.autotune.model.Autotune;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Reads a profiling xml document.
|
||||
*/
|
||||
public class AutoTuneXmlReader {
|
||||
|
||||
/**
|
||||
* Read and return a Profiling from an xml file.
|
||||
*/
|
||||
public Autotune read(File file) {
|
||||
|
||||
try {
|
||||
return readFile(file);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected Autotune readFile(File file) throws IOException {
|
||||
if (!file.exists()) {
|
||||
return new Autotune();
|
||||
}
|
||||
FileInputStream is = new FileInputStream(file);
|
||||
try {
|
||||
return read(is);
|
||||
} finally {
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and return a Profiling from an xml document.
|
||||
*/
|
||||
public Autotune read(InputStream is) {
|
||||
|
||||
try {
|
||||
JAXBContext jaxbContext = JAXBContext.newInstance(Autotune.class);
|
||||
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
|
||||
return (Autotune) unmarshaller.unmarshal(is);
|
||||
} catch (JAXBException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.avaje.ebeaninternal.server.autotune.service;
|
||||
|
||||
|
||||
import com.avaje.ebeaninternal.server.autotune.model.Autotune;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Simple writer for output of the AutoTune Profiling as an XML document.
|
||||
*/
|
||||
public class AutoTuneXmlWriter {
|
||||
|
||||
/**
|
||||
* Write Profiling to a file as xml.
|
||||
*/
|
||||
public void write(Autotune profiling, File file) {
|
||||
|
||||
try {
|
||||
JAXBContext jaxbContext = JAXBContext.newInstance(Autotune.class);
|
||||
Marshaller marshaller = jaxbContext.createMarshaller();
|
||||
marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
|
||||
marshaller.marshal(profiling, file);
|
||||
|
||||
} catch (JAXBException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
package com.avaje.ebeaninternal.server.autotune.service;
|
||||
|
||||
import com.avaje.ebean.bean.CallStack;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.config.AutoTuneConfig;
|
||||
import com.avaje.ebean.config.AutoTuneMode;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.autotune.ProfilingListener;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class BaseQueryTuner {
|
||||
|
||||
private final boolean queryTuning;
|
||||
|
||||
private final boolean profiling;
|
||||
|
||||
private final AutoTuneMode 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;
|
||||
|
||||
/**
|
||||
* Flag set true when there is no profiling or query tuning.
|
||||
*/
|
||||
private final boolean skipAll;
|
||||
|
||||
public BaseQueryTuner(AutoTuneConfig config, SpiEbeanServer server, ProfilingListener profilingListener) {
|
||||
this.server = server;
|
||||
this.profilingListener = profilingListener;
|
||||
this.mode = config.getMode();
|
||||
this.queryTuning = config.isQueryTuning();
|
||||
this.profiling = config.isProfiling();
|
||||
this.skipAll = !queryTuning && !profiling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the tuned query information.
|
||||
*/
|
||||
public void load(String key, TunedQueryInfo queryInfo) {
|
||||
tunedQueryInfoMap.put(key, queryInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the detail currently used for tuning.
|
||||
* This returns null if there is currently no matching tuning.
|
||||
*/
|
||||
public OrmQueryDetail get(String key) {
|
||||
TunedQueryInfo info = tunedQueryInfoMap.get(key);
|
||||
return (info == null) ? null : info.getTunedDetail();
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto tune the query and enable profiling.
|
||||
*/
|
||||
public boolean tuneQuery(SpiQuery<?> query) {
|
||||
|
||||
if (skipAll || !tunableQuery(query)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!useTuning(query)) {
|
||||
if (profiling) {
|
||||
profiling(query, server.createCallStack());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (query.getParentNode() != 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);
|
||||
|
||||
if (profiling) {
|
||||
if (profilingListener.isProfileRequest(origin, query)) {
|
||||
// collect more profiling based on profiling rate etc
|
||||
query.setProfilingListener(profilingListener);
|
||||
}
|
||||
}
|
||||
|
||||
if (queryTuning) {
|
||||
// get current "tuned fetch" for this query point
|
||||
TunedQueryInfo tuneInfo = tunedQueryInfoMap.get(origin.getOriginQueryPoint().getKey());
|
||||
return tuneInfo != null && tuneInfo.tuneQuery(query);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return false for row count, find ids, subQuery, delete and Versions queries.
|
||||
* <p>
|
||||
* These queries are not applicable for autoTune in that they don't have a select/fetch (fetch group).
|
||||
* </p>
|
||||
* <p>
|
||||
* We also exclude queries that are explicitly set to load the L2 bean cache as we want full beans
|
||||
* in that case.
|
||||
* </p>
|
||||
*/
|
||||
private boolean tunableQuery(SpiQuery<?> query) {
|
||||
SpiQuery.Type type = query.getType();
|
||||
switch (type) {
|
||||
case ROWCOUNT:
|
||||
case ID_LIST:
|
||||
case DELETE:
|
||||
case SUBQUERY:
|
||||
return false;
|
||||
default:
|
||||
// not using autoTune when explicitly loading the l2 bean cache
|
||||
// or when using Versions query
|
||||
return !query.isLoadBeanCache() && SpiQuery.TemporalMode.VERSIONS != query.getTemporalMode();
|
||||
}
|
||||
}
|
||||
|
||||
private void profiling(SpiQuery<?> query, CallStack stack) {
|
||||
|
||||
// create a query point to identify the query
|
||||
ObjectGraphNode origin = query.setOrigin(stack);
|
||||
if (profilingListener.isProfileRequest(origin, query)) {
|
||||
// collect more profiling based on profiling rate etc
|
||||
query.setProfilingListener(profilingListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we should try to tune this query.
|
||||
*/
|
||||
private boolean useTuning(SpiQuery<?> query) {
|
||||
|
||||
Boolean autoTune = query.isAutoTune();
|
||||
if (autoTune != null) {
|
||||
// explicitly set...
|
||||
return autoTune;
|
||||
|
||||
} 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 AutoTuneMode " + mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the keys as a set.
|
||||
*/
|
||||
public Set<String> keySet() {
|
||||
return tunedQueryInfoMap.keySet();
|
||||
}
|
||||
}
|
||||
+300
@@ -0,0 +1,300 @@
|
||||
package com.avaje.ebeaninternal.server.autotune.service;
|
||||
|
||||
import com.avaje.ebean.bean.ObjectGraphOrigin;
|
||||
import com.avaje.ebean.config.AutoTuneConfig;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.autotune.AutoTuneCollection;
|
||||
import com.avaje.ebeaninternal.server.autotune.AutoTuneService;
|
||||
import com.avaje.ebeaninternal.server.autotune.model.Autotune;
|
||||
import com.avaje.ebeaninternal.server.autotune.model.Origin;
|
||||
import com.avaje.ebeaninternal.server.autotune.model.ProfileDiff;
|
||||
import com.avaje.ebeaninternal.server.autotune.model.ProfileEmpty;
|
||||
import com.avaje.ebeaninternal.server.autotune.model.ProfileNew;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetailParser;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Implementation of the AutoTuneService which is comprised of profiling and query tuning.
|
||||
*/
|
||||
public class DefaultAutoTuneService implements AutoTuneService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DefaultAutoTuneService.class);
|
||||
|
||||
private final long defaultGarbageCollectionWait;
|
||||
|
||||
private final boolean skipCollectionOnShutdown;
|
||||
|
||||
private final BaseQueryTuner queryTuner;
|
||||
|
||||
private final ProfileManager profileManager;
|
||||
|
||||
private final boolean profiling;
|
||||
|
||||
private final boolean queryTuning;
|
||||
|
||||
private final String tuningFile;
|
||||
|
||||
private final String profilingFile;
|
||||
|
||||
private final String serverName;
|
||||
|
||||
public DefaultAutoTuneService(SpiEbeanServer server, ServerConfig serverConfig) {
|
||||
|
||||
AutoTuneConfig config = serverConfig.getAutoTuneConfig();
|
||||
|
||||
this.queryTuning = config.isQueryTuning();
|
||||
this.profiling = config.isProfiling();
|
||||
this.tuningFile = config.getQueryTuningFile();
|
||||
this.profilingFile = config.getProfilingFile();
|
||||
this.serverName = server.getName();
|
||||
this.profileManager = new ProfileManager(config, server);
|
||||
this.queryTuner = new BaseQueryTuner(config, server, profileManager);
|
||||
this.skipCollectionOnShutdown = config.isSkipCollectionOnShutdown();
|
||||
this.defaultGarbageCollectionWait = (long) config.getGarbageCollectionWait();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the query tuning information from it's data store.
|
||||
*/
|
||||
@Override
|
||||
public void startup() {
|
||||
|
||||
if (queryTuning) {
|
||||
File file = new File(tuningFile);
|
||||
if (!file.exists()) {
|
||||
logger.warn("AutoTune file {} not found - no automatic tuning will be applied", file.getAbsolutePath());
|
||||
|
||||
} else {
|
||||
AutoTuneXmlReader reader = new AutoTuneXmlReader();
|
||||
Autotune profiling = reader.read(file);
|
||||
logger.info("AutoTune loading {} tuning entries", profiling.getOrigin().size());
|
||||
for (Origin origin : profiling.getOrigin()) {
|
||||
queryTuner.load(origin.getKey(), createTunedQueryInfo(origin));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private TunedQueryInfo createTunedQueryInfo(Origin origin) {
|
||||
OrmQueryDetail detail = new OrmQueryDetailParser(origin.getDetail()).parse();
|
||||
return new TunedQueryInfo(detail);
|
||||
}
|
||||
|
||||
private void saveProfiling(boolean reset) {
|
||||
|
||||
Autotune document = new Autotune();
|
||||
|
||||
AutoTuneCollection autoTuneCollection = profileManager.profilingCollection(reset);
|
||||
|
||||
List<AutoTuneCollection.Entry> entries = autoTuneCollection.getEntries();
|
||||
|
||||
// count "new" and "diff" profiling entries
|
||||
AtomicInteger newCounter = new AtomicInteger();
|
||||
AtomicInteger diffCounter = new AtomicInteger();
|
||||
|
||||
Set<String> profileKeys = new HashSet<String>();
|
||||
for (AutoTuneCollection.Entry entry : entries) {
|
||||
saveProfilingEntry(document, entry, newCounter, diffCounter);
|
||||
profileKeys.add(entry.getOrigin().getKey());
|
||||
}
|
||||
|
||||
// report the origin keys that we didn't collect any profiling on
|
||||
Set<String> tunerKeys = queryTuner.keySet();
|
||||
for (String tuneKey : tunerKeys) {
|
||||
if (!profileKeys.contains(tuneKey)) {
|
||||
ProfileEmpty profileEmpty = document.getProfileEmpty();
|
||||
if (profileEmpty == null) {
|
||||
profileEmpty = new ProfileEmpty();
|
||||
document.setProfileEmpty(profileEmpty);
|
||||
}
|
||||
Origin emptyOrigin = new Origin();
|
||||
emptyOrigin.setKey(tuneKey);
|
||||
profileEmpty.getOrigin().add(emptyOrigin);
|
||||
}
|
||||
}
|
||||
|
||||
int totalNew = newCounter.get();
|
||||
int totalDiff = diffCounter.get();
|
||||
if (totalNew == 0 && totalDiff == 0) {
|
||||
logger.info("No new or diff entries for profiling server:{}", serverName);
|
||||
|
||||
} else {
|
||||
sortDocument(document);
|
||||
|
||||
SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss");
|
||||
String now = df.format(new Date());
|
||||
|
||||
// write the file with serverName and now suffix as we can output the profiling many times
|
||||
File file = new File(profilingFile + "-" + serverName + "-" + now + ".xml");
|
||||
AutoTuneXmlWriter writer = new AutoTuneXmlWriter();
|
||||
writer.write(document, file);
|
||||
|
||||
logger.info("writing new:{} diff:{} profiling entries for server:{}", totalNew, totalDiff, serverName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the diff and new entries by bean type followed by key.
|
||||
*/
|
||||
private void sortDocument(Autotune document) {
|
||||
|
||||
ProfileDiff profileDiff = document.getProfileDiff();
|
||||
if (profileDiff != null) {
|
||||
Collections.sort(profileDiff.getOrigin(), new OriginNameKeySort());
|
||||
}
|
||||
ProfileNew profileNew = document.getProfileNew();
|
||||
if (profileNew != null) {
|
||||
Collections.sort(profileNew.getOrigin(), new OriginNameKeySort());
|
||||
}
|
||||
ProfileEmpty profileEmpty = document.getProfileEmpty();
|
||||
if (profileEmpty != null) {
|
||||
Collections.sort(profileEmpty.getOrigin(), new OriginKeySort());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparator sort by bean type then key.
|
||||
*/
|
||||
class OriginNameKeySort implements Comparator<Origin> {
|
||||
|
||||
@Override
|
||||
public int compare(Origin o1, Origin o2) {
|
||||
int comp = o1.getBeanType().compareTo(o2.getBeanType());
|
||||
if (comp == 0) {
|
||||
comp = o1.getKey().compareTo(o2.getKey());
|
||||
}
|
||||
return comp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparator sort by bean type then key.
|
||||
*/
|
||||
class OriginKeySort implements Comparator<Origin> {
|
||||
|
||||
@Override
|
||||
public int compare(Origin o1, Origin o2) {
|
||||
return o1.getKey().compareTo(o2.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
private void saveProfilingEntry(Autotune document, AutoTuneCollection.Entry entry, AtomicInteger newCount, AtomicInteger diffCount) {
|
||||
|
||||
ObjectGraphOrigin point = entry.getOrigin();
|
||||
OrmQueryDetail profileDetail = entry.getDetail();
|
||||
|
||||
// compare with the existing query tuning entry
|
||||
OrmQueryDetail tuneDetail = queryTuner.get(point.getKey());
|
||||
if (tuneDetail == null) {
|
||||
// New entry
|
||||
newCount.incrementAndGet();
|
||||
ProfileNew profileNew = document.getProfileNew();
|
||||
if (profileNew == null) {
|
||||
profileNew = new ProfileNew();
|
||||
document.setProfileNew(profileNew);
|
||||
}
|
||||
Origin origin = createOrigin(entry, point);
|
||||
origin.setOriginal(entry.getOriginalQuery());
|
||||
profileNew.getOrigin().add(origin);
|
||||
|
||||
} else if (!tuneDetail.isAutoTuneEqual(profileDetail)) {
|
||||
// Diff entry
|
||||
diffCount.incrementAndGet();
|
||||
Origin origin = createOrigin(entry, point);
|
||||
origin.setOriginal(tuneDetail.toString());
|
||||
ProfileDiff diff = document.getProfileDiff();
|
||||
if (diff == null) {
|
||||
diff = new ProfileDiff();
|
||||
document.setProfileDiff(diff);
|
||||
}
|
||||
diff.getOrigin().add(origin);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the XML Origin bean for the given entry and ObjectGraphOrigin.
|
||||
*/
|
||||
@NotNull
|
||||
private Origin createOrigin(AutoTuneCollection.Entry entry, ObjectGraphOrigin point) {
|
||||
Origin origin = new Origin();
|
||||
origin.setKey(point.getKey());
|
||||
origin.setBeanType(point.getBeanType());
|
||||
origin.setDetail(entry.getDetail().toString());
|
||||
origin.setCallStack(point.getCallStack().description("\n"));
|
||||
return origin;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
*/
|
||||
@Override
|
||||
public void shutdown() {
|
||||
if (profiling && !skipCollectionOnShutdown) {
|
||||
collectProfiling(-1);
|
||||
saveProfiling(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
*/
|
||||
@Override
|
||||
public void collectProfiling() {
|
||||
collectProfiling(-1);
|
||||
}
|
||||
|
||||
public void collectProfiling(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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto tune the query and enable profiling.
|
||||
*/
|
||||
@Override
|
||||
public boolean tuneQuery(SpiQuery<?> query) {
|
||||
return queryTuner.tuneQuery(query);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.avaje.ebeaninternal.server.autotune.service;
|
||||
|
||||
import com.avaje.ebean.bean.NodeUsageCollector;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.bean.ObjectGraphOrigin;
|
||||
import com.avaje.ebean.config.AutoTuneConfig;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.autotune.AutoTuneCollection;
|
||||
import com.avaje.ebeaninternal.server.autotune.ProfilingListener;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Manages the collection of object graph usage profiling.
|
||||
*/
|
||||
public class ProfileManager implements ProfilingListener {
|
||||
|
||||
private final boolean queryTuningAddVersion;
|
||||
|
||||
/**
|
||||
* Converted from a 0-100 int to a double. Effectively a percentage rate at
|
||||
* which to collect profiling information.
|
||||
*/
|
||||
private final double profilingRate;
|
||||
|
||||
private final int profilingBase;
|
||||
|
||||
/**
|
||||
* Map of the usage and query statistics gathered.
|
||||
*/
|
||||
private final Map<String, ProfileOrigin> profileMap = new ConcurrentHashMap<String, ProfileOrigin>();
|
||||
|
||||
private final Object monitor = new Object();
|
||||
|
||||
private final SpiEbeanServer server;
|
||||
|
||||
public ProfileManager(AutoTuneConfig config, SpiEbeanServer server) {
|
||||
this.server = server;
|
||||
this.profilingRate = config.getProfilingRate();
|
||||
this.profilingBase = config.getProfilingBase();
|
||||
this.queryTuningAddVersion = config.isQueryTuningAddVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProfileRequest(ObjectGraphNode origin, SpiQuery<?> query) {
|
||||
|
||||
ProfileOrigin profileOrigin = profileMap.get(origin.getOriginQueryPoint().getKey());
|
||||
if (profileOrigin == null) {
|
||||
profileMap.put(origin.getOriginQueryPoint().getKey(), createProfileOrigin(origin, query));
|
||||
return true;
|
||||
} else {
|
||||
return profileOrigin.isProfile();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the profile origin noting the query detail currently being used.
|
||||
* <p>
|
||||
* For new profiling entries it is useful to compare the profiling against the current
|
||||
* query detail that is specified in the code (as the query might already be manually optimised).
|
||||
* </p>
|
||||
*/
|
||||
private ProfileOrigin createProfileOrigin(ObjectGraphNode origin, SpiQuery<?> query) {
|
||||
ProfileOrigin profileOrigin = new ProfileOrigin(origin.getOriginQueryPoint(), queryTuningAddVersion, profilingBase, profilingRate);
|
||||
// set the current query detail (fetch group) so that we can compare against profiling for new entries
|
||||
profileOrigin.setOriginalQuery(query.getDetail().toString());
|
||||
return profileOrigin;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, profilingBase, profilingRate);
|
||||
profileMap.put(originQueryPoint.getKey(), stats);
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect all the profiling information.
|
||||
*/
|
||||
public AutoTuneCollection profilingCollection(boolean reset) {
|
||||
|
||||
AutoTuneCollection req = new AutoTuneCollection();
|
||||
|
||||
for (ProfileOrigin origin : profileMap.values()) {
|
||||
|
||||
BeanDescriptor<?> desc = server.getBeanDescriptorById(origin.getOrigin().getBeanType());
|
||||
if (desc != null) {
|
||||
origin.profilingCollection(desc, req, reset);
|
||||
}
|
||||
}
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package com.avaje.ebeaninternal.server.autotune.service;
|
||||
|
||||
import com.avaje.ebean.bean.NodeUsageCollector;
|
||||
import com.avaje.ebean.bean.ObjectGraphNode;
|
||||
import com.avaje.ebean.bean.ObjectGraphOrigin;
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
import com.avaje.ebean.text.PathProperties.Props;
|
||||
import com.avaje.ebeaninternal.server.autotune.AutoTuneCollection;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class ProfileOrigin {
|
||||
|
||||
private static final long RESET_COUNT = -1000000000L;
|
||||
|
||||
private final ObjectGraphOrigin origin;
|
||||
|
||||
private final boolean queryTuningAddVersion;
|
||||
|
||||
private final int profilingBase;
|
||||
|
||||
private final double profilingRate;
|
||||
|
||||
private final Map<String, ProfileOriginQuery> queryStatsMap = new ConcurrentHashMap<String, ProfileOriginQuery>();
|
||||
|
||||
private final Map<String, ProfileOriginNodeUsage> nodeUsageMap = new ConcurrentHashMap<String, ProfileOriginNodeUsage>();
|
||||
|
||||
private final Object monitor = new Object();
|
||||
|
||||
private final AtomicLong requestCount = new AtomicLong();
|
||||
|
||||
private final AtomicLong profileCount = new AtomicLong();
|
||||
|
||||
private String originalQuery;
|
||||
|
||||
public ProfileOrigin(ObjectGraphOrigin origin, boolean queryTuningAddVersion, int profilingBase, double profilingRate) {
|
||||
this.origin = origin;
|
||||
this.queryTuningAddVersion = queryTuningAddVersion;
|
||||
this.profilingBase = profilingBase;
|
||||
this.profilingRate = profilingRate;
|
||||
}
|
||||
|
||||
public String getOriginalQuery() {
|
||||
return originalQuery;
|
||||
}
|
||||
|
||||
public void setOriginalQuery(String originalQuery) {
|
||||
this.originalQuery = originalQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this query should be profiled based on a percentage rate.
|
||||
*/
|
||||
public boolean isProfile() {
|
||||
|
||||
long count = requestCount.incrementAndGet();
|
||||
if (count < profilingBase) {
|
||||
return true;
|
||||
}
|
||||
long hits = profileCount.get();
|
||||
if (profilingRate > (double) hits / count) {
|
||||
profileCount.incrementAndGet();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect profiling information with the option to reset the underlying profiling detail.
|
||||
*/
|
||||
public void profilingCollection(BeanDescriptor<?> rootDesc, AutoTuneCollection req, boolean reset) {
|
||||
|
||||
synchronized (monitor) {
|
||||
if (nodeUsageMap.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
OrmQueryDetail detail = buildDetail(rootDesc);
|
||||
AutoTuneCollection.Entry entry = req.add(origin, detail, originalQuery);
|
||||
|
||||
Collection<ProfileOriginQuery> values = queryStatsMap.values();
|
||||
for (ProfileOriginQuery queryEntry : values) {
|
||||
entry.addQuery(queryEntry.createEntryQuery(reset));
|
||||
}
|
||||
if (reset) {
|
||||
nodeUsageMap.clear();
|
||||
if (requestCount.get() > RESET_COUNT) {
|
||||
requestCount.set(profilingBase);
|
||||
profileCount.set(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private OrmQueryDetail buildDetail(BeanDescriptor<?> rootDesc) {
|
||||
|
||||
PathProperties pathProps = new PathProperties();
|
||||
for (ProfileOriginNodeUsage statsNode : nodeUsageMap.values()) {
|
||||
statsNode.buildTunedFetch(pathProps, rootDesc, queryTuningAddVersion);
|
||||
}
|
||||
|
||||
OrmQueryDetail detail = new OrmQueryDetail();
|
||||
|
||||
Collection<Props> pathProperties = pathProps.getPathProps();
|
||||
for (Props props : pathProperties) {
|
||||
if (!props.isEmpty()) {
|
||||
detail.addFetch(props.getPath(), props.getPropertiesAsString(), null);
|
||||
}
|
||||
}
|
||||
|
||||
detail.sortFetchPaths(rootDesc);
|
||||
return detail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the origin.
|
||||
*/
|
||||
public ObjectGraphOrigin getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect query execution summary statistics.
|
||||
* <p>
|
||||
* This can give us a quick overview into bad lazy loading areas etc.
|
||||
* </p>
|
||||
*/
|
||||
public void collectQueryInfo(ObjectGraphNode node, long beansLoaded, long micros) {
|
||||
|
||||
String key = node.getPath();
|
||||
if (key == null) {
|
||||
key = "";
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the usage information for from a instance for this node.
|
||||
*/
|
||||
public void collectUsageInfo(NodeUsageCollector profile) {
|
||||
|
||||
//logger.info("COLLECT USAGE {}", profile.toString());
|
||||
|
||||
if (!profile.isEmpty()) {
|
||||
ProfileOriginNodeUsage nodeStats = getNodeStats(profile.getNode().getPath());
|
||||
nodeStats.collectUsageInfo(profile);
|
||||
}
|
||||
}
|
||||
|
||||
private ProfileOriginNodeUsage getNodeStats(String path) {
|
||||
|
||||
synchronized (monitor) {
|
||||
// handle null paths as using ConcurrentHashMap
|
||||
path = (path == null) ? "" : path;
|
||||
ProfileOriginNodeUsage nodeStats = nodeUsageMap.get(path);
|
||||
if (nodeStats == null) {
|
||||
nodeStats = new ProfileOriginNodeUsage(path);
|
||||
nodeUsageMap.put(path, nodeStats);
|
||||
}
|
||||
return nodeStats;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+17
-21
@@ -1,4 +1,4 @@
|
||||
package com.avaje.ebeaninternal.server.autofetch;
|
||||
package com.avaje.ebeaninternal.server.autotune.service;
|
||||
|
||||
import com.avaje.ebean.bean.NodeUsageCollector;
|
||||
import com.avaje.ebean.text.PathProperties;
|
||||
@@ -10,27 +10,20 @@ import com.avaje.ebeaninternal.server.query.SplitName;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Collects usages statistics for a given node in the object graph.
|
||||
*/
|
||||
public class StatisticsNodeUsage implements Serializable {
|
||||
public class ProfileOriginNodeUsage {
|
||||
|
||||
private static final long serialVersionUID = -1663951463963779547L;
|
||||
private static final Logger logger = LoggerFactory.getLogger(ProfileOriginNodeUsage.class);
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(StatisticsNodeUsage.class);
|
||||
|
||||
@SuppressWarnings("RedundantStringConstructorCall")
|
||||
private final String monitor = new String();
|
||||
private final Object monitor = new Object();
|
||||
|
||||
private final String path;
|
||||
|
||||
private final boolean queryTuningAddVersion;
|
||||
|
||||
private int profileCount;
|
||||
|
||||
private int profileUsedCount;
|
||||
@@ -39,12 +32,12 @@ public class StatisticsNodeUsage implements Serializable {
|
||||
|
||||
private final Set<String> aggregateUsed = new LinkedHashSet<String>();
|
||||
|
||||
public StatisticsNodeUsage(String path, boolean queryTuningAddVersion) {
|
||||
this.path = path;
|
||||
this.queryTuningAddVersion = queryTuningAddVersion;
|
||||
public ProfileOriginNodeUsage(String path) {
|
||||
// handle null paths as using ConcurrentHashMap
|
||||
this.path = "".equals(path) ? null : path;
|
||||
}
|
||||
|
||||
public void buildTunedFetch(PathProperties pathProps, BeanDescriptor<?> rootDesc) {
|
||||
protected void buildTunedFetch(PathProperties pathProps, BeanDescriptor<?> rootDesc, boolean addVersionProperty) {
|
||||
|
||||
synchronized (monitor) {
|
||||
|
||||
@@ -53,7 +46,7 @@ public class StatisticsNodeUsage implements Serializable {
|
||||
ElPropertyValue elGetValue = rootDesc.getElGetValue(path);
|
||||
if (elGetValue == null) {
|
||||
desc = null;
|
||||
logger.warn("Autofetch: Can't find join for path[" + path + "] for " + rootDesc.getName());
|
||||
logger.warn("AutoTune: Can't find join for path[" + path + "] for " + rootDesc.getName());
|
||||
|
||||
} else {
|
||||
BeanProperty beanProperty = elGetValue.getBeanProperty();
|
||||
@@ -66,7 +59,7 @@ public class StatisticsNodeUsage implements Serializable {
|
||||
for (String propName : aggregateUsed) {
|
||||
BeanProperty beanProp = desc.getBeanPropertyFromPath(propName);
|
||||
if (beanProp == null) {
|
||||
logger.warn("Autofetch: Can't find property[" + propName + "] for " + desc.getName());
|
||||
logger.warn("AutoTune: Can't find property[" + propName + "] for " + desc.getName());
|
||||
|
||||
} else {
|
||||
if (beanProp instanceof BeanPropertyAssoc<?>) {
|
||||
@@ -77,7 +70,7 @@ public class StatisticsNodeUsage implements Serializable {
|
||||
} else {
|
||||
//noinspection StatementWithEmptyBody
|
||||
if (beanProp.isLob() && !beanProp.isFetchEager()) {
|
||||
// AutoFetch will not include Lob's marked FetchLazy
|
||||
// AutoTune will not include Lob's marked FetchLazy
|
||||
// (which is the default for Lob's so typical).
|
||||
} else {
|
||||
pathProps.addToPath(path, beanProp.getName());
|
||||
@@ -86,7 +79,7 @@ public class StatisticsNodeUsage implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
if ((modified || queryTuningAddVersion) && desc != null) {
|
||||
if ((modified || addVersionProperty) && desc != null) {
|
||||
BeanProperty versionProp = desc.getVersionProperty();
|
||||
if (versionProp != null) {
|
||||
pathProps.addToPath(path, versionProp.getName());
|
||||
@@ -95,11 +88,14 @@ public class StatisticsNodeUsage implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
public void publish(NodeUsageCollector profile) {
|
||||
/**
|
||||
* Collect usage from a node.
|
||||
*/
|
||||
protected void collectUsageInfo(NodeUsageCollector profile) {
|
||||
|
||||
synchronized (monitor) {
|
||||
|
||||
HashSet<String> used = profile.getUsed();
|
||||
Set<String> used = profile.getUsed();
|
||||
|
||||
profileCount++;
|
||||
if (!used.isEmpty()) {
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.avaje.ebeaninternal.server.autotune.service;
|
||||
|
||||
import com.avaje.ebeaninternal.server.autotune.AutoTuneCollection;
|
||||
import com.avaje.ebeaninternal.server.util.LongAdder;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Used to accumulate query execution statistics for paths relative to the origin query.
|
||||
*/
|
||||
public class ProfileOriginQuery implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -1133958958072778811L;
|
||||
|
||||
private final String path;
|
||||
|
||||
private final LongAdder exeCount = new LongAdder();
|
||||
|
||||
private final LongAdder totalBeanLoaded = new LongAdder();
|
||||
|
||||
private final LongAdder totalMicros = new LongAdder();
|
||||
|
||||
public ProfileOriginQuery(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public void add(long beansLoaded, long micros) {
|
||||
exeCount.increment();
|
||||
totalBeanLoaded.add(beansLoaded);
|
||||
totalMicros.add(micros);
|
||||
}
|
||||
|
||||
public AutoTuneCollection.EntryQuery createEntryQuery(boolean reset){
|
||||
|
||||
if (reset) {
|
||||
return new AutoTuneCollection.EntryQuery(path, exeCount.sumThenReset(), totalBeanLoaded.sumThenReset(), totalMicros.sumThenReset());
|
||||
|
||||
} else {
|
||||
return new AutoTuneCollection.EntryQuery(path, exeCount.sum(), totalBeanLoaded.sum(), totalMicros.sum());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.avaje.ebeaninternal.server.autotune.service;
|
||||
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Holds tuned query information. Is immutable so this represents the tuning at
|
||||
* a given point in time.
|
||||
*/
|
||||
public class TunedQueryInfo implements Serializable {
|
||||
|
||||
private final OrmQueryDetail tunedDetail;
|
||||
|
||||
public TunedQueryInfo(OrmQueryDetail tunedDetail) {
|
||||
this.tunedDetail = tunedDetail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the tuned detail (for comparison with profiling information).
|
||||
*/
|
||||
public OrmQueryDetail getTunedDetail() {
|
||||
return tunedDetail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tune the query by replacing its OrmQueryDetail with a tuned one.
|
||||
*
|
||||
* @return true if the query was tuned, otherwise false.
|
||||
*/
|
||||
public boolean tuneQuery(SpiQuery<?> query) {
|
||||
if (tunedDetail == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean tuned;
|
||||
if (query.isDetailEmpty()) {
|
||||
tuned = true;
|
||||
// tune by 'replacement'
|
||||
query.setDetail(tunedDetail.copy());
|
||||
} else {
|
||||
// tune by 'addition'
|
||||
tuned = query.tuneFetchProperties(tunedDetail);
|
||||
}
|
||||
if (tuned) {
|
||||
query.setAutoTuned(true);
|
||||
}
|
||||
return tuned;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return tunedDetail.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import com.avaje.ebean.bean.CallStack;
|
||||
|
||||
/**
|
||||
* Creates CallStack based on the stack trace.
|
||||
*/
|
||||
public interface CallStackFactory {
|
||||
|
||||
/**
|
||||
* Create and return the CallStack given the stack trace elements.
|
||||
*/
|
||||
CallStack createCallStack(StackTraceElement[] finalTrace);
|
||||
}
|
||||
@@ -421,7 +421,7 @@ public class DefaultBeanLoader {
|
||||
query.select("id," + embeddedBeanPropertyName);
|
||||
}
|
||||
|
||||
// don't collect autoFetch usage profiling information
|
||||
// don't collect AutoTune usage profiling information
|
||||
// as we just copy the data out of these fetched beans
|
||||
// and put the data into the original bean
|
||||
query.setUsageProfiling(false);
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.avaje.ebeaninternal.server.core;
|
||||
|
||||
import com.avaje.ebean.bean.CallStack;
|
||||
|
||||
/**
|
||||
* Default CallStackFactory where the Hash function for StackTraceElement includes the line number.
|
||||
*/
|
||||
public class DefaultCallStackFactory implements CallStackFactory {
|
||||
|
||||
@Override
|
||||
public CallStack createCallStack(StackTraceElement[] finalTrace) {
|
||||
return new CallStack(finalTrace, finalTrace[0].hashCode(), pathHash(finalTrace));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hash code for the path excluding the first element.
|
||||
*/
|
||||
private int pathHash(StackTraceElement[] callStack) {
|
||||
|
||||
int hc = 0;
|
||||
for (int i = 1; i < callStack.length; i++) {
|
||||
hc = 31 * hc + callStack[i].hashCode();
|
||||
}
|
||||
return hc;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,20 +24,16 @@ import com.avaje.ebeaninternal.server.lib.sql.SimpleDataSourceAlert;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Default Server side implementation of ServerFactory.
|
||||
@@ -50,7 +46,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 +115,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 +127,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
|
||||
|
||||
@@ -36,7 +36,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.autotune.AutoTuneService;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
@@ -45,7 +45,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;
|
||||
@@ -71,9 +70,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;
|
||||
@@ -97,20 +93,22 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
private static final int IGNORE_LEADING_ELEMENTS = 5;
|
||||
|
||||
private static final String AVAJE_EBEAN = Ebean.class.getName().substring(0, 15);
|
||||
|
||||
private static final String COM_AVAJE_EBEAN = "com.avaje.ebean";
|
||||
|
||||
private static final String ORG_AVAJE_EBEAN = "org.avaje.ebean";
|
||||
|
||||
private final ServerConfig serverConfig;
|
||||
|
||||
private final String serverName;
|
||||
|
||||
private final DatabasePlatform databasePlatform;
|
||||
|
||||
private final AdminAutofetch adminAutofetch;
|
||||
|
||||
private final TransactionManager transactionManager;
|
||||
|
||||
private final TransactionScopeManager transactionScopeManager;
|
||||
|
||||
private final CallStackFactory callStackFactory = new DefaultCallStackFactory();
|
||||
|
||||
private final int maxCallStack;
|
||||
|
||||
/**
|
||||
@@ -132,7 +130,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
private final BeanDescriptorManager beanDescriptorManager;
|
||||
|
||||
private final AutoFetchManager autoFetchManager;
|
||||
private final AutoTuneService autoTuneService;
|
||||
|
||||
private final ReadAuditPrepare readAuditPrepare;
|
||||
|
||||
@@ -156,21 +154,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.
|
||||
*/
|
||||
@@ -238,8 +226,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.createAutoTuneService(this);
|
||||
this.readAuditPrepare = config.getReadAuditPrepare();
|
||||
this.readAuditLogger = config.getReadAuditLogger();
|
||||
|
||||
@@ -258,6 +245,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
private void configureServerPlugins() {
|
||||
|
||||
autoTuneService.startup();
|
||||
|
||||
for (SpiServerPlugin plugin : serverPlugins) {
|
||||
plugin.configure(this);
|
||||
}
|
||||
@@ -350,12 +339,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return ddlGenerator;
|
||||
}
|
||||
|
||||
public AdminAutofetch getAdminAutofetch() {
|
||||
return adminAutofetch;
|
||||
}
|
||||
|
||||
public AutoFetchManager getAutoFetchManager() {
|
||||
return autoFetchManager;
|
||||
@Override
|
||||
public AutoTune getAutoTune() {
|
||||
return autoTuneService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -388,41 +374,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.
|
||||
*/
|
||||
@@ -454,16 +405,8 @@ 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)
|
||||
@@ -1115,7 +1058,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.isAutoTunable() && !query.isSqlSelect() && !autoTuneService.tuneQuery(query)) {
|
||||
// use deployment FetchType.LAZY/EAGER annotations
|
||||
// to define the 'default' select clause
|
||||
query.setDefaultSelectClause();
|
||||
@@ -1123,13 +1066,13 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
if (query.selectAllForLazyLoadProperty()) {
|
||||
// we need to select all properties to ensure the lazy load property
|
||||
// was included (was not included by default or via autofetch).
|
||||
// was included (was not included by default or via autoTune).
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Using selectAllForLazyLoadProperty");
|
||||
}
|
||||
}
|
||||
|
||||
// if determine cost and no origin for Autofetch
|
||||
// if determine cost and no origin for AutoTune
|
||||
if (query.getParentNode() == null) {
|
||||
query.setOrigin(createCallStack());
|
||||
}
|
||||
@@ -1423,7 +1366,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public <T> PagedList<T> findPagedList(Query<T> query, Transaction transaction, int pageIndex, int pageSize) {
|
||||
|
||||
|
||||
return new LimitOffsetPagedList<T>(this, (SpiQuery<T>)query, pageIndex, pageSize);
|
||||
}
|
||||
|
||||
@@ -2091,7 +2034,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
// find the first non-avaje stackElement
|
||||
for (; startIndex < stackTrace.length; startIndex++) {
|
||||
if (!stackTrace[startIndex].getClassName().startsWith(AVAJE_EBEAN)) {
|
||||
if (!stackTrace[startIndex].getClassName().startsWith(COM_AVAJE_EBEAN)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (; startIndex < stackTrace.length; startIndex++) {
|
||||
if (!stackTrace[startIndex].getClassName().startsWith(ORG_AVAJE_EBEAN)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2111,10 +2059,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
throw new RuntimeException("StackTraceElement size 0? stack: " + Arrays.toString(stackTrace));
|
||||
}
|
||||
|
||||
return new CallStack(finalTrace);
|
||||
return callStackFactory.createCallStack(finalTrace);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public JsonContext json() {
|
||||
// immutable thread safe so return shared instance
|
||||
|
||||
@@ -15,8 +15,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.autotune.AutoTuneService;
|
||||
import com.avaje.ebeaninternal.server.autotune.service.AutoTuneServiceFactory;
|
||||
import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogListener;
|
||||
import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogPrepare;
|
||||
import com.avaje.ebeaninternal.server.changelog.DefaultChangeLogRegister;
|
||||
@@ -36,8 +36,6 @@ import com.avaje.ebeaninternal.server.query.DefaultOrmQueryEngine;
|
||||
import com.avaje.ebeaninternal.server.query.DefaultRelationalQueryEngine;
|
||||
import com.avaje.ebeaninternal.server.readaudit.DefaultReadAuditLogger;
|
||||
import com.avaje.ebeaninternal.server.readaudit.DefaultReadAuditPrepare;
|
||||
import com.avaje.ebeaninternal.server.resource.ResourceManager;
|
||||
import com.avaje.ebeaninternal.server.resource.ResourceManagerFactory;
|
||||
import com.avaje.ebeaninternal.server.text.json.DJsonContext;
|
||||
import com.avaje.ebeaninternal.server.transaction.AutoCommitTransactionManager;
|
||||
import com.avaje.ebeaninternal.server.transaction.DefaultTransactionScopeManager;
|
||||
@@ -70,8 +68,6 @@ public class InternalConfiguration {
|
||||
|
||||
private final DeployInherit deployInherit;
|
||||
|
||||
private final ResourceManager resourceManager;
|
||||
|
||||
private final DeployOrmXml deployOrmXml;
|
||||
|
||||
private final TypeManager typeManager;
|
||||
@@ -122,8 +118,7 @@ public class InternalConfiguration {
|
||||
|
||||
this.typeManager = new DefaultTypeManager(serverConfig, bootupClasses);
|
||||
|
||||
this.resourceManager = ResourceManagerFactory.createResourceManager(serverConfig);
|
||||
this.deployOrmXml = new DeployOrmXml(resourceManager.getResourceSource());
|
||||
this.deployOrmXml = new DeployOrmXml();
|
||||
this.deployInherit = new DeployInherit(bootupClasses);
|
||||
|
||||
this.deployCreateProperties = new DeployCreateProperties(typeManager);
|
||||
@@ -253,8 +248,8 @@ public class InternalConfiguration {
|
||||
return xmlConfig;
|
||||
}
|
||||
|
||||
public AutoFetchManager createAutoFetchManager(SpiEbeanServer server) {
|
||||
return AutoFetchManagerFactory.create(server, serverConfig, resourceManager);
|
||||
public AutoTuneService createAutoTuneService(SpiEbeanServer server) {
|
||||
return AutoTuneServiceFactory.create(server, serverConfig);
|
||||
}
|
||||
|
||||
public RelationalQueryEngine createRelationalQueryEngine() {
|
||||
@@ -305,10 +300,6 @@ public class InternalConfiguration {
|
||||
return deployInherit;
|
||||
}
|
||||
|
||||
public ResourceManager getResourceManager() {
|
||||
return resourceManager;
|
||||
}
|
||||
|
||||
public DeployOrmXml getDeployOrmXml() {
|
||||
return deployOrmXml;
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the query plan hash AFTER any potential AutoFetch tuning.
|
||||
* Calculate the query plan hash AFTER any potential AutoTune tuning.
|
||||
*/
|
||||
public void calculateQueryPlanHash() {
|
||||
this.queryPlanHash = query.queryPlanHash(this);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import com.avaje.ebean.OrderBy;
|
||||
import com.avaje.ebean.SqlUpdate;
|
||||
import com.avaje.ebean.Transaction;
|
||||
import com.avaje.ebean.ValuePair;
|
||||
@@ -129,7 +130,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
*/
|
||||
private final String selectLastInsertedId;
|
||||
|
||||
private final boolean autoFetchTunable;
|
||||
private final boolean autoTunable;
|
||||
|
||||
/**
|
||||
* The concurrency mode for beans of this type.
|
||||
@@ -375,7 +376,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
this.baseTable = InternString.intern(deploy.getBaseTable());
|
||||
this.baseTableAsOf = deploy.getBaseTableAsOf();
|
||||
this.baseTableVersionsBetween = deploy.getBaseTableVersionsBetween();
|
||||
this.autoFetchTunable = EntityType.ORM.equals(entityType) && (beanFinder == null);
|
||||
this.autoTunable = EntityType.ORM.equals(entityType) && (beanFinder == null);
|
||||
|
||||
// helper object used to derive lists of properties
|
||||
DeployBeanPropertyLists listHelper = new DeployBeanPropertyLists(owner, this, deploy);
|
||||
@@ -1687,10 +1688,10 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if queries for beans of this type are autoFetch tunable.
|
||||
* Return true if queries for beans of this type are auto tunable.
|
||||
*/
|
||||
public boolean isAutoFetchTunable() {
|
||||
return autoFetchTunable;
|
||||
public boolean isAutoTunable() {
|
||||
return autoTunable;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2098,6 +2099,26 @@ public class BeanDescriptor<T> implements MetaBeanInfo, SpiBeanType<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the Id property to the OrderBy clause if it is not believed
|
||||
* to be already contained in the order by.
|
||||
* <p>
|
||||
* This is primarily used for paging queries to ensure that an order by clause is provided and that the order by
|
||||
* provides unique ordering of the rows (so that the paging is predicable).
|
||||
* </p>
|
||||
*/
|
||||
public void appendOrderById(SpiQuery<T> query) {
|
||||
|
||||
if (idProperty != null) {
|
||||
OrderBy<T> orderBy = query.getOrderBy();
|
||||
if (orderBy == null || orderBy.isEmpty()) {
|
||||
query.order().asc(idProperty.getName());
|
||||
} else if (!orderBy.containsProperty(idProperty.getName())){
|
||||
query.order().asc(idProperty.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* All the BeanPropertyAssocOne that are not embedded. These are effectively
|
||||
* joined beans. For ManyToOne and OneToOne associations.
|
||||
|
||||
@@ -42,10 +42,10 @@ public interface DbReadContext {
|
||||
/**
|
||||
* Return true if we are profiling this query.
|
||||
*/
|
||||
boolean isAutoFetchProfiling();
|
||||
boolean isAutoTuneProfiling();
|
||||
|
||||
/**
|
||||
* Add autoFetch profiling for a loaded entity bean.
|
||||
* Add AutoTune profiling for a loaded entity bean.
|
||||
*/
|
||||
void profileBean(EntityBeanIntercept ebi, String prefix);
|
||||
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
package com.avaje.ebeaninternal.server.deploy;
|
||||
|
||||
import com.avaje.ebeaninternal.server.lib.util.Dnode;
|
||||
import com.avaje.ebeaninternal.server.lib.util.DnodeReader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import com.avaje.ebeaninternal.server.lib.resource.ResourceContent;
|
||||
import com.avaje.ebeaninternal.server.lib.resource.ResourceSource;
|
||||
import com.avaje.ebeaninternal.server.lib.util.Dnode;
|
||||
import com.avaje.ebeaninternal.server.lib.util.DnodeReader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Controls the creation and caching of BeanManager's, BeanDescriptors,
|
||||
* BeanTable etc for both beans and tables(MapBeans).
|
||||
@@ -27,15 +25,10 @@ public class DeployOrmXml {
|
||||
private final HashMap<String, DNativeQuery> nativeQueryCache;
|
||||
|
||||
private final ArrayList<Dnode> ormXmlList;
|
||||
|
||||
private final ResourceSource resSource;
|
||||
|
||||
public DeployOrmXml(ResourceSource resSource) {
|
||||
|
||||
this.resSource = resSource;
|
||||
public DeployOrmXml() {
|
||||
this.nativeQueryCache = new HashMap<String, DNativeQuery>();
|
||||
this.ormXmlList = findAllOrmXml();
|
||||
|
||||
|
||||
initialiseNativeQueries();
|
||||
}
|
||||
@@ -106,17 +99,7 @@ public class DeployOrmXml {
|
||||
private void readOrmXml(String ormXmlName, ArrayList<Dnode> ormXmlList) {
|
||||
|
||||
try {
|
||||
Dnode ormXml;
|
||||
ResourceContent content = resSource.getContent(ormXmlName);
|
||||
if (content != null) {
|
||||
// servlet resource or file system...
|
||||
ormXml = readOrmXml(content.getInputStream());
|
||||
|
||||
} else {
|
||||
// try the classpath...
|
||||
ormXml = readOrmXmlFromClasspath(ormXmlName);
|
||||
}
|
||||
|
||||
Dnode ormXml = readOrmXmlFromClasspath(ormXmlName);
|
||||
if (ormXml != null) {
|
||||
ormXml.setAttribute("ebean.filename", ormXmlName);
|
||||
ormXmlList.add(ormXml);
|
||||
|
||||
@@ -84,7 +84,7 @@ class AllEqualsExpression implements SpiExpression {
|
||||
* The null check is required due to the "is null" sql being generated.
|
||||
* </p>
|
||||
*/
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
|
||||
builder.add(AllEqualsExpression.class);
|
||||
|
||||
@@ -97,7 +97,7 @@ class AllEqualsExpression implements SpiExpression {
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
@@ -31,13 +31,13 @@ class BetweenExpression extends AbstractExpression {
|
||||
request.append(getPropertyName()).append(BETWEEN).append(" ? and ? ");
|
||||
}
|
||||
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(BetweenExpression.class).add(propName);
|
||||
builder.bind(2);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
+2
-2
@@ -53,13 +53,13 @@ class BetweenPropertyExpression implements SpiExpression {
|
||||
request.append(" ? ").append(BETWEEN).append(name(lowProperty)).append(" and ").append(name(highProperty));
|
||||
}
|
||||
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(BetweenPropertyExpression.class).add(lowProperty).add(highProperty);
|
||||
builder.bind(1);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
+2
-2
@@ -41,13 +41,13 @@ class CaseInsensitiveEqualExpression extends AbstractExpression {
|
||||
request.append("lower(").append(pname).append(") =? ");
|
||||
}
|
||||
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(CaseInsensitiveEqualExpression.class).add(propName);
|
||||
builder.bind(1);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
+2
-2
@@ -153,9 +153,9 @@ public class DefaultExampleExpression implements SpiExpression, ExampleExpressio
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hash for autoFetch query identification.
|
||||
* Return a hash for AutoTune query identification.
|
||||
*/
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
// we have not yet built the list of expressions
|
||||
// so just based on the class name
|
||||
builder.add(DefaultExampleExpression.class);
|
||||
|
||||
@@ -27,10 +27,10 @@ public class ExistsExpression implements SpiExpression {
|
||||
this.not = not;
|
||||
}
|
||||
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(ExistsExpression.class).add(not);
|
||||
|
||||
subQuery.queryAutofetchHash(builder);
|
||||
subQuery.queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
@@ -39,7 +39,7 @@ public class ExistsExpression implements SpiExpression {
|
||||
// ... so compiledQuery will exist
|
||||
compiledSubQuery = compileSubQuery(request);
|
||||
|
||||
queryAutoFetchHash(builder);
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -50,13 +50,13 @@ class IdExpression implements SpiExpression {
|
||||
/**
|
||||
* No properties so this is just a unique static number.
|
||||
*/
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(IdExpression.class);
|
||||
builder.bind(1);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
@@ -68,13 +68,13 @@ public class IdInExpression implements SpiExpression {
|
||||
/**
|
||||
* Incorporates the number of Id values to bind.
|
||||
*/
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(IdInExpression.class).add(idList.size());
|
||||
builder.bind(idList.size());
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
@@ -90,13 +90,13 @@ class InExpression extends AbstractExpression {
|
||||
/**
|
||||
* Based on the number of values in the in clause.
|
||||
*/
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(InExpression.class).add(propName).add(values.length).add(not);
|
||||
builder.bind(values.length);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
@@ -28,9 +28,9 @@ class InQueryExpression extends AbstractExpression {
|
||||
this.not = not;
|
||||
}
|
||||
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(InQueryExpression.class).add(propName).add(not);
|
||||
subQuery.queryAutofetchHash(builder);
|
||||
subQuery.queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
@@ -39,7 +39,7 @@ class InQueryExpression extends AbstractExpression {
|
||||
// ... so compiledQuery will exist
|
||||
compiledSubQuery = compileSubQuery(request);
|
||||
|
||||
queryAutoFetchHash(builder);
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -129,11 +129,11 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
* Based on Junction type and all the expression contained.
|
||||
*/
|
||||
@Override
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(JunctionExpression.class).add(joinType);
|
||||
List<SpiExpression> list = exprList.internalList();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
list.get(i).queryAutoFetchHash(builder);
|
||||
list.get(i).queryAutoTuneHash(builder);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,13 +61,13 @@ class LikeExpression extends AbstractExpression {
|
||||
/**
|
||||
* Based on caseInsensitive and the property name.
|
||||
*/
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(LikeExpression.class).add(caseInsensitive).add(propName);
|
||||
builder.bind(1);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
@@ -70,10 +70,10 @@ abstract class LogicExpression implements SpiExpression {
|
||||
/**
|
||||
* Based on the joinType plus the two expressions.
|
||||
*/
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(LogicExpression.class).add(joinType);
|
||||
expOne.queryAutoFetchHash(builder);
|
||||
expTwo.queryAutoFetchHash(builder);
|
||||
expOne.queryAutoTuneHash(builder);
|
||||
expTwo.queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
|
||||
@@ -20,13 +20,13 @@ class NoopExpression implements SpiExpression {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(NoopExpression.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -37,9 +37,9 @@ final class NotExpression implements SpiExpression {
|
||||
/**
|
||||
* Based on the expression.
|
||||
*/
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(NotExpression.class);
|
||||
exp.queryAutoFetchHash(builder);
|
||||
exp.queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
|
||||
@@ -42,12 +42,12 @@ class NullExpression extends AbstractExpression {
|
||||
/**
|
||||
* Based on notNull flag and the propertyName.
|
||||
*/
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(NullExpression.class).add(notNull).add(propName);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
@@ -39,12 +39,12 @@ class RawExpression implements SpiExpression {
|
||||
/**
|
||||
* Based on the sql.
|
||||
*/
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(RawExpression.class).add(sql);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
@@ -86,13 +86,13 @@ public class SimpleExpression extends AbstractExpression {
|
||||
/**
|
||||
* Based on the type and propertyName.
|
||||
*/
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
public void queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(SimpleExpression.class).add(propName).add(type.name());
|
||||
builder.bind(1);
|
||||
}
|
||||
|
||||
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
queryAutoFetchHash(builder);
|
||||
queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
public int queryBindHash() {
|
||||
|
||||
@@ -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;
|
||||
@@ -1,108 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.lib.resource;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Helper object used to find directories typically from the current working
|
||||
* directory.
|
||||
*/
|
||||
public class DirectoryFinder {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(DirectoryFinder.class);
|
||||
|
||||
/**
|
||||
* Find a directory by search through subdirectories.
|
||||
* <p>
|
||||
* For example, used to find the WEB-INF directory starting from the current
|
||||
* working directory.
|
||||
* </p>
|
||||
*
|
||||
* <pre class="code">
|
||||
*
|
||||
* // search to a depth of 3 from the current working directory
|
||||
* // looking for a directory WEB-INF that contains the subdirectory
|
||||
* // data
|
||||
*
|
||||
* File dir = DirectoryFinder.find(null, "WEB-INF/data", 3);
|
||||
* if (dir != null) {
|
||||
* //found the directory
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public static File find(File startDir, String match, int maxDepth) {
|
||||
|
||||
String matchSub = null;
|
||||
int slashPos = match.indexOf('/');
|
||||
if (slashPos > -1) {
|
||||
// match has sub directories
|
||||
matchSub = match.substring(slashPos + 1);
|
||||
match = match.substring(0, slashPos);
|
||||
}
|
||||
|
||||
// search for the directory
|
||||
File found = find(startDir, match, matchSub, 0, maxDepth);
|
||||
|
||||
if (found != null && matchSub != null) {
|
||||
// match has sub directories
|
||||
return new File(found, matchSub);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
private static File find(File dir, String match, String matchSub, int depth, int maxDepth) {
|
||||
|
||||
if (dir == null) {
|
||||
String curDir = System.getProperty("user.dir");
|
||||
dir = new File(curDir);
|
||||
}
|
||||
|
||||
if (dir.exists()) {
|
||||
File[] list = dir.listFiles();
|
||||
if (list != null){
|
||||
for (int i = 0; i < list.length; i++) {
|
||||
if (isMatch(list[i], match, matchSub)) {
|
||||
return list[i];
|
||||
}
|
||||
}
|
||||
|
||||
// go through the directories again
|
||||
// Aka *NOT* a depth first search
|
||||
if (depth < maxDepth) {
|
||||
for (int i = 0; i < list.length; i++) {
|
||||
if (list[i].isDirectory()) {
|
||||
File found = find(list[i], match, matchSub, depth + 1, maxDepth);
|
||||
if (found != null) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isMatch(File f, String match, String matchSub) {
|
||||
if (f == null) {
|
||||
return false;
|
||||
}
|
||||
if (!f.isDirectory()) {
|
||||
return false;
|
||||
}
|
||||
if (!f.getName().equalsIgnoreCase(match)) {
|
||||
return false;
|
||||
}
|
||||
if (matchSub == null) {
|
||||
return true;
|
||||
}
|
||||
File sub = new File(f, matchSub);
|
||||
if (logger.isTraceEnabled()){
|
||||
logger.trace("search; " + f.getPath());
|
||||
}
|
||||
return sub.exists();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.lib.resource;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Content from a file system file.
|
||||
*/
|
||||
public class FileResourceContent implements ResourceContent {
|
||||
|
||||
/**
|
||||
* The underlying file.
|
||||
*/
|
||||
final File file;
|
||||
|
||||
final String entryName;
|
||||
|
||||
/**
|
||||
* Create with a File and the entryName.
|
||||
*/
|
||||
public FileResourceContent(File file, String entryName) {
|
||||
this.file = file;
|
||||
this.entryName = entryName;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("[").append(getName());
|
||||
sb.append("] size[").append(size());
|
||||
sb.append("] lastModified[").append(new Date(lastModified()));
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entry name which contains the path from the base directory.
|
||||
* <p>
|
||||
* This does not return the full path of the file, but the path relative to
|
||||
* the FileIoSource directory.
|
||||
* </p>
|
||||
*/
|
||||
public String getName() {
|
||||
return entryName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time the file was last modified.
|
||||
*/
|
||||
public long lastModified() {
|
||||
return file.lastModified();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the size of the file.
|
||||
*/
|
||||
public long size() {
|
||||
return file.length();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the input stream for this file.
|
||||
*/
|
||||
public InputStream getInputStream() throws IOException {
|
||||
|
||||
return new FileInputStream(file);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.lib.resource;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* A file system directory represented as a FileSource.
|
||||
*/
|
||||
public class FileResourceSource implements ResourceSource {
|
||||
|
||||
/**
|
||||
* The directory name.
|
||||
*/
|
||||
final String directory;
|
||||
|
||||
final String baseDir;
|
||||
|
||||
/**
|
||||
* Create the source based on a directory name.
|
||||
*/
|
||||
public FileResourceSource(String directory) {
|
||||
this.directory = directory;
|
||||
this.baseDir = directory + File.separator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the source based on a directory file.
|
||||
*/
|
||||
public FileResourceSource(File dir) {
|
||||
this(dir.getPath());
|
||||
}
|
||||
|
||||
|
||||
public String getRealPath() {
|
||||
return directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the given file and return as IoContent.
|
||||
*/
|
||||
public ResourceContent getContent(String entry) {
|
||||
|
||||
String fullPath = baseDir + entry;
|
||||
|
||||
File f = new File(fullPath);
|
||||
if (f.exists()) {
|
||||
return new FileResourceContent(f, entry);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.lib.resource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Represents content that can be read via the ResourceManager.
|
||||
* <p>
|
||||
* Typically either content from a File or a URL.
|
||||
* </p>
|
||||
*/
|
||||
public interface ResourceContent {
|
||||
|
||||
/**
|
||||
* The name of the content.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* The size of the content in bytes.
|
||||
*/
|
||||
long size();
|
||||
|
||||
/**
|
||||
* The last modified timestamp of the content.
|
||||
*/
|
||||
long lastModified();
|
||||
|
||||
/**
|
||||
* The content itself.
|
||||
*/
|
||||
InputStream getInputStream() throws IOException;
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.avaje.ebeaninternal.server.lib.resource;
|
||||
|
||||
/**
|
||||
* A Source for ResourceManager.
|
||||
* <p>
|
||||
* Typically a File System Directory based source or a ServletContext URL
|
||||
* resource based source (for Servlet WAR files).
|
||||
* </p>
|
||||
*/
|
||||
public interface ResourceSource {
|
||||
|
||||
/**
|
||||
* Return the File System path of the root of the ResourceSource.
|
||||
* <p>
|
||||
* This will return null IF the ResourceSource is an unpacked WAR file.
|
||||
* </p>
|
||||
*/
|
||||
String getRealPath();
|
||||
|
||||
/**
|
||||
* Find the content with a given entry name. This will return null if no
|
||||
* matching content was found.
|
||||
*/
|
||||
ResourceContent getContent(String entry);
|
||||
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="CONTENT-TYPE" content="text/html; charset=iso-8859-1">
|
||||
<title>AvajeLib</title>
|
||||
</head>
|
||||
<body BGCOLOR="#ffffff">
|
||||
Service for reading content from either the file system or servlet context.
|
||||
<p>
|
||||
A service used to read deployment content such as xml files, images etc
|
||||
taking into account the environment (servlet war file or file system).
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -61,8 +61,8 @@ public class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContex
|
||||
if (queryProps != null) {
|
||||
queryProps.configureBeanQuery(query);
|
||||
}
|
||||
if (parent.isUseAutofetchManager()) {
|
||||
query.setAutofetch(true);
|
||||
if (parent.isUseAutoTune()) {
|
||||
query.setAutoTune(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,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>();
|
||||
|
||||
@@ -69,7 +69,7 @@ public class DLoadContext implements LoadContext {
|
||||
this.disableReadAudit = query.isDisableReadAudit();
|
||||
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) {
|
||||
@@ -199,8 +199,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) {
|
||||
|
||||
@@ -73,8 +73,8 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
|
||||
queryProps.configureBeanQuery(query);
|
||||
}
|
||||
|
||||
if (parent.isUseAutofetchManager()) {
|
||||
query.setAutofetch(true);
|
||||
if (parent.isUseAutoTune()) {
|
||||
query.setAutoTune(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -491,7 +491,7 @@ public final class DefaultPersister implements Persister {
|
||||
for (int i = 0; i < propImportDelete.length; i++) {
|
||||
sb.append(propImportDelete[i].getName()).append(",");
|
||||
}
|
||||
q.setAutofetch(false);
|
||||
q.setAutoTune(false);
|
||||
q.select(sb.toString());
|
||||
return q;
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ public class DeleteUnloadedForeignKeys {
|
||||
// run query in a separate persistence context
|
||||
q.setPersistenceContext(new DefaultPersistenceContext());
|
||||
q.setPersistenceContextScope(PersistenceContextScope.QUERY);
|
||||
q.setAutofetch(false);
|
||||
q.setAutoTune(false);
|
||||
q.select(sb.toString());
|
||||
q.where().idEq(id);
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import com.avaje.ebean.event.readaudit.ReadEvent;
|
||||
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.autotune.ProfilingListener;
|
||||
import com.avaje.ebeaninternal.server.core.Message;
|
||||
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest;
|
||||
@@ -161,13 +161,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;
|
||||
|
||||
@@ -198,11 +198,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...
|
||||
@@ -549,8 +548,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);
|
||||
|
||||
@@ -674,11 +673,11 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
|
||||
* This is true for all queries except lazy load bean queries.
|
||||
* </p>
|
||||
*/
|
||||
public boolean isAutoFetchProfiling() {
|
||||
public boolean isAutoTuneProfiling() {
|
||||
// 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) {
|
||||
@@ -700,8 +699,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) {
|
||||
|
||||
@@ -277,6 +277,14 @@ public class CQueryEngine {
|
||||
*/
|
||||
public <T> BeanCollection<T> findMany(OrmQueryRequest<T> request) {
|
||||
|
||||
|
||||
SpiQuery<T> query = request.getQuery();
|
||||
if (query.getMaxRows() > 1 || query.getFirstRow() > 0) {
|
||||
// deemed to be a be a paging query - check that the order by contains
|
||||
// the id property to ensure unique row ordering for predicable paging
|
||||
request.getBeanDescriptor().appendOrderById(query);
|
||||
}
|
||||
|
||||
CQuery<T> cquery = queryBuilder.buildQuery(request);
|
||||
request.setCancelableQuery(cquery);
|
||||
|
||||
@@ -293,7 +301,7 @@ public class CQueryEngine {
|
||||
|
||||
BeanCollection<T> beanCollection = cquery.readCollection();
|
||||
|
||||
BeanCollectionTouched collectionTouched = request.getQuery().getBeanCollectionTouched();
|
||||
BeanCollectionTouched collectionTouched = query.getBeanCollectionTouched();
|
||||
if (collectionTouched != null) {
|
||||
// register a listener that wants to be notified when the
|
||||
// bean collection is first used
|
||||
@@ -319,7 +327,7 @@ public class CQueryEngine {
|
||||
if (cquery != null) {
|
||||
cquery.close();
|
||||
}
|
||||
if (request.getQuery().isFutureFetch()) {
|
||||
if (query.isFutureFetch()) {
|
||||
// end the transaction for futureFindIds
|
||||
// as it had it's own transaction
|
||||
logger.debug("Future fetch completed!");
|
||||
@@ -404,7 +412,7 @@ public class CQueryEngine {
|
||||
msg.append("mode[").append(loadMode).append("] ");
|
||||
}
|
||||
msg.append("type[").append(q.getBeanName()).append("] ");
|
||||
if (query.isAutofetchTuned()) {
|
||||
if (query.isAutoTuned()) {
|
||||
msg.append("tuned[true] ");
|
||||
}
|
||||
if (originKey != null) {
|
||||
@@ -447,7 +455,7 @@ public class CQueryEngine {
|
||||
msg.append("mode[").append(loadMode).append("] ");
|
||||
}
|
||||
msg.append("type[").append(q.getBeanName()).append("] ");
|
||||
if (query.isAutofetchTuned()) {
|
||||
if (query.isAutoTuned()) {
|
||||
msg.append("tuned[true] ");
|
||||
}
|
||||
if (originKey != null) {
|
||||
|
||||
@@ -250,7 +250,7 @@ public class CQueryFetchIds {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isAutoFetchProfiling() {
|
||||
public boolean isAutoTuneProfiling() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ public class CQueryPlan {
|
||||
|
||||
private final SpiEbeanServer server;
|
||||
|
||||
private final boolean autofetchTuned;
|
||||
private final boolean autoTuned;
|
||||
|
||||
private final HashQueryPlan hash;
|
||||
|
||||
@@ -80,7 +80,7 @@ public class CQueryPlan {
|
||||
this.beanType = request.getBeanDescriptor().getBeanType();
|
||||
this.stats = new CQueryPlanStats(this, server.isCollectQueryOrigins());
|
||||
this.hash = request.getQueryPlanHash();
|
||||
this.autofetchTuned = request.getQuery().isAutofetchTuned();
|
||||
this.autoTuned = request.getQuery().isAutoTuned();
|
||||
if (sqlRes != null) {
|
||||
this.sql = sqlRes.getSql();
|
||||
this.rowNumberIncluded = sqlRes.isIncludesRowNumberColumn();
|
||||
@@ -104,7 +104,7 @@ public class CQueryPlan {
|
||||
this.beanType = request.getBeanDescriptor().getBeanType();
|
||||
this.stats = new CQueryPlanStats(this, server.isCollectQueryOrigins());
|
||||
this.hash = buildHash(sql, rawSql, rowNumberIncluded, logWhereSql);
|
||||
this.autofetchTuned = false;
|
||||
this.autoTuned = false;
|
||||
this.sql = sql;
|
||||
this.sqlTree = sqlTree;
|
||||
this.rawSql = rawSql;
|
||||
@@ -143,8 +143,8 @@ public class CQueryPlan {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAutofetchTuned() {
|
||||
return autofetchTuned;
|
||||
public boolean isAutoTuned() {
|
||||
return autoTuned;
|
||||
}
|
||||
|
||||
public HashQueryPlan getHash() {
|
||||
|
||||
@@ -226,8 +226,8 @@ public final class CQueryPlanStats {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutofetchTuned() {
|
||||
return queryPlan.isAutofetchTuned();
|
||||
public boolean isAutoTuned() {
|
||||
return queryPlan.isAutoTuned();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -326,8 +326,8 @@ public class SqlTreeNodeBean implements SqlTreeNode {
|
||||
ebi.setFullyLoadedBean(true);
|
||||
}
|
||||
|
||||
if (ctx.isAutoFetchProfiling() && !disableLazyLoad) {
|
||||
// collect autofetch profiling for this bean...
|
||||
if (ctx.isAutoTuneProfiling() && !disableLazyLoad) {
|
||||
// collect autoTune profiling for this bean...
|
||||
ctx.profileBean(ebi, prefix);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -27,7 +18,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.autotune.ProfilingListener;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.DRawSqlSelect;
|
||||
@@ -37,6 +28,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.
|
||||
*/
|
||||
@@ -58,7 +57,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
*/
|
||||
private transient TableJoin includeTableJoin;
|
||||
|
||||
private transient AutoFetchManager autoFetchManager;
|
||||
private transient ProfilingListener profilingListener;
|
||||
|
||||
private transient BeanDescriptor<?> beanDescriptor;
|
||||
|
||||
@@ -200,7 +199,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
/**
|
||||
* Allow for explicit on off or null for default.
|
||||
*/
|
||||
private Boolean autoFetch;
|
||||
private Boolean autoTune;
|
||||
|
||||
/**
|
||||
* Allow to fetch a record "for update" which should lock it on read
|
||||
@@ -208,9 +207,9 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
private boolean forUpdate;
|
||||
|
||||
/**
|
||||
* Set to true if this query has been tuned by autoFetch.
|
||||
* Set to true if this query has been tuned by autoTune.
|
||||
*/
|
||||
private boolean autoFetchTuned;
|
||||
private boolean autoTuned;
|
||||
|
||||
private boolean logSecondaryQuery;
|
||||
|
||||
@@ -229,7 +228,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
private BeanPropertyAssocMany<?> lazyLoadForParentsProperty;
|
||||
|
||||
/**
|
||||
* Hash of final query after AutoFetch tuning.
|
||||
* Hash of final query after AutoTune tuning.
|
||||
*/
|
||||
private HashQueryPlan queryPlanHash;
|
||||
|
||||
@@ -478,18 +477,11 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
public DefaultOrmQuery<T> copy(EbeanServer server) {
|
||||
// Not including these in the copy:
|
||||
// contextAdditions
|
||||
// queryListener
|
||||
// transactionContext
|
||||
// autoFetchTuned
|
||||
// autoFetchQueryPlanHash
|
||||
// copy.generatedSql
|
||||
|
||||
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;
|
||||
@@ -525,7 +517,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
copy.persistenceContextScope = persistenceContextScope;
|
||||
copy.usageProfiling = usageProfiling;
|
||||
copy.autoFetch = autoFetch;
|
||||
copy.autoTune = autoTune;
|
||||
copy.parentNode = parentNode;
|
||||
copy.forUpdate = forUpdate;
|
||||
copy.rawSql = rawSql;
|
||||
@@ -603,40 +595,44 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return detail.isEmpty();
|
||||
}
|
||||
|
||||
public boolean isAutofetchTuned() {
|
||||
return autoFetchTuned;
|
||||
public boolean isAutoTuned() {
|
||||
return autoTuned;
|
||||
}
|
||||
|
||||
public void setAutoFetchTuned(boolean autoFetchTuned) {
|
||||
this.autoFetchTuned = autoFetchTuned;
|
||||
public void setAutoTuned(boolean autoTuned) {
|
||||
this.autoTuned = autoTuned;
|
||||
}
|
||||
|
||||
public Boolean isAutofetch() {
|
||||
return sqlSelect ? Boolean.FALSE : autoFetch;
|
||||
public Boolean isAutoTune() {
|
||||
return sqlSelect ? Boolean.FALSE : autoTune;
|
||||
}
|
||||
|
||||
public boolean isForUpdate() {
|
||||
return forUpdate;
|
||||
}
|
||||
|
||||
public DefaultOrmQuery<T> setAutofetch(boolean autoFetch) {
|
||||
this.autoFetch = autoFetch;
|
||||
public DefaultOrmQuery<T> setAutoTune(boolean autoTune) {
|
||||
this.autoTune = autoTune;
|
||||
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;
|
||||
}
|
||||
@@ -718,7 +714,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the query hash for either AutoFetch query tuning or Query Plan caching.
|
||||
* Calculate the query hash for either AutoTune query tuning or Query Plan caching.
|
||||
*/
|
||||
private HashQueryPlan calculateHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
|
||||
|
||||
@@ -729,7 +725,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
builder.add((type == null ? 0 : type.ordinal() + 1));
|
||||
builder.add(autoFetchTuned).add(distinct).add(sqlDistinct).add(query);
|
||||
builder.add(autoTuned).add(distinct).add(sqlDistinct).add(query);
|
||||
builder.add(firstRow).add(maxRows).add(orderBy).add(forUpdate);
|
||||
builder.add(rawWhereClause).add(additionalWhere).add(additionalHaving);
|
||||
builder.add(mapKey);
|
||||
@@ -749,13 +745,13 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
if (request == null) {
|
||||
// for AutoFetch...
|
||||
// for AutoTune...
|
||||
builder.add(true);
|
||||
if (whereExpressions != null) {
|
||||
whereExpressions.queryAutoFetchHash(builder);
|
||||
whereExpressions.queryAutoTuneHash(builder);
|
||||
}
|
||||
if (havingExpressions != null) {
|
||||
havingExpressions.queryAutoFetchHash(builder);
|
||||
havingExpressions.queryAutoTuneHash(builder);
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -773,10 +769,10 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a hash used by AutoFetch to identify when a query has changed (and hence potentially
|
||||
* Calculate a hash used by AutoTune to identify when a query has changed (and hence potentially
|
||||
* needs a new tuned query plan to be developed).
|
||||
*/
|
||||
public HashQueryPlan queryAutofetchHash(HashQueryPlanBuilder builder) {
|
||||
public HashQueryPlan queryAutoTuneHash(HashQueryPlanBuilder builder) {
|
||||
|
||||
return calculateHash(null, builder);
|
||||
}
|
||||
@@ -787,7 +783,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
* This can used to enable the caching and reuse of a 'query plan'.
|
||||
* </p>
|
||||
* <p>
|
||||
* This is calculated AFTER AutoFetch query tuning has occurred.
|
||||
* This is calculated AFTER AutoTune query tuning has occurred.
|
||||
* </p>
|
||||
*/
|
||||
public HashQueryPlan queryPlanHash(BeanQueryRequest<?> request) {
|
||||
@@ -821,7 +817,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
* </p>
|
||||
*/
|
||||
public HashQuery queryHash() {
|
||||
// calculateQueryPlanHash is called just after potential AutoFetch tuning
|
||||
// calculateQueryPlanHash is called just after potential AutoTune tuning
|
||||
// so queryPlanHash is calculated well before this method is called
|
||||
int hc = queryBindHash();
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user