mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#411 - AutoTune - add reporting empty tuned profiles, ignore org.avaje.ebean type safe finders, refactor CallStack creation
This commit is contained in:
@@ -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++) {
|
||||
@@ -99,7 +95,7 @@ public final class CallStack implements Serializable {
|
||||
}
|
||||
|
||||
public String getOriginKey(int queryHash) {
|
||||
return zeroHash + "." + enc(queryHash) + "." + pathHash;
|
||||
return enc(queryHash)+ "." + zeroHash + "." + pathHash;
|
||||
}
|
||||
|
||||
private static final int radix = 1 << 6;
|
||||
|
||||
@@ -22,6 +22,7 @@ import javax.xml.bind.annotation.XmlType;
|
||||
* <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>
|
||||
@@ -34,7 +35,8 @@ import javax.xml.bind.annotation.XmlType;
|
||||
@XmlType(name = "", propOrder = {
|
||||
"origin",
|
||||
"profileDiff",
|
||||
"profileNew"
|
||||
"profileNew",
|
||||
"profileEmpty"
|
||||
})
|
||||
@XmlRootElement(name = "autotune")
|
||||
public class Autotune {
|
||||
@@ -42,6 +44,7 @@ public class Autotune {
|
||||
protected List<Origin> origin;
|
||||
protected ProfileDiff profileDiff;
|
||||
protected ProfileNew profileNew;
|
||||
protected ProfileEmpty profileEmpty;
|
||||
|
||||
/**
|
||||
* Gets the value of the origin property.
|
||||
@@ -120,4 +123,28 @@ public class Autotune {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ 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.
|
||||
* 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
|
||||
@@ -45,6 +45,14 @@ public class ObjectFactory {
|
||||
return new Origin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link ProfileEmpty }
|
||||
*
|
||||
*/
|
||||
public ProfileEmpty createProfileEmpty() {
|
||||
return new ProfileEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link Autotune }
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
@@ -167,4 +168,11 @@ public class BaseQueryTuner {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the keys as a set.
|
||||
*/
|
||||
public Set<String> keySet() {
|
||||
return tunedQueryInfoMap.keySet();
|
||||
}
|
||||
}
|
||||
|
||||
+37
-2
@@ -10,6 +10,7 @@ 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;
|
||||
@@ -22,7 +23,9 @@ 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;
|
||||
|
||||
/**
|
||||
@@ -105,14 +108,31 @@ public class DefaultAutoTuneService implements AutoTuneService {
|
||||
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.debug("No new or diff entries for profiling server:{}", serverName);
|
||||
logger.info("No new or diff entries for profiling server:{}", serverName);
|
||||
|
||||
} else {
|
||||
sortDocument(document);
|
||||
@@ -125,7 +145,7 @@ public class DefaultAutoTuneService implements AutoTuneService {
|
||||
AutoTuneXmlWriter writer = new AutoTuneXmlWriter();
|
||||
writer.write(document, file);
|
||||
|
||||
logger.debug("writing new:{} diff:{} profiling entries for server:{}", totalNew, totalDiff, serverName);
|
||||
logger.info("writing new:{} diff:{} profiling entries for server:{}", totalNew, totalDiff, serverName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +162,10 @@ public class DefaultAutoTuneService implements AutoTuneService {
|
||||
if (profileNew != null) {
|
||||
Collections.sort(profileNew.getOrigin(), new OriginNameKeySort());
|
||||
}
|
||||
ProfileEmpty profileEmpty = document.getProfileEmpty();
|
||||
if (profileEmpty != null) {
|
||||
Collections.sort(profileEmpty.getOrigin(), new OriginKeySort());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,6 +183,17 @@ public class DefaultAutoTuneService implements AutoTuneService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -93,8 +93,10 @@ 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;
|
||||
@@ -105,6 +107,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
private final TransactionScopeManager transactionScopeManager;
|
||||
|
||||
private final CallStackFactory callStackFactory = new DefaultCallStackFactory();
|
||||
|
||||
private final int maxCallStack;
|
||||
|
||||
/**
|
||||
@@ -2030,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;
|
||||
}
|
||||
}
|
||||
@@ -2050,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
|
||||
|
||||
Reference in New Issue
Block a user