#1873 - Refactor metrics reporting and collection, simplify removing MetricOrmQueryNode, MetricOrmQueryOrigin (#1874)

* #1873 - Refactor metrics reporting and collection, simplify removing MetricOrmQueryNode, MetricOrmQueryOrigin

* #1873 - fix test
This commit is contained in:
Rob Bygrave
2019-11-29 23:35:21 +13:00
committed by GitHub
parent 269f2de333
commit a04594540c
29 changed files with 51 additions and 663 deletions
@@ -28,7 +28,6 @@ import io.ebean.event.changelog.ChangeLogPrepare;
import io.ebean.event.changelog.ChangeLogRegister;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.meta.MetaInfoManager;
import io.ebean.migration.MigrationRunner;
import io.ebean.util.StringHelper;
@@ -415,10 +414,6 @@ public class ServerConfig {
private ServerCachePlugin serverCachePlugin;
private boolean collectQueryStatsByNode = true;
private boolean collectQueryOrigins = true;
/**
* The default PersistenceContextScope used if one is not explicitly set on a query.
*/
@@ -2418,52 +2413,6 @@ public class ServerConfig {
this.updateAllPropertiesInBatch = updateAllPropertiesInBatch;
}
/**
* Return true if query statistics should be collected by ObjectGraphNode.
*/
public boolean isCollectQueryStatsByNode() {
return collectQueryStatsByNode;
}
/**
* Set to true to collection query execution statistics by ObjectGraphNode.
* <p>
* These statistics can be used to highlight code/query 'origin points' that result in lots of lazy loading.
* </p>
* <p>
* It is considered safe/fine to have this set to true for production.
* </p>
* <p>
* This information can be later retrieved via {@link MetaInfoManager}.
* </p>
*
* @see MetaInfoManager
*/
public void setCollectQueryStatsByNode(boolean collectQueryStatsByNode) {
this.collectQueryStatsByNode = collectQueryStatsByNode;
}
/**
* Return true if query plans should also collect their 'origins'. This means for a given query plan you
* can identify the code/origin points where this query resulted from including lazy loading origins.
*/
public boolean isCollectQueryOrigins() {
return collectQueryOrigins;
}
/**
* Set to true if query plans should collect their 'origin' points. This means for a given query plan you
* can identify the code/origin points where this query resulted from including lazy loading origins.
* <p>
* This information can be later retrieved via {@link MetaInfoManager}.
* </p>
*
* @see MetaInfoManager
*/
public void setCollectQueryOrigins(boolean collectQueryOrigins) {
this.collectQueryOrigins = collectQueryOrigins;
}
/**
* Returns the resource directory.
*/
@@ -2921,9 +2870,6 @@ public class ServerConfig {
String packagesProp = p.get("search.packages", p.get("packages", null));
packages = getSearchList(packagesProp, packages);
collectQueryStatsByNode = p.getBoolean("collectQueryStatsByNode", collectQueryStatsByNode);
collectQueryOrigins = p.getBoolean("collectQueryOrigins", collectQueryOrigins);
skipCacheAfterWrite = p.getBoolean("skipCacheAfterWrite", skipCacheAfterWrite);
updateAllPropertiesInBatch = p.getBoolean("updateAllPropertiesInBatch", updateAllPropertiesInBatch);
@@ -9,9 +9,8 @@ import java.util.List;
public class BasicMetricVisitor extends AbstractMetricVisitor implements ServerMetrics {
private final List<MetaTimedMetric> timed = new ArrayList<>();
private final List<MetaQueryMetric> dtoQuery = new ArrayList<>();
private final List<MetaOrmQueryMetric> ormQuery = new ArrayList<>();
private final List<MetaCountMetric> countMetrics = new ArrayList<>();
private final List<MetaQueryMetric> query = new ArrayList<>();
private final List<MetaCountMetric> count = new ArrayList<>();
/**
* Construct to reset and collect everything.
@@ -27,33 +26,19 @@ public class BasicMetricVisitor extends AbstractMetricVisitor implements ServerM
super(reset, collectTransactionMetrics, collectQueryMetrics, collectL2Metrics);
}
/**
* Return timed metrics for Transactions, labelled SqlQuery, labelled SqlUpdate.
*/
@Override
public List<MetaTimedMetric> getTimedMetrics() {
return timed;
}
/**
* Return the DTO query metrics.
*/
@Override
public List<MetaQueryMetric> getDtoQueryMetrics() {
return dtoQuery;
}
/**
* Return the ORM query metrics.
*/
@Override
public List<MetaOrmQueryMetric> getOrmQueryMetrics() {
return ormQuery;
public List<MetaQueryMetric> getQueryMetrics() {
return query;
}
@Override
public List<MetaCountMetric> getCountMetrics() {
return countMetrics;
return count;
}
@Override
@@ -63,16 +48,11 @@ public class BasicMetricVisitor extends AbstractMetricVisitor implements ServerM
@Override
public void visitQuery(MetaQueryMetric metric) {
dtoQuery.add(metric);
}
@Override
public void visitOrmQuery(MetaOrmQueryMetric metric) {
ormQuery.add(metric);
query.add(metric);
}
@Override
public void visitCount(MetaCountMetric metric) {
countMetrics.add(metric);
count.add(metric);
}
}
@@ -54,16 +54,4 @@ public interface MetaInfoManager {
*/
void resetAllMetrics();
/**
* Collect and return the ObjectGraphNode statistics.
* <p>
* These show query executions based on an origin point and relative path.
* This is used to look at the amount of lazy loading occurring for a given
* query origin point and highlight potential for tuning a query.
* </p>
*
* @param reset Set to true to reset the underlying statistics after collection.
*/
List<MetaOrmQueryNode> collectNodeStatistics(boolean reset);
}
@@ -1,30 +0,0 @@
package io.ebean.meta;
import java.util.List;
/**
* Query execution statistics for Orm queries.
*/
public interface MetaOrmQueryMetric extends MetaQueryMetric {
/**
* Return true if this query plan was tuned by AutoTune.
*/
boolean isAutoTuned();
/**
* Return the time of the last query executed using this plan.
*/
long getLastQueryTime();
/**
* Return the 'origin' points and paths that resulted in the query being
* executed and the associated number of times the query was executed via that
* path.
* <p>
* This includes direct and lazy loading paths.
* </p>
*/
List<MetaOrmQueryOrigin> getOrigins();
}
@@ -1,39 +0,0 @@
package io.ebean.meta;
import io.ebean.bean.ObjectGraphNode;
/**
* Statistics for query execution based on object graph origin and paths.
* <p>
* These statistics can be used to identify origin queries that result in lots
* of lazy loading.
* </p>
*/
public interface MetaOrmQueryNode {
/**
* Return the ObjectGraphNode which has the origin point and relative path.
*/
ObjectGraphNode getNode();
/**
* Return the startTime of statistics collection.
*/
long getStartTime();
/**
* Return the total count of queries executed for this node.
*/
long getCount();
/**
* Return the total time of queries executed for this node.
*/
long getTotalTime();
/**
* Return the total beans loaded by queries for this node.
*/
long getTotalBeans();
}
@@ -1,30 +0,0 @@
package io.ebean.meta;
import io.ebean.bean.ObjectGraphNode;
/**
* Holds a query 'origin' point and count for the number of queries executed for
* this 'origin'.
* <p>
* This basically points to the bit of original code and query that results in
* this query directly or via lazy loading.
* </p>
*/
public interface MetaOrmQueryOrigin {
/**
* The 'origin' and path which this query belongs to.
* <p>
* For lazy loading queries this points to the original query and associated
* navigation path that resulted in this query being executed.
* </p>
*/
ObjectGraphNode getObjectGraphNode();
/**
* The number of times a query was fired for this node since the counter was
* last reset.
*/
long getCount();
}
@@ -40,11 +40,6 @@ public interface MetricVisitor {
*/
void visitQuery(MetaQueryMetric metric);
/**
* Visit ORM query metrics.
*/
void visitOrmQuery(MetaOrmQueryMetric metric);
/**
* Visit a Counter metric.
*/
@@ -13,14 +13,9 @@ public interface ServerMetrics {
List<MetaTimedMetric> getTimedMetrics();
/**
* Return the DTO query metrics.
* Return the query metrics.
*/
List<MetaQueryMetric> getDtoQueryMetrics();
/**
* Return the ORM query metrics.
*/
List<MetaOrmQueryMetric> getOrmQueryMetrics();
List<MetaQueryMetric> getQueryMetrics();
/**
* Return the Counter metrics.
@@ -49,11 +49,6 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanLoader,
*/
void shutdownManaged();
/**
* Return true if query origins should be collected.
*/
boolean isCollectQueryOrigins();
/**
* Return true if updates in JDBC batch should include all columns if unspecified on the transaction.
*/
@@ -201,11 +196,6 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanLoader,
*/
boolean isSupportedType(java.lang.reflect.Type genericType);
/**
* Collect query statistics by ObjectGraphNode. Used for Lazy loading reporting.
*/
void collectQueryStats(ObjectGraphNode objectGraphNode, long loadedBeanCount, long timeMicros);
/**
* Return the ReadAuditLogger to use for logging all read audit events.
*/
@@ -1,94 +0,0 @@
package io.ebeaninternal.server.core;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.meta.MetaOrmQueryNode;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
/**
* Helper to collect the query execution statistics for a given node.
*/
public class CObjectGraphNodeStatistics {
private final ObjectGraphNode node;
private final LongAdder count = new LongAdder();
private final LongAdder totalTime = new LongAdder();
private final LongAdder totalBeans = new LongAdder();
private final AtomicLong startTime = new AtomicLong(System.currentTimeMillis());
CObjectGraphNodeStatistics(ObjectGraphNode node) {
this.node = node;
}
public boolean isEmpty() {
return count.sum() == 0;
}
public void add(long beanCount, long exeMicros) {
count.increment();
totalTime.add(exeMicros);
totalBeans.add(beanCount);
}
public MetaOrmQueryNode get(boolean reset) {
if (reset) {
return new Snapshot(node, startTime.getAndSet(System.currentTimeMillis()), count.sumThenReset(),
totalTime.sumThenReset(), totalBeans.sumThenReset());
} else {
return new Snapshot(node, startTime.get(), count.sum(), totalTime.sum(), totalBeans.sum());
}
}
private static class Snapshot implements MetaOrmQueryNode {
private final ObjectGraphNode node;
private final long startTime;
private final long count;
private final long totalTime;
private final long totalBeans;
public Snapshot(ObjectGraphNode node, long startTime, long count, long totalTime, long totalBeans) {
this.node = node;
this.startTime = startTime;
this.count = count;
this.totalTime = totalTime;
this.totalBeans = totalBeans;
}
@Override
public String toString() {
return node + " count[" + count + "] time[" + totalTime + "] beans[" + totalBeans + "]";
}
@Override
public ObjectGraphNode getNode() {
return node;
}
@Override
public long getStartTime() {
return startTime;
}
@Override
public long getCount() {
return count;
}
@Override
public long getTotalTime() {
return totalTime;
}
@Override
public long getTotalBeans() {
return totalBeans;
}
}
}
@@ -4,8 +4,6 @@ import io.ebean.meta.AbstractMetricVisitor;
import io.ebean.meta.BasicMetricVisitor;
import io.ebean.meta.MetaCountMetric;
import io.ebean.meta.MetaInfoManager;
import io.ebean.meta.MetaOrmQueryMetric;
import io.ebean.meta.MetaOrmQueryNode;
import io.ebean.meta.MetaQueryMetric;
import io.ebean.meta.MetaQueryPlan;
import io.ebean.meta.MetaTimedMetric;
@@ -15,7 +13,6 @@ import io.ebean.meta.QueryPlanRequest;
import io.ebean.meta.ServerMetrics;
import io.ebean.meta.ServerMetricsAsJson;
import java.util.ArrayList;
import java.util.List;
/**
@@ -66,18 +63,6 @@ public class DefaultMetaInfoManager implements MetaInfoManager {
server.visitMetrics(new ResetVisitor());
}
@Override
public List<MetaOrmQueryNode> collectNodeStatistics(boolean reset) {
List<MetaOrmQueryNode> list = new ArrayList<>();
for (CObjectGraphNodeStatistics nodeStatistics : server.objectGraphStats.values()) {
if (!nodeStatistics.isEmpty()) {
list.add(nodeStatistics.get(reset));
}
}
return list;
}
/**
* Visitor that resets the statistics but doesn't collect them.
*/
@@ -97,11 +82,6 @@ public class DefaultMetaInfoManager implements MetaInfoManager {
// ignore
}
@Override
public void visitOrmQuery(MetaOrmQueryMetric metric) {
// ignore
}
@Override
public void visitCount(MetaCountMetric metric) {
// ignore
@@ -240,19 +240,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private final boolean updateAllPropertiesInBatch;
private final boolean collectQueryOrigins;
private final boolean collectQueryStatsByNode;
private final long slowQueryMicros;
private final SlowQueryListener slowQueryListener;
/**
* Cache used to collect statistics based on ObjectGraphNode (used to highlight lazy loading origin points).
*/
protected final ConcurrentHashMap<ObjectGraphNode, CObjectGraphNodeStatistics> objectGraphStats;
/**
* Create the DefaultServer.
*/
@@ -261,7 +252,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
this.logManager = config.getLogManager();
this.dtoBeanManager = config.getDtoBeanManager();
this.serverConfig = config.getServerConfig();
this.objectGraphStats = new ConcurrentHashMap<>();
this.metaInfoManager = new DefaultMetaInfoManager(this);
this.serverCacheManager = cache;
this.databasePlatform = config.getDatabasePlatform();
@@ -282,8 +272,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
beanDescriptorManager.setEbeanServer(this);
this.updateAllPropertiesInBatch = serverConfig.isUpdateAllPropertiesInBatch();
this.collectQueryOrigins = serverConfig.isCollectQueryOrigins();
this.collectQueryStatsByNode = serverConfig.isCollectQueryStatsByNode();
this.callStackFactory = initCallStackFactory(serverConfig);
this.persister = config.createPersister(this);
@@ -352,11 +340,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return logManager;
}
@Override
public boolean isCollectQueryOrigins() {
return collectQueryOrigins;
}
@Override
public boolean isUpdateAllPropertiesInBatch() {
return updateAllPropertiesInBatch;
@@ -2357,21 +2340,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return jsonContext;
}
@Override
public void collectQueryStats(ObjectGraphNode node, long loadedBeanCount, long timeMicros) {
if (collectQueryStatsByNode) {
CObjectGraphNodeStatistics nodeStatistics = objectGraphStats.get(node);
if (nodeStatistics == null) {
// race condition here but I actually don't care too much if we miss a
// few early statistics - especially when the server is warming up etc
nodeStatistics = new CObjectGraphNodeStatistics(node);
objectGraphStats.put(node, nodeStatistics);
}
nodeStatistics.add(loadedBeanCount, timeMicros);
}
}
@Override
public void slowQueryCheck(long timeMicros, int rowCount, SpiQuery<?> query) {
if (timeMicros > slowQueryMicros && slowQueryListener != null) {
@@ -1,7 +1,6 @@
package io.ebeaninternal.server.core;
import io.ebean.meta.MetaCountMetric;
import io.ebean.meta.MetaOrmQueryMetric;
import io.ebean.meta.MetaQueryMetric;
import io.ebean.meta.MetaTimedMetric;
import io.ebean.meta.ServerMetrics;
@@ -92,23 +91,14 @@ class DumpMetrics {
}
}
List<MetaOrmQueryMetric> ormQueryMetrics = serverMetrics.getOrmQueryMetrics();
if (!ormQueryMetrics.isEmpty()) {
out("\n-- ORM queries --");
ormQueryMetrics.sort(sortBy);
for (MetaOrmQueryMetric metric : ormQueryMetrics) {
List<MetaQueryMetric> queryMetrics = serverMetrics.getQueryMetrics();
if (!queryMetrics.isEmpty()) {
out("\n-- Queries --");
queryMetrics.sort(sortBy);
for (MetaQueryMetric metric : queryMetrics) {
logQuery(metric);
}
}
List<MetaQueryMetric> dtoQueryMetrics = serverMetrics.getDtoQueryMetrics();
if (!dtoQueryMetrics.isEmpty()) {
out("\n-- DTO queries --");
dtoQueryMetrics.sort(sortBy);
for (MetaQueryMetric metric : dtoQueryMetrics) {
logDtoQuery(metric);
}
}
}
private void logCount(MetaCountMetric metric) {
@@ -123,7 +113,7 @@ class DumpMetrics {
System.out.println(sb);
}
private void logQuery(MetaOrmQueryMetric metric) {
private void logQuery(MetaQueryMetric metric) {
StringBuilder sb = new StringBuilder();
@@ -136,17 +126,6 @@ class DumpMetrics {
out(sb.toString());
}
private void logDtoQuery(MetaQueryMetric metric) {
StringBuilder sb = new StringBuilder();
appendQueryName(metric, sb);
appendCounters(metric, sb);
appendProfileAndSql(metric, sb);
out(sb.toString());
}
private void appendQueryName(MetaQueryMetric metric, StringBuilder sb) {
sb.append("query:").append(padName(metric.getName())).append(" ");
}
@@ -3,7 +3,6 @@ package io.ebeaninternal.server.core;
import io.ebean.Database;
import io.ebean.meta.MetaCountMetric;
import io.ebean.meta.MetaMetric;
import io.ebean.meta.MetaOrmQueryMetric;
import io.ebean.meta.MetaQueryMetric;
import io.ebean.meta.MetaTimedMetric;
import io.ebean.meta.MetricData;
@@ -34,8 +33,7 @@ class DumpMetricsData {
final List<MetaTimedMetric> timedMetrics = serverMetrics.getTimedMetrics();
final List<MetaCountMetric> countMetrics = serverMetrics.getCountMetrics();
final List<MetaOrmQueryMetric> ormQueryMetrics = serverMetrics.getOrmQueryMetrics();
final List<MetaQueryMetric> dtoQueryMetrics = serverMetrics.getDtoQueryMetrics();
final List<MetaQueryMetric> queryMetrics = serverMetrics.getQueryMetrics();
for (MetaTimedMetric metric : timedMetrics) {
add(metric);
@@ -43,12 +41,9 @@ class DumpMetricsData {
for (MetaCountMetric metric : countMetrics) {
addCount(metric);
}
for (MetaOrmQueryMetric metric : ormQueryMetrics) {
for (MetaQueryMetric metric : queryMetrics) {
addQuery(metric);
}
for (MetaQueryMetric metric : dtoQueryMetrics) {
addDtoQuery(metric);
}
}
private MetricData create(MetaMetric metric) {
@@ -68,19 +63,13 @@ class DumpMetricsData {
data.setCount(metric.getCount());
}
private void addQuery(MetaOrmQueryMetric metric) {
private void addQuery(MetaQueryMetric metric) {
final MetricData data = create(metric);
appendCounters(data, metric);
appendLocationAndSql(data, metric);
data.setHash(metric.getHash());
}
private void addDtoQuery(MetaQueryMetric metric) {
final MetricData data = create(metric);
appendCounters(data, metric);
appendLocationAndSql(data, metric);
}
private void appendLocationAndSql(MetricData data, MetaQueryMetric metric) {
data.setLoc(metric.getLocation());
data.setSql(metric.getSql());
@@ -3,7 +3,6 @@ package io.ebeaninternal.server.core;
import io.ebean.Database;
import io.ebean.meta.MetaCountMetric;
import io.ebean.meta.MetaMetric;
import io.ebean.meta.MetaOrmQueryMetric;
import io.ebean.meta.MetaQueryMetric;
import io.ebean.meta.MetaTimedMetric;
import io.ebean.meta.ServerMetrics;
@@ -110,25 +109,16 @@ class DumpMetricsJson implements ServerMetricsAsJson {
}
}
List<MetaOrmQueryMetric> ormQueryMetrics = serverMetrics.getOrmQueryMetrics();
if (!ormQueryMetrics.isEmpty()) {
List<MetaQueryMetric> queryMetrics = serverMetrics.getQueryMetrics();
if (!queryMetrics.isEmpty()) {
if (sortBy != null) {
ormQueryMetrics.sort(sortBy);
queryMetrics.sort(sortBy);
}
for (MetaOrmQueryMetric metric : ormQueryMetrics) {
for (MetaQueryMetric metric : queryMetrics) {
logQuery(metric);
}
}
List<MetaQueryMetric> dtoQueryMetrics = serverMetrics.getDtoQueryMetrics();
if (!dtoQueryMetrics.isEmpty()) {
if (sortBy != null) {
dtoQueryMetrics.sort(sortBy);
}
for (MetaQueryMetric metric : dtoQueryMetrics) {
logQuery(metric);
}
}
end();
} catch (IOException e) {
throw new RuntimeException("Error writing metrics as JSON", e);
@@ -1697,7 +1697,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
iudMetrics.visit(visitor);
for (CQueryPlan queryPlan : queryPlanCache.values()) {
if (!queryPlan.isEmptyStats()) {
visitor.visitOrmQuery(queryPlan.getSnapshot(visitor.isReset()));
visitor.visitQuery(queryPlan.getSnapshot(visitor.isReset()));
}
}
}
@@ -601,7 +601,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
if (autoTuneProfiling) {
profilingListener.collectQueryInfo(objectGraphNode, loadedBeanCount, executionTimeMicros);
}
if (queryPlan.executionTime(loadedBeanCount, executionTimeMicros, objectGraphNode)) {
if (queryPlan.executionTime(loadedBeanCount, executionTimeMicros)) {
queryPlan.captureBindForQueryPlan(predicates, executionTimeMicros);
}
getTransaction().profileEvent(this);
@@ -117,7 +117,7 @@ class CQueryFetchSingleAttribute implements SpiProfileTransactionEvent {
executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
request.slowQueryCheck(executionTimeMicros, rowCount);
if (queryPlan.executionTime(rowCount, executionTimeMicros, null)) {
if (queryPlan.executionTime(rowCount, executionTimeMicros)) {
queryPlan.captureBindForQueryPlan(predicates, executionTimeMicros);
}
getTransaction().profileEvent(this);
@@ -123,7 +123,7 @@ public class CQueryPlan {
this.rawSql = rawSql;
this.logWhereSql = logWhereSql;
this.encryptedProps = sqlTree.getEncryptedProps();
this.stats = new CQueryPlanStats(this, server.isCollectQueryOrigins());
this.stats = new CQueryPlanStats(this);
this.dependentTables = sqlTree.dependentTables();
this.bindCapture = initBindCapture(server.getServerConfig(), query);
this.hash = md5Hash();
@@ -151,7 +151,7 @@ public class CQueryPlan {
this.rowNumberIncluded = rowNumberIncluded;
this.logWhereSql = logWhereSql;
this.encryptedProps = sqlTree.getEncryptedProps();
this.stats = new CQueryPlanStats(this, server.isCollectQueryOrigins());
this.stats = new CQueryPlanStats(this);
this.dependentTables = sqlTree.dependentTables();
this.bindCapture = initBindCapture(server.getServerConfig(), query);
this.hash = md5Hash();
@@ -312,14 +312,8 @@ public class CQueryPlan {
/**
* Register an execution time against this query plan;
*/
boolean executionTime(long loadedBeanCount, long timeMicros, ObjectGraphNode objectGraphNode) {
stats.add(loadedBeanCount, timeMicros, objectGraphNode);
if (objectGraphNode != null) {
// collect stats based on objectGraphNode for lazy loading reporting
server.collectQueryStats(objectGraphNode, loadedBeanCount, timeMicros);
}
boolean executionTime(long loadedBeanCount, long timeMicros) {
stats.add(loadedBeanCount, timeMicros);
return bindCapture != null && bindCapture.collectFor(timeMicros);
}
@@ -1,19 +1,10 @@
package io.ebeaninternal.server.query;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.meta.MetaOrmQueryMetric;
import io.ebean.meta.MetaOrmQueryOrigin;
import io.ebean.meta.MetaQueryMetric;
import io.ebean.meta.MetricType;
import io.ebean.metric.TimedMetric;
import io.ebean.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.LongAdder;
/**
* Statistics for a specific query plan that can accumulate.
*/
@@ -27,14 +18,11 @@ public final class CQueryPlanStats {
private long lastQueryTime;
private final ConcurrentHashMap<ObjectGraphNode, LongAdder> origins;
/**
* Construct for a given query plan.
*/
CQueryPlanStats(CQueryPlan queryPlan, boolean collectQueryOrigins) {
CQueryPlanStats(CQueryPlan queryPlan) {
this.queryPlan = queryPlan;
this.origins = !collectQueryOrigins ? null : new ConcurrentHashMap<>();
this.timedMetric = queryPlan.createTimedMetric();
}
@@ -48,25 +36,10 @@ public final class CQueryPlanStats {
/**
* Add a query execution to the statistics.
*/
public void add(long loadedBeanCount, long timeMicros, ObjectGraphNode objectGraphNode) {
public void add(long loadedBeanCount, long timeMicros) {
timedMetric.add(timeMicros, loadedBeanCount);
// not safe but should be atomic
lastQueryTime = System.currentTimeMillis();
if (origins != null && objectGraphNode != null) {
// Maintain the origin points this query fires from
// with a simple counter
LongAdder counter = origins.get(objectGraphNode);
if (counter == null) {
// race condition - we can miss counters here but going
// to live with that. Don't want to lock/synchronize etc
counter = new LongAdder();
origins.put(objectGraphNode, counter);
}
counter.increment();
}
}
/**
@@ -74,11 +47,6 @@ public final class CQueryPlanStats {
*/
public void reset() {
timedMetric.reset();
if (origins != null) {
for (LongAdder counter : origins.values()) {
counter.reset();
}
}
}
/**
@@ -94,77 +62,24 @@ public final class CQueryPlanStats {
Snapshot getSnapshot(boolean reset) {
TimedMetricStats collect = timedMetric.collect(reset);
List<MetaOrmQueryOrigin> origins = getOrigins(reset);
Snapshot snapshot = new Snapshot(collected, queryPlan, collect, lastQueryTime, origins);
Snapshot snapshot = new Snapshot(collected, queryPlan, collect);
collected = true;
return snapshot;
}
/**
* Return the list/snapshot of the origins and their counter value.
*/
private List<MetaOrmQueryOrigin> getOrigins(boolean reset) {
if (origins == null) {
return Collections.emptyList();
}
List<MetaOrmQueryOrigin> list = new ArrayList<>(origins.size());
for (Entry<ObjectGraphNode, LongAdder> entry : origins.entrySet()) {
if (reset) {
list.add(new OriginSnapshot(entry.getKey(), entry.getValue().sumThenReset()));
} else {
list.add(new OriginSnapshot(entry.getKey(), entry.getValue().sum()));
}
}
return list;
}
/**
* Snapshot of the origin ObjectGraphNode and counter value.
*/
private static class OriginSnapshot implements MetaOrmQueryOrigin {
private final ObjectGraphNode objectGraphNode;
private final long count;
OriginSnapshot(ObjectGraphNode objectGraphNode, long count) {
this.objectGraphNode = objectGraphNode;
this.count = count;
}
@Override
public String toString() {
return "node[" + objectGraphNode + "] count[" + count + "]";
}
@Override
public ObjectGraphNode getObjectGraphNode() {
return objectGraphNode;
}
@Override
public long getCount() {
return count;
}
}
/**
* A snapshot of the current statistics for a query plan.
*/
static class Snapshot implements MetaOrmQueryMetric {
static class Snapshot implements MetaQueryMetric {
private final boolean collected;
private final CQueryPlan queryPlan;
private final TimedMetricStats metrics;
private final long lastQueryTime;
private final List<MetaOrmQueryOrigin> origins;
Snapshot(boolean collected, CQueryPlan queryPlan, TimedMetricStats metrics, long lastQueryTime, List<MetaOrmQueryOrigin> origins) {
Snapshot(boolean collected, CQueryPlan queryPlan, TimedMetricStats metrics) {
this.collected = collected;
this.queryPlan = queryPlan;
this.metrics = metrics;
this.lastQueryTime = lastQueryTime;
this.origins = origins;
}
@Override
@@ -227,16 +142,6 @@ public final class CQueryPlanStats {
return metrics.getStartTime();
}
@Override
public long getLastQueryTime() {
return lastQueryTime;
}
@Override
public boolean isAutoTuned() {
return queryPlan.isAutoTuned();
}
@Override
public String getHash() {
return queryPlan.getHash();
@@ -252,11 +157,6 @@ public final class CQueryPlanStats {
return !collected;
}
@Override
public List<MetaOrmQueryOrigin> getOrigins() {
return origins;
}
}
}
@@ -126,7 +126,7 @@ class CQueryRowCount implements SpiProfileTransactionEvent {
executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
request.slowQueryCheck(executionTimeMicros, rowCount);
if (queryPlan.executionTime(rowCount, executionTimeMicros, query.getParentNode())) {
if (queryPlan.executionTime(rowCount, executionTimeMicros)) {
queryPlan.captureBindForQueryPlan(predicates, executionTimeMicros);
}
t.profileEvent(this);
@@ -93,7 +93,7 @@ class CQueryUpdate implements SpiProfileTransactionEvent {
long executionTimeMicros = (System.nanoTime() - startNano) / 1000L;
request.slowQueryCheck(executionTimeMicros, rowCount);
if (queryPlan.executionTime(rowCount, executionTimeMicros, null)) {
if (queryPlan.executionTime(rowCount, executionTimeMicros)) {
queryPlan.captureBindForQueryPlan(predicates, executionTimeMicros);
}
t.profileEvent(this);