mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
WIP AutoTune refactor
This commit is contained in:
@@ -87,6 +87,17 @@ 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;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@ public class AutoTuneCollection {
|
||||
return entry;
|
||||
}
|
||||
|
||||
public List<Entry> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Profiling entry at a given origin point.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
|
||||
package com.avaje.ebeaninternal.server.autofetch.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"/>
|
||||
* </sequence>
|
||||
* </restriction>
|
||||
* </complexContent>
|
||||
* </complexType>
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
*/
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlType(name = "", propOrder = {
|
||||
"origin",
|
||||
"profileDiff",
|
||||
"profileNew"
|
||||
})
|
||||
@XmlRootElement(name = "autotune")
|
||||
public class Autotune {
|
||||
|
||||
protected List<Origin> origin;
|
||||
protected ProfileDiff profileDiff;
|
||||
protected ProfileNew profileNew;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
|
||||
package com.avaje.ebeaninternal.server.autofetch.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.autofetch.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.autofetch.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 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.autofetch.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="tuneDetail" 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 = "tuneDetail")
|
||||
protected String tuneDetail;
|
||||
|
||||
/**
|
||||
* 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 tuneDetail property.
|
||||
*
|
||||
* @return
|
||||
* possible object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public String getTuneDetail() {
|
||||
return tuneDetail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the tuneDetail property.
|
||||
*
|
||||
* @param value
|
||||
* allowed object is
|
||||
* {@link String }
|
||||
*
|
||||
*/
|
||||
public void setTuneDetail(String value) {
|
||||
this.tuneDetail = value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
|
||||
package com.avaje.ebeaninternal.server.autofetch.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.autofetch.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.autofetch.model;
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.avaje.ebeaninternal.server.autofetch.service;
|
||||
|
||||
|
||||
import com.avaje.ebeaninternal.server.autofetch.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.autofetch.service;
|
||||
|
||||
|
||||
import com.avaje.ebeaninternal.server.autofetch.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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+65
-1
@@ -1,14 +1,25 @@
|
||||
package com.avaje.ebeaninternal.server.autofetch.service;
|
||||
|
||||
import com.avaje.ebean.bean.ObjectGraphOrigin;
|
||||
import com.avaje.ebean.config.AutofetchConfig;
|
||||
import com.avaje.ebean.config.ServerConfig;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.autofetch.AutoTuneService;
|
||||
import com.avaje.ebeaninternal.server.autofetch.AutoTuneCollection;
|
||||
import com.avaje.ebeaninternal.server.autofetch.AutoTuneService;
|
||||
import com.avaje.ebeaninternal.server.autofetch.model.Autotune;
|
||||
import com.avaje.ebeaninternal.server.autofetch.model.Origin;
|
||||
import com.avaje.ebeaninternal.server.autofetch.model.ProfileDiff;
|
||||
import com.avaje.ebeaninternal.server.autofetch.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.util.List;
|
||||
|
||||
/**
|
||||
* Implementation of the AutoTuneService which is comprised of profiling and query tuning.
|
||||
*/
|
||||
@@ -40,12 +51,65 @@ public class BaseAutoTuneService implements AutoTuneService {
|
||||
*/
|
||||
public void startup() {
|
||||
|
||||
File file = new File("ebean-autotune.xml");
|
||||
AutoTuneXmlReader reader = new AutoTuneXmlReader();
|
||||
Autotune profiling = reader.read(file);
|
||||
List<Origin> originList = profiling.getOrigin();
|
||||
for (Origin origin : originList) {
|
||||
String key = origin.getKey();
|
||||
String detail = origin.getDetail();
|
||||
OrmQueryDetailParser parser = new OrmQueryDetailParser(detail);
|
||||
OrmQueryDetail fetchDetail = parser.parse();
|
||||
TunedQueryInfo tunedQueryInfo = new TunedQueryInfo(fetchDetail);
|
||||
queryTuner.load(key, tunedQueryInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveProfiling() {
|
||||
|
||||
Autotune document = new Autotune();
|
||||
|
||||
AutoTuneCollection autoTuneCollection = profileManager.profilingCollection(false);
|
||||
|
||||
List<AutoTuneCollection.Entry> entries = autoTuneCollection.getEntries();
|
||||
for (AutoTuneCollection.Entry entry : entries) {
|
||||
ObjectGraphOrigin point = entry.getOrigin();
|
||||
OrmQueryDetail profileDetail = entry.getDetail();
|
||||
|
||||
OrmQueryDetail tuneDetail = queryTuner.get(point.getKey());
|
||||
if (tuneDetail == null) {
|
||||
ProfileNew profileNew = document.getProfileNew();
|
||||
if (profileNew == null) {
|
||||
profileNew = new ProfileNew();
|
||||
document.setProfileNew(profileNew);
|
||||
}
|
||||
profileNew.getOrigin().add( createOrigin(entry, point));
|
||||
|
||||
} else if (!tuneDetail.isAutoTuneEqual(profileDetail)) {
|
||||
Origin origin1 = createOrigin(entry, point);
|
||||
origin1.setTuneDetail(tuneDetail.toString());
|
||||
ProfileDiff diff = document.getProfileDiff();
|
||||
if (diff == null) {
|
||||
diff = new ProfileDiff();
|
||||
document.setProfileDiff(diff);
|
||||
}
|
||||
diff.getOrigin().add(origin1);
|
||||
}
|
||||
}
|
||||
|
||||
File file = new File("ebean-autotune-profiling.xml");
|
||||
AutoTuneXmlWriter writer = new AutoTuneXmlWriter();
|
||||
writer.write(document, file);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Origin createOrigin(AutoTuneCollection.Entry entry, ObjectGraphOrigin point) {
|
||||
Origin origin1 = new Origin();
|
||||
origin1.setKey(point.getKey());
|
||||
origin1.setBeanType(point.getBeanType());
|
||||
origin1.setDetail(entry.getDetail().toString());
|
||||
origin1.setCallStack(point.getCallStack().description("\n"));
|
||||
return origin1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.avaje.ebean.config.AutofetchMode;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.autofetch.ProfilingListener;
|
||||
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.Map;
|
||||
@@ -48,6 +49,17 @@ public class BaseQueryTuner {
|
||||
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();
|
||||
}
|
||||
|
||||
boolean fullProfiling = true;
|
||||
|
||||
/**
|
||||
* Auto tune the query and enable profiling.
|
||||
*/
|
||||
@@ -58,7 +70,11 @@ public class BaseQueryTuner {
|
||||
}
|
||||
|
||||
if (!useAutoTune(query)) {
|
||||
// not using autoFetch for this query
|
||||
// not tuning this query but maybe profiling
|
||||
if (fullProfiling) {
|
||||
CallStack stack = server.createCallStack();
|
||||
profiling(query, stack);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -88,6 +104,16 @@ public class BaseQueryTuner {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void profiling(SpiQuery<?> query, CallStack stack) {
|
||||
|
||||
// create a query point to identify the query
|
||||
ObjectGraphNode origin = query.setOrigin(stack);
|
||||
if (profilingListener.isProfileRequest(origin)) {
|
||||
// collect more profiling based on profiling rate etc
|
||||
query.setProfilingListener(profilingListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we should try to tune this query.
|
||||
*/
|
||||
|
||||
@@ -16,6 +16,8 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class ProfileOrigin {
|
||||
|
||||
//private static final Logger logger = LoggerFactory.getLogger(ProfileOrigin.class);
|
||||
|
||||
private static final long RESET_COUNT = -1000000000L;
|
||||
|
||||
private final ObjectGraphOrigin origin;
|
||||
@@ -89,10 +91,10 @@ public class ProfileOrigin {
|
||||
}
|
||||
|
||||
private OrmQueryDetail buildDetail(BeanDescriptor<?> rootDesc) {
|
||||
PathProperties pathProps = new PathProperties();
|
||||
|
||||
PathProperties pathProps = new PathProperties();
|
||||
for (ProfileOriginNodeUsage statsNode : nodeUsageMap.values()) {
|
||||
statsNode.buildTunedFetch(pathProps, rootDesc);
|
||||
statsNode.buildTunedFetch(pathProps, rootDesc, queryTuningAddVersion);
|
||||
}
|
||||
|
||||
OrmQueryDetail detail = new OrmQueryDetail();
|
||||
@@ -142,10 +144,10 @@ public class ProfileOrigin {
|
||||
*/
|
||||
public void collectUsageInfo(NodeUsageCollector profile) {
|
||||
|
||||
if (!profile.isEmpty()) {
|
||||
ObjectGraphNode node = profile.getNode();
|
||||
//logger.info("COLLECT USAGE {}", profile.toString());
|
||||
|
||||
ProfileOriginNodeUsage nodeStats = getNodeStats(node.getPath());
|
||||
if (!profile.isEmpty()) {
|
||||
ProfileOriginNodeUsage nodeStats = getNodeStats(profile.getNode().getPath());
|
||||
nodeStats.collectUsageInfo(profile);
|
||||
}
|
||||
}
|
||||
@@ -153,9 +155,11 @@ public class ProfileOrigin {
|
||||
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, queryTuningAddVersion);
|
||||
nodeStats = new ProfileOriginNodeUsage(path);
|
||||
nodeUsageMap.put(path, nodeStats);
|
||||
}
|
||||
return nodeStats;
|
||||
|
||||
+7
-8
@@ -24,8 +24,6 @@ public class ProfileOriginNodeUsage {
|
||||
|
||||
private final String path;
|
||||
|
||||
private final boolean queryTuningAddVersion;
|
||||
|
||||
private int profileCount;
|
||||
|
||||
private int profileUsedCount;
|
||||
@@ -34,12 +32,12 @@ public class ProfileOriginNodeUsage {
|
||||
|
||||
private final Set<String> aggregateUsed = new LinkedHashSet<String>();
|
||||
|
||||
public ProfileOriginNodeUsage(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) {
|
||||
|
||||
@@ -59,6 +57,7 @@ public class ProfileOriginNodeUsage {
|
||||
}
|
||||
|
||||
for (String propName : aggregateUsed) {
|
||||
//propName = "".equals(propName) ? null : propName;
|
||||
BeanProperty beanProp = desc.getBeanPropertyFromPath(propName);
|
||||
if (beanProp == null) {
|
||||
logger.warn("AutoTune: Can't find property[" + propName + "] for " + desc.getName());
|
||||
@@ -81,7 +80,7 @@ public class ProfileOriginNodeUsage {
|
||||
}
|
||||
}
|
||||
|
||||
if ((modified || queryTuningAddVersion) && desc != null) {
|
||||
if ((modified || addVersionProperty) && desc != null) {
|
||||
BeanProperty versionProp = desc.getVersionProperty();
|
||||
if (versionProp != null) {
|
||||
pathProps.addToPath(path, versionProp.getName());
|
||||
@@ -93,7 +92,7 @@ public class ProfileOriginNodeUsage {
|
||||
/**
|
||||
* Collect usage from a node.
|
||||
*/
|
||||
public void collectUsageInfo(NodeUsageCollector profile) {
|
||||
protected void collectUsageInfo(NodeUsageCollector profile) {
|
||||
|
||||
synchronized (monitor) {
|
||||
|
||||
|
||||
@@ -21,6 +21,18 @@ public class TunedQueryInfo implements Serializable {
|
||||
this.tunedDetail = tunedDetail;
|
||||
}
|
||||
|
||||
public TunedQueryInfo(OrmQueryDetail tunedDetail) {
|
||||
this.origin = null;
|
||||
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.
|
||||
*
|
||||
@@ -47,7 +59,7 @@ public class TunedQueryInfo implements Serializable {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return origin.getBeanType() + " " + origin.getKey() + " " + tunedDetail;
|
||||
return tunedDetail.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -236,6 +236,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
private void configureServerPlugins() {
|
||||
|
||||
autoTuneService.startup();
|
||||
|
||||
for (SpiServerPlugin plugin : serverPlugins) {
|
||||
plugin.configure(this);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
@@ -83,25 +84,48 @@ public class OrmQueryDetail implements Serializable {
|
||||
* Return true if equal in terms of autofetch (select and joins).
|
||||
*/
|
||||
public boolean isAutoTuneEqual(OrmQueryDetail otherDetail) {
|
||||
return autofetchPlanHash() == otherDetail.autofetchPlanHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the hash for the query plan.
|
||||
*/
|
||||
private int autofetchPlanHash() {
|
||||
|
||||
int hc = (baseProps == null ? 1 : baseProps.autofetchPlanHash());
|
||||
|
||||
if (fetchPaths != null) {
|
||||
for (OrmQueryProperties p : fetchPaths.values()) {
|
||||
hc = hc * 31 + p.autofetchPlanHash();
|
||||
if (!isSame(baseProps, otherDetail.baseProps)) {
|
||||
return false;
|
||||
}
|
||||
if (fetchPaths == null) {
|
||||
return otherDetail.fetchPaths == null;
|
||||
}
|
||||
Set<Map.Entry<String, OrmQueryProperties>> entries = fetchPaths.entrySet();
|
||||
for (Map.Entry<String, OrmQueryProperties> entry : entries) {
|
||||
OrmQueryProperties chunk = otherDetail.getChunk(entry.getKey(), false);
|
||||
if (!isSame(entry.getValue(), chunk)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return hc;
|
||||
return true;
|
||||
//return autofetchPlanHash() == otherDetail.autofetchPlanHash();
|
||||
}
|
||||
|
||||
private boolean isSame(OrmQueryProperties p1, OrmQueryProperties p2) {
|
||||
if (p1 == null) {
|
||||
return p2 == null;
|
||||
}
|
||||
return p1.isSame(p2);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Calculate the hash for the query plan.
|
||||
// */
|
||||
// private int autofetchPlanHash() {
|
||||
//
|
||||
// int hc = (baseProps == null ? 1 : baseProps.autofetchPlanHash());
|
||||
//
|
||||
// if (fetchPaths != null) {
|
||||
// for (OrmQueryProperties p : fetchPaths.values()) {
|
||||
// hc = hc * 31 + p.autofetchPlanHash();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return hc;
|
||||
// }
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (baseProps != null) {
|
||||
|
||||
@@ -27,10 +27,11 @@ public class OrmQueryDetailParser {
|
||||
this.parser = new SimpleTextParser(oql);
|
||||
}
|
||||
|
||||
public void parse() throws PersistenceException {
|
||||
public OrmQueryDetail parse() throws PersistenceException {
|
||||
|
||||
parser.nextWord();
|
||||
processInitial();
|
||||
return detail;
|
||||
}
|
||||
|
||||
protected void assign(DefaultOrmQuery<?> query) {
|
||||
@@ -42,7 +43,9 @@ public class OrmQueryDetailParser {
|
||||
}
|
||||
|
||||
private void processInitial() {
|
||||
if (parser.isMatch("find")) {
|
||||
if (parser.isMatch("select")) {
|
||||
readSelect();
|
||||
} else if (parser.isMatch("find")) {
|
||||
OrmQueryProperties props = readFindFetch();
|
||||
detail.setBase(props);
|
||||
} else {
|
||||
@@ -149,6 +152,19 @@ public class OrmQueryDetailParser {
|
||||
}
|
||||
}
|
||||
|
||||
private void readSelect() {
|
||||
String path = null;
|
||||
String props = parser.nextWord();
|
||||
if (props.startsWith("(")) {
|
||||
props = props.substring(1, props.length() - 1);
|
||||
OrmQueryProperties base = new OrmQueryProperties(path, props);
|
||||
detail.setBase(base);
|
||||
parser.nextWord();
|
||||
} else {
|
||||
process();
|
||||
}
|
||||
}
|
||||
|
||||
private OrmQueryProperties readFindFetch() {
|
||||
|
||||
boolean readAlias = false;
|
||||
|
||||
@@ -573,4 +573,11 @@ public class OrmQueryProperties implements Serializable {
|
||||
|
||||
return Collections.unmodifiableSet(set);
|
||||
}
|
||||
|
||||
public boolean isSame(OrmQueryProperties p2) {
|
||||
if (included == null) {
|
||||
return p2.included == null;
|
||||
}
|
||||
return included.equals(p2.included);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user