From 85bfe3d03e856367a018bc6a718b011a4340df90 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Sat, 10 Mar 2018 02:51:51 +1300 Subject: [PATCH] #1345 - Refactor io.ebean.meta.MetaInfoManager API (query execution metrics). Breaking change for people collecting query execution metrics. --- src/main/java/io/ebean/ExpressionList.java | 5 + src/main/java/io/ebean/Query.java | 9 ++ src/main/java/io/ebean/SqlQuery.java | 5 + src/main/java/io/ebean/Update.java | 5 + .../io/ebean/meta/AbstractMetricVisitor.java | 43 ++++++ .../io/ebean/meta/BasicMetricVisitor.java | 64 ++++++++ .../java/io/ebean/meta/MetaInfoManager.java | 19 +-- .../io/ebean/meta/MetaOrmQueryMetric.java | 42 ++++++ ...phNodeStats.java => MetaOrmQueryNode.java} | 4 +- ...iginCount.java => MetaOrmQueryOrigin.java} | 5 +- .../io/ebean/meta/MetaQueryPlanStatistic.java | 98 ------------- .../java/io/ebean/meta/MetaTimedMetric.java | 5 + src/main/java/io/ebean/meta/MetricType.java | 30 ++++ .../java/io/ebean/meta/MetricVisitor.java | 48 ++++++ .../io/ebeaninternal/api/SpiEbeanServer.java | 6 + .../java/io/ebeaninternal/api/SpiQuery.java | 5 + .../io/ebeaninternal/api/SpiSqlBinding.java | 5 + .../java/io/ebeaninternal/api/SpiUpdate.java | 5 + .../ebeaninternal/metric/MetricFactory.java | 12 +- .../ebeaninternal/metric/QueryPlanMetric.java | 6 +- .../io/ebeaninternal/metric/TimedMetric.java | 15 +- .../ebeaninternal/metric/TimedMetricMap.java | 13 +- .../server/core/AbstractSqlQueryRequest.java | 2 +- .../core/CObjectGraphNodeStatistics.java | 6 +- .../server/core/DefaultMetaInfoManager.java | 57 +++++--- .../server/core/DefaultServer.java | 29 ++-- .../server/core/PersistRequest.java | 25 +++- .../core/PersistRequestCallableSql.java | 9 +- .../server/core/PersistRequestOrmUpdate.java | 6 +- .../server/core/PersistRequestUpdateSql.java | 6 +- .../ebeaninternal/server/core/Persister.java | 5 + .../server/core/RelationalQueryEngine.java | 10 ++ .../server/core/RelationalQueryRequest.java | 6 +- .../server/deploy/BeanDescriptor.java | 9 +- .../server/deploy/BeanDescriptorManager.java | 7 + .../server/dto/DtoBeanDescriptor.java | 6 +- .../server/dto/DtoBeanManager.java | 13 +- .../server/dto/DtoMappingRequest.java | 3 +- .../server/dto/DtoQueryPlan.java | 6 +- .../server/dto/DtoQueryPlanBase.java | 7 +- .../expression/DefaultExpressionList.java | 5 + .../server/expression/JunctionExpression.java | 5 + .../server/persist/DefaultPersistExecute.java | 39 ++++- .../server/persist/DefaultPersister.java | 6 + .../server/persist/ExeCallableSql.java | 1 + .../server/persist/ExeOrmUpdate.java | 1 + .../server/persist/ExeUpdateSql.java | 1 + .../server/persist/PersistExecute.java | 20 +++ .../server/profile/DMetricFactory.java | 18 +-- .../profile/DProfileLocationFactory.java | 3 +- .../server/profile/DQueryPlanMetric.java | 18 ++- .../server/profile/DTimeMetricStats.java | 11 +- .../server/profile/DTimedMetric.java | 43 +++--- .../server/profile/DTimedMetricMap.java | 20 ++- .../server/profile/DTimedProfileLocation.java | 12 +- .../server/profile/TimedProfileLocation.java | 8 +- .../server/query/CQueryPlan.java | 50 +++++-- .../server/query/CQueryPlanStats.java | 137 ++++++++---------- .../query/CQueryPlanStatsCollector.java | 32 ---- .../query/DefaultRelationalQueryEngine.java | 17 +++ .../server/querydefn/DefaultDtoQuery.java | 3 +- .../server/querydefn/DefaultOrmQuery.java | 13 ++ .../server/querydefn/DefaultOrmUpdate.java | 14 ++ .../querydefn/DefaultRelationalQuery.java | 13 ++ .../transaction/TransactionManager.java | 30 ++-- src/test/java/io/ebean/BaseTestCase.java | 25 ++++ .../java/io/ebean/DtoQueryFromOrmTest.java | 44 ++++-- src/test/java/io/ebean/DtoQueryTest.java | 14 +- src/test/java/io/ebean/UpdateQueryTest.java | 13 +- .../ebeaninternal/api/TDSpiEbeanServer.java | 6 + .../profile/BasicProfileLocationTest.java | 3 +- .../batchinsert/TestBatchInsertFlush.java | 8 +- .../org/tests/m2m/TestM2mDeleteObject.java | 15 +- .../query/finder/TestCustomerFinder.java | 16 +- .../TestObjectGraphNodeStatsCollection.java | 23 +-- .../tests/query/sqlquery/SqlQueryTests.java | 24 ++- .../transaction/TestNestedMandatory.java | 8 +- .../org/tests/update/TestSqlUpdateInTxn.java | 12 +- src/test/resources/ebean.properties | 2 +- 79 files changed, 928 insertions(+), 466 deletions(-) create mode 100644 src/main/java/io/ebean/meta/AbstractMetricVisitor.java create mode 100644 src/main/java/io/ebean/meta/BasicMetricVisitor.java create mode 100644 src/main/java/io/ebean/meta/MetaOrmQueryMetric.java rename src/main/java/io/ebean/meta/{MetaObjectGraphNodeStats.java => MetaOrmQueryNode.java} (88%) rename src/main/java/io/ebean/meta/{MetaQueryPlanOriginCount.java => MetaOrmQueryOrigin.java} (83%) delete mode 100644 src/main/java/io/ebean/meta/MetaQueryPlanStatistic.java create mode 100644 src/main/java/io/ebean/meta/MetricType.java create mode 100644 src/main/java/io/ebean/meta/MetricVisitor.java delete mode 100644 src/main/java/io/ebeaninternal/server/query/CQueryPlanStatsCollector.java diff --git a/src/main/java/io/ebean/ExpressionList.java b/src/main/java/io/ebean/ExpressionList.java index 588a6f7c0..4723ab212 100644 --- a/src/main/java/io/ebean/ExpressionList.java +++ b/src/main/java/io/ebean/ExpressionList.java @@ -545,6 +545,11 @@ public interface ExpressionList { */ Query setDisableReadAuditing(); + /** + * Set a label on the query (to help identify query execution statistics). + */ + Query setLabel(String label); + /** * Add expressions to the having clause. *

diff --git a/src/main/java/io/ebean/Query.java b/src/main/java/io/ebean/Query.java index c5a5f425e..4d18a580d 100644 --- a/src/main/java/io/ebean/Query.java +++ b/src/main/java/io/ebean/Query.java @@ -1419,6 +1419,15 @@ public interface Query { */ Query setProfileLocation(ProfileLocation profileLocation); + /** + * Set a label on the query. + *

+ * 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. + *

+ */ + Query setLabel(String label); + /** * Set to true if this query should execute against the doc store. *

diff --git a/src/main/java/io/ebean/SqlQuery.java b/src/main/java/io/ebean/SqlQuery.java index a83aa9319..6c1bcf00b 100644 --- a/src/main/java/io/ebean/SqlQuery.java +++ b/src/main/java/io/ebean/SqlQuery.java @@ -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(). *

diff --git a/src/main/java/io/ebean/Update.java b/src/main/java/io/ebean/Update.java index 637fb2c8c..70c225ce3 100644 --- a/src/main/java/io/ebean/Update.java +++ b/src/main/java/io/ebean/Update.java @@ -152,6 +152,11 @@ public interface Update { */ Update setNullParameter(String name, int jdbcType); + /** + * Set a label meaning performance metrics will be collected for the execution of this update. + */ + Update setLabel(String label); + /** * Return the sql that is actually executed. */ diff --git a/src/main/java/io/ebean/meta/AbstractMetricVisitor.java b/src/main/java/io/ebean/meta/AbstractMetricVisitor.java new file mode 100644 index 000000000..c2eb25b99 --- /dev/null +++ b/src/main/java/io/ebean/meta/AbstractMetricVisitor.java @@ -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 + } +} + diff --git a/src/main/java/io/ebean/meta/BasicMetricVisitor.java b/src/main/java/io/ebean/meta/BasicMetricVisitor.java new file mode 100644 index 000000000..bef165ed9 --- /dev/null +++ b/src/main/java/io/ebean/meta/BasicMetricVisitor.java @@ -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 timed = new ArrayList<>(); + private final List dtoQuery = new ArrayList<>(); + private final List 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 getTimedMetrics() { + return timed; + } + + /** + * Return the DTO query metrics. + */ + public List getDtoQueryMetrics() { + return dtoQuery; + } + + /** + * Return the ORM query metrics. + */ + public List 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); + } +} diff --git a/src/main/java/io/ebean/meta/MetaInfoManager.java b/src/main/java/io/ebean/meta/MetaInfoManager.java index c8d88cf83..9535de536 100644 --- a/src/main/java/io/ebean/meta/MetaInfoManager.java +++ b/src/main/java/io/ebean/meta/MetaInfoManager.java @@ -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 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 collectQueryStatistics(boolean reset); + BasicMetricVisitor visitBasic(); /** - * Collect and return the non-empty query plan statistics for all the beans. - *

- * Note that this excludes the query plan statistics where there has been no - * executions (since the last collection with reset). - *

+ * Just reset all the metrics. Maybe only useful for testing purposes. */ - List 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 collectNodeStatistics(boolean reset); + List collectNodeStatistics(boolean reset); } diff --git a/src/main/java/io/ebean/meta/MetaOrmQueryMetric.java b/src/main/java/io/ebean/meta/MetaOrmQueryMetric.java new file mode 100644 index 000000000..7a74409f2 --- /dev/null +++ b/src/main/java/io/ebean/meta/MetaOrmQueryMetric.java @@ -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. + *

+ * This includes direct and lazy loading paths. + *

+ */ + List getOrigins(); + +} diff --git a/src/main/java/io/ebean/meta/MetaObjectGraphNodeStats.java b/src/main/java/io/ebean/meta/MetaOrmQueryNode.java similarity index 88% rename from src/main/java/io/ebean/meta/MetaObjectGraphNodeStats.java rename to src/main/java/io/ebean/meta/MetaOrmQueryNode.java index 81421978b..0f1d6bd24 100644 --- a/src/main/java/io/ebean/meta/MetaObjectGraphNodeStats.java +++ b/src/main/java/io/ebean/meta/MetaOrmQueryNode.java @@ -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. *

- * - * @see MetaInfoManager#collectNodeStatistics(boolean) */ -public interface MetaObjectGraphNodeStats { +public interface MetaOrmQueryNode { /** * Return the ObjectGraphNode which has the origin point and relative path. diff --git a/src/main/java/io/ebean/meta/MetaQueryPlanOriginCount.java b/src/main/java/io/ebean/meta/MetaOrmQueryOrigin.java similarity index 83% rename from src/main/java/io/ebean/meta/MetaQueryPlanOriginCount.java rename to src/main/java/io/ebean/meta/MetaOrmQueryOrigin.java index 6ca5b3827..9f4d2f096 100644 --- a/src/main/java/io/ebean/meta/MetaQueryPlanOriginCount.java +++ b/src/main/java/io/ebean/meta/MetaOrmQueryOrigin.java @@ -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. *

- * - * @see MetaQueryPlanStatistic - * @see MetaInfoManager#collectQueryPlanStatistics(boolean) */ -public interface MetaQueryPlanOriginCount { +public interface MetaOrmQueryOrigin { /** * The 'origin' and path which this query belongs to. diff --git a/src/main/java/io/ebean/meta/MetaQueryPlanStatistic.java b/src/main/java/io/ebean/meta/MetaQueryPlanStatistic.java deleted file mode 100644 index fc01a0e7e..000000000 --- a/src/main/java/io/ebean/meta/MetaQueryPlanStatistic.java +++ /dev/null @@ -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. - *

- * This excludes background fetching. - *

- */ - 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. - *

- * This excludes background fetching. - *

- */ - long getAvgTimeMicros(); - - /** - * Return the average number of bean loaded per query. - *

- * This excludes background fetching. - *

- */ - 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. - *

- * This includes direct and lazy loading paths. - *

- */ - List getOrigins(); - -} diff --git a/src/main/java/io/ebean/meta/MetaTimedMetric.java b/src/main/java/io/ebean/meta/MetaTimedMetric.java index ada73a66f..53f67109d 100644 --- a/src/main/java/io/ebean/meta/MetaTimedMetric.java +++ b/src/main/java/io/ebean/meta/MetaTimedMetric.java @@ -6,6 +6,11 @@ package io.ebean.meta; */ public interface MetaTimedMetric { + /** + * Return the metric type. + */ + MetricType getMetricType(); + /** * Return the metric name. */ diff --git a/src/main/java/io/ebean/meta/MetricType.java b/src/main/java/io/ebean/meta/MetricType.java new file mode 100644 index 000000000..6b39cbc47 --- /dev/null +++ b/src/main/java/io/ebean/meta/MetricType.java @@ -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. + *

+ * SqlQuery and SqlUpdate without a label have no metrics collected. + */ + SQL + +} diff --git a/src/main/java/io/ebean/meta/MetricVisitor.java b/src/main/java/io/ebean/meta/MetricVisitor.java new file mode 100644 index 000000000..8b39e6499 --- /dev/null +++ b/src/main/java/io/ebean/meta/MetricVisitor.java @@ -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(); + +} diff --git a/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java b/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java index 305672805..eb449157f 100644 --- a/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java +++ b/src/main/java/io/ebeaninternal/api/SpiEbeanServer.java @@ -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); } diff --git a/src/main/java/io/ebeaninternal/api/SpiQuery.java b/src/main/java/io/ebeaninternal/api/SpiQuery.java index adaa9f422..4f7184830 100644 --- a/src/main/java/io/ebeaninternal/api/SpiQuery.java +++ b/src/main/java/io/ebeaninternal/api/SpiQuery.java @@ -179,6 +179,11 @@ public interface SpiQuery extends Query, 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. */ diff --git a/src/main/java/io/ebeaninternal/api/SpiSqlBinding.java b/src/main/java/io/ebeaninternal/api/SpiSqlBinding.java index 6da641dea..849ad0cb0 100644 --- a/src/main/java/io/ebeaninternal/api/SpiSqlBinding.java +++ b/src/main/java/io/ebeaninternal/api/SpiSqlBinding.java @@ -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. */ diff --git a/src/main/java/io/ebeaninternal/api/SpiUpdate.java b/src/main/java/io/ebeaninternal/api/SpiUpdate.java index ebe8e5ab8..2e2ed8531 100644 --- a/src/main/java/io/ebeaninternal/api/SpiUpdate.java +++ b/src/main/java/io/ebeaninternal/api/SpiUpdate.java @@ -43,6 +43,11 @@ public interface SpiUpdate extends Update { */ Class getBeanType(); + /** + * Return the label (for metrics collection). + */ + String getLabel(); + /** * Return the type of this - insert, update or delete. */ diff --git a/src/main/java/io/ebeaninternal/metric/MetricFactory.java b/src/main/java/io/ebeaninternal/metric/MetricFactory.java index 6f83c3e89..72ccbb3ad 100644 --- a/src/main/java/io/ebeaninternal/metric/MetricFactory.java +++ b/src/main/java/io/ebeaninternal/metric/MetricFactory.java @@ -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); } diff --git a/src/main/java/io/ebeaninternal/metric/QueryPlanMetric.java b/src/main/java/io/ebeaninternal/metric/QueryPlanMetric.java index 893a97593..f0d5e31db 100644 --- a/src/main/java/io/ebeaninternal/metric/QueryPlanMetric.java +++ b/src/main/java/io/ebeaninternal/metric/QueryPlanMetric.java @@ -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); } diff --git a/src/main/java/io/ebeaninternal/metric/TimedMetric.java b/src/main/java/io/ebeaninternal/metric/TimedMetric.java index a2a29e030..1bba42bb0 100644 --- a/src/main/java/io/ebeaninternal/metric/TimedMetric.java +++ b/src/main/java/io/ebeaninternal/metric/TimedMetric.java @@ -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 result); + void visit(MetricVisitor visitor); } diff --git a/src/main/java/io/ebeaninternal/metric/TimedMetricMap.java b/src/main/java/io/ebeaninternal/metric/TimedMetricMap.java index 3976b5121..801bc74c1 100644 --- a/src/main/java/io/ebeaninternal/metric/TimedMetricMap.java +++ b/src/main/java/io/ebeaninternal/metric/TimedMetricMap.java @@ -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 result); + void add(String key, long exeMicros, int rows); + + /** + * Visit the metric. + */ + void visit(MetricVisitor visitor); } diff --git a/src/main/java/io/ebeaninternal/server/core/AbstractSqlQueryRequest.java b/src/main/java/io/ebeaninternal/server/core/AbstractSqlQueryRequest.java index 03feedaf4..41371c912 100644 --- a/src/main/java/io/ebeaninternal/server/core/AbstractSqlQueryRequest.java +++ b/src/main/java/io/ebeaninternal/server/core/AbstractSqlQueryRequest.java @@ -24,7 +24,7 @@ import java.sql.SQLException; */ public abstract class AbstractSqlQueryRequest { - private final SpiSqlBinding query; + protected final SpiSqlBinding query; protected final SpiEbeanServer server; diff --git a/src/main/java/io/ebeaninternal/server/core/CObjectGraphNodeStatistics.java b/src/main/java/io/ebeaninternal/server/core/CObjectGraphNodeStatistics.java index 754c0a990..8b53a86aa 100644 --- a/src/main/java/io/ebeaninternal/server/core/CObjectGraphNodeStatistics.java +++ b/src/main/java/io/ebeaninternal/server/core/CObjectGraphNodeStatistics.java @@ -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; diff --git a/src/main/java/io/ebeaninternal/server/core/DefaultMetaInfoManager.java b/src/main/java/io/ebeaninternal/server/core/DefaultMetaInfoManager.java index b52c66390..6ebc4152d 100644 --- a/src/main/java/io/ebeaninternal/server/core/DefaultMetaInfoManager.java +++ b/src/main/java/io/ebeaninternal/server/core/DefaultMetaInfoManager.java @@ -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 collectTransactionStatistics(boolean reset) { - return server.collectTransactionStatistics(reset); + public void visitMetrics(MetricVisitor visitor) { + server.visitMetrics(visitor); } @Override - public List collectQueryStatistics(boolean reset) { - return server.collectQueryStatistics(reset); + public BasicMetricVisitor visitBasic() { + BasicMetricVisitor basic = new BasicMetricVisitor(); + visitMetrics(basic); + return basic; } @Override - public List 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 collectNodeStatistics(boolean reset) { + public List collectNodeStatistics(boolean reset) { - List list = new ArrayList<>(); + List 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 + } + } + } diff --git a/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/src/main/java/io/ebeaninternal/server/core/DefaultServer.java index 6629aee54..700f10439 100644 --- a/src/main/java/io/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/io/ebeaninternal/server/core/DefaultServer.java @@ -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 collectTransactionStatistics(boolean reset) { - return transactionManager.collectTransactionStatistics(reset); - } - @Override public Set 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 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 ret = new LinkedHashSet<>(); - for (Property prop : props) { - ret.add(prop); - } + Collections.addAll(ret, props); return ret; } return null; } - public List 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(); } } diff --git a/src/main/java/io/ebeaninternal/server/core/PersistRequest.java b/src/main/java/io/ebeaninternal/server/core/PersistRequest.java index c74515a7c..46e7aa605 100644 --- a/src/main/java/io/ebeaninternal/server/core/PersistRequest.java +++ b/src/main/java/io/ebeaninternal/server/core/PersistRequest.java @@ -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(); + } } /** diff --git a/src/main/java/io/ebeaninternal/server/core/PersistRequestCallableSql.java b/src/main/java/io/ebeaninternal/server/core/PersistRequestCallableSql.java index 7f203678e..432a4eb49 100644 --- a/src/main/java/io/ebeaninternal/server/core/PersistRequestCallableSql.java +++ b/src/main/java/io/ebeaninternal/server/core/PersistRequestCallableSql.java @@ -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); diff --git a/src/main/java/io/ebeaninternal/server/core/PersistRequestOrmUpdate.java b/src/main/java/io/ebeaninternal/server/core/PersistRequestOrmUpdate.java index 4a55f9db6..aa2f3092e 100644 --- a/src/main/java/io/ebeaninternal/server/core/PersistRequestOrmUpdate.java +++ b/src/main/java/io/ebeaninternal/server/core/PersistRequestOrmUpdate.java @@ -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(); diff --git a/src/main/java/io/ebeaninternal/server/core/PersistRequestUpdateSql.java b/src/main/java/io/ebeaninternal/server/core/PersistRequestUpdateSql.java index 05fd26cd6..5261de3f9 100644 --- a/src/main/java/io/ebeaninternal/server/core/PersistRequestUpdateSql.java +++ b/src/main/java/io/ebeaninternal/server/core/PersistRequestUpdateSql.java @@ -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); diff --git a/src/main/java/io/ebeaninternal/server/core/Persister.java b/src/main/java/io/ebeaninternal/server/core/Persister.java index f6044d040..981032053 100644 --- a/src/main/java/io/ebeaninternal/server/core/Persister.java +++ b/src/main/java/io/ebeaninternal/server/core/Persister.java @@ -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 { */ List draftRestore(Query query, Transaction transaction); + /** + * Visit the metrics. + */ + void visitMetrics(MetricVisitor visitor); } diff --git a/src/main/java/io/ebeaninternal/server/core/RelationalQueryEngine.java b/src/main/java/io/ebeaninternal/server/core/RelationalQueryEngine.java index ae4dde8f1..fa2a13b93 100644 --- a/src/main/java/io/ebeaninternal/server/core/RelationalQueryEngine.java +++ b/src/main/java/io/ebeaninternal/server/core/RelationalQueryEngine.java @@ -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 consumer); + /** + * Collect SQL query execution statistics. + */ + void collect(String label, long exeMicros, int rows); + + /** + * Visit the metrics. + */ + void visitMetrics(MetricVisitor visitor); } diff --git a/src/main/java/io/ebeaninternal/server/core/RelationalQueryRequest.java b/src/main/java/io/ebeaninternal/server/core/RelationalQueryRequest.java index dace84109..24278ea11 100644 --- a/src/main/java/io/ebeaninternal/server/core/RelationalQueryRequest.java +++ b/src/main/java/io/ebeaninternal/server/core/RelationalQueryRequest.java @@ -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 consumer) { diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java index 06810d7e0..8ba1b5ae6 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java @@ -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 implements BeanType { 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())); } } } diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java index b0044d91a..d1d449120 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java @@ -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. */ diff --git a/src/main/java/io/ebeaninternal/server/dto/DtoBeanDescriptor.java b/src/main/java/io/ebeaninternal/server/dto/DtoBeanDescriptor.java index 81920e858..04060e8a4 100644 --- a/src/main/java/io/ebeaninternal/server/dto/DtoBeanDescriptor.java +++ b/src/main/java/io/ebeaninternal/server/dto/DtoBeanDescriptor.java @@ -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 { 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); } } } diff --git a/src/main/java/io/ebeaninternal/server/dto/DtoBeanManager.java b/src/main/java/io/ebeaninternal/server/dto/DtoBeanManager.java index 87e58a453..7cbd9488d 100644 --- a/src/main/java/io/ebeaninternal/server/dto/DtoBeanManager.java +++ b/src/main/java/io/ebeaninternal/server/dto/DtoBeanManager.java @@ -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 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(); } } diff --git a/src/main/java/io/ebeaninternal/server/dto/DtoMappingRequest.java b/src/main/java/io/ebeaninternal/server/dto/DtoMappingRequest.java index d781e35c0..c7ab221ef 100644 --- a/src/main/java/io/ebeaninternal/server/dto/DtoMappingRequest.java +++ b/src/main/java/io/ebeaninternal/server/dto/DtoMappingRequest.java @@ -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); } } diff --git a/src/main/java/io/ebeaninternal/server/dto/DtoQueryPlan.java b/src/main/java/io/ebeaninternal/server/dto/DtoQueryPlan.java index 36d49c351..897e8cc5e 100644 --- a/src/main/java/io/ebeaninternal/server/dto/DtoQueryPlan.java +++ b/src/main/java/io/ebeaninternal/server/dto/DtoQueryPlan.java @@ -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); } diff --git a/src/main/java/io/ebeaninternal/server/dto/DtoQueryPlanBase.java b/src/main/java/io/ebeaninternal/server/dto/DtoQueryPlanBase.java index b4a867a50..b6218cbcd 100644 --- a/src/main/java/io/ebeaninternal/server/dto/DtoQueryPlanBase.java +++ b/src/main/java/io/ebeaninternal/server/dto/DtoQueryPlanBase.java @@ -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); } - } diff --git a/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java b/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java index ec0e9b738..29747b439 100644 --- a/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java +++ b/src/main/java/io/ebeaninternal/server/expression/DefaultExpressionList.java @@ -513,6 +513,11 @@ public class DefaultExpressionList implements SpiExpressionList { return query.setDisableReadAuditing(); } + @Override + public Query setLabel(String label) { + return query.setLabel(label); + } + @Override public ExpressionList having() { return query.having(); diff --git a/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java b/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java index 6b54867ff..4d4c3a53c 100644 --- a/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java +++ b/src/main/java/io/ebeaninternal/server/expression/JunctionExpression.java @@ -798,6 +798,11 @@ class JunctionExpression implements SpiJunction, SpiExpression, Expression return exprList.setCountDistinct(orderBy); } + @Override + public Query setLabel(String label) { + return exprList.setLabel(label); + } + @Override public ExpressionList startsWith(String propertyName, String value) { return exprList.startsWith(propertyName, value); diff --git a/src/main/java/io/ebeaninternal/server/persist/DefaultPersistExecute.java b/src/main/java/io/ebeaninternal/server/persist/DefaultPersistExecute.java index edaee965d..621f8338a 100644 --- a/src/main/java/io/ebeaninternal/server/persist/DefaultPersistExecute.java +++ b/src/main/java/io/ebeaninternal/server/persist/DefaultPersistExecute.java @@ -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 diff --git a/src/main/java/io/ebeaninternal/server/persist/DefaultPersister.java b/src/main/java/io/ebeaninternal/server/persist/DefaultPersister.java index a825c1653..0be2f64ae 100644 --- a/src/main/java/io/ebeaninternal/server/persist/DefaultPersister.java +++ b/src/main/java/io/ebeaninternal/server/persist/DefaultPersister.java @@ -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. */ diff --git a/src/main/java/io/ebeaninternal/server/persist/ExeCallableSql.java b/src/main/java/io/ebeaninternal/server/persist/ExeCallableSql.java index a1e8daa4e..3bc2dc3d2 100644 --- a/src/main/java/io/ebeaninternal/server/persist/ExeCallableSql.java +++ b/src/main/java/io/ebeaninternal/server/persist/ExeCallableSql.java @@ -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(); diff --git a/src/main/java/io/ebeaninternal/server/persist/ExeOrmUpdate.java b/src/main/java/io/ebeaninternal/server/persist/ExeOrmUpdate.java index 32faa0924..42cc54a98 100644 --- a/src/main/java/io/ebeaninternal/server/persist/ExeOrmUpdate.java +++ b/src/main/java/io/ebeaninternal/server/persist/ExeOrmUpdate.java @@ -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(); diff --git a/src/main/java/io/ebeaninternal/server/persist/ExeUpdateSql.java b/src/main/java/io/ebeaninternal/server/persist/ExeUpdateSql.java index fd1f0c7d4..88879458d 100644 --- a/src/main/java/io/ebeaninternal/server/persist/ExeUpdateSql.java +++ b/src/main/java/io/ebeaninternal/server/persist/ExeUpdateSql.java @@ -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(); diff --git a/src/main/java/io/ebeaninternal/server/persist/PersistExecute.java b/src/main/java/io/ebeaninternal/server/persist/PersistExecute.java index 04ce2db7c..283923fa8 100644 --- a/src/main/java/io/ebeaninternal/server/persist/PersistExecute.java +++ b/src/main/java/io/ebeaninternal/server/persist/PersistExecute.java @@ -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); } diff --git a/src/main/java/io/ebeaninternal/server/profile/DMetricFactory.java b/src/main/java/io/ebeaninternal/server/profile/DMetricFactory.java index 8eae9626c..40cd2b36a 100644 --- a/src/main/java/io/ebeaninternal/server/profile/DMetricFactory.java +++ b/src/main/java/io/ebeaninternal/server/profile/DMetricFactory.java @@ -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); - } } diff --git a/src/main/java/io/ebeaninternal/server/profile/DProfileLocationFactory.java b/src/main/java/io/ebeaninternal/server/profile/DProfileLocationFactory.java index 241c2a0ac..950e10740 100644 --- a/src/main/java/io/ebeaninternal/server/profile/DProfileLocationFactory.java +++ b/src/main/java/io/ebeaninternal/server/profile/DProfileLocationFactory.java @@ -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); diff --git a/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java b/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java index b358a5584..1d7dff3e9 100644 --- a/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java +++ b/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java @@ -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(); diff --git a/src/main/java/io/ebeaninternal/server/profile/DTimeMetricStats.java b/src/main/java/io/ebeaninternal/server/profile/DTimeMetricStats.java index 1a9d61f8f..dc446b4a6 100644 --- a/src/main/java/io/ebeaninternal/server/profile/DTimeMetricStats.java +++ b/src/main/java/io/ebeaninternal/server/profile/DTimeMetricStats.java @@ -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; diff --git a/src/main/java/io/ebeaninternal/server/profile/DTimedMetric.java b/src/main/java/io/ebeaninternal/server/profile/DTimedMetric.java index 42a7f682c..bb7ee0b6f 100644 --- a/src/main/java/io/ebeaninternal/server/profile/DTimedMetric.java +++ b/src/main/java/io/ebeaninternal/server/profile/DTimedMetric.java @@ -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 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(); - } - } diff --git a/src/main/java/io/ebeaninternal/server/profile/DTimedMetricMap.java b/src/main/java/io/ebeaninternal/server/profile/DTimedMetricMap.java index d077e7aea..37f1b7632 100644 --- a/src/main/java/io/ebeaninternal/server/profile/DTimedMetricMap.java +++ b/src/main/java/io/ebeaninternal/server/profile/DTimedMetricMap.java @@ -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 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 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); } } } diff --git a/src/main/java/io/ebeaninternal/server/profile/DTimedProfileLocation.java b/src/main/java/io/ebeaninternal/server/profile/DTimedProfileLocation.java index ea62a335b..5479bc686 100644 --- a/src/main/java/io/ebeaninternal/server/profile/DTimedProfileLocation.java +++ b/src/main/java/io/ebeaninternal/server/profile/DTimedProfileLocation.java @@ -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 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); } } - } diff --git a/src/main/java/io/ebeaninternal/server/profile/TimedProfileLocation.java b/src/main/java/io/ebeaninternal/server/profile/TimedProfileLocation.java index 36f702ecd..6402993ce 100644 --- a/src/main/java/io/ebeaninternal/server/profile/TimedProfileLocation.java +++ b/src/main/java/io/ebeaninternal/server/profile/TimedProfileLocation.java @@ -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 list); + void visit(MetricVisitor visitor); } diff --git a/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java b/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java index 670098f5b..a9f7921cd 100644 --- a/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java +++ b/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java @@ -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); + } } diff --git a/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java b/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java index 371801037..83defb38a 100644 --- a/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java +++ b/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java @@ -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 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 origins = getOrigins(reset); + return new Snapshot(queryPlan, collect, lastQueryTime, origins); } /** * Return the list/snapshot of the origins and their counter value. */ - private List getOrigins(boolean reset) { + private List getOrigins(boolean reset) { if (origins == null) { return Collections.emptyList(); } - List list = new ArrayList<>(origins.size()); + List list = new ArrayList<>(origins.size()); for (Entry 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 origins; - - Snapshot(CQueryPlan queryPlan, long count, long totalTime, long totalBeans, long maxTime, long startTime, long lastQueryTime, - List origins) { + private final List origins; + Snapshot(CQueryPlan queryPlan, TimedMetricStats metrics, long lastQueryTime, List 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 getOrigins() { + public List getOrigins() { return origins; } diff --git a/src/main/java/io/ebeaninternal/server/query/CQueryPlanStatsCollector.java b/src/main/java/io/ebeaninternal/server/query/CQueryPlanStatsCollector.java deleted file mode 100644 index cb458e3c9..000000000 --- a/src/main/java/io/ebeaninternal/server/query/CQueryPlanStatsCollector.java +++ /dev/null @@ -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 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 getList() { - return list; - } -} diff --git a/src/main/java/io/ebeaninternal/server/query/DefaultRelationalQueryEngine.java b/src/main/java/io/ebeaninternal/server/query/DefaultRelationalQueryEngine.java index 4be7bec7b..f1282f9d2 100644 --- a/src/main/java/io/ebeaninternal/server/query/DefaultRelationalQueryEngine.java +++ b/src/main/java/io/ebeaninternal/server/query/DefaultRelationalQueryEngine.java @@ -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 diff --git a/src/main/java/io/ebeaninternal/server/querydefn/DefaultDtoQuery.java b/src/main/java/io/ebeaninternal/server/querydefn/DefaultDtoQuery.java index cf3158974..3c7049fe2 100644 --- a/src/main/java/io/ebeaninternal/server/querydefn/DefaultDtoQuery.java +++ b/src/main/java/io/ebeaninternal/server/querydefn/DefaultDtoQuery.java @@ -51,6 +51,7 @@ public class DefaultDtoQuery implements SpiDtoQuery { this.server = server; this.descriptor = descriptor; this.ormQuery = ormQuery; + this.label = ormQuery.getLabel(); } /** @@ -65,7 +66,7 @@ public class DefaultDtoQuery implements SpiDtoQuery { @Override public String planKey() { - return sql+":first"+firstRow+":max"+maxRows; + return sql + ":first" + firstRow + ":max" + maxRows; } @Override diff --git a/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java index 7a908b410..0359e97c2 100644 --- a/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -96,6 +96,8 @@ public class DefaultOrmQuery implements SpiQuery { private Type type; + private String label; + private Mode mode = Mode.NORMAL; private Object tenantId; @@ -340,6 +342,17 @@ public class DefaultOrmQuery implements SpiQuery { return this; } + @Override + public String getLabel() { + return label; + } + + @Override + public Query setLabel(String label) { + this.label = label; + return this; + } + @Override public boolean isAutoTunable() { return nativeSql == null && beanDescriptor.isAutoTunable(); diff --git a/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmUpdate.java b/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmUpdate.java index 0e232909f..0381df322 100644 --- a/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmUpdate.java +++ b/src/main/java/io/ebeaninternal/server/querydefn/DefaultOrmUpdate.java @@ -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 implements SpiUpdate, Serializable { */ private final String name; + private String label; + /** * The parameters used to bind to the sql. */ @@ -137,6 +140,17 @@ public final class DefaultOrmUpdate implements SpiUpdate, Serializable { return name; } + @Override + public Update setLabel(String label) { + this.label = label; + return this; + } + + @Override + public String getLabel() { + return label; + } + @Override public String getUpdateStatement() { return updateStatement; diff --git a/src/main/java/io/ebeaninternal/server/querydefn/DefaultRelationalQuery.java b/src/main/java/io/ebeaninternal/server/querydefn/DefaultRelationalQuery.java index d2c9dfa6b..eb344b003 100644 --- a/src/main/java/io/ebeaninternal/server/querydefn/DefaultRelationalQuery.java +++ b/src/main/java/io/ebeaninternal/server/querydefn/DefaultRelationalQuery.java @@ -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; diff --git a/src/main/java/io/ebeaninternal/server/transaction/TransactionManager.java b/src/main/java/io/ebeaninternal/server/transaction/TransactionManager.java index 2a8bf37cc..c76358829 100644 --- a/src/main/java/io/ebeaninternal/server/transaction/TransactionManager.java +++ b/src/main/java/io/ebeaninternal/server/transaction/TransactionManager.java @@ -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 collectTransactionStatistics(boolean reset) { - - List 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); } } diff --git a/src/test/java/io/ebean/BaseTestCase.java b/src/test/java/io/ebean/BaseTestCase.java index 16e4ef45d..cee47bcaf 100644 --- a/src/test/java/io/ebean/BaseTestCase.java +++ b/src/test/java/io/ebean/BaseTestCase.java @@ -1,6 +1,9 @@ package io.ebean; import io.ebean.annotation.Platform; +import io.ebean.meta.BasicMetricVisitor; +import io.ebean.meta.MetaTimedMetric; +import io.ebean.meta.MetricType; import io.ebean.util.StringHelper; import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.api.SpiQuery; @@ -14,6 +17,8 @@ import org.slf4j.LoggerFactory; import org.tests.model.basic.Country; import java.sql.Types; +import java.util.List; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; @@ -52,6 +57,26 @@ public abstract class BaseTestCase { } } + protected void resetAllMetrics() { + server().getMetaInfoManager().resetAllMetrics(); + } + + protected BasicMetricVisitor visitMetricsBasic() { + return server().getMetaInfoManager().visitBasic(); + } + + protected List visitTimedMetrics() { + return visitMetricsBasic().getTimedMetrics(); + } + + protected List sqlMetrics() { + List timedMetrics = visitTimedMetrics(); + + return timedMetrics.stream() + .filter((it) -> it.getMetricType() == MetricType.SQL) + .collect(Collectors.toList()); + } + /** * Return the generated sql trimming column alias if required. */ diff --git a/src/test/java/io/ebean/DtoQueryFromOrmTest.java b/src/test/java/io/ebean/DtoQueryFromOrmTest.java index c72cfa330..0278a51c8 100644 --- a/src/test/java/io/ebean/DtoQueryFromOrmTest.java +++ b/src/test/java/io/ebean/DtoQueryFromOrmTest.java @@ -1,7 +1,11 @@ package io.ebean; +import io.ebean.meta.BasicMetricVisitor; import io.ebean.meta.MetaQueryMetric; +import io.ebean.meta.MetaTimedMetric; import org.ebeantest.LoggedSqlCollector; +import org.junit.AfterClass; +import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import org.tests.model.basic.Contact; @@ -13,18 +17,23 @@ import static org.assertj.core.api.Assertions.assertThat; public class DtoQueryFromOrmTest extends BaseTestCase { -// @BeforeClass -// public static void resetStats() { -// Ebean.getDefaultServer().getMetaInfoManager().collectQueryStatistics(true); -// } -// -// @AfterClass -// public static void reportStats() { -// List stats = Ebean.getDefaultServer().getMetaInfoManager().collectQueryStatistics(false); -// for (MetaQueryMetric stat : stats) { -// System.out.println(stat); -// } -// } + @BeforeClass + public static void resetStats() { + Ebean.getDefaultServer().getMetaInfoManager().resetAllMetrics(); + } + + @AfterClass + public static void reportStats() { + BasicMetricVisitor basic = Ebean.getDefaultServer().getMetaInfoManager().visitBasic(); + for (MetaQueryMetric metric : basic.getDtoQueryMetrics()) { + System.out.println(metric); + } + + System.out.println("-- transaction metrics --"); + for (MetaTimedMetric metric : basic.getTimedMetrics()) { + System.out.println(metric); + } + } @Ignore @Test @@ -32,9 +41,9 @@ public class DtoQueryFromOrmTest extends BaseTestCase { ResetBasicData.reset(); - Ebean.getDefaultServer().getMetaInfoManager().collectQueryStatistics(true); + resetAllMetrics(); - String[] prefix = {"Bl", "B", "Red"}; + String[] prefix = {"Bl", "B", "Red", "jim"}; for (String val : prefix) { List list = Ebean.find(Contact.class) @@ -43,12 +52,15 @@ public class DtoQueryFromOrmTest extends BaseTestCase { .orderBy().asc("lastName") .setMaxRows(10) .asDto(ContactDto.class) + .setLabel("prefixLoop") .findList(); System.out.println("List:" + list); } - List stats = Ebean.getDefaultServer().getMetaInfoManager().collectQueryStatistics(true); + BasicMetricVisitor basic = visitMetricsBasic(); + + List stats = basic.getDtoQueryMetrics(); for (MetaQueryMetric stat : stats) { System.out.println(stat); } @@ -72,6 +84,7 @@ public class DtoQueryFromOrmTest extends BaseTestCase { .where().isNotNull("email").isNotNull("lastName") .orderBy().asc("lastName") .asDto(ContactDto.class) + .setLabel("explicitId") .setRelaxedMode(); List dtos = query.findList(); @@ -126,6 +139,7 @@ public class DtoQueryFromOrmTest extends BaseTestCase { List contactDtos = Ebean.find(Contact.class) + .setLabel("emailFullName") .select("email, concat(lastName,', ',firstName) as fullName") .where().isNotNull("email").isNotNull("lastName") .orderBy().asc("lastName") diff --git a/src/test/java/io/ebean/DtoQueryTest.java b/src/test/java/io/ebean/DtoQueryTest.java index 53bf02730..7957559dc 100644 --- a/src/test/java/io/ebean/DtoQueryTest.java +++ b/src/test/java/io/ebean/DtoQueryTest.java @@ -1,5 +1,6 @@ package io.ebean; +import io.ebean.meta.BasicMetricVisitor; import io.ebean.meta.MetaQueryMetric; import org.ebeantest.LoggedSqlCollector; import org.junit.Test; @@ -102,7 +103,7 @@ public class DtoQueryTest extends BaseTestCase { ResetBasicData.reset(); - server().getMetaInfoManager().collectQueryStatistics(true); + resetAllMetrics(); String[] names = {"Rob", "Fiona", "Shrek"}; @@ -116,14 +117,17 @@ public class DtoQueryTest extends BaseTestCase { log.info("Found " + custs); } - List stats = server().getMetaInfoManager().collectQueryStatistics(false); + // collect without reset + BasicMetricVisitor basic = new BasicMetricVisitor(false, true, true); + server().getMetaInfoManager().visitMetrics(basic); + List stats = basic.getDtoQueryMetrics(); assertThat(stats).hasSize(1); MetaQueryMetric queryMetric = stats.get(0); - assertThat(queryMetric.getLabel()).isEqualTo("basic"); assertThat(queryMetric.getCount()).isEqualTo(3); + assertThat(queryMetric.getName()).isEqualTo("basic"); server().findDto(DCust.class, "select c4.id, c4.name from o_customer c4 where lower(c4.name) = :name") @@ -131,7 +135,9 @@ public class DtoQueryTest extends BaseTestCase { .setParameter("name", "rob") .findList(); - stats = server().getMetaInfoManager().collectQueryStatistics(true); + BasicMetricVisitor metric2 = server().getMetaInfoManager().visitBasic(); + + stats = metric2.getDtoQueryMetrics(); assertThat(stats).hasSize(2); log.info("stats " + stats); diff --git a/src/test/java/io/ebean/UpdateQueryTest.java b/src/test/java/io/ebean/UpdateQueryTest.java index 0a8e81fbc..00498227d 100644 --- a/src/test/java/io/ebean/UpdateQueryTest.java +++ b/src/test/java/io/ebean/UpdateQueryTest.java @@ -2,11 +2,14 @@ package io.ebean; import io.ebean.annotation.IgnorePlatform; import io.ebean.annotation.Platform; +import io.ebean.meta.BasicMetricVisitor; +import io.ebean.meta.MetaOrmQueryMetric; import org.junit.Test; import org.tests.model.basic.Country; import org.tests.model.basic.Customer; import java.sql.Timestamp; +import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @@ -16,6 +19,8 @@ public class UpdateQueryTest extends BaseTestCase { @Test public void basic() { + resetAllMetrics(); + EbeanServer server = server(); UpdateQuery update = server.update(Customer.class); Query query = update @@ -24,11 +29,17 @@ public class UpdateQueryTest extends BaseTestCase { .where() .eq("status", Customer.Status.NEW) .gt("id", 1000) - .query(); + .setLabel("updateActive"); query.update(); assertThat(query.getGeneratedSql()).contains("update o_customer set status=?, updtime=? where status = ? and id > ?"); + + BasicMetricVisitor basic = visitMetricsBasic(); + List ormQueryMetrics = basic.getOrmQueryMetrics(); + assertThat(ormQueryMetrics).hasSize(1); + assertThat(ormQueryMetrics.get(0).getType()).isEqualTo(Customer.class); + assertThat(ormQueryMetrics.get(0).getLabel()).isEqualTo("updateActive"); } @Test diff --git a/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java b/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java index 349767266..62265c0fb 100644 --- a/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java +++ b/src/test/java/io/ebeaninternal/api/TDSpiEbeanServer.java @@ -36,6 +36,7 @@ import io.ebean.config.dbplatform.DatabasePlatform; import io.ebean.event.readaudit.ReadAuditLogger; import io.ebean.event.readaudit.ReadAuditPrepare; import io.ebean.meta.MetaInfoManager; +import io.ebean.meta.MetricVisitor; import io.ebean.plugin.Property; import io.ebean.plugin.SpiServer; import io.ebean.text.csv.CsvReader; @@ -275,6 +276,11 @@ public class TDSpiEbeanServer implements SpiEbeanServer { } + @Override + public void visitMetrics(MetricVisitor visitor) { + + } + @Override public void loadMany(BeanCollection collection, boolean onlyIds) { diff --git a/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java b/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java index a6295b842..457bfad89 100644 --- a/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java +++ b/src/test/java/io/ebeaninternal/server/profile/BasicProfileLocationTest.java @@ -1,5 +1,6 @@ package io.ebeaninternal.server.profile; +import io.ebean.meta.MetricType; import io.ebeaninternal.metric.MetricFactory; import org.junit.Test; @@ -10,7 +11,7 @@ public class BasicProfileLocationTest { @Test public void obtain() { - DProfileLocation loc = new DTimedProfileLocation(12, "foo", MetricFactory.get().createTimedMetric("junk")); + DProfileLocation loc = new DTimedProfileLocation(12, "foo", MetricFactory.get().createTimedMetric(MetricType.TXN, "junk")); assertThat(loc.obtain()).endsWith(":12)"); assertThat(loc.shortDescription()).isEqualTo("NativeMethodAccessorImpl.invoke0(Native Method:12)"); diff --git a/src/test/java/org/tests/batchinsert/TestBatchInsertFlush.java b/src/test/java/org/tests/batchinsert/TestBatchInsertFlush.java index 7046d8c64..37998bfdc 100644 --- a/src/test/java/org/tests/batchinsert/TestBatchInsertFlush.java +++ b/src/test/java/org/tests/batchinsert/TestBatchInsertFlush.java @@ -6,7 +6,7 @@ import io.ebean.EbeanServer; import io.ebean.Transaction; import io.ebean.annotation.PersistBatch; import io.ebean.annotation.Transactional; -import io.ebean.meta.MetaInfoManager; +import io.ebean.meta.BasicMetricVisitor; import io.ebean.meta.MetaTimedMetric; import io.ebeaninternal.api.SpiTransaction; import org.ebeantest.LoggedSqlCollector; @@ -29,8 +29,7 @@ public class TestBatchInsertFlush extends BaseTestCase { EbeanServer server = Ebean.getDefaultServer(); - MetaInfoManager metaInfoManager = server.getMetaInfoManager(); - metaInfoManager.collectTransactionStatistics(true); + resetAllMetrics(); Transaction transaction = server.beginTransaction(); try { @@ -76,7 +75,8 @@ public class TestBatchInsertFlush extends BaseTestCase { transaction.end(); } - List txnStats = metaInfoManager.collectTransactionStatistics(true); + BasicMetricVisitor basic = visitMetricsBasic(); + List txnStats = basic.getTimedMetrics(); for (MetaTimedMetric txnMetric : txnStats) { System.out.println(txnMetric); } diff --git a/src/test/java/org/tests/m2m/TestM2mDeleteObject.java b/src/test/java/org/tests/m2m/TestM2mDeleteObject.java index 9fed238ca..3ba05ee68 100644 --- a/src/test/java/org/tests/m2m/TestM2mDeleteObject.java +++ b/src/test/java/org/tests/m2m/TestM2mDeleteObject.java @@ -2,25 +2,34 @@ package org.tests.m2m; import io.ebean.BaseTestCase; import io.ebean.Ebean; +import io.ebean.meta.MetaTimedMetric; +import org.junit.Assert; +import org.junit.Test; import org.tests.model.m2m.Permission; import org.tests.model.m2m.Role; import org.tests.model.m2m.Tenant; -import org.junit.Assert; -import org.junit.Test; import java.util.HashSet; import java.util.List; import java.util.Set; +import static org.assertj.core.api.Assertions.assertThat; + public class TestM2mDeleteObject extends BaseTestCase { @Test public void test() { - Ebean.createUpdate(Permission.class, "delete from Permission").execute(); + resetAllMetrics(); + + Ebean.createUpdate(Permission.class, "delete from Permission").setLabel("deleteAllPermissions").execute(); Ebean.createUpdate(Tenant.class, "delete from Tenant").execute(); Ebean.createUpdate(Role.class, "delete from Role").execute(); + List sqlMetrics = sqlMetrics(); + assertThat(sqlMetrics).hasSize(1); + assertThat(sqlMetrics.get(0).getName()).isEqualTo("orm.update.deleteAllPermissions"); + Tenant t = new Tenant("tenant"); Ebean.save(t); diff --git a/src/test/java/org/tests/query/finder/TestCustomerFinder.java b/src/test/java/org/tests/query/finder/TestCustomerFinder.java index bfd51e476..63e41a954 100644 --- a/src/test/java/org/tests/query/finder/TestCustomerFinder.java +++ b/src/test/java/org/tests/query/finder/TestCustomerFinder.java @@ -3,8 +3,8 @@ package org.tests.query.finder; import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.Transaction; -import io.ebean.meta.MetaInfoManager; -import io.ebean.meta.MetaQueryPlanStatistic; +import io.ebean.meta.BasicMetricVisitor; +import io.ebean.meta.MetaOrmQueryMetric; import io.ebean.meta.MetaTimedMetric; import org.ebeantest.LoggedSqlCollector; import org.junit.Test; @@ -136,9 +136,7 @@ public class TestCustomerFinder extends BaseTestCase { ResetBasicData.reset(); - MetaInfoManager metaInfoManager = Ebean.getDefaultServer().getMetaInfoManager(); - metaInfoManager.collectQueryPlanStatistics(true); - metaInfoManager.collectTransactionStatistics(true); + resetAllMetrics(); List customers = Customer.find.all(); assertThat(customers).isNotEmpty(); @@ -153,14 +151,16 @@ public class TestCustomerFinder extends BaseTestCase { Customer.find.updateNames("Junk", 2000); Customer.find.byId(3); - List planStats = metaInfoManager.collectQueryPlanStatistics(true); + BasicMetricVisitor basic = server().getMetaInfoManager().visitBasic(); + + List planStats = basic.getOrmQueryMetrics(); assertThat(planStats.size()).isGreaterThan(4); - for (MetaQueryPlanStatistic planStat : planStats) { + for (MetaOrmQueryMetric planStat : planStats) { System.out.println(planStat); } - for (MetaTimedMetric txnTimed : metaInfoManager.collectTransactionStatistics(true)) { + for (MetaTimedMetric txnTimed : basic.getTimedMetrics()) { System.out.println(txnTimed); } } diff --git a/src/test/java/org/tests/query/other/TestObjectGraphNodeStatsCollection.java b/src/test/java/org/tests/query/other/TestObjectGraphNodeStatsCollection.java index 228568a8e..a7c26b691 100644 --- a/src/test/java/org/tests/query/other/TestObjectGraphNodeStatsCollection.java +++ b/src/test/java/org/tests/query/other/TestObjectGraphNodeStatsCollection.java @@ -3,16 +3,17 @@ package org.tests.query.other; import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.EbeanServer; +import io.ebean.meta.BasicMetricVisitor; import io.ebean.meta.MetaInfoManager; -import io.ebean.meta.MetaObjectGraphNodeStats; -import io.ebean.meta.MetaQueryPlanStatistic; +import io.ebean.meta.MetaOrmQueryMetric; +import io.ebean.meta.MetaOrmQueryNode; +import org.junit.Assert; +import org.junit.Test; import org.tests.model.basic.Address; import org.tests.model.basic.Customer; import org.tests.model.basic.Order; import org.tests.model.basic.OrderDetail; import org.tests.model.basic.ResetBasicData; -import org.junit.Assert; -import org.junit.Test; import java.util.List; @@ -30,19 +31,19 @@ public class TestObjectGraphNodeStatsCollection extends BaseTestCase { server.find(Order.class).findCount(); infoManager.collectNodeStatistics(true); - infoManager.collectQueryPlanStatistics(true); + infoManager.resetAllMetrics(); runFindOrderQuery(server); runFindCustomerQuery(server); - List nodeStatistics = infoManager.collectNodeStatistics(true); - for (MetaObjectGraphNodeStats stat : nodeStatistics) { - stat.toString(); + List nodeStatistics = infoManager.collectNodeStatistics(true); + for (MetaOrmQueryNode stat : nodeStatistics) { + System.out.println(stat); } - List planStatistics = infoManager.collectQueryPlanStatistics(true); - for (MetaQueryPlanStatistic planStatistic : planStatistics) { - planStatistic.getSql(); + BasicMetricVisitor basic = infoManager.visitBasic(); + for (MetaOrmQueryMetric queryMetric : basic.getOrmQueryMetrics()) { + System.out.println(queryMetric); } } diff --git a/src/test/java/org/tests/query/sqlquery/SqlQueryTests.java b/src/test/java/org/tests/query/sqlquery/SqlQueryTests.java index 39d71a2d3..1e0a5e830 100644 --- a/src/test/java/org/tests/query/sqlquery/SqlQueryTests.java +++ b/src/test/java/org/tests/query/sqlquery/SqlQueryTests.java @@ -4,6 +4,7 @@ import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.SqlQuery; import io.ebean.SqlRow; +import io.ebean.meta.MetaTimedMetric; import org.ebeantest.LoggedSqlCollector; import org.junit.Test; import org.tests.model.basic.Order; @@ -114,9 +115,13 @@ public class SqlQueryTests extends BaseTestCase { ResetBasicData.reset(); - SqlQuery sqlQuery = Ebean.createSqlQuery("select * from o_order where o_order.id > :id order by id"); - sqlQuery.setParameter("id", 3); - sqlQuery.setMaxRows(10); + resetAllMetrics(); + + SqlQuery sqlQuery = Ebean.createSqlQuery("select * from o_order where o_order.id > :id order by id") + .setParameter("id", 3) + .setMaxRows(10) + .setLabel("findList-3-10"); + LoggedSqlCollector.start(); sqlQuery.findList(); @@ -129,6 +134,8 @@ public class SqlQueryTests extends BaseTestCase { } else { assertThat(sql.get(0)).contains("select * from o_order where o_order.id > ? order by id limit 10;"); } + + assertThat(sqlMetrics()).isNotEmpty(); } @Test @@ -136,8 +143,11 @@ public class SqlQueryTests extends BaseTestCase { ResetBasicData.reset(); - SqlQuery sqlQuery = Ebean.createSqlQuery("Select * from o_order"); - sqlQuery.setMaxRows(10); + resetAllMetrics(); + + SqlQuery sqlQuery = Ebean.createSqlQuery("Select * from o_order") + .setMaxRows(10) + .setLabel("findEach-Max10Rows"); LoggedSqlCollector.start(); sqlQuery.findEach(bean -> bean.get("id")); @@ -150,6 +160,10 @@ public class SqlQueryTests extends BaseTestCase { } else { assertThat(sql.get(0)).contains("limit 10"); } + + List sqlMetrics = sqlMetrics(); + assertThat(sqlMetrics).hasSize(1); + assertThat(sqlMetrics.get(0).getName()).isEqualTo("sql.query.findEach-Max10Rows"); } @Test diff --git a/src/test/java/org/tests/transaction/TestNestedMandatory.java b/src/test/java/org/tests/transaction/TestNestedMandatory.java index 97578d46b..91eb4e14f 100644 --- a/src/test/java/org/tests/transaction/TestNestedMandatory.java +++ b/src/test/java/org/tests/transaction/TestNestedMandatory.java @@ -5,7 +5,6 @@ import io.ebean.Ebean; import io.ebean.Transaction; import io.ebean.annotation.Transactional; import io.ebean.annotation.TxType; -import io.ebean.meta.MetaInfoManager; import io.ebean.meta.MetaTimedMetric; import org.junit.Test; import org.slf4j.Logger; @@ -19,17 +18,16 @@ public class TestNestedMandatory extends BaseTestCase { private static final Logger log = LoggerFactory.getLogger(TestNestedMandatory.class); - static Transaction outerTxn; + private static Transaction outerTxn; @Test public void test() { - MetaInfoManager metaInfoManager = Ebean.getDefaultServer().getMetaInfoManager(); - metaInfoManager.collectTransactionStatistics(true); + resetAllMetrics(); new Outer().doOuter(); - List txnMetrics = metaInfoManager.collectTransactionStatistics(true); + List txnMetrics = visitTimedMetrics(); for (MetaTimedMetric txnTimed : txnMetrics) { System.out.println(txnTimed); } diff --git a/src/test/java/org/tests/update/TestSqlUpdateInTxn.java b/src/test/java/org/tests/update/TestSqlUpdateInTxn.java index 3b167abc1..e306e477d 100644 --- a/src/test/java/org/tests/update/TestSqlUpdateInTxn.java +++ b/src/test/java/org/tests/update/TestSqlUpdateInTxn.java @@ -3,10 +3,13 @@ package org.tests.update; import io.ebean.BaseTestCase; import io.ebean.Ebean; import io.ebean.SqlUpdate; +import io.ebean.meta.MetaTimedMetric; import org.junit.Assert; import org.junit.Test; import org.tests.idkeys.db.AuditLog; +import java.util.List; + import static org.assertj.core.api.Assertions.assertThat; public class TestSqlUpdateInTxn extends BaseTestCase { @@ -40,11 +43,18 @@ public class TestSqlUpdateInTxn extends BaseTestCase { String sql = " \nupdate audit_log \nset description = description \nwhere id = id"; - SqlUpdate sqlUpdate = Ebean.createSqlUpdate(sql); + resetAllMetrics(); + + SqlUpdate sqlUpdate = Ebean.createSqlUpdate(sql).setLabel("auditLargeUpdate"); sqlUpdate.execute(); assertThat(sqlUpdate.getSql()).isEqualTo(sql.trim()); assertThat(sqlUpdate.getGeneratedSql()).isEqualTo(sql.trim()); + + List sqlMetrics = sqlMetrics(); + assertThat(sqlMetrics).hasSize(1); + assertThat(sqlMetrics.get(0).getName()).isEqualTo("sql.update.auditLargeUpdate"); + assertThat(sqlMetrics.get(0).getCount()).isEqualTo(1); } @Test diff --git a/src/test/resources/ebean.properties b/src/test/resources/ebean.properties index b33a6e823..3b048c4c2 100644 --- a/src/test/resources/ebean.properties +++ b/src/test/resources/ebean.properties @@ -19,7 +19,7 @@ ebean.ddl.generate=true ebean.ddl.run=true ebean.ddl.header=-- Generated by ebean ${version} at ${timestamp} ebean.packages=org.tests -datasource.default=pg +datasource.default=h2 ebean.autoReadOnlyDataSource=true