mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#1345 - Refactor io.ebean.meta.MetaInfoManager API (query execution metrics). Breaking change for people collecting query execution metrics.
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user