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:
@@ -545,6 +545,11 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
Query<T> setDisableReadAuditing();
|
||||
|
||||
/**
|
||||
* Set a label on the query (to help identify query execution statistics).
|
||||
*/
|
||||
Query<T> setLabel(String label);
|
||||
|
||||
/**
|
||||
* Add expressions to the having clause.
|
||||
* <p>
|
||||
|
||||
@@ -1419,6 +1419,15 @@ public interface Query<T> {
|
||||
*/
|
||||
Query<T> setProfileLocation(ProfileLocation profileLocation);
|
||||
|
||||
/**
|
||||
* Set a label on the query.
|
||||
* <p>
|
||||
* This label can be used to help identify query performance metrics but we can also use
|
||||
* profile location enhancement on Finders so for some that would be a better option.
|
||||
* </p>
|
||||
*/
|
||||
Query<T> setLabel(String label);
|
||||
|
||||
/**
|
||||
* Set to true if this query should execute against the doc store.
|
||||
* <p>
|
||||
|
||||
@@ -115,6 +115,11 @@ public interface SqlQuery extends Serializable {
|
||||
*/
|
||||
SqlQuery setTimeout(int secs);
|
||||
|
||||
/**
|
||||
* Set a label that can be put on performance metrics that are collected.
|
||||
*/
|
||||
SqlQuery setLabel(String label);
|
||||
|
||||
/**
|
||||
* A hint which for JDBC translates to the Statement.fetchSize().
|
||||
* <p>
|
||||
|
||||
@@ -152,6 +152,11 @@ public interface Update<T> {
|
||||
*/
|
||||
Update<T> setNullParameter(String name, int jdbcType);
|
||||
|
||||
/**
|
||||
* Set a label meaning performance metrics will be collected for the execution of this update.
|
||||
*/
|
||||
Update<T> setLabel(String label);
|
||||
|
||||
/**
|
||||
* Return the sql that is actually executed.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
/**
|
||||
* An abstract MetricVisitor that handles the boolean flags - reset, collectTransactionMetrics and collectQueryMetrics.
|
||||
*/
|
||||
public abstract class AbstractMetricVisitor implements MetricVisitor {
|
||||
|
||||
private final boolean reset;
|
||||
private final boolean collectTransactionMetrics;
|
||||
private final boolean collectQueryMetrics;
|
||||
|
||||
public AbstractMetricVisitor(boolean reset, boolean collectTransactionMetrics, boolean collectQueryMetrics) {
|
||||
this.reset = reset;
|
||||
this.collectTransactionMetrics = collectTransactionMetrics;
|
||||
this.collectQueryMetrics = collectQueryMetrics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReset() {
|
||||
return reset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCollectTransactionMetrics() {
|
||||
return collectTransactionMetrics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCollectQueryMetrics() {
|
||||
return collectQueryMetrics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitStart() {
|
||||
// do nothing by default
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
// do nothing by default
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A simple MetricVisitor that can collect the desired metrics into lists.
|
||||
*/
|
||||
public class BasicMetricVisitor extends AbstractMetricVisitor {
|
||||
|
||||
private final List<MetaTimedMetric> timed = new ArrayList<>();
|
||||
private final List<MetaQueryMetric> dtoQuery = new ArrayList<>();
|
||||
private final List<MetaOrmQueryMetric> ormQuery = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Construct to reset and collect everything.
|
||||
*/
|
||||
public BasicMetricVisitor() {
|
||||
super(true, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct specifying reset and what to collect.
|
||||
*/
|
||||
public BasicMetricVisitor(boolean reset, boolean collectTransactionMetrics, boolean collectQueryMetrics) {
|
||||
super(reset, collectTransactionMetrics, collectQueryMetrics);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return timed metrics for Transactions, labelled SqlQuery, labelled SqlUpdate.
|
||||
*/
|
||||
public List<MetaTimedMetric> getTimedMetrics() {
|
||||
return timed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DTO query metrics.
|
||||
*/
|
||||
public List<MetaQueryMetric> getDtoQueryMetrics() {
|
||||
return dtoQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ORM query metrics.
|
||||
*/
|
||||
public List<MetaOrmQueryMetric> getOrmQueryMetrics() {
|
||||
return ormQuery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTimed(MetaTimedMetric metric) {
|
||||
timed.add(metric);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitQuery(MetaQueryMetric metric) {
|
||||
dtoQuery.add(metric);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOrmQuery(MetaOrmQueryMetric metric) {
|
||||
ormQuery.add(metric);
|
||||
}
|
||||
}
|
||||
@@ -8,23 +8,20 @@ import java.util.List;
|
||||
public interface MetaInfoManager {
|
||||
|
||||
/**
|
||||
* Collect and return the transaction execution metrics.
|
||||
* Visit the metrics resetting and collecting/reporting as desired.
|
||||
*/
|
||||
List<MetaTimedMetric> collectTransactionStatistics(boolean reset);
|
||||
void visitMetrics(MetricVisitor visitor);
|
||||
|
||||
/**
|
||||
* Collect query plan statistics (new, will migrate ORM query stats over to this).
|
||||
* Run a visit collecting all the metrics and returning BasicMetricVisitor
|
||||
* which holds all the metrics in simple lists.
|
||||
*/
|
||||
List<MetaQueryMetric> collectQueryStatistics(boolean reset);
|
||||
BasicMetricVisitor visitBasic();
|
||||
|
||||
/**
|
||||
* Collect and return the non-empty query plan statistics for all the beans.
|
||||
* <p>
|
||||
* Note that this excludes the query plan statistics where there has been no
|
||||
* executions (since the last collection with reset).
|
||||
* </p>
|
||||
* Just reset all the metrics. Maybe only useful for testing purposes.
|
||||
*/
|
||||
List<MetaQueryPlanStatistic> collectQueryPlanStatistics(boolean reset);
|
||||
void resetAllMetrics();
|
||||
|
||||
/**
|
||||
* Collect and return the ObjectGraphNode statistics.
|
||||
@@ -36,6 +33,6 @@ public interface MetaInfoManager {
|
||||
*
|
||||
* @param reset Set to true to reset the underlying statistics after collection.
|
||||
*/
|
||||
List<MetaObjectGraphNodeStats> collectNodeStatistics(boolean reset);
|
||||
List<MetaOrmQueryNode> collectNodeStatistics(boolean reset);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Query execution statistics for Orm queries.
|
||||
*/
|
||||
public interface MetaOrmQueryMetric extends MetaQueryMetric {
|
||||
|
||||
/**
|
||||
* Return the profile location.
|
||||
*/
|
||||
ProfileLocation getProfileLocation();
|
||||
|
||||
/**
|
||||
* Return true if this query plan was tuned by AutoTune.
|
||||
*/
|
||||
boolean isAutoTuned();
|
||||
|
||||
/**
|
||||
* Return a string representation of the query plan hash.
|
||||
*/
|
||||
String getQueryPlanHash();
|
||||
|
||||
/**
|
||||
* 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
-3
@@ -8,10 +8,8 @@ import io.ebean.bean.ObjectGraphNode;
|
||||
* These statistics can be used to identify origin queries that result in lots
|
||||
* of lazy loading.
|
||||
* </p>
|
||||
*
|
||||
* @see MetaInfoManager#collectNodeStatistics(boolean)
|
||||
*/
|
||||
public interface MetaObjectGraphNodeStats {
|
||||
public interface MetaOrmQueryNode {
|
||||
|
||||
/**
|
||||
* Return the ObjectGraphNode which has the origin point and relative path.
|
||||
+1
-4
@@ -9,11 +9,8 @@ import io.ebean.bean.ObjectGraphNode;
|
||||
* This basically points to the bit of original code and query that results in
|
||||
* this query directly or via lazy loading.
|
||||
* </p>
|
||||
*
|
||||
* @see MetaQueryPlanStatistic
|
||||
* @see MetaInfoManager#collectQueryPlanStatistics(boolean)
|
||||
*/
|
||||
public interface MetaQueryPlanOriginCount {
|
||||
public interface MetaOrmQueryOrigin {
|
||||
|
||||
/**
|
||||
* The 'origin' and path which this query belongs to.
|
||||
@@ -1,98 +0,0 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Query execution statistics Meta data.
|
||||
*
|
||||
* @see MetaInfoManager#collectQueryPlanStatistics(boolean)
|
||||
*/
|
||||
public interface MetaQueryPlanStatistic {
|
||||
|
||||
/**
|
||||
* Return the bean type this query plan is for.
|
||||
*/
|
||||
Class<?> getBeanType();
|
||||
|
||||
/**
|
||||
* Return the profile location.
|
||||
*/
|
||||
ProfileLocation getProfileLocation();
|
||||
|
||||
/**
|
||||
* Return true if this query plan was tuned by AutoTune.
|
||||
*/
|
||||
boolean isAutoTuned();
|
||||
|
||||
/**
|
||||
* Return a string representation of the query plan hash.
|
||||
*/
|
||||
String getQueryPlanHash();
|
||||
|
||||
/**
|
||||
* Return the sql executed.
|
||||
*/
|
||||
String getSql();
|
||||
|
||||
/**
|
||||
* Return the total number of queries executed.
|
||||
*/
|
||||
long getExecutionCount();
|
||||
|
||||
/**
|
||||
* Return the total number of beans loaded by the queries.
|
||||
* <p>
|
||||
* This excludes background fetching.
|
||||
* </p>
|
||||
*/
|
||||
long getTotalLoadedBeans();
|
||||
|
||||
/**
|
||||
* Return the total time taken by executions of this query.
|
||||
*/
|
||||
long getTotalTimeMicros();
|
||||
|
||||
/**
|
||||
* Return the max execution time for this query.
|
||||
*/
|
||||
long getMaxTimeMicros();
|
||||
|
||||
/**
|
||||
* Return the time collection started (or was last reset).
|
||||
*/
|
||||
long getCollectionStart();
|
||||
|
||||
/**
|
||||
* Return the time of the last query executed using this plan.
|
||||
*/
|
||||
long getLastQueryTime();
|
||||
|
||||
/**
|
||||
* Return the average query execution time in microseconds.
|
||||
* <p>
|
||||
* This excludes background fetching.
|
||||
* </p>
|
||||
*/
|
||||
long getAvgTimeMicros();
|
||||
|
||||
/**
|
||||
* Return the average number of bean loaded per query.
|
||||
* <p>
|
||||
* This excludes background fetching.
|
||||
* </p>
|
||||
*/
|
||||
long getAvgLoadedBeans();
|
||||
|
||||
/**
|
||||
* 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<MetaQueryPlanOriginCount> getOrigins();
|
||||
|
||||
}
|
||||
@@ -6,6 +6,11 @@ package io.ebean.meta;
|
||||
*/
|
||||
public interface MetaTimedMetric {
|
||||
|
||||
/**
|
||||
* Return the metric type.
|
||||
*/
|
||||
MetricType getMetricType();
|
||||
|
||||
/**
|
||||
* Return the metric name.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
/**
|
||||
* The type of Metric.
|
||||
*/
|
||||
public enum MetricType {
|
||||
|
||||
/**
|
||||
* Transactions.
|
||||
*/
|
||||
TXN,
|
||||
|
||||
/**
|
||||
* ORM queries.
|
||||
*/
|
||||
ORM,
|
||||
|
||||
/**
|
||||
* DTO queries.
|
||||
*/
|
||||
DTO,
|
||||
|
||||
/**
|
||||
* SQL queries with a label will have metrics collected.
|
||||
* <p>
|
||||
* SqlQuery and SqlUpdate without a label have no metrics collected.
|
||||
*/
|
||||
SQL
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
/**
|
||||
* Defines visitor to read and report the transaction and query metrics.
|
||||
*/
|
||||
public interface MetricVisitor {
|
||||
|
||||
/**
|
||||
* Return true if the metrics should be reset.
|
||||
*/
|
||||
boolean isReset();
|
||||
|
||||
/**
|
||||
* Return true if we should visit the transaction metrics.
|
||||
*/
|
||||
boolean isCollectTransactionMetrics();
|
||||
|
||||
/**
|
||||
* Return true if we should visit the ORM and SQL query metrics.
|
||||
*/
|
||||
boolean isCollectQueryMetrics();
|
||||
|
||||
/**
|
||||
* Visit has started.
|
||||
*/
|
||||
void visitStart();
|
||||
|
||||
/**
|
||||
* Visit transaction metrics (and L2 cache metrics in future).
|
||||
*/
|
||||
void visitTimed(MetaTimedMetric metric);
|
||||
|
||||
/**
|
||||
* Visit DTO and SQL query metrics.
|
||||
*/
|
||||
void visitQuery(MetaQueryMetric metric);
|
||||
|
||||
/**
|
||||
* Visit ORM query metrics.
|
||||
*/
|
||||
void visitOrmQuery(MetaOrmQueryMetric metric);
|
||||
|
||||
/**
|
||||
* Visit has completed.
|
||||
*/
|
||||
void visitEnd();
|
||||
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import io.ebean.config.ServerConfig;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.event.readaudit.ReadAuditLogger;
|
||||
import io.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler;
|
||||
import io.ebeaninternal.server.core.SpiResultSet;
|
||||
import io.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
@@ -257,4 +258,9 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
|
||||
* Execute the underlying ORM query returning as a JDBC ResultSet to map to DTO beans.
|
||||
*/
|
||||
SpiResultSet findResultSet(SpiQuery<?> ormQuery, SpiTransaction transaction);
|
||||
|
||||
/**
|
||||
* Visit all the metrics (typically reporting them).
|
||||
*/
|
||||
void visitMetrics(MetricVisitor visitor);
|
||||
}
|
||||
|
||||
@@ -179,6 +179,11 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
*/
|
||||
ProfileLocation getProfileLocation();
|
||||
|
||||
/**
|
||||
* Return the label set on the query.
|
||||
*/
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
* Return true if this is a "find by id" query. This includes a check for a single "equal to" expression for the Id.
|
||||
*/
|
||||
|
||||
@@ -15,6 +15,11 @@ public interface SpiSqlBinding {
|
||||
*/
|
||||
String getQuery();
|
||||
|
||||
/**
|
||||
* Return the label (to collect metrics on when set).
|
||||
*/
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
* Return the first row to fetch.
|
||||
*/
|
||||
|
||||
@@ -43,6 +43,11 @@ public interface SpiUpdate<T> extends Update<T> {
|
||||
*/
|
||||
Class<?> getBeanType();
|
||||
|
||||
/**
|
||||
* Return the label (for metrics collection).
|
||||
*/
|
||||
String getLabel();
|
||||
|
||||
/**
|
||||
* Return the type of this - insert, update or delete.
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package io.ebeaninternal.metric;
|
||||
|
||||
import io.ebean.meta.MetricType;
|
||||
|
||||
/**
|
||||
* Factory to create timed metric counters.
|
||||
*/
|
||||
@@ -15,20 +17,16 @@ public interface MetricFactory {
|
||||
/**
|
||||
* Create a timed metric group.
|
||||
*/
|
||||
TimedMetricMap createTimedMetricMap(String name);
|
||||
TimedMetricMap createTimedMetricMap(MetricType metricType, String name);
|
||||
|
||||
/**
|
||||
* Create a Timed metric.
|
||||
*/
|
||||
TimedMetric createTimedMetric(String name);
|
||||
TimedMetric createTimedMetric(MetricType metricType, String name);
|
||||
|
||||
/**
|
||||
* Create a Timed metric.
|
||||
*/
|
||||
QueryPlanMetric createQueryPlanMetric(Class<?> type, String label, String sql);
|
||||
QueryPlanMetric createQueryPlanMetric(MetricType metricType, Class<?> type, String label, String sql);
|
||||
|
||||
/**
|
||||
* Return a instance used to collect Query plan metrics.
|
||||
*/
|
||||
QueryPlanCollector createCollector(boolean reset);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package io.ebeaninternal.metric;
|
||||
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
|
||||
/**
|
||||
* Internal Query plan metric holder.
|
||||
*/
|
||||
@@ -11,7 +13,7 @@ public interface QueryPlanMetric {
|
||||
TimedMetric getMetric();
|
||||
|
||||
/**
|
||||
* Collect the non-empty query plan metrics.
|
||||
* Visit the underlying metric.
|
||||
*/
|
||||
void collect(QueryPlanCollector collector);
|
||||
void visit(MetricVisitor visitor);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package io.ebeaninternal.metric;
|
||||
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
|
||||
import java.util.List;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
|
||||
/**
|
||||
* Metric for timed events like transaction execution times.
|
||||
@@ -25,12 +23,17 @@ public interface TimedMetric {
|
||||
boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Collect the timed metric statistics.
|
||||
* Reset the statistics.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Collect and return a snapshot of the metrics.
|
||||
*/
|
||||
TimedMetricStats collect(boolean reset);
|
||||
|
||||
/**
|
||||
* Add non empty metrics to the result.
|
||||
* Visit non empty metrics.
|
||||
*/
|
||||
void collect(boolean reset, List<MetaTimedMetric> result);
|
||||
void visit(MetricVisitor visitor);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package io.ebeaninternal.metric;
|
||||
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
|
||||
import java.util.List;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
|
||||
/**
|
||||
* A map of timed metrics keyed by a string.
|
||||
@@ -15,7 +13,12 @@ public interface TimedMetricMap {
|
||||
void add(String key, long exeMicros);
|
||||
|
||||
/**
|
||||
* Add non empty metrics to the given result.
|
||||
* Add an execution for the given key including row/bean count.
|
||||
*/
|
||||
void collect(boolean reset, List<MetaTimedMetric> result);
|
||||
void add(String key, long exeMicros, int rows);
|
||||
|
||||
/**
|
||||
* Visit the metric.
|
||||
*/
|
||||
void visit(MetricVisitor visitor);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import java.sql.SQLException;
|
||||
*/
|
||||
public abstract class AbstractSqlQueryRequest {
|
||||
|
||||
private final SpiSqlBinding query;
|
||||
protected final SpiSqlBinding query;
|
||||
|
||||
protected final SpiEbeanServer server;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.bean.ObjectGraphNode;
|
||||
import io.ebean.meta.MetaObjectGraphNodeStats;
|
||||
import io.ebean.meta.MetaOrmQueryNode;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
@@ -35,7 +35,7 @@ public class CObjectGraphNodeStatistics {
|
||||
totalBeans.add(beanCount);
|
||||
}
|
||||
|
||||
public MetaObjectGraphNodeStats get(boolean reset) {
|
||||
public MetaOrmQueryNode get(boolean reset) {
|
||||
if (reset) {
|
||||
return new Snapshot(node, startTime.getAndSet(System.currentTimeMillis()), count.sumThenReset(),
|
||||
totalTime.sumThenReset(), totalBeans.sumThenReset());
|
||||
@@ -44,7 +44,7 @@ public class CObjectGraphNodeStatistics {
|
||||
}
|
||||
}
|
||||
|
||||
private static class Snapshot implements MetaObjectGraphNodeStats {
|
||||
private static class Snapshot implements MetaOrmQueryNode {
|
||||
|
||||
private final ObjectGraphNode node;
|
||||
private final long startTime;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.meta.AbstractMetricVisitor;
|
||||
import io.ebean.meta.BasicMetricVisitor;
|
||||
import io.ebean.meta.MetaInfoManager;
|
||||
import io.ebean.meta.MetaObjectGraphNodeStats;
|
||||
import io.ebean.meta.MetaOrmQueryMetric;
|
||||
import io.ebean.meta.MetaOrmQueryNode;
|
||||
import io.ebean.meta.MetaQueryMetric;
|
||||
import io.ebean.meta.MetaQueryPlanStatistic;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.query.CQueryPlanStatsCollector;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -23,29 +24,26 @@ public class DefaultMetaInfoManager implements MetaInfoManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaTimedMetric> collectTransactionStatistics(boolean reset) {
|
||||
return server.collectTransactionStatistics(reset);
|
||||
public void visitMetrics(MetricVisitor visitor) {
|
||||
server.visitMetrics(visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaQueryMetric> collectQueryStatistics(boolean reset) {
|
||||
return server.collectQueryStatistics(reset);
|
||||
public BasicMetricVisitor visitBasic() {
|
||||
BasicMetricVisitor basic = new BasicMetricVisitor();
|
||||
visitMetrics(basic);
|
||||
return basic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaQueryPlanStatistic> collectQueryPlanStatistics(boolean reset) {
|
||||
|
||||
CQueryPlanStatsCollector collector = new CQueryPlanStatsCollector(reset);
|
||||
for (BeanDescriptor<?> desc : server.getBeanDescriptors()) {
|
||||
desc.collectQueryPlanStatistics(collector);
|
||||
}
|
||||
return collector.getList();
|
||||
public void resetAllMetrics() {
|
||||
server.visitMetrics(new ResetVisitor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaObjectGraphNodeStats> collectNodeStatistics(boolean reset) {
|
||||
public List<MetaOrmQueryNode> collectNodeStatistics(boolean reset) {
|
||||
|
||||
List<MetaObjectGraphNodeStats> list = new ArrayList<>();
|
||||
List<MetaOrmQueryNode> list = new ArrayList<>();
|
||||
for (CObjectGraphNodeStatistics nodeStatistics : server.objectGraphStats.values()) {
|
||||
if (!nodeStatistics.isEmpty()) {
|
||||
list.add(nodeStatistics.get(reset));
|
||||
@@ -54,4 +52,29 @@ public class DefaultMetaInfoManager implements MetaInfoManager {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visitor that resets the statistics but doesn't collect them.
|
||||
*/
|
||||
private static class ResetVisitor extends AbstractMetricVisitor {
|
||||
|
||||
ResetVisitor() {
|
||||
super(true, true, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTimed(MetaTimedMetric metric) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitQuery(MetaQueryMetric metric) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOrmQuery(MetaOrmQueryMetric metric) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,8 +48,7 @@ import io.ebean.event.BeanPersistController;
|
||||
import io.ebean.event.readaudit.ReadAuditLogger;
|
||||
import io.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import io.ebean.meta.MetaInfoManager;
|
||||
import io.ebean.meta.MetaQueryMetric;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.plugin.Plugin;
|
||||
import io.ebean.plugin.Property;
|
||||
@@ -2192,10 +2191,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
public List<MetaTimedMetric> collectTransactionStatistics(boolean reset) {
|
||||
return transactionManager.collectTransactionStatistics(reset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Property> checkUniqueness(Object bean) {
|
||||
return checkUniqueness(bean, null);
|
||||
@@ -2237,8 +2232,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
|
||||
/**
|
||||
* Returns a set of properties if saving the bean will violate the unique constraints
|
||||
* (definded by given properties).
|
||||
* Returns a set of properties if saving the bean will violate the unique constraints (defined by given properties).
|
||||
*/
|
||||
private Set<Property> checkUniqueness(EntityBean entityBean, BeanDescriptor<?> beanDesc, BeanProperty[] props,
|
||||
Transaction transaction) {
|
||||
@@ -2261,15 +2255,24 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
if (findCount(query, transaction) > 0) {
|
||||
Set<Property> ret = new LinkedHashSet<>();
|
||||
for (Property prop : props) {
|
||||
ret.add(prop);
|
||||
}
|
||||
Collections.addAll(ret, props);
|
||||
return ret;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<MetaQueryMetric> collectQueryStatistics(boolean reset) {
|
||||
return dtoBeanManager.collectStats(reset);
|
||||
@Override
|
||||
public void visitMetrics(MetricVisitor visitor) {
|
||||
visitor.visitStart();
|
||||
if (visitor.isCollectTransactionMetrics()) {
|
||||
transactionManager.visitMetrics(visitor);
|
||||
}
|
||||
if (visitor.isCollectQueryMetrics()) {
|
||||
beanDescriptorManager.visitMetrics(visitor);
|
||||
dtoBeanManager.visitMetrics(visitor);
|
||||
relationalQueryEngine.visitMetrics(visitor);
|
||||
persister.visitMetrics(visitor);
|
||||
}
|
||||
visitor.visitEnd();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ public abstract class PersistRequest extends BeanRequest implements BatchPostExe
|
||||
UPDATESQL(EVT_UPDATESQL),
|
||||
CALLABLESQL(EVT_CALLABLESQL);
|
||||
String profileEventId;
|
||||
|
||||
Type(String profileEventId) {
|
||||
this.profileEventId = profileEventId;
|
||||
}
|
||||
@@ -36,12 +37,30 @@ public abstract class PersistRequest extends BeanRequest implements BatchPostExe
|
||||
|
||||
final PersistExecute persistExecute;
|
||||
|
||||
protected String label;
|
||||
|
||||
protected long startNanos;
|
||||
|
||||
PersistRequest(SpiEbeanServer server, SpiTransaction t, PersistExecute persistExecute) {
|
||||
super(server, t);
|
||||
this.persistExecute = persistExecute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by CallableSqlRequest and UpdateSqlRequest.
|
||||
*/
|
||||
public PersistRequest(SpiEbeanServer server, SpiTransaction t, PersistExecute persistExecute) {
|
||||
super(server, t);
|
||||
this.persistExecute = persistExecute;
|
||||
PersistRequest(SpiEbeanServer server, SpiTransaction t, PersistExecute persistExecute, String label) {
|
||||
this(server, t, persistExecute);
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Effectively set start nanos if we are collecting metrics on a label.
|
||||
*/
|
||||
public void startBind(boolean batchThisRequest) {
|
||||
if (!batchThisRequest && label != null) {
|
||||
startNanos = System.nanoTime();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,10 +31,9 @@ public final class PersistRequestCallableSql extends PersistRequest {
|
||||
/**
|
||||
* Create.
|
||||
*/
|
||||
public PersistRequestCallableSql(SpiEbeanServer server,
|
||||
CallableSql cs, SpiTransaction t, PersistExecute persistExecute) {
|
||||
public PersistRequestCallableSql(SpiEbeanServer server, CallableSql cs, SpiTransaction t, PersistExecute persistExecute) {
|
||||
|
||||
super(server, t, persistExecute);
|
||||
super(server, t, persistExecute, cs.getLabel());
|
||||
this.type = PersistRequest.Type.CALLABLESQL;
|
||||
this.callableSql = (SpiCallableSql) cs;
|
||||
}
|
||||
@@ -88,7 +87,9 @@ public final class PersistRequestCallableSql extends PersistRequest {
|
||||
*/
|
||||
@Override
|
||||
public void postExecute() {
|
||||
|
||||
if (startNanos > 0) {
|
||||
persistExecute.collectSqlCall(label, startNanos, rowCount);
|
||||
}
|
||||
if (transaction.isLogSummary()) {
|
||||
String m = "CallableSql label[" + callableSql.getLabel() + "]" + " rows[" + rowCount + "]" + " bind[" + bindLog + "]";
|
||||
transaction.logSummary(m);
|
||||
|
||||
@@ -27,7 +27,7 @@ public final class PersistRequestOrmUpdate extends PersistRequest {
|
||||
public PersistRequestOrmUpdate(SpiEbeanServer server, BeanManager<?> mgr, SpiUpdate<?> ormUpdate,
|
||||
SpiTransaction t, PersistExecute persistExecute) {
|
||||
|
||||
super(server, t, persistExecute);
|
||||
super(server, t, persistExecute, ormUpdate.getLabel());
|
||||
this.beanDescriptor = mgr.getBeanDescriptor();
|
||||
this.ormUpdate = ormUpdate;
|
||||
}
|
||||
@@ -86,7 +86,9 @@ public final class PersistRequestOrmUpdate extends PersistRequest {
|
||||
*/
|
||||
@Override
|
||||
public void postExecute() {
|
||||
|
||||
if (startNanos > 0) {
|
||||
persistExecute.collectOrmUpdate(label, startNanos, rowCount);
|
||||
}
|
||||
OrmUpdateType ormUpdateType = ormUpdate.getOrmUpdateType();
|
||||
String tableName = ormUpdate.getBaseTable();
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ public final class PersistRequestUpdateSql extends PersistRequest {
|
||||
public PersistRequestUpdateSql(SpiEbeanServer server, SqlUpdate updateSql,
|
||||
SpiTransaction t, PersistExecute persistExecute) {
|
||||
|
||||
super(server, t, persistExecute);
|
||||
super(server, t, persistExecute, updateSql.getLabel());
|
||||
this.type = Type.UPDATESQL;
|
||||
this.updateSql = (SpiSqlUpdate) updateSql;
|
||||
}
|
||||
@@ -102,7 +102,9 @@ public final class PersistRequestUpdateSql extends PersistRequest {
|
||||
*/
|
||||
@Override
|
||||
public void postExecute() {
|
||||
|
||||
if (startNanos > 0) {
|
||||
persistExecute.collectSqlUpdate(label, startNanos, rowCount);
|
||||
}
|
||||
if (transaction.isLogSummary()) {
|
||||
String m = description + " table[" + tableName + "] rows[" + rowCount + "] bind[" + bindLog + "]";
|
||||
transaction.logSummary(m);
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.ebean.SqlUpdate;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.Update;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -78,4 +79,8 @@ public interface Persister {
|
||||
*/
|
||||
<T> List<T> draftRestore(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Visit the metrics.
|
||||
*/
|
||||
void visitMetrics(MetricVisitor visitor);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.core;
|
||||
|
||||
|
||||
import io.ebean.SqlRow;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
@@ -29,4 +30,13 @@ public interface RelationalQueryEngine {
|
||||
*/
|
||||
void findEach(RelationalQueryRequest request, Predicate<SqlRow> consumer);
|
||||
|
||||
/**
|
||||
* Collect SQL query execution statistics.
|
||||
*/
|
||||
void collect(String label, long exeMicros, int rows);
|
||||
|
||||
/**
|
||||
* Visit the metrics.
|
||||
*/
|
||||
void visitMetrics(MetricVisitor visitor);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,11 @@ public final class RelationalQueryRequest extends AbstractSqlQueryRequest {
|
||||
|
||||
@Override
|
||||
protected void requestComplete() {
|
||||
|
||||
String label = query.getLabel();
|
||||
if (label != null) {
|
||||
long exeMicros = (System.nanoTime() - startNano) / 1000L;
|
||||
queryEngine.collect(label, exeMicros, rows);
|
||||
}
|
||||
}
|
||||
|
||||
public void findEach(Consumer<SqlRow> consumer) {
|
||||
|
||||
@@ -27,6 +27,7 @@ import io.ebean.event.changelog.ChangeType;
|
||||
import io.ebean.event.readaudit.ReadAuditLogger;
|
||||
import io.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import io.ebean.event.readaudit.ReadEvent;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.plugin.BeanDocType;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.plugin.ExpressionPath;
|
||||
@@ -61,7 +62,6 @@ import io.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.persist.DmlUtil;
|
||||
import io.ebeaninternal.server.query.CQueryPlan;
|
||||
import io.ebeaninternal.server.query.CQueryPlanStatsCollector;
|
||||
import io.ebeaninternal.server.query.SqlTreeProperty;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import io.ebeaninternal.server.rawsql.SpiRawSql;
|
||||
@@ -1598,10 +1598,13 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
return new DeployUpdateParser(this).parse(ormUpdateStatement);
|
||||
}
|
||||
|
||||
public void collectQueryPlanStatistics(CQueryPlanStatsCollector collector) {
|
||||
/**
|
||||
* Visit all the ORM query plan metrics (includes UpdateQuery with updates and deletes).
|
||||
*/
|
||||
public void visitMetrics(MetricVisitor visitor) {
|
||||
for (CQueryPlan queryPlan : queryPlanCache.values()) {
|
||||
if (!queryPlan.isEmptyStats()) {
|
||||
collector.add(queryPlan.getSnapshot(collector.isReset()));
|
||||
visitor.visitOrmQuery(queryPlan.getSnapshot(visitor.isReset()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import io.ebean.event.changelog.ChangeLogFilter;
|
||||
import io.ebean.event.changelog.ChangeLogListener;
|
||||
import io.ebean.event.changelog.ChangeLogPrepare;
|
||||
import io.ebean.event.changelog.ChangeLogRegister;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.util.AnnotationUtil;
|
||||
import io.ebeaninternal.api.ConcurrencyMode;
|
||||
@@ -1632,6 +1633,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
target.setPrimaryKeyJoin(inverseJoin);
|
||||
}
|
||||
|
||||
public void visitMetrics(MetricVisitor visitor) {
|
||||
for (BeanDescriptor<?> desc : immutableDescriptorList) {
|
||||
desc.visitMetrics(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparator to sort the BeanDescriptors by name.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebeaninternal.metric.QueryPlanCollector;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -37,9 +37,9 @@ public class DtoBeanDescriptor<T> {
|
||||
plans.put(planKey, plan);
|
||||
}
|
||||
|
||||
public void collectStats(QueryPlanCollector collector) {
|
||||
public void visit(MetricVisitor visitor) {
|
||||
for (DtoQueryPlan plan : plans.values()) {
|
||||
plan.collectStats(collector);
|
||||
plan.visit(visitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebean.meta.MetaQueryMetric;
|
||||
import io.ebeaninternal.metric.MetricFactory;
|
||||
import io.ebeaninternal.metric.QueryPlanCollector;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.server.type.TypeManager;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@@ -41,13 +38,9 @@ public class DtoBeanManager {
|
||||
}
|
||||
}
|
||||
|
||||
public List<MetaQueryMetric> collectStats(boolean reset) {
|
||||
|
||||
QueryPlanCollector collector = MetricFactory.get().createCollector(reset);
|
||||
|
||||
public void visitMetrics(MetricVisitor visitor) {
|
||||
for (DtoBeanDescriptor value : descriptorMap.values()) {
|
||||
value.collectStats(collector);
|
||||
value.visit(visitor);
|
||||
}
|
||||
return collector.complete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebeaninternal.api.SpiDtoQuery;
|
||||
import io.ebeaninternal.metric.MetricFactory;
|
||||
import io.ebeaninternal.metric.QueryPlanMetric;
|
||||
@@ -44,6 +45,6 @@ public class DtoMappingRequest {
|
||||
}
|
||||
|
||||
public QueryPlanMetric createMetric() {
|
||||
return MetricFactory.get().createQueryPlanMetric(type, label, sql);
|
||||
return MetricFactory.get().createQueryPlanMetric(MetricType.DTO, type, label, sql);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebeaninternal.metric.QueryPlanCollector;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.server.type.DataReader;
|
||||
|
||||
import java.sql.SQLException;
|
||||
@@ -21,7 +21,7 @@ public interface DtoQueryPlan {
|
||||
void collect(long exeMicros, int rows);
|
||||
|
||||
/**
|
||||
* Collect the query plan statistics.
|
||||
* Visit the metric (if not empty).
|
||||
*/
|
||||
void collectStats(QueryPlanCollector collector);
|
||||
void visit(MetricVisitor visitor);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebeaninternal.metric.QueryPlanCollector;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.metric.QueryPlanMetric;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
|
||||
@@ -21,8 +21,7 @@ abstract class DtoQueryPlanBase implements DtoQueryPlan {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collectStats(QueryPlanCollector collector) {
|
||||
planMetric.collect(collector);
|
||||
public void visit(MetricVisitor visitor) {
|
||||
planMetric.visit(visitor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -513,6 +513,11 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
return query.setDisableReadAuditing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> setLabel(String label) {
|
||||
return query.setLabel(label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> having() {
|
||||
return query.having();
|
||||
|
||||
@@ -798,6 +798,11 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
|
||||
return exprList.setCountDistinct(orderBy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> setLabel(String label) {
|
||||
return exprList.setLabel(label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> startsWith(String propertyName, String value) {
|
||||
return exprList.startsWith(propertyName, value);
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package io.ebeaninternal.server.persist;
|
||||
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.metric.MetricFactory;
|
||||
import io.ebeaninternal.metric.TimedMetricMap;
|
||||
import io.ebeaninternal.server.core.PersistRequestCallableSql;
|
||||
import io.ebeaninternal.server.core.PersistRequestOrmUpdate;
|
||||
import io.ebeaninternal.server.core.PersistRequestUpdateSql;
|
||||
@@ -24,15 +28,48 @@ final class DefaultPersistExecute implements PersistExecute {
|
||||
*/
|
||||
private final int defaultBatchSize;
|
||||
|
||||
private final TimedMetricMap ormUpdateMetric;
|
||||
|
||||
private final TimedMetricMap sqlUpdateMetric;
|
||||
|
||||
private final TimedMetricMap sqlCallMetric;
|
||||
|
||||
/**
|
||||
* Construct this DmlPersistExecute.
|
||||
*/
|
||||
DefaultPersistExecute(Binder binder, int defaultBatchSize) {
|
||||
|
||||
this.exeOrmUpdate = new ExeOrmUpdate(binder);
|
||||
this.exeUpdateSql = new ExeUpdateSql(binder);
|
||||
this.exeCallableSql = new ExeCallableSql(binder);
|
||||
this.defaultBatchSize = defaultBatchSize;
|
||||
this.ormUpdateMetric = MetricFactory.get().createTimedMetricMap(MetricType.SQL, "orm.update.");
|
||||
this.sqlUpdateMetric = MetricFactory.get().createTimedMetricMap(MetricType.SQL, "sql.update.");
|
||||
this.sqlCallMetric = MetricFactory.get().createTimedMetricMap(MetricType.SQL, "sql.call.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMetrics(MetricVisitor visitor) {
|
||||
ormUpdateMetric.visit(visitor);
|
||||
sqlUpdateMetric.visit(visitor);
|
||||
sqlCallMetric.visit(visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collectOrmUpdate(String label, long startNanos, int rowCount) {
|
||||
long exeMicros = (System.nanoTime() - startNanos) / 1000L;
|
||||
ormUpdateMetric.add(label, exeMicros, rowCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collectSqlUpdate(String label, long startNanos, int rowCount) {
|
||||
long exeMicros = (System.nanoTime() - startNanos) / 1000L;
|
||||
sqlUpdateMetric.add(label, exeMicros, rowCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collectSqlCall(String label, long startNanos, int rowCount) {
|
||||
long exeMicros = (System.nanoTime() - startNanos) / 1000L;
|
||||
sqlCallMetric.add(label, exeMicros, rowCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -10,6 +10,7 @@ import io.ebean.bean.BeanCollection.ModifyListenMode;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.event.BeanPersistController;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.SpiUpdate;
|
||||
@@ -79,6 +80,11 @@ public final class DefaultPersister implements Persister {
|
||||
this.persistExecute = new DefaultPersistExecute(binder, server.getServerConfig().getPersistBatchSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMetrics(MetricVisitor visitor) {
|
||||
persistExecute.visitMetrics(visitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the CallableSql.
|
||||
*/
|
||||
|
||||
@@ -60,6 +60,7 @@ class ExeCallableSql {
|
||||
|
||||
private CallableStatement bindStmt(PersistRequestCallableSql request, boolean batchThisRequest) throws SQLException {
|
||||
|
||||
request.startBind(batchThisRequest);
|
||||
SpiCallableSql callableSql = request.getCallableSql();
|
||||
SpiTransaction t = request.getTransaction();
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ class ExeOrmUpdate {
|
||||
|
||||
private PreparedStatement bindStmt(PersistRequestOrmUpdate request, boolean batchThisRequest) throws SQLException {
|
||||
|
||||
request.startBind(batchThisRequest);
|
||||
SpiUpdate<?> ormUpdate = request.getOrmUpdate();
|
||||
SpiTransaction t = request.getTransaction();
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ class ExeUpdateSql {
|
||||
|
||||
private PreparedStatement bindStmt(PersistRequestUpdateSql request, boolean batchThisRequest) throws SQLException {
|
||||
|
||||
request.startBind(batchThisRequest);
|
||||
SpiSqlUpdate updateSql = request.getUpdateSql();
|
||||
SpiTransaction t = request.getTransaction();
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.persist;
|
||||
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.server.core.PersistRequestCallableSql;
|
||||
import io.ebeaninternal.server.core.PersistRequestOrmUpdate;
|
||||
@@ -34,4 +35,23 @@ public interface PersistExecute {
|
||||
*/
|
||||
int executeSqlUpdate(PersistRequestUpdateSql request);
|
||||
|
||||
/**
|
||||
* Collect execution metrics for sql update.
|
||||
*/
|
||||
void collectOrmUpdate(String label, long startNanos, int rowCount);
|
||||
|
||||
/**
|
||||
* Collect execution metrics for sql update.
|
||||
*/
|
||||
void collectSqlUpdate(String label, long startNanos, int rowCount);
|
||||
|
||||
/**
|
||||
* Collect execution metrics for sql callable.
|
||||
*/
|
||||
void collectSqlCall(String label, long startNanos, int rowCount);
|
||||
|
||||
/**
|
||||
* Visit the metrics.
|
||||
*/
|
||||
void visitMetrics(MetricVisitor visitor);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebeaninternal.metric.MetricFactory;
|
||||
import io.ebeaninternal.metric.QueryPlanCollector;
|
||||
import io.ebeaninternal.metric.QueryPlanMetric;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
import io.ebeaninternal.metric.TimedMetricMap;
|
||||
@@ -12,22 +12,18 @@ import io.ebeaninternal.metric.TimedMetricMap;
|
||||
public class DMetricFactory implements MetricFactory {
|
||||
|
||||
@Override
|
||||
public TimedMetricMap createTimedMetricMap(String name) {
|
||||
return new DTimedMetricMap(name);
|
||||
public TimedMetricMap createTimedMetricMap(MetricType metricType, String name) {
|
||||
return new DTimedMetricMap(metricType, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimedMetric createTimedMetric(String name) {
|
||||
return new DTimedMetric(name);
|
||||
public TimedMetric createTimedMetric(MetricType metricType, String name) {
|
||||
return new DTimedMetric(metricType, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryPlanMetric createQueryPlanMetric(Class<?> type, String label, String sql) {
|
||||
return new DQueryPlanMetric(new DQueryPlanMeta(type, label, sql), createTimedMetric(label));
|
||||
public QueryPlanMetric createQueryPlanMetric(MetricType metricType, Class<?> type, String label, String sql) {
|
||||
return new DQueryPlanMetric(new DQueryPlanMeta(type, label, sql), new DTimedMetric(metricType, label));
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryPlanCollector createCollector(boolean reset) {
|
||||
return new DQueryPlanCollector(reset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebean.service.SpiProfileLocationFactory;
|
||||
import io.ebeaninternal.metric.MetricFactory;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
@@ -18,7 +19,7 @@ public class DProfileLocationFactory implements SpiProfileLocationFactory {
|
||||
@Override
|
||||
public ProfileLocation create(int lineNumber, String label) {
|
||||
|
||||
TimedMetric timedMetric = MetricFactory.get().createTimedMetric("txn.named." + label);
|
||||
TimedMetric timedMetric = MetricFactory.get().createTimedMetric(MetricType.TXN, "txn.named." + label);
|
||||
|
||||
DTimedProfileLocation loc = new DTimedProfileLocation(lineNumber, label, timedMetric);
|
||||
TimedProfileLocationRegistry.register(loc);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.MetaQueryMetric;
|
||||
import io.ebeaninternal.metric.QueryPlanCollector;
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.metric.QueryPlanMetric;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
import io.ebeaninternal.metric.TimedMetricStats;
|
||||
@@ -9,18 +10,18 @@ import io.ebeaninternal.metric.TimedMetricStats;
|
||||
class DQueryPlanMetric implements QueryPlanMetric {
|
||||
|
||||
private final DQueryPlanMeta meta;
|
||||
private final TimedMetric metric;
|
||||
private final DTimedMetric metric;
|
||||
|
||||
DQueryPlanMetric(DQueryPlanMeta meta, TimedMetric metric) {
|
||||
DQueryPlanMetric(DQueryPlanMeta meta, DTimedMetric metric) {
|
||||
this.meta = meta;
|
||||
this.metric = metric;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(QueryPlanCollector collector) {
|
||||
TimedMetricStats stats = metric.collect(collector.isReset());
|
||||
public void visit(MetricVisitor visitor) {
|
||||
TimedMetricStats stats = metric.collect(visitor.isReset());
|
||||
if (stats != null) {
|
||||
collector.add(new Stats(meta, stats));
|
||||
visitor.visitQuery(new Stats(meta, stats));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +45,11 @@ class DQueryPlanMetric implements QueryPlanMetric {
|
||||
return meta + " " + stats + " sql:" + getSql();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetricType getMetricType() {
|
||||
return stats.getMetricType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getType() {
|
||||
return meta.getType();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebeaninternal.metric.TimedMetricStats;
|
||||
|
||||
/**
|
||||
@@ -7,6 +8,8 @@ import io.ebeaninternal.metric.TimedMetricStats;
|
||||
*/
|
||||
class DTimeMetricStats implements TimedMetricStats {
|
||||
|
||||
private final MetricType metricType;
|
||||
|
||||
private final String name;
|
||||
|
||||
private String location;
|
||||
@@ -21,7 +24,8 @@ class DTimeMetricStats implements TimedMetricStats {
|
||||
|
||||
private final long beanCount;
|
||||
|
||||
DTimeMetricStats(String name, long collectionStart, long count, long total, long max, long beanCount) {
|
||||
DTimeMetricStats(MetricType metricType, String name, long collectionStart, long count, long total, long max, long beanCount) {
|
||||
this.metricType = metricType;
|
||||
this.name = name;
|
||||
this.startTime = collectionStart;
|
||||
this.count = count;
|
||||
@@ -53,6 +57,11 @@ class DTimeMetricStats implements TimedMetricStats {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetricType getMetricType() {
|
||||
return metricType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.LongAccumulator;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
@@ -16,6 +16,8 @@ import java.util.concurrent.atomic.LongAdder;
|
||||
*/
|
||||
class DTimedMetric implements TimedMetric {
|
||||
|
||||
private final MetricType metricType;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final LongAdder beanCount = new LongAdder();
|
||||
@@ -28,7 +30,8 @@ class DTimedMetric implements TimedMetric {
|
||||
|
||||
private final AtomicLong startTime = new AtomicLong(System.currentTimeMillis());
|
||||
|
||||
DTimedMetric(String name) {
|
||||
DTimedMetric(MetricType metricType, String name) {
|
||||
this.metricType = metricType;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@@ -54,11 +57,23 @@ class DTimedMetric implements TimedMetric {
|
||||
return count.sum() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all the internal counters and start time.
|
||||
*/
|
||||
@Override
|
||||
public void collect(boolean reset, List<MetaTimedMetric> result) {
|
||||
DTimeMetricStats metric = collect(reset);
|
||||
public void reset() {
|
||||
startTime.set(System.currentTimeMillis());
|
||||
max.reset();
|
||||
count.reset();
|
||||
total.reset();
|
||||
beanCount.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(MetricVisitor visitor) {
|
||||
DTimeMetricStats metric = collect(visitor.isReset());
|
||||
if (metric != null) {
|
||||
result.add(metric);
|
||||
visitor.visitTimed(metric);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +93,7 @@ class DTimedMetric implements TimedMetric {
|
||||
/**
|
||||
* Return the current statistics resetting the internal values if reset is true.
|
||||
*/
|
||||
public DTimeMetricStats getStatistics(boolean reset) {
|
||||
private DTimeMetricStats getStatistics(boolean reset) {
|
||||
|
||||
if (reset) {
|
||||
// Note these values are not guaranteed to be consistent wrt each other
|
||||
@@ -88,21 +103,11 @@ class DTimedMetric implements TimedMetric {
|
||||
final long totalVal = total.sumThenReset();
|
||||
final long countVal = count.sumThenReset();
|
||||
final long startTimeVal = startTime.getAndSet(System.currentTimeMillis());
|
||||
return new DTimeMetricStats(name, startTimeVal, countVal, totalVal, maxVal, beans);
|
||||
return new DTimeMetricStats(metricType, name, startTimeVal, countVal, totalVal, maxVal, beans);
|
||||
|
||||
} else {
|
||||
return new DTimeMetricStats(name, startTime.get(), count.sum(), total.sum(), max.get(), beanCount.sum());
|
||||
return new DTimeMetricStats(metricType, name, startTime.get(), count.sum(), total.sum(), max.get(), beanCount.sum());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all the internal counters and start time.
|
||||
*/
|
||||
public void reset() {
|
||||
startTime.set(System.currentTimeMillis());
|
||||
max.reset();
|
||||
count.reset();
|
||||
total.reset();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,30 +1,38 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.metric.TimedMetricMap;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
class DTimedMetricMap implements TimedMetricMap {
|
||||
|
||||
private final MetricType metricType;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final ConcurrentHashMap<String, DTimedMetric> map = new ConcurrentHashMap<>();
|
||||
|
||||
DTimedMetricMap(String name) {
|
||||
DTimedMetricMap(MetricType metricType, String name) {
|
||||
this.metricType = metricType;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(String key, long exeMicros) {
|
||||
map.computeIfAbsent(key, (k)-> new DTimedMetric(name + key)).add(exeMicros);
|
||||
map.computeIfAbsent(key, (k) -> new DTimedMetric(metricType, name + key)).add(exeMicros);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(boolean reset, List<MetaTimedMetric> list) {
|
||||
public void add(String key, long exeMicros, int rows) {
|
||||
map.computeIfAbsent(key, (k) -> new DTimedMetric(metricType, name + key)).add(exeMicros, rows);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(MetricVisitor visitor) {
|
||||
for (DTimedMetric value : map.values()) {
|
||||
value.collect(reset, list);
|
||||
value.visit(visitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
import io.ebeaninternal.metric.TimedMetricStats;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Default profile location that uses stack trace.
|
||||
*/
|
||||
@@ -37,13 +35,11 @@ class DTimedProfileLocation extends DProfileLocation implements TimedProfileLoca
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(boolean reset, List<MetaTimedMetric> list) {
|
||||
|
||||
TimedMetricStats collect = timedMetric.collect(reset);
|
||||
public void visit(MetricVisitor visitor) {
|
||||
TimedMetricStats collect = timedMetric.collect(visitor.isReset());
|
||||
if (collect != null) {
|
||||
collect.setLocation(obtain());
|
||||
list.add(collect);
|
||||
visitor.visitTimed(collect);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ProfileLocation that collects timing metrics.
|
||||
*/
|
||||
@@ -22,7 +20,7 @@ public interface TimedProfileLocation extends ProfileLocation {
|
||||
TimedMetric getMetric();
|
||||
|
||||
/**
|
||||
* Collect the metrics adding to the given list if the metrics are non empty.
|
||||
* Visit the non empty metrics.
|
||||
*/
|
||||
void collect(boolean reset, List<MetaTimedMetric> list);
|
||||
void visit(MetricVisitor visitor);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -51,6 +51,7 @@ public class DefaultDtoQuery<T> implements SpiDtoQuery<T> {
|
||||
this.server = server;
|
||||
this.descriptor = descriptor;
|
||||
this.ormQuery = ormQuery;
|
||||
this.label = ormQuery.getLabel();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,7 +66,7 @@ public class DefaultDtoQuery<T> implements SpiDtoQuery<T> {
|
||||
|
||||
@Override
|
||||
public String planKey() {
|
||||
return sql+":first"+firstRow+":max"+maxRows;
|
||||
return sql + ":first" + firstRow + ":max" + maxRows;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -96,6 +96,8 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
|
||||
private Type type;
|
||||
|
||||
private String label;
|
||||
|
||||
private Mode mode = Mode.NORMAL;
|
||||
|
||||
private Object tenantId;
|
||||
@@ -340,6 +342,17 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> setLabel(String label) {
|
||||
this.label = label;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoTunable() {
|
||||
return nativeSql == null && beanDescriptor.isAutoTunable();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.querydefn;
|
||||
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.Update;
|
||||
import io.ebeaninternal.api.BindParams;
|
||||
import io.ebeaninternal.api.SpiUpdate;
|
||||
|
||||
@@ -23,6 +24,8 @@ public final class DefaultOrmUpdate<T> implements SpiUpdate<T>, Serializable {
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
private String label;
|
||||
|
||||
/**
|
||||
* The parameters used to bind to the sql.
|
||||
*/
|
||||
@@ -137,6 +140,17 @@ public final class DefaultOrmUpdate<T> implements SpiUpdate<T>, Serializable {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Update<T> setLabel(String label) {
|
||||
this.label = label;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUpdateStatement() {
|
||||
return updateStatement;
|
||||
|
||||
@@ -19,6 +19,8 @@ public class DefaultRelationalQuery implements SpiSqlQuery {
|
||||
|
||||
private final transient EbeanServer server;
|
||||
|
||||
private String label;
|
||||
|
||||
private String query;
|
||||
|
||||
private int firstRow;
|
||||
@@ -122,6 +124,17 @@ public class DefaultRelationalQuery implements SpiSqlQuery {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultRelationalQuery setLabel(String label) {
|
||||
this.label = label;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindParams getBindParams() {
|
||||
return bindParams;
|
||||
|
||||
@@ -11,7 +11,8 @@ import io.ebean.config.dbplatform.DatabasePlatform.OnQueryOnly;
|
||||
import io.ebean.event.changelog.ChangeLogListener;
|
||||
import io.ebean.event.changelog.ChangeLogPrepare;
|
||||
import io.ebean.event.changelog.ChangeSet;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.api.ScopeTrans;
|
||||
import io.ebeaninternal.api.ScopedTransaction;
|
||||
import io.ebeaninternal.api.SpiProfileHandler;
|
||||
@@ -37,7 +38,6 @@ import javax.persistence.PersistenceException;
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -170,9 +170,9 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
this.transactionFactory = TransactionFactoryBuilder.build(this, dataSourceSupplier, tenantProvider);
|
||||
|
||||
MetricFactory metricFactory = MetricFactory.get();
|
||||
this.txnMain = metricFactory.createTimedMetric("txn.main");
|
||||
this.txnReadOnly = metricFactory.createTimedMetric("txn.readonly");
|
||||
this.txnNamed = metricFactory.createTimedMetricMap("txn.named.");
|
||||
this.txnMain = metricFactory.createTimedMetric(MetricType.TXN, "txn.main");
|
||||
this.txnReadOnly = metricFactory.createTimedMetric(MetricType.TXN, "txn.readonly");
|
||||
this.txnNamed = metricFactory.createTimedMetricMap(MetricType.TXN, "txn.named.");
|
||||
|
||||
scopeManager.register(this);
|
||||
}
|
||||
@@ -528,21 +528,13 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
txnNamed.add(label, exeMicros);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the transaction execution statistics since the last reset.
|
||||
*/
|
||||
public List<MetaTimedMetric> collectTransactionStatistics(boolean reset) {
|
||||
|
||||
List<MetaTimedMetric> list = new ArrayList<>();
|
||||
|
||||
txnMain.collect(reset, list);
|
||||
txnReadOnly.collect(reset, list);
|
||||
public void visitMetrics(MetricVisitor visitor) {
|
||||
txnMain.visit(visitor);
|
||||
txnReadOnly.visit(visitor);
|
||||
txnNamed.visit(visitor);
|
||||
for (TimedProfileLocation timedLocation : TimedProfileLocationRegistry.registered()) {
|
||||
timedLocation.collect(reset, list);
|
||||
timedLocation.visit(visitor);
|
||||
}
|
||||
txnNamed.collect(reset, list);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -562,7 +554,7 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
if (st instanceof ScopedTransaction) {
|
||||
// can be null for Supports as that can start as a 'No Transaction' and then
|
||||
// effectively be replaced by transactions inside the scope
|
||||
((ScopedTransaction)st).complete(returnOrThrowable, opCode);
|
||||
((ScopedTransaction) st).complete(returnOrThrowable, opCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user