#1663 - AutoTune - merge profiling such that "select (orderDate) fetch customer (id)" instead merges to "select (orderDate,customer)"

This commit is contained in:
rob bygrave
2019-03-29 00:19:21 +13:00
parent e417b3cde2
commit 4971db54c8
7 changed files with 184 additions and 34 deletions
@@ -103,8 +103,7 @@ public class ProfileManager implements ProfilingListener {
private ProfileOrigin getProfileOrigin(ObjectGraphOrigin originQueryPoint) {
synchronized (monitor) {
ProfileOrigin stats = profileMap.computeIfAbsent(originQueryPoint.getKey(), k -> new ProfileOrigin(originQueryPoint, queryTuningAddVersion, profilingBase, profilingRate));
return stats;
return profileMap.computeIfAbsent(originQueryPoint.getKey(), k -> new ProfileOrigin(originQueryPoint, queryTuningAddVersion, profilingBase, profilingRate));
}
}
@@ -98,7 +98,7 @@ public class ProfileOrigin {
}
}
private OrmQueryDetail buildDetail(BeanDescriptor<?> rootDesc) {
OrmQueryDetail buildDetail(BeanDescriptor<?> rootDesc) {
PathProperties pathProps = new PathProperties();
for (ProfileOriginNodeUsage statsNode : nodeUsageMap.values()) {
@@ -107,8 +107,7 @@ public class ProfileOrigin {
OrmQueryDetail detail = new OrmQueryDetail();
Collection<Props> pathProperties = pathProps.getPathProps();
for (Props props : pathProperties) {
for (Props props : pathProps.getPathProps()) {
if (!props.isEmpty()) {
detail.fetch(props.getPath(), props.getPropertiesAsString(), null);
}
@@ -151,12 +150,8 @@ public class ProfileOrigin {
* 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);
getNodeStats(profile.getNode().getPath()).collectUsageInfo(profile);
}
}
@@ -55,23 +55,27 @@ public class ProfileOriginNodeUsage {
}
}
BeanProperty toOneIdProperty = null;
boolean addedToPath = false;
for (String propName : aggregateUsed) {
BeanProperty beanProp = desc.findPropertyFromPath(propName);
if (beanProp == null) {
logger.warn("AutoTune: Can't find property[" + propName + "] for " + desc.getName());
} else {
if (beanProp instanceof BeanPropertyAssoc<?>) {
BeanPropertyAssoc<?> assocProp = (BeanPropertyAssoc<?>) beanProp;
String targetIdProp = assocProp.getTargetIdProperty();
String manyPath = SplitName.add(path, assocProp.getName());
pathProps.addToPath(manyPath, targetIdProp);
if (beanProp.isId()) {
// remember and maybe add ToOne property to parent path
toOneIdProperty = beanProp;
} else if (beanProp instanceof BeanPropertyAssoc<?>) {
// intentionally skip
} else {
//noinspection StatementWithEmptyBody
if (beanProp.isLob() && !beanProp.isFetchEager()) {
// AutoTune will not include Lob's marked FetchLazy
// (which is the default for Lob's so typical).
} else {
addedToPath = true;
pathProps.addToPath(path, beanProp.getName());
}
}
@@ -81,9 +85,16 @@ public class ProfileOriginNodeUsage {
if ((modified || addVersionProperty) && desc != null) {
BeanProperty versionProp = desc.getVersionProperty();
if (versionProp != null) {
addedToPath = true;
pathProps.addToPath(path, versionProp.getName());
}
}
if (toOneIdProperty != null && !addedToPath) {
// add ToOne property to parent path
ElPropertyValue assocOne = rootDesc.getElGetValue(path);
pathProps.addToPath(SplitName.parent(path), assocOne.getName());
}
}
}
@@ -0,0 +1,143 @@
package io.ebeaninternal.server.autotune.service;
import io.ebean.BaseTestCase;
import io.ebean.DB;
import io.ebean.bean.NodeUsageCollector;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.bean.ObjectGraphOrigin;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
import org.junit.Test;
import org.tests.model.basic.Order;
import org.tests.model.basic.ResetBasicData;
import static org.assertj.core.api.Assertions.assertThat;
public class ProfileOriginTest extends BaseTestCase {
private BeanDescriptor<Order> desc = getBeanDescriptor(Order.class);
@Test
public void buildDetail() {
NodeUsageCollector c = node("customer");
c.addUsed("id");
c.addUsed("name");
ProfileOrigin po = new ProfileOrigin(null, false, 1, 1);
po.collectUsageInfo(c);
OrmQueryDetail detail = po.buildDetail(desc);
assertThat(detail.asString().trim()).isEqualTo("fetch customer (name)");
}
@Test
public void buildDetail_selectFetch() {
NodeUsageCollector c = node("customer");
c.addUsed("id");
c.addUsed("name");
ProfileOrigin po = new ProfileOrigin(null, false, 1, 1);
po.collectUsageInfo(c);
c = node(null);
c.addUsed("orderDate");
po.collectUsageInfo(c);
OrmQueryDetail detail = po.buildDetail(desc);
assertThat(detail.asString().trim()).isEqualTo("select (orderDate) fetch customer (name)");
}
@Test
public void buildDetail_expect_mergeFetchToSelect() {
NodeUsageCollector c = node("customer");
c.addUsed("id");
ProfileOrigin po = new ProfileOrigin(null, false, 1, 1);
po.collectUsageInfo(c);
c = node(null);
c.addUsed("orderDate");
po.collectUsageInfo(c);
OrmQueryDetail detail = po.buildDetail(desc);
assertThat(detail.asString().trim()).isEqualTo("select (orderDate,customer)");
}
@Test
public void buildDetail_expect_mergeFetchToParentFetch() {
ProfileOrigin po = new ProfileOrigin(null, false, 1, 1);
NodeUsageCollector c = node(null);
c.addUsed("orderDate");
c.addUsed("customer");
po.collectUsageInfo(c);
c = node("customer");
c.addUsed("billingAddress");
po.collectUsageInfo(c);
c = node("customer.billingAddress");
c.addUsed("id");
po.collectUsageInfo(c);
OrmQueryDetail detail = po.buildDetail(desc);
assertThat(detail.asString().trim()).isEqualTo("select (orderDate) fetch customer (billingAddress)");
}
@Test
public void buildDetail_expect_mergeMulit() {
ProfileOrigin po = new ProfileOrigin(null, false, 1, 1);
//fetch details (id,orderQty,shipQty,unitPrice)
NodeUsageCollector c = node(null);
c.addUsed("details");
po.collectUsageInfo(c);
c = node("details");
c.addUsed("id");
c.addUsed("orderQty");
c.addUsed("shipQty");
c.addUsed("unitPrice");
c.addUsed("product");
po.collectUsageInfo(c);
//fetch details.product (id,name)
c = node("details.product");
c.addUsed("id");
c.addUsed("name");
po.collectUsageInfo(c);
OrmQueryDetail detail = po.buildDetail(desc);
assertThat(detail.asString().trim()).isEqualTo("fetch details (orderQty,shipQty,unitPrice) fetch details.product (name)");
}
private NodeUsageCollector node(String path) {
ObjectGraphNode node = new ObjectGraphNode((ObjectGraphOrigin)null, path);
return new NodeUsageCollector(node, null);
}
@Test
public void testQueries() {
ResetBasicData.reset();
//DB.createQuery(Order.class, "select (orderDate) fetch customer (billingAddress)").findList();
// we prefer this first query other the second one
DB.createQuery(Order.class, "select (orderDate,customer)").findList();
DB.createQuery(Order.class, "select (orderDate) fetch customer (id)").findList();
}
}
@@ -1,14 +1,14 @@
package org.tests.query.autotune;
import io.ebean.BaseTestCase;
import io.ebean.Ebean;
import io.ebean.DB;
import org.junit.Ignore;
import org.junit.Test;
import org.tests.model.basic.Address;
import org.tests.model.basic.Customer;
import org.tests.model.basic.Order;
import org.tests.model.basic.OrderDetail;
import org.tests.model.basic.ResetBasicData;
import org.junit.Ignore;
import org.junit.Test;
import java.util.List;
import java.util.Random;
@@ -49,16 +49,17 @@ public class TestAutoTuneProfiling extends BaseTestCase {
private void execute() {
useOrderDate();
useOrderDateCustomerName();
useLots();
// useOrderDate();
// useOrderDateCustomerName();
// useLots();
useLotUntuned();
}
private Order findById(long id) {
return Ebean.find(Order.class)
return DB.find(Order.class)
.select("status, orderDate, shipDate")
.setId(id)
.setUseCache(false)
.findOne();
}
@@ -99,19 +100,19 @@ public class TestAutoTuneProfiling extends BaseTestCase {
detail.getUnitPrice();
}
Customer customer = order.getCustomer();
customer.getName();
Address billingAddress = customer.getBillingAddress();
if (billingAddress != null) {
billingAddress.getCity();
billingAddress.getLine1();
billingAddress.getLine2();
}
// Customer customer = order.getCustomer();
// customer.getName();
// Address billingAddress = customer.getBillingAddress();
// if (billingAddress != null) {
// billingAddress.getCity();
// billingAddress.getLine1();
// billingAddress.getLine2();
// }
}
private static void collectUsage() {
Ebean.getDefaultServer().getAutoTune().collectProfiling();
DB.getDefault().getAutoTune().collectProfiling();
}
}
+1
View File
@@ -13,6 +13,7 @@ ebean.encryptKeyManager=org.tests.basic.encrypt.BasicEncyptKeyManager
#ebean.disableL2Cache=true
#ebean.autoTune.queryTuning=true
#ebean.autoTune.mode=DEFAULT_ON
#ebean.autoTune.profiling=true
#ebean.autoTune.profilingUpdateFrequency=5
+3 -3
View File
@@ -82,9 +82,9 @@
<!--<logger name="org.avaje.docker" level="TRACE"/>-->
<!--<logger name="io.ebean.DDL" level="DEBUG"/>-->
<!--<logger name="io.ebean.SQL" level="TRACE"/>-->
<!--<logger name="io.ebean.TXN" level="TRACE"/>-->
<!--<logger name="io.ebean.SUM" level="TRACE"/>-->
<logger name="io.ebean.SQL" level="TRACE"/>
<logger name="io.ebean.TXN" level="TRACE"/>
<logger name="io.ebean.SUM" level="TRACE"/>
<!--<logger name="io.ebean.cache.TABLEMOD" level="TRACE"/>-->