#1345 - Refactor io.ebean.meta.MetaInfoManager API (query execution metrics). Breaking change for people collecting query execution metrics.

This commit is contained in:
Rob Bygrave
2018-03-10 02:51:51 +13:00
parent eda6350d89
commit 85bfe3d03e
79 changed files with 928 additions and 466 deletions
@@ -3,8 +3,12 @@ package io.ebeaninternal.server.query;
import io.ebean.ProfileLocation;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.config.dbplatform.SqlLimitResponse;
import io.ebean.meta.MetricType;
import io.ebeaninternal.api.CQueryPlanKey;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.metric.MetricFactory;
import io.ebeaninternal.metric.TimedMetric;
import io.ebeaninternal.server.core.OrmQueryRequest;
import io.ebeaninternal.server.core.timezone.DataTimeZone;
import io.ebeaninternal.server.deploy.BeanProperty;
@@ -51,6 +55,10 @@ public class CQueryPlan {
private final ProfileLocation profileLocation;
private final String location;
private final String label;
private final CQueryPlanKey planKey;
private final boolean rawSql;
@@ -90,9 +98,12 @@ public class CQueryPlan {
this.dataTimeZone = server.getDataTimeZone();
this.beanType = request.getBeanDescriptor().getBeanType();
this.planKey = request.getQueryPlanKey();
this.profileLocation = request.getQuery().getProfileLocation();
this.autoTuned = request.getQuery().isAutoTuned();
this.asOfTableCount = request.getQuery().getAsOfTableCount();
SpiQuery<?> query = request.getQuery();
this.profileLocation = query.getProfileLocation();
this.label = query.getLabel();
this.location = location();
this.autoTuned = query.isAutoTuned();
this.asOfTableCount = query.getAsOfTableCount();
this.sql = sqlRes.getSql();
this.rowNumberIncluded = sqlRes.isIncludesRowNumberColumn();
this.sqlTree = sqlTree;
@@ -105,13 +116,15 @@ public class CQueryPlan {
/**
* Create a query plan for a raw sql query.
*/
CQueryPlan(OrmQueryRequest<?> request, String sql, SqlTree sqlTree,
boolean rawSql, boolean rowNumberIncluded, String logWhereSql) {
CQueryPlan(OrmQueryRequest<?> request, String sql, SqlTree sqlTree, boolean rawSql, boolean rowNumberIncluded, String logWhereSql) {
this.server = request.getServer();
this.dataTimeZone = server.getDataTimeZone();
this.beanType = request.getBeanDescriptor().getBeanType();
this.profileLocation = request.getQuery().getProfileLocation();
SpiQuery<?> query = request.getQuery();
this.profileLocation = query.getProfileLocation();
this.label = query.getLabel();
this.location = location();
this.planKey = buildPlanKey(sql, rawSql, rowNumberIncluded, logWhereSql);
this.autoTuned = false;
this.asOfTableCount = 0;
@@ -124,6 +137,9 @@ public class CQueryPlan {
this.stats = new CQueryPlanStats(this, server.isCollectQueryOrigins());
}
private String location() {
return (profileLocation == null) ? "" : profileLocation.shortDescription();
}
private CQueryPlanKey buildPlanKey(String sql, boolean rawSql, boolean rowNumberIncluded, String logWhereSql) {
@@ -143,6 +159,14 @@ public class CQueryPlan {
return profileLocation;
}
public String getLabel() {
return label;
}
public String getLocation() {
return location;
}
public DataReader createDataReader(ResultSet rset) {
return new RsetDataReader(dataTimeZone, rset);
}
@@ -240,17 +264,13 @@ public class CQueryPlan {
}
}
/**
* Return a copy of the current query statistics.
*/
public Snapshot getSnapshot(boolean reset) {
return stats.getSnapshot(reset);
}
/**
* Return the current query statistics.
*/
public CQueryPlanStats getQueryStats() {
return stats;
}
/**
* Return the time this query plan was last used.
*/
@@ -268,4 +288,8 @@ public class CQueryPlan {
public boolean isEmptyStats() {
return stats.isEmpty();
}
public TimedMetric createTimedMetric() {
return MetricFactory.get().createTimedMetric(MetricType.ORM, label);
}
}
@@ -2,16 +2,17 @@ package io.ebeaninternal.server.query;
import io.ebean.ProfileLocation;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.meta.MetaQueryPlanOriginCount;
import io.ebean.meta.MetaQueryPlanStatistic;
import io.ebean.meta.MetaOrmQueryMetric;
import io.ebean.meta.MetaOrmQueryOrigin;
import io.ebean.meta.MetricType;
import io.ebeaninternal.metric.TimedMetric;
import io.ebeaninternal.metric.TimedMetricStats;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;
/**
@@ -21,15 +22,7 @@ public final class CQueryPlanStats {
private final CQueryPlan queryPlan;
private final LongAdder count = new LongAdder();
private final LongAdder totalTime = new LongAdder();
private final LongAdder totalBeans = new LongAdder();
private final LongAccumulator maxTime = new LongAccumulator(Math::max, Long.MIN_VALUE);
private final AtomicLong startTime = new AtomicLong(System.currentTimeMillis());
private final TimedMetric timedMetric;
private long lastQueryTime;
@@ -39,16 +32,16 @@ public final class CQueryPlanStats {
* Construct for a given query plan.
*/
CQueryPlanStats(CQueryPlan queryPlan, boolean collectQueryOrigins) {
this.queryPlan = queryPlan;
this.origins = !collectQueryOrigins ? null : new ConcurrentHashMap<>();
this.timedMetric = queryPlan.createTimedMetric();
}
/**
* Return true if there are no statistics collected since the last reset.
*/
public boolean isEmpty() {
return count.sum() == 0;
return timedMetric.isEmpty();
}
/**
@@ -56,10 +49,7 @@ public final class CQueryPlanStats {
*/
public void add(long loadedBeanCount, long timeMicros, ObjectGraphNode objectGraphNode) {
count.increment();
totalBeans.add(loadedBeanCount);
totalTime.add(timeMicros);
maxTime.accumulate(timeMicros);
timedMetric.add(timeMicros, loadedBeanCount);
// not safe but should be atomic
lastQueryTime = System.currentTimeMillis();
@@ -82,14 +72,7 @@ public final class CQueryPlanStats {
* Reset the internal statistics counters.
*/
public void reset() {
// Racey but near enough for our purposes as we don't want locks
count.reset();
totalBeans.reset();
totalTime.reset();
maxTime.reset();
startTime.set(System.currentTimeMillis());
timedMetric.reset();
if (origins != null) {
for (LongAdder counter : origins.values()) {
counter.reset();
@@ -109,25 +92,20 @@ public final class CQueryPlanStats {
*/
Snapshot getSnapshot(boolean reset) {
List<MetaQueryPlanOriginCount> origins = getOrigins(reset);
// not guaranteed to be consistent due to time gaps between getting each value out of LongAdders but can live with that
// relative to the cost of making sure count and totalTime etc are all guaranteed to be consistent
if (reset) {
return new Snapshot(queryPlan, count.sumThenReset(), totalTime.sumThenReset(), totalBeans.sumThenReset(), maxTime.getThenReset(), startTime.getAndSet(System.currentTimeMillis()), lastQueryTime, origins);
}
return new Snapshot(queryPlan, count.sum(), totalTime.sum(), totalBeans.sum(), maxTime.get(), startTime.get(), lastQueryTime, origins);
TimedMetricStats collect = timedMetric.collect(reset);
List<MetaOrmQueryOrigin> origins = getOrigins(reset);
return new Snapshot(queryPlan, collect, lastQueryTime, origins);
}
/**
* Return the list/snapshot of the origins and their counter value.
*/
private List<MetaQueryPlanOriginCount> getOrigins(boolean reset) {
private List<MetaOrmQueryOrigin> getOrigins(boolean reset) {
if (origins == null) {
return Collections.emptyList();
}
List<MetaQueryPlanOriginCount> list = new ArrayList<>(origins.size());
List<MetaOrmQueryOrigin> list = new ArrayList<>(origins.size());
for (Entry<ObjectGraphNode, LongAdder> entry : origins.entrySet()) {
if (reset) {
@@ -142,7 +120,7 @@ public final class CQueryPlanStats {
/**
* Snapshot of the origin ObjectGraphNode and counter value.
*/
private static class OriginSnapshot implements MetaQueryPlanOriginCount {
private static class OriginSnapshot implements MetaOrmQueryOrigin {
private final ObjectGraphNode objectGraphNode;
private final long count;
@@ -170,70 +148,83 @@ public final class CQueryPlanStats {
/**
* A snapshot of the current statistics for a query plan.
*/
public static class Snapshot implements MetaQueryPlanStatistic {
static class Snapshot implements MetaOrmQueryMetric {
private final CQueryPlan queryPlan;
private final long count;
private final long totalTime;
private final long totalBeans;
private final long maxTime;
private final long startTime;
private final TimedMetricStats metrics;
private final long lastQueryTime;
private final List<MetaQueryPlanOriginCount> origins;
Snapshot(CQueryPlan queryPlan, long count, long totalTime, long totalBeans, long maxTime, long startTime, long lastQueryTime,
List<MetaQueryPlanOriginCount> origins) {
private final List<MetaOrmQueryOrigin> origins;
Snapshot(CQueryPlan queryPlan, TimedMetricStats metrics, long lastQueryTime, List<MetaOrmQueryOrigin> origins) {
this.queryPlan = queryPlan;
this.count = count;
this.totalTime = totalTime;
this.totalBeans = totalBeans;
this.maxTime = maxTime;
this.startTime = startTime;
this.metrics = metrics;
this.lastQueryTime = lastQueryTime;
this.origins = origins;
}
@Override
public String toString() {
ProfileLocation profileLocation = queryPlan.getProfileLocation();
String loc = (profileLocation == null) ? "" : profileLocation.shortDescription();
return "location:" + loc + " count:" + count + " time:" + totalTime + " maxTime:" + maxTime + " beans:" + totalBeans + " sql:" + getSql();
return "location:" + getLocation() + " metrics:" + metrics + " sql:" + getSql();
}
@Override
public Class<?> getBeanType() {
public MetricType getMetricType() {
return MetricType.ORM;
}
@Override
public Class<?> getType() {
return queryPlan.getBeanType();
}
@Override
public String getLabel() {
return queryPlan.getLabel();
}
@Override
public String getName() {
return queryPlan.getLabel();
}
@Override
public String getLocation() {
return queryPlan.getLocation();
}
@Override
public ProfileLocation getProfileLocation() {
return queryPlan.getProfileLocation();
}
@Override
public long getExecutionCount() {
return count;
public long getBeanCount() {
return metrics.getBeanCount();
}
@Override
public long getTotalTimeMicros() {
return totalTime;
public long getCount() {
return metrics.getCount();
}
@Override
public long getTotalLoadedBeans() {
return totalBeans;
public long getTotal() {
return metrics.getTotal();
}
@Override
public long getMaxTimeMicros() {
return maxTime;
public long getMax() {
return metrics.getMax();
}
@Override
public long getCollectionStart() {
return startTime;
public long getMean() {
return metrics.getMean();
}
@Override
public long getStartTime() {
return metrics.getStartTime();
}
@Override
@@ -257,17 +248,7 @@ public final class CQueryPlanStats {
}
@Override
public long getAvgTimeMicros() {
return count < 1 ? 0 : totalTime / count;
}
@Override
public long getAvgLoadedBeans() {
return count < 1 ? 0 : totalBeans / count;
}
@Override
public List<MetaQueryPlanOriginCount> getOrigins() {
public List<MetaOrmQueryOrigin> getOrigins() {
return origins;
}
@@ -1,32 +0,0 @@
package io.ebeaninternal.server.query;
import io.ebean.meta.MetaQueryPlanStatistic;
import java.util.ArrayList;
import java.util.List;
/**
* Helper to collect query plan execution statistics.
*/
public class CQueryPlanStatsCollector {
private final boolean reset;
List<MetaQueryPlanStatistic> list = new ArrayList<>();
public CQueryPlanStatsCollector(boolean reset) {
this.reset = reset;
}
public boolean isReset() {
return reset;
}
public void add(MetaQueryPlanStatistic planStatistic) {
list.add(planStatistic);
}
public List<MetaQueryPlanStatistic> getList() {
return list;
}
}
@@ -1,7 +1,11 @@
package io.ebeaninternal.server.query;
import io.ebean.SqlRow;
import io.ebean.meta.MetricType;
import io.ebean.meta.MetricVisitor;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.metric.MetricFactory;
import io.ebeaninternal.metric.TimedMetricMap;
import io.ebeaninternal.server.core.Message;
import io.ebeaninternal.server.core.RelationalQueryEngine;
import io.ebeaninternal.server.core.RelationalQueryRequest;
@@ -25,10 +29,23 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
private final boolean binaryOptimizedUUID;
private final TimedMetricMap timedMetricMap;
public DefaultRelationalQueryEngine(Binder binder, String dbTrueValue, boolean binaryOptimizedUUID) {
this.binder = binder;
this.dbTrueValue = dbTrueValue == null ? "true" : dbTrueValue;
this.binaryOptimizedUUID = binaryOptimizedUUID;
this.timedMetricMap = MetricFactory.get().createTimedMetricMap(MetricType.SQL, "sql.query.");
}
@Override
public void collect(String label, long exeMicros, int rows) {
timedMetricMap.add(label, exeMicros, rows);
}
@Override
public void visitMetrics(MetricVisitor visitor) {
timedMetricMap.visit(visitor);
}
@Override