diff --git a/src/main/java/io/ebeaninternal/server/autotune/service/ProfileManager.java b/src/main/java/io/ebeaninternal/server/autotune/service/ProfileManager.java index 183007d0e..d89da0073 100644 --- a/src/main/java/io/ebeaninternal/server/autotune/service/ProfileManager.java +++ b/src/main/java/io/ebeaninternal/server/autotune/service/ProfileManager.java @@ -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)); } } diff --git a/src/main/java/io/ebeaninternal/server/autotune/service/ProfileOrigin.java b/src/main/java/io/ebeaninternal/server/autotune/service/ProfileOrigin.java index 0e3e9c1a7..5f119d326 100644 --- a/src/main/java/io/ebeaninternal/server/autotune/service/ProfileOrigin.java +++ b/src/main/java/io/ebeaninternal/server/autotune/service/ProfileOrigin.java @@ -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 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); } } diff --git a/src/main/java/io/ebeaninternal/server/autotune/service/ProfileOriginNodeUsage.java b/src/main/java/io/ebeaninternal/server/autotune/service/ProfileOriginNodeUsage.java index 9b9ebc50c..be6d840b8 100644 --- a/src/main/java/io/ebeaninternal/server/autotune/service/ProfileOriginNodeUsage.java +++ b/src/main/java/io/ebeaninternal/server/autotune/service/ProfileOriginNodeUsage.java @@ -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()); + } } } diff --git a/src/test/java/io/ebeaninternal/server/autotune/service/ProfileOriginTest.java b/src/test/java/io/ebeaninternal/server/autotune/service/ProfileOriginTest.java new file mode 100644 index 000000000..4660165fd --- /dev/null +++ b/src/test/java/io/ebeaninternal/server/autotune/service/ProfileOriginTest.java @@ -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 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(); + + } +} diff --git a/src/test/java/org/tests/query/autotune/TestAutoTuneProfiling.java b/src/test/java/org/tests/query/autotune/TestAutoTuneProfiling.java index 4b4c7005c..6f1a020fd 100644 --- a/src/test/java/org/tests/query/autotune/TestAutoTuneProfiling.java +++ b/src/test/java/org/tests/query/autotune/TestAutoTuneProfiling.java @@ -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(); } } diff --git a/src/test/resources/ebean.properties b/src/test/resources/ebean.properties index cd6b5c07c..65af4407e 100644 --- a/src/test/resources/ebean.properties +++ b/src/test/resources/ebean.properties @@ -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 diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index cab30c58a..7236805d5 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -82,9 +82,9 @@ - - - + + +