#411 - AutoTune - add reporting empty tuned profiles, ignore org.avaje.ebean type safe finders, refactor CallStack creation

This commit is contained in:
Robin Bygrave
2015-09-14 16:16:22 +12:00
parent 3dcb82b1f5
commit b76c4a83ec
10 changed files with 219 additions and 18 deletions
@@ -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;
* &lt;element ref="{http://ebean-orm.github.io/xml/ns/autotune}origin" maxOccurs="unbounded" minOccurs="0"/>
* &lt;element ref="{http://ebean-orm.github.io/xml/ns/autotune}profileDiff" minOccurs="0"/>
* &lt;element ref="{http://ebean-orm.github.io/xml/ns/autotune}profileNew" minOccurs="0"/>
* &lt;element ref="{http://ebean-orm.github.io/xml/ns/autotune}profileEmpty" minOccurs="0"/>
* &lt;/sequence>
* &lt;/restriction>
* &lt;/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>
* &lt;complexType>
* &lt;complexContent>
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* &lt;sequence>
* &lt;element ref="{http://ebean-orm.github.io/xml/ns/autotune}origin" maxOccurs="unbounded" minOccurs="0"/>
* &lt;/sequence>
* &lt;/restriction>
* &lt;/complexContent>
* &lt;/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();
}
}
@@ -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