mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#1345 - Refactor io.ebean.meta.MetaInfoManager API (query execution metrics). Breaking change for people collecting query execution metrics.
This commit is contained in:
@@ -24,7 +24,7 @@ import java.sql.SQLException;
|
||||
*/
|
||||
public abstract class AbstractSqlQueryRequest {
|
||||
|
||||
private final SpiSqlBinding query;
|
||||
protected final SpiSqlBinding query;
|
||||
|
||||
protected final SpiEbeanServer server;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.bean.ObjectGraphNode;
|
||||
import io.ebean.meta.MetaObjectGraphNodeStats;
|
||||
import io.ebean.meta.MetaOrmQueryNode;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
@@ -35,7 +35,7 @@ public class CObjectGraphNodeStatistics {
|
||||
totalBeans.add(beanCount);
|
||||
}
|
||||
|
||||
public MetaObjectGraphNodeStats get(boolean reset) {
|
||||
public MetaOrmQueryNode get(boolean reset) {
|
||||
if (reset) {
|
||||
return new Snapshot(node, startTime.getAndSet(System.currentTimeMillis()), count.sumThenReset(),
|
||||
totalTime.sumThenReset(), totalBeans.sumThenReset());
|
||||
@@ -44,7 +44,7 @@ public class CObjectGraphNodeStatistics {
|
||||
}
|
||||
}
|
||||
|
||||
private static class Snapshot implements MetaObjectGraphNodeStats {
|
||||
private static class Snapshot implements MetaOrmQueryNode {
|
||||
|
||||
private final ObjectGraphNode node;
|
||||
private final long startTime;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.meta.AbstractMetricVisitor;
|
||||
import io.ebean.meta.BasicMetricVisitor;
|
||||
import io.ebean.meta.MetaInfoManager;
|
||||
import io.ebean.meta.MetaObjectGraphNodeStats;
|
||||
import io.ebean.meta.MetaOrmQueryMetric;
|
||||
import io.ebean.meta.MetaOrmQueryNode;
|
||||
import io.ebean.meta.MetaQueryMetric;
|
||||
import io.ebean.meta.MetaQueryPlanStatistic;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.query.CQueryPlanStatsCollector;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -23,29 +24,26 @@ public class DefaultMetaInfoManager implements MetaInfoManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaTimedMetric> collectTransactionStatistics(boolean reset) {
|
||||
return server.collectTransactionStatistics(reset);
|
||||
public void visitMetrics(MetricVisitor visitor) {
|
||||
server.visitMetrics(visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaQueryMetric> collectQueryStatistics(boolean reset) {
|
||||
return server.collectQueryStatistics(reset);
|
||||
public BasicMetricVisitor visitBasic() {
|
||||
BasicMetricVisitor basic = new BasicMetricVisitor();
|
||||
visitMetrics(basic);
|
||||
return basic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaQueryPlanStatistic> collectQueryPlanStatistics(boolean reset) {
|
||||
|
||||
CQueryPlanStatsCollector collector = new CQueryPlanStatsCollector(reset);
|
||||
for (BeanDescriptor<?> desc : server.getBeanDescriptors()) {
|
||||
desc.collectQueryPlanStatistics(collector);
|
||||
}
|
||||
return collector.getList();
|
||||
public void resetAllMetrics() {
|
||||
server.visitMetrics(new ResetVisitor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaObjectGraphNodeStats> collectNodeStatistics(boolean reset) {
|
||||
public List<MetaOrmQueryNode> collectNodeStatistics(boolean reset) {
|
||||
|
||||
List<MetaObjectGraphNodeStats> list = new ArrayList<>();
|
||||
List<MetaOrmQueryNode> list = new ArrayList<>();
|
||||
for (CObjectGraphNodeStatistics nodeStatistics : server.objectGraphStats.values()) {
|
||||
if (!nodeStatistics.isEmpty()) {
|
||||
list.add(nodeStatistics.get(reset));
|
||||
@@ -54,4 +52,29 @@ public class DefaultMetaInfoManager implements MetaInfoManager {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Visitor that resets the statistics but doesn't collect them.
|
||||
*/
|
||||
private static class ResetVisitor extends AbstractMetricVisitor {
|
||||
|
||||
ResetVisitor() {
|
||||
super(true, true, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTimed(MetaTimedMetric metric) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitQuery(MetaQueryMetric metric) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOrmQuery(MetaOrmQueryMetric metric) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,8 +48,7 @@ import io.ebean.event.BeanPersistController;
|
||||
import io.ebean.event.readaudit.ReadAuditLogger;
|
||||
import io.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import io.ebean.meta.MetaInfoManager;
|
||||
import io.ebean.meta.MetaQueryMetric;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.plugin.Plugin;
|
||||
import io.ebean.plugin.Property;
|
||||
@@ -2192,10 +2191,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
public List<MetaTimedMetric> collectTransactionStatistics(boolean reset) {
|
||||
return transactionManager.collectTransactionStatistics(reset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Property> checkUniqueness(Object bean) {
|
||||
return checkUniqueness(bean, null);
|
||||
@@ -2237,8 +2232,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
|
||||
/**
|
||||
* Returns a set of properties if saving the bean will violate the unique constraints
|
||||
* (definded by given properties).
|
||||
* Returns a set of properties if saving the bean will violate the unique constraints (defined by given properties).
|
||||
*/
|
||||
private Set<Property> checkUniqueness(EntityBean entityBean, BeanDescriptor<?> beanDesc, BeanProperty[] props,
|
||||
Transaction transaction) {
|
||||
@@ -2261,15 +2255,24 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
if (findCount(query, transaction) > 0) {
|
||||
Set<Property> ret = new LinkedHashSet<>();
|
||||
for (Property prop : props) {
|
||||
ret.add(prop);
|
||||
}
|
||||
Collections.addAll(ret, props);
|
||||
return ret;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<MetaQueryMetric> collectQueryStatistics(boolean reset) {
|
||||
return dtoBeanManager.collectStats(reset);
|
||||
@Override
|
||||
public void visitMetrics(MetricVisitor visitor) {
|
||||
visitor.visitStart();
|
||||
if (visitor.isCollectTransactionMetrics()) {
|
||||
transactionManager.visitMetrics(visitor);
|
||||
}
|
||||
if (visitor.isCollectQueryMetrics()) {
|
||||
beanDescriptorManager.visitMetrics(visitor);
|
||||
dtoBeanManager.visitMetrics(visitor);
|
||||
relationalQueryEngine.visitMetrics(visitor);
|
||||
persister.visitMetrics(visitor);
|
||||
}
|
||||
visitor.visitEnd();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ public abstract class PersistRequest extends BeanRequest implements BatchPostExe
|
||||
UPDATESQL(EVT_UPDATESQL),
|
||||
CALLABLESQL(EVT_CALLABLESQL);
|
||||
String profileEventId;
|
||||
|
||||
Type(String profileEventId) {
|
||||
this.profileEventId = profileEventId;
|
||||
}
|
||||
@@ -36,12 +37,30 @@ public abstract class PersistRequest extends BeanRequest implements BatchPostExe
|
||||
|
||||
final PersistExecute persistExecute;
|
||||
|
||||
protected String label;
|
||||
|
||||
protected long startNanos;
|
||||
|
||||
PersistRequest(SpiEbeanServer server, SpiTransaction t, PersistExecute persistExecute) {
|
||||
super(server, t);
|
||||
this.persistExecute = persistExecute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by CallableSqlRequest and UpdateSqlRequest.
|
||||
*/
|
||||
public PersistRequest(SpiEbeanServer server, SpiTransaction t, PersistExecute persistExecute) {
|
||||
super(server, t);
|
||||
this.persistExecute = persistExecute;
|
||||
PersistRequest(SpiEbeanServer server, SpiTransaction t, PersistExecute persistExecute, String label) {
|
||||
this(server, t, persistExecute);
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Effectively set start nanos if we are collecting metrics on a label.
|
||||
*/
|
||||
public void startBind(boolean batchThisRequest) {
|
||||
if (!batchThisRequest && label != null) {
|
||||
startNanos = System.nanoTime();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,10 +31,9 @@ public final class PersistRequestCallableSql extends PersistRequest {
|
||||
/**
|
||||
* Create.
|
||||
*/
|
||||
public PersistRequestCallableSql(SpiEbeanServer server,
|
||||
CallableSql cs, SpiTransaction t, PersistExecute persistExecute) {
|
||||
public PersistRequestCallableSql(SpiEbeanServer server, CallableSql cs, SpiTransaction t, PersistExecute persistExecute) {
|
||||
|
||||
super(server, t, persistExecute);
|
||||
super(server, t, persistExecute, cs.getLabel());
|
||||
this.type = PersistRequest.Type.CALLABLESQL;
|
||||
this.callableSql = (SpiCallableSql) cs;
|
||||
}
|
||||
@@ -88,7 +87,9 @@ public final class PersistRequestCallableSql extends PersistRequest {
|
||||
*/
|
||||
@Override
|
||||
public void postExecute() {
|
||||
|
||||
if (startNanos > 0) {
|
||||
persistExecute.collectSqlCall(label, startNanos, rowCount);
|
||||
}
|
||||
if (transaction.isLogSummary()) {
|
||||
String m = "CallableSql label[" + callableSql.getLabel() + "]" + " rows[" + rowCount + "]" + " bind[" + bindLog + "]";
|
||||
transaction.logSummary(m);
|
||||
|
||||
@@ -27,7 +27,7 @@ public final class PersistRequestOrmUpdate extends PersistRequest {
|
||||
public PersistRequestOrmUpdate(SpiEbeanServer server, BeanManager<?> mgr, SpiUpdate<?> ormUpdate,
|
||||
SpiTransaction t, PersistExecute persistExecute) {
|
||||
|
||||
super(server, t, persistExecute);
|
||||
super(server, t, persistExecute, ormUpdate.getLabel());
|
||||
this.beanDescriptor = mgr.getBeanDescriptor();
|
||||
this.ormUpdate = ormUpdate;
|
||||
}
|
||||
@@ -86,7 +86,9 @@ public final class PersistRequestOrmUpdate extends PersistRequest {
|
||||
*/
|
||||
@Override
|
||||
public void postExecute() {
|
||||
|
||||
if (startNanos > 0) {
|
||||
persistExecute.collectOrmUpdate(label, startNanos, rowCount);
|
||||
}
|
||||
OrmUpdateType ormUpdateType = ormUpdate.getOrmUpdateType();
|
||||
String tableName = ormUpdate.getBaseTable();
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ public final class PersistRequestUpdateSql extends PersistRequest {
|
||||
public PersistRequestUpdateSql(SpiEbeanServer server, SqlUpdate updateSql,
|
||||
SpiTransaction t, PersistExecute persistExecute) {
|
||||
|
||||
super(server, t, persistExecute);
|
||||
super(server, t, persistExecute, updateSql.getLabel());
|
||||
this.type = Type.UPDATESQL;
|
||||
this.updateSql = (SpiSqlUpdate) updateSql;
|
||||
}
|
||||
@@ -102,7 +102,9 @@ public final class PersistRequestUpdateSql extends PersistRequest {
|
||||
*/
|
||||
@Override
|
||||
public void postExecute() {
|
||||
|
||||
if (startNanos > 0) {
|
||||
persistExecute.collectSqlUpdate(label, startNanos, rowCount);
|
||||
}
|
||||
if (transaction.isLogSummary()) {
|
||||
String m = description + " table[" + tableName + "] rows[" + rowCount + "] bind[" + bindLog + "]";
|
||||
transaction.logSummary(m);
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.ebean.SqlUpdate;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.Update;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -78,4 +79,8 @@ public interface Persister {
|
||||
*/
|
||||
<T> List<T> draftRestore(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Visit the metrics.
|
||||
*/
|
||||
void visitMetrics(MetricVisitor visitor);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.core;
|
||||
|
||||
|
||||
import io.ebean.SqlRow;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
@@ -29,4 +30,13 @@ public interface RelationalQueryEngine {
|
||||
*/
|
||||
void findEach(RelationalQueryRequest request, Predicate<SqlRow> consumer);
|
||||
|
||||
/**
|
||||
* Collect SQL query execution statistics.
|
||||
*/
|
||||
void collect(String label, long exeMicros, int rows);
|
||||
|
||||
/**
|
||||
* Visit the metrics.
|
||||
*/
|
||||
void visitMetrics(MetricVisitor visitor);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,11 @@ public final class RelationalQueryRequest extends AbstractSqlQueryRequest {
|
||||
|
||||
@Override
|
||||
protected void requestComplete() {
|
||||
|
||||
String label = query.getLabel();
|
||||
if (label != null) {
|
||||
long exeMicros = (System.nanoTime() - startNano) / 1000L;
|
||||
queryEngine.collect(label, exeMicros, rows);
|
||||
}
|
||||
}
|
||||
|
||||
public void findEach(Consumer<SqlRow> consumer) {
|
||||
|
||||
@@ -27,6 +27,7 @@ import io.ebean.event.changelog.ChangeType;
|
||||
import io.ebean.event.readaudit.ReadAuditLogger;
|
||||
import io.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import io.ebean.event.readaudit.ReadEvent;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.plugin.BeanDocType;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.plugin.ExpressionPath;
|
||||
@@ -61,7 +62,6 @@ import io.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.persist.DmlUtil;
|
||||
import io.ebeaninternal.server.query.CQueryPlan;
|
||||
import io.ebeaninternal.server.query.CQueryPlanStatsCollector;
|
||||
import io.ebeaninternal.server.query.SqlTreeProperty;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import io.ebeaninternal.server.rawsql.SpiRawSql;
|
||||
@@ -1598,10 +1598,13 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
return new DeployUpdateParser(this).parse(ormUpdateStatement);
|
||||
}
|
||||
|
||||
public void collectQueryPlanStatistics(CQueryPlanStatsCollector collector) {
|
||||
/**
|
||||
* Visit all the ORM query plan metrics (includes UpdateQuery with updates and deletes).
|
||||
*/
|
||||
public void visitMetrics(MetricVisitor visitor) {
|
||||
for (CQueryPlan queryPlan : queryPlanCache.values()) {
|
||||
if (!queryPlan.isEmptyStats()) {
|
||||
collector.add(queryPlan.getSnapshot(collector.isReset()));
|
||||
visitor.visitOrmQuery(queryPlan.getSnapshot(visitor.isReset()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import io.ebean.event.changelog.ChangeLogFilter;
|
||||
import io.ebean.event.changelog.ChangeLogListener;
|
||||
import io.ebean.event.changelog.ChangeLogPrepare;
|
||||
import io.ebean.event.changelog.ChangeLogRegister;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.util.AnnotationUtil;
|
||||
import io.ebeaninternal.api.ConcurrencyMode;
|
||||
@@ -1632,6 +1633,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
target.setPrimaryKeyJoin(inverseJoin);
|
||||
}
|
||||
|
||||
public void visitMetrics(MetricVisitor visitor) {
|
||||
for (BeanDescriptor<?> desc : immutableDescriptorList) {
|
||||
desc.visitMetrics(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparator to sort the BeanDescriptors by name.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebeaninternal.metric.QueryPlanCollector;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -37,9 +37,9 @@ public class DtoBeanDescriptor<T> {
|
||||
plans.put(planKey, plan);
|
||||
}
|
||||
|
||||
public void collectStats(QueryPlanCollector collector) {
|
||||
public void visit(MetricVisitor visitor) {
|
||||
for (DtoQueryPlan plan : plans.values()) {
|
||||
plan.collectStats(collector);
|
||||
plan.visit(visitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebean.meta.MetaQueryMetric;
|
||||
import io.ebeaninternal.metric.MetricFactory;
|
||||
import io.ebeaninternal.metric.QueryPlanCollector;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.server.type.TypeManager;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@@ -41,13 +38,9 @@ public class DtoBeanManager {
|
||||
}
|
||||
}
|
||||
|
||||
public List<MetaQueryMetric> collectStats(boolean reset) {
|
||||
|
||||
QueryPlanCollector collector = MetricFactory.get().createCollector(reset);
|
||||
|
||||
public void visitMetrics(MetricVisitor visitor) {
|
||||
for (DtoBeanDescriptor value : descriptorMap.values()) {
|
||||
value.collectStats(collector);
|
||||
value.visit(visitor);
|
||||
}
|
||||
return collector.complete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebeaninternal.api.SpiDtoQuery;
|
||||
import io.ebeaninternal.metric.MetricFactory;
|
||||
import io.ebeaninternal.metric.QueryPlanMetric;
|
||||
@@ -44,6 +45,6 @@ public class DtoMappingRequest {
|
||||
}
|
||||
|
||||
public QueryPlanMetric createMetric() {
|
||||
return MetricFactory.get().createQueryPlanMetric(type, label, sql);
|
||||
return MetricFactory.get().createQueryPlanMetric(MetricType.DTO, type, label, sql);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebeaninternal.metric.QueryPlanCollector;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.server.type.DataReader;
|
||||
|
||||
import java.sql.SQLException;
|
||||
@@ -21,7 +21,7 @@ public interface DtoQueryPlan {
|
||||
void collect(long exeMicros, int rows);
|
||||
|
||||
/**
|
||||
* Collect the query plan statistics.
|
||||
* Visit the metric (if not empty).
|
||||
*/
|
||||
void collectStats(QueryPlanCollector collector);
|
||||
void visit(MetricVisitor visitor);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebeaninternal.server.dto;
|
||||
|
||||
import io.ebeaninternal.metric.QueryPlanCollector;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.metric.QueryPlanMetric;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
|
||||
@@ -21,8 +21,7 @@ abstract class DtoQueryPlanBase implements DtoQueryPlan {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collectStats(QueryPlanCollector collector) {
|
||||
planMetric.collect(collector);
|
||||
public void visit(MetricVisitor visitor) {
|
||||
planMetric.visit(visitor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -513,6 +513,11 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
return query.setDisableReadAuditing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> setLabel(String label) {
|
||||
return query.setLabel(label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> having() {
|
||||
return query.having();
|
||||
|
||||
@@ -798,6 +798,11 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
|
||||
return exprList.setCountDistinct(orderBy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> setLabel(String label) {
|
||||
return exprList.setLabel(label);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> startsWith(String propertyName, String value) {
|
||||
return exprList.startsWith(propertyName, value);
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package io.ebeaninternal.server.persist;
|
||||
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.metric.MetricFactory;
|
||||
import io.ebeaninternal.metric.TimedMetricMap;
|
||||
import io.ebeaninternal.server.core.PersistRequestCallableSql;
|
||||
import io.ebeaninternal.server.core.PersistRequestOrmUpdate;
|
||||
import io.ebeaninternal.server.core.PersistRequestUpdateSql;
|
||||
@@ -24,15 +28,48 @@ final class DefaultPersistExecute implements PersistExecute {
|
||||
*/
|
||||
private final int defaultBatchSize;
|
||||
|
||||
private final TimedMetricMap ormUpdateMetric;
|
||||
|
||||
private final TimedMetricMap sqlUpdateMetric;
|
||||
|
||||
private final TimedMetricMap sqlCallMetric;
|
||||
|
||||
/**
|
||||
* Construct this DmlPersistExecute.
|
||||
*/
|
||||
DefaultPersistExecute(Binder binder, int defaultBatchSize) {
|
||||
|
||||
this.exeOrmUpdate = new ExeOrmUpdate(binder);
|
||||
this.exeUpdateSql = new ExeUpdateSql(binder);
|
||||
this.exeCallableSql = new ExeCallableSql(binder);
|
||||
this.defaultBatchSize = defaultBatchSize;
|
||||
this.ormUpdateMetric = MetricFactory.get().createTimedMetricMap(MetricType.SQL, "orm.update.");
|
||||
this.sqlUpdateMetric = MetricFactory.get().createTimedMetricMap(MetricType.SQL, "sql.update.");
|
||||
this.sqlCallMetric = MetricFactory.get().createTimedMetricMap(MetricType.SQL, "sql.call.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMetrics(MetricVisitor visitor) {
|
||||
ormUpdateMetric.visit(visitor);
|
||||
sqlUpdateMetric.visit(visitor);
|
||||
sqlCallMetric.visit(visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collectOrmUpdate(String label, long startNanos, int rowCount) {
|
||||
long exeMicros = (System.nanoTime() - startNanos) / 1000L;
|
||||
ormUpdateMetric.add(label, exeMicros, rowCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collectSqlUpdate(String label, long startNanos, int rowCount) {
|
||||
long exeMicros = (System.nanoTime() - startNanos) / 1000L;
|
||||
sqlUpdateMetric.add(label, exeMicros, rowCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collectSqlCall(String label, long startNanos, int rowCount) {
|
||||
long exeMicros = (System.nanoTime() - startNanos) / 1000L;
|
||||
sqlCallMetric.add(label, exeMicros, rowCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -10,6 +10,7 @@ import io.ebean.bean.BeanCollection.ModifyListenMode;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.event.BeanPersistController;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.SpiUpdate;
|
||||
@@ -79,6 +80,11 @@ public final class DefaultPersister implements Persister {
|
||||
this.persistExecute = new DefaultPersistExecute(binder, server.getServerConfig().getPersistBatchSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMetrics(MetricVisitor visitor) {
|
||||
persistExecute.visitMetrics(visitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the CallableSql.
|
||||
*/
|
||||
|
||||
@@ -60,6 +60,7 @@ class ExeCallableSql {
|
||||
|
||||
private CallableStatement bindStmt(PersistRequestCallableSql request, boolean batchThisRequest) throws SQLException {
|
||||
|
||||
request.startBind(batchThisRequest);
|
||||
SpiCallableSql callableSql = request.getCallableSql();
|
||||
SpiTransaction t = request.getTransaction();
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ class ExeOrmUpdate {
|
||||
|
||||
private PreparedStatement bindStmt(PersistRequestOrmUpdate request, boolean batchThisRequest) throws SQLException {
|
||||
|
||||
request.startBind(batchThisRequest);
|
||||
SpiUpdate<?> ormUpdate = request.getOrmUpdate();
|
||||
SpiTransaction t = request.getTransaction();
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ class ExeUpdateSql {
|
||||
|
||||
private PreparedStatement bindStmt(PersistRequestUpdateSql request, boolean batchThisRequest) throws SQLException {
|
||||
|
||||
request.startBind(batchThisRequest);
|
||||
SpiSqlUpdate updateSql = request.getUpdateSql();
|
||||
SpiTransaction t = request.getTransaction();
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.persist;
|
||||
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.server.core.PersistRequestCallableSql;
|
||||
import io.ebeaninternal.server.core.PersistRequestOrmUpdate;
|
||||
@@ -34,4 +35,23 @@ public interface PersistExecute {
|
||||
*/
|
||||
int executeSqlUpdate(PersistRequestUpdateSql request);
|
||||
|
||||
/**
|
||||
* Collect execution metrics for sql update.
|
||||
*/
|
||||
void collectOrmUpdate(String label, long startNanos, int rowCount);
|
||||
|
||||
/**
|
||||
* Collect execution metrics for sql update.
|
||||
*/
|
||||
void collectSqlUpdate(String label, long startNanos, int rowCount);
|
||||
|
||||
/**
|
||||
* Collect execution metrics for sql callable.
|
||||
*/
|
||||
void collectSqlCall(String label, long startNanos, int rowCount);
|
||||
|
||||
/**
|
||||
* Visit the metrics.
|
||||
*/
|
||||
void visitMetrics(MetricVisitor visitor);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebeaninternal.metric.MetricFactory;
|
||||
import io.ebeaninternal.metric.QueryPlanCollector;
|
||||
import io.ebeaninternal.metric.QueryPlanMetric;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
import io.ebeaninternal.metric.TimedMetricMap;
|
||||
@@ -12,22 +12,18 @@ import io.ebeaninternal.metric.TimedMetricMap;
|
||||
public class DMetricFactory implements MetricFactory {
|
||||
|
||||
@Override
|
||||
public TimedMetricMap createTimedMetricMap(String name) {
|
||||
return new DTimedMetricMap(name);
|
||||
public TimedMetricMap createTimedMetricMap(MetricType metricType, String name) {
|
||||
return new DTimedMetricMap(metricType, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimedMetric createTimedMetric(String name) {
|
||||
return new DTimedMetric(name);
|
||||
public TimedMetric createTimedMetric(MetricType metricType, String name) {
|
||||
return new DTimedMetric(metricType, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryPlanMetric createQueryPlanMetric(Class<?> type, String label, String sql) {
|
||||
return new DQueryPlanMetric(new DQueryPlanMeta(type, label, sql), createTimedMetric(label));
|
||||
public QueryPlanMetric createQueryPlanMetric(MetricType metricType, Class<?> type, String label, String sql) {
|
||||
return new DQueryPlanMetric(new DQueryPlanMeta(type, label, sql), new DTimedMetric(metricType, label));
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryPlanCollector createCollector(boolean reset) {
|
||||
return new DQueryPlanCollector(reset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebean.service.SpiProfileLocationFactory;
|
||||
import io.ebeaninternal.metric.MetricFactory;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
@@ -18,7 +19,7 @@ public class DProfileLocationFactory implements SpiProfileLocationFactory {
|
||||
@Override
|
||||
public ProfileLocation create(int lineNumber, String label) {
|
||||
|
||||
TimedMetric timedMetric = MetricFactory.get().createTimedMetric("txn.named." + label);
|
||||
TimedMetric timedMetric = MetricFactory.get().createTimedMetric(MetricType.TXN, "txn.named." + label);
|
||||
|
||||
DTimedProfileLocation loc = new DTimedProfileLocation(lineNumber, label, timedMetric);
|
||||
TimedProfileLocationRegistry.register(loc);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.MetaQueryMetric;
|
||||
import io.ebeaninternal.metric.QueryPlanCollector;
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.metric.QueryPlanMetric;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
import io.ebeaninternal.metric.TimedMetricStats;
|
||||
@@ -9,18 +10,18 @@ import io.ebeaninternal.metric.TimedMetricStats;
|
||||
class DQueryPlanMetric implements QueryPlanMetric {
|
||||
|
||||
private final DQueryPlanMeta meta;
|
||||
private final TimedMetric metric;
|
||||
private final DTimedMetric metric;
|
||||
|
||||
DQueryPlanMetric(DQueryPlanMeta meta, TimedMetric metric) {
|
||||
DQueryPlanMetric(DQueryPlanMeta meta, DTimedMetric metric) {
|
||||
this.meta = meta;
|
||||
this.metric = metric;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(QueryPlanCollector collector) {
|
||||
TimedMetricStats stats = metric.collect(collector.isReset());
|
||||
public void visit(MetricVisitor visitor) {
|
||||
TimedMetricStats stats = metric.collect(visitor.isReset());
|
||||
if (stats != null) {
|
||||
collector.add(new Stats(meta, stats));
|
||||
visitor.visitQuery(new Stats(meta, stats));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +45,11 @@ class DQueryPlanMetric implements QueryPlanMetric {
|
||||
return meta + " " + stats + " sql:" + getSql();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetricType getMetricType() {
|
||||
return stats.getMetricType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getType() {
|
||||
return meta.getType();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebeaninternal.metric.TimedMetricStats;
|
||||
|
||||
/**
|
||||
@@ -7,6 +8,8 @@ import io.ebeaninternal.metric.TimedMetricStats;
|
||||
*/
|
||||
class DTimeMetricStats implements TimedMetricStats {
|
||||
|
||||
private final MetricType metricType;
|
||||
|
||||
private final String name;
|
||||
|
||||
private String location;
|
||||
@@ -21,7 +24,8 @@ class DTimeMetricStats implements TimedMetricStats {
|
||||
|
||||
private final long beanCount;
|
||||
|
||||
DTimeMetricStats(String name, long collectionStart, long count, long total, long max, long beanCount) {
|
||||
DTimeMetricStats(MetricType metricType, String name, long collectionStart, long count, long total, long max, long beanCount) {
|
||||
this.metricType = metricType;
|
||||
this.name = name;
|
||||
this.startTime = collectionStart;
|
||||
this.count = count;
|
||||
@@ -53,6 +57,11 @@ class DTimeMetricStats implements TimedMetricStats {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MetricType getMetricType() {
|
||||
return metricType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.LongAccumulator;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
@@ -16,6 +16,8 @@ import java.util.concurrent.atomic.LongAdder;
|
||||
*/
|
||||
class DTimedMetric implements TimedMetric {
|
||||
|
||||
private final MetricType metricType;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final LongAdder beanCount = new LongAdder();
|
||||
@@ -28,7 +30,8 @@ class DTimedMetric implements TimedMetric {
|
||||
|
||||
private final AtomicLong startTime = new AtomicLong(System.currentTimeMillis());
|
||||
|
||||
DTimedMetric(String name) {
|
||||
DTimedMetric(MetricType metricType, String name) {
|
||||
this.metricType = metricType;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@@ -54,11 +57,23 @@ class DTimedMetric implements TimedMetric {
|
||||
return count.sum() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all the internal counters and start time.
|
||||
*/
|
||||
@Override
|
||||
public void collect(boolean reset, List<MetaTimedMetric> result) {
|
||||
DTimeMetricStats metric = collect(reset);
|
||||
public void reset() {
|
||||
startTime.set(System.currentTimeMillis());
|
||||
max.reset();
|
||||
count.reset();
|
||||
total.reset();
|
||||
beanCount.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(MetricVisitor visitor) {
|
||||
DTimeMetricStats metric = collect(visitor.isReset());
|
||||
if (metric != null) {
|
||||
result.add(metric);
|
||||
visitor.visitTimed(metric);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +93,7 @@ class DTimedMetric implements TimedMetric {
|
||||
/**
|
||||
* Return the current statistics resetting the internal values if reset is true.
|
||||
*/
|
||||
public DTimeMetricStats getStatistics(boolean reset) {
|
||||
private DTimeMetricStats getStatistics(boolean reset) {
|
||||
|
||||
if (reset) {
|
||||
// Note these values are not guaranteed to be consistent wrt each other
|
||||
@@ -88,21 +103,11 @@ class DTimedMetric implements TimedMetric {
|
||||
final long totalVal = total.sumThenReset();
|
||||
final long countVal = count.sumThenReset();
|
||||
final long startTimeVal = startTime.getAndSet(System.currentTimeMillis());
|
||||
return new DTimeMetricStats(name, startTimeVal, countVal, totalVal, maxVal, beans);
|
||||
return new DTimeMetricStats(metricType, name, startTimeVal, countVal, totalVal, maxVal, beans);
|
||||
|
||||
} else {
|
||||
return new DTimeMetricStats(name, startTime.get(), count.sum(), total.sum(), max.get(), beanCount.sum());
|
||||
return new DTimeMetricStats(metricType, name, startTime.get(), count.sum(), total.sum(), max.get(), beanCount.sum());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all the internal counters and start time.
|
||||
*/
|
||||
public void reset() {
|
||||
startTime.set(System.currentTimeMillis());
|
||||
max.reset();
|
||||
count.reset();
|
||||
total.reset();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,30 +1,38 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.metric.TimedMetricMap;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
class DTimedMetricMap implements TimedMetricMap {
|
||||
|
||||
private final MetricType metricType;
|
||||
|
||||
private final String name;
|
||||
|
||||
private final ConcurrentHashMap<String, DTimedMetric> map = new ConcurrentHashMap<>();
|
||||
|
||||
DTimedMetricMap(String name) {
|
||||
DTimedMetricMap(MetricType metricType, String name) {
|
||||
this.metricType = metricType;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(String key, long exeMicros) {
|
||||
map.computeIfAbsent(key, (k)-> new DTimedMetric(name + key)).add(exeMicros);
|
||||
map.computeIfAbsent(key, (k) -> new DTimedMetric(metricType, name + key)).add(exeMicros);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(boolean reset, List<MetaTimedMetric> list) {
|
||||
public void add(String key, long exeMicros, int rows) {
|
||||
map.computeIfAbsent(key, (k) -> new DTimedMetric(metricType, name + key)).add(exeMicros, rows);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(MetricVisitor visitor) {
|
||||
for (DTimedMetric value : map.values()) {
|
||||
value.collect(reset, list);
|
||||
value.visit(visitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
import io.ebeaninternal.metric.TimedMetricStats;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Default profile location that uses stack trace.
|
||||
*/
|
||||
@@ -37,13 +35,11 @@ class DTimedProfileLocation extends DProfileLocation implements TimedProfileLoca
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(boolean reset, List<MetaTimedMetric> list) {
|
||||
|
||||
TimedMetricStats collect = timedMetric.collect(reset);
|
||||
public void visit(MetricVisitor visitor) {
|
||||
TimedMetricStats collect = timedMetric.collect(visitor.isReset());
|
||||
if (collect != null) {
|
||||
collect.setLocation(obtain());
|
||||
list.add(collect);
|
||||
visitor.visitTimed(collect);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ProfileLocation that collects timing metrics.
|
||||
*/
|
||||
@@ -22,7 +20,7 @@ public interface TimedProfileLocation extends ProfileLocation {
|
||||
TimedMetric getMetric();
|
||||
|
||||
/**
|
||||
* Collect the metrics adding to the given list if the metrics are non empty.
|
||||
* Visit the non empty metrics.
|
||||
*/
|
||||
void collect(boolean reset, List<MetaTimedMetric> list);
|
||||
void visit(MetricVisitor visitor);
|
||||
}
|
||||
|
||||
@@ -3,8 +3,12 @@ package io.ebeaninternal.server.query;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.bean.ObjectGraphNode;
|
||||
import io.ebean.config.dbplatform.SqlLimitResponse;
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebeaninternal.api.CQueryPlanKey;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.metric.MetricFactory;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
@@ -51,6 +55,10 @@ public class CQueryPlan {
|
||||
|
||||
private final ProfileLocation profileLocation;
|
||||
|
||||
private final String location;
|
||||
|
||||
private final String label;
|
||||
|
||||
private final CQueryPlanKey planKey;
|
||||
|
||||
private final boolean rawSql;
|
||||
@@ -90,9 +98,12 @@ public class CQueryPlan {
|
||||
this.dataTimeZone = server.getDataTimeZone();
|
||||
this.beanType = request.getBeanDescriptor().getBeanType();
|
||||
this.planKey = request.getQueryPlanKey();
|
||||
this.profileLocation = request.getQuery().getProfileLocation();
|
||||
this.autoTuned = request.getQuery().isAutoTuned();
|
||||
this.asOfTableCount = request.getQuery().getAsOfTableCount();
|
||||
SpiQuery<?> query = request.getQuery();
|
||||
this.profileLocation = query.getProfileLocation();
|
||||
this.label = query.getLabel();
|
||||
this.location = location();
|
||||
this.autoTuned = query.isAutoTuned();
|
||||
this.asOfTableCount = query.getAsOfTableCount();
|
||||
this.sql = sqlRes.getSql();
|
||||
this.rowNumberIncluded = sqlRes.isIncludesRowNumberColumn();
|
||||
this.sqlTree = sqlTree;
|
||||
@@ -105,13 +116,15 @@ public class CQueryPlan {
|
||||
/**
|
||||
* Create a query plan for a raw sql query.
|
||||
*/
|
||||
CQueryPlan(OrmQueryRequest<?> request, String sql, SqlTree sqlTree,
|
||||
boolean rawSql, boolean rowNumberIncluded, String logWhereSql) {
|
||||
CQueryPlan(OrmQueryRequest<?> request, String sql, SqlTree sqlTree, boolean rawSql, boolean rowNumberIncluded, String logWhereSql) {
|
||||
|
||||
this.server = request.getServer();
|
||||
this.dataTimeZone = server.getDataTimeZone();
|
||||
this.beanType = request.getBeanDescriptor().getBeanType();
|
||||
this.profileLocation = request.getQuery().getProfileLocation();
|
||||
SpiQuery<?> query = request.getQuery();
|
||||
this.profileLocation = query.getProfileLocation();
|
||||
this.label = query.getLabel();
|
||||
this.location = location();
|
||||
this.planKey = buildPlanKey(sql, rawSql, rowNumberIncluded, logWhereSql);
|
||||
this.autoTuned = false;
|
||||
this.asOfTableCount = 0;
|
||||
@@ -124,6 +137,9 @@ public class CQueryPlan {
|
||||
this.stats = new CQueryPlanStats(this, server.isCollectQueryOrigins());
|
||||
}
|
||||
|
||||
private String location() {
|
||||
return (profileLocation == null) ? "" : profileLocation.shortDescription();
|
||||
}
|
||||
|
||||
private CQueryPlanKey buildPlanKey(String sql, boolean rawSql, boolean rowNumberIncluded, String logWhereSql) {
|
||||
|
||||
@@ -143,6 +159,14 @@ public class CQueryPlan {
|
||||
return profileLocation;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public String getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public DataReader createDataReader(ResultSet rset) {
|
||||
return new RsetDataReader(dataTimeZone, rset);
|
||||
}
|
||||
@@ -240,17 +264,13 @@ public class CQueryPlan {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of the current query statistics.
|
||||
*/
|
||||
public Snapshot getSnapshot(boolean reset) {
|
||||
return stats.getSnapshot(reset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current query statistics.
|
||||
*/
|
||||
public CQueryPlanStats getQueryStats() {
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the time this query plan was last used.
|
||||
*/
|
||||
@@ -268,4 +288,8 @@ public class CQueryPlan {
|
||||
public boolean isEmptyStats() {
|
||||
return stats.isEmpty();
|
||||
}
|
||||
|
||||
public TimedMetric createTimedMetric() {
|
||||
return MetricFactory.get().createTimedMetric(MetricType.ORM, label);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,16 +2,17 @@ package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.bean.ObjectGraphNode;
|
||||
import io.ebean.meta.MetaQueryPlanOriginCount;
|
||||
import io.ebean.meta.MetaQueryPlanStatistic;
|
||||
import io.ebean.meta.MetaOrmQueryMetric;
|
||||
import io.ebean.meta.MetaOrmQueryOrigin;
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
import io.ebeaninternal.metric.TimedMetricStats;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.LongAccumulator;
|
||||
import java.util.concurrent.atomic.LongAdder;
|
||||
|
||||
/**
|
||||
@@ -21,15 +22,7 @@ public final class CQueryPlanStats {
|
||||
|
||||
private final CQueryPlan queryPlan;
|
||||
|
||||
private final LongAdder count = new LongAdder();
|
||||
|
||||
private final LongAdder totalTime = new LongAdder();
|
||||
|
||||
private final LongAdder totalBeans = new LongAdder();
|
||||
|
||||
private final LongAccumulator maxTime = new LongAccumulator(Math::max, Long.MIN_VALUE);
|
||||
|
||||
private final AtomicLong startTime = new AtomicLong(System.currentTimeMillis());
|
||||
private final TimedMetric timedMetric;
|
||||
|
||||
private long lastQueryTime;
|
||||
|
||||
@@ -39,16 +32,16 @@ public final class CQueryPlanStats {
|
||||
* Construct for a given query plan.
|
||||
*/
|
||||
CQueryPlanStats(CQueryPlan queryPlan, boolean collectQueryOrigins) {
|
||||
|
||||
this.queryPlan = queryPlan;
|
||||
this.origins = !collectQueryOrigins ? null : new ConcurrentHashMap<>();
|
||||
this.timedMetric = queryPlan.createTimedMetric();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there are no statistics collected since the last reset.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return count.sum() == 0;
|
||||
return timedMetric.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,10 +49,7 @@ public final class CQueryPlanStats {
|
||||
*/
|
||||
public void add(long loadedBeanCount, long timeMicros, ObjectGraphNode objectGraphNode) {
|
||||
|
||||
count.increment();
|
||||
totalBeans.add(loadedBeanCount);
|
||||
totalTime.add(timeMicros);
|
||||
maxTime.accumulate(timeMicros);
|
||||
timedMetric.add(timeMicros, loadedBeanCount);
|
||||
|
||||
// not safe but should be atomic
|
||||
lastQueryTime = System.currentTimeMillis();
|
||||
@@ -82,14 +72,7 @@ public final class CQueryPlanStats {
|
||||
* Reset the internal statistics counters.
|
||||
*/
|
||||
public void reset() {
|
||||
|
||||
// Racey but near enough for our purposes as we don't want locks
|
||||
count.reset();
|
||||
totalBeans.reset();
|
||||
totalTime.reset();
|
||||
maxTime.reset();
|
||||
startTime.set(System.currentTimeMillis());
|
||||
|
||||
timedMetric.reset();
|
||||
if (origins != null) {
|
||||
for (LongAdder counter : origins.values()) {
|
||||
counter.reset();
|
||||
@@ -109,25 +92,20 @@ public final class CQueryPlanStats {
|
||||
*/
|
||||
Snapshot getSnapshot(boolean reset) {
|
||||
|
||||
List<MetaQueryPlanOriginCount> origins = getOrigins(reset);
|
||||
|
||||
// not guaranteed to be consistent due to time gaps between getting each value out of LongAdders but can live with that
|
||||
// relative to the cost of making sure count and totalTime etc are all guaranteed to be consistent
|
||||
if (reset) {
|
||||
return new Snapshot(queryPlan, count.sumThenReset(), totalTime.sumThenReset(), totalBeans.sumThenReset(), maxTime.getThenReset(), startTime.getAndSet(System.currentTimeMillis()), lastQueryTime, origins);
|
||||
}
|
||||
return new Snapshot(queryPlan, count.sum(), totalTime.sum(), totalBeans.sum(), maxTime.get(), startTime.get(), lastQueryTime, origins);
|
||||
TimedMetricStats collect = timedMetric.collect(reset);
|
||||
List<MetaOrmQueryOrigin> origins = getOrigins(reset);
|
||||
return new Snapshot(queryPlan, collect, lastQueryTime, origins);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list/snapshot of the origins and their counter value.
|
||||
*/
|
||||
private List<MetaQueryPlanOriginCount> getOrigins(boolean reset) {
|
||||
private List<MetaOrmQueryOrigin> getOrigins(boolean reset) {
|
||||
if (origins == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<MetaQueryPlanOriginCount> list = new ArrayList<>(origins.size());
|
||||
List<MetaOrmQueryOrigin> list = new ArrayList<>(origins.size());
|
||||
|
||||
for (Entry<ObjectGraphNode, LongAdder> entry : origins.entrySet()) {
|
||||
if (reset) {
|
||||
@@ -142,7 +120,7 @@ public final class CQueryPlanStats {
|
||||
/**
|
||||
* Snapshot of the origin ObjectGraphNode and counter value.
|
||||
*/
|
||||
private static class OriginSnapshot implements MetaQueryPlanOriginCount {
|
||||
private static class OriginSnapshot implements MetaOrmQueryOrigin {
|
||||
private final ObjectGraphNode objectGraphNode;
|
||||
private final long count;
|
||||
|
||||
@@ -170,70 +148,83 @@ public final class CQueryPlanStats {
|
||||
/**
|
||||
* A snapshot of the current statistics for a query plan.
|
||||
*/
|
||||
public static class Snapshot implements MetaQueryPlanStatistic {
|
||||
static class Snapshot implements MetaOrmQueryMetric {
|
||||
|
||||
private final CQueryPlan queryPlan;
|
||||
private final long count;
|
||||
private final long totalTime;
|
||||
private final long totalBeans;
|
||||
private final long maxTime;
|
||||
private final long startTime;
|
||||
private final TimedMetricStats metrics;
|
||||
private final long lastQueryTime;
|
||||
private final List<MetaQueryPlanOriginCount> origins;
|
||||
|
||||
Snapshot(CQueryPlan queryPlan, long count, long totalTime, long totalBeans, long maxTime, long startTime, long lastQueryTime,
|
||||
List<MetaQueryPlanOriginCount> origins) {
|
||||
private final List<MetaOrmQueryOrigin> origins;
|
||||
|
||||
Snapshot(CQueryPlan queryPlan, TimedMetricStats metrics, long lastQueryTime, List<MetaOrmQueryOrigin> origins) {
|
||||
this.queryPlan = queryPlan;
|
||||
this.count = count;
|
||||
this.totalTime = totalTime;
|
||||
this.totalBeans = totalBeans;
|
||||
this.maxTime = maxTime;
|
||||
this.startTime = startTime;
|
||||
this.metrics = metrics;
|
||||
this.lastQueryTime = lastQueryTime;
|
||||
this.origins = origins;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ProfileLocation profileLocation = queryPlan.getProfileLocation();
|
||||
String loc = (profileLocation == null) ? "" : profileLocation.shortDescription();
|
||||
return "location:" + loc + " count:" + count + " time:" + totalTime + " maxTime:" + maxTime + " beans:" + totalBeans + " sql:" + getSql();
|
||||
return "location:" + getLocation() + " metrics:" + metrics + " sql:" + getSql();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getBeanType() {
|
||||
public MetricType getMetricType() {
|
||||
return MetricType.ORM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getType() {
|
||||
return queryPlan.getBeanType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return queryPlan.getLabel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return queryPlan.getLabel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLocation() {
|
||||
return queryPlan.getLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProfileLocation getProfileLocation() {
|
||||
return queryPlan.getProfileLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getExecutionCount() {
|
||||
return count;
|
||||
public long getBeanCount() {
|
||||
return metrics.getBeanCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalTimeMicros() {
|
||||
return totalTime;
|
||||
public long getCount() {
|
||||
return metrics.getCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalLoadedBeans() {
|
||||
return totalBeans;
|
||||
public long getTotal() {
|
||||
return metrics.getTotal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaxTimeMicros() {
|
||||
return maxTime;
|
||||
public long getMax() {
|
||||
return metrics.getMax();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCollectionStart() {
|
||||
return startTime;
|
||||
public long getMean() {
|
||||
return metrics.getMean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStartTime() {
|
||||
return metrics.getStartTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -257,17 +248,7 @@ public final class CQueryPlanStats {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAvgTimeMicros() {
|
||||
return count < 1 ? 0 : totalTime / count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAvgLoadedBeans() {
|
||||
return count < 1 ? 0 : totalBeans / count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaQueryPlanOriginCount> getOrigins() {
|
||||
public List<MetaOrmQueryOrigin> getOrigins() {
|
||||
return origins;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.meta.MetaQueryPlanStatistic;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Helper to collect query plan execution statistics.
|
||||
*/
|
||||
public class CQueryPlanStatsCollector {
|
||||
|
||||
private final boolean reset;
|
||||
|
||||
List<MetaQueryPlanStatistic> list = new ArrayList<>();
|
||||
|
||||
public CQueryPlanStatsCollector(boolean reset) {
|
||||
this.reset = reset;
|
||||
}
|
||||
|
||||
public boolean isReset() {
|
||||
return reset;
|
||||
}
|
||||
|
||||
public void add(MetaQueryPlanStatistic planStatistic) {
|
||||
list.add(planStatistic);
|
||||
}
|
||||
|
||||
public List<MetaQueryPlanStatistic> getList() {
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.SqlRow;
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.metric.MetricFactory;
|
||||
import io.ebeaninternal.metric.TimedMetricMap;
|
||||
import io.ebeaninternal.server.core.Message;
|
||||
import io.ebeaninternal.server.core.RelationalQueryEngine;
|
||||
import io.ebeaninternal.server.core.RelationalQueryRequest;
|
||||
@@ -25,10 +29,23 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
|
||||
|
||||
private final boolean binaryOptimizedUUID;
|
||||
|
||||
private final TimedMetricMap timedMetricMap;
|
||||
|
||||
public DefaultRelationalQueryEngine(Binder binder, String dbTrueValue, boolean binaryOptimizedUUID) {
|
||||
this.binder = binder;
|
||||
this.dbTrueValue = dbTrueValue == null ? "true" : dbTrueValue;
|
||||
this.binaryOptimizedUUID = binaryOptimizedUUID;
|
||||
this.timedMetricMap = MetricFactory.get().createTimedMetricMap(MetricType.SQL, "sql.query.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(String label, long exeMicros, int rows) {
|
||||
timedMetricMap.add(label, exeMicros, rows);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMetrics(MetricVisitor visitor) {
|
||||
timedMetricMap.visit(visitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -51,6 +51,7 @@ public class DefaultDtoQuery<T> implements SpiDtoQuery<T> {
|
||||
this.server = server;
|
||||
this.descriptor = descriptor;
|
||||
this.ormQuery = ormQuery;
|
||||
this.label = ormQuery.getLabel();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,7 +66,7 @@ public class DefaultDtoQuery<T> implements SpiDtoQuery<T> {
|
||||
|
||||
@Override
|
||||
public String planKey() {
|
||||
return sql+":first"+firstRow+":max"+maxRows;
|
||||
return sql + ":first" + firstRow + ":max" + maxRows;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -96,6 +96,8 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
|
||||
private Type type;
|
||||
|
||||
private String label;
|
||||
|
||||
private Mode mode = Mode.NORMAL;
|
||||
|
||||
private Object tenantId;
|
||||
@@ -340,6 +342,17 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> setLabel(String label) {
|
||||
this.label = label;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoTunable() {
|
||||
return nativeSql == null && beanDescriptor.isAutoTunable();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.querydefn;
|
||||
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.Update;
|
||||
import io.ebeaninternal.api.BindParams;
|
||||
import io.ebeaninternal.api.SpiUpdate;
|
||||
|
||||
@@ -23,6 +24,8 @@ public final class DefaultOrmUpdate<T> implements SpiUpdate<T>, Serializable {
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
private String label;
|
||||
|
||||
/**
|
||||
* The parameters used to bind to the sql.
|
||||
*/
|
||||
@@ -137,6 +140,17 @@ public final class DefaultOrmUpdate<T> implements SpiUpdate<T>, Serializable {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Update<T> setLabel(String label) {
|
||||
this.label = label;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUpdateStatement() {
|
||||
return updateStatement;
|
||||
|
||||
@@ -19,6 +19,8 @@ public class DefaultRelationalQuery implements SpiSqlQuery {
|
||||
|
||||
private final transient EbeanServer server;
|
||||
|
||||
private String label;
|
||||
|
||||
private String query;
|
||||
|
||||
private int firstRow;
|
||||
@@ -122,6 +124,17 @@ public class DefaultRelationalQuery implements SpiSqlQuery {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultRelationalQuery setLabel(String label) {
|
||||
this.label = label;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BindParams getBindParams() {
|
||||
return bindParams;
|
||||
|
||||
@@ -11,7 +11,8 @@ import io.ebean.config.dbplatform.DatabasePlatform.OnQueryOnly;
|
||||
import io.ebean.event.changelog.ChangeLogListener;
|
||||
import io.ebean.event.changelog.ChangeLogPrepare;
|
||||
import io.ebean.event.changelog.ChangeSet;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebeaninternal.api.ScopeTrans;
|
||||
import io.ebeaninternal.api.ScopedTransaction;
|
||||
import io.ebeaninternal.api.SpiProfileHandler;
|
||||
@@ -37,7 +38,6 @@ import javax.persistence.PersistenceException;
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -170,9 +170,9 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
this.transactionFactory = TransactionFactoryBuilder.build(this, dataSourceSupplier, tenantProvider);
|
||||
|
||||
MetricFactory metricFactory = MetricFactory.get();
|
||||
this.txnMain = metricFactory.createTimedMetric("txn.main");
|
||||
this.txnReadOnly = metricFactory.createTimedMetric("txn.readonly");
|
||||
this.txnNamed = metricFactory.createTimedMetricMap("txn.named.");
|
||||
this.txnMain = metricFactory.createTimedMetric(MetricType.TXN, "txn.main");
|
||||
this.txnReadOnly = metricFactory.createTimedMetric(MetricType.TXN, "txn.readonly");
|
||||
this.txnNamed = metricFactory.createTimedMetricMap(MetricType.TXN, "txn.named.");
|
||||
|
||||
scopeManager.register(this);
|
||||
}
|
||||
@@ -528,21 +528,13 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
txnNamed.add(label, exeMicros);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the transaction execution statistics since the last reset.
|
||||
*/
|
||||
public List<MetaTimedMetric> collectTransactionStatistics(boolean reset) {
|
||||
|
||||
List<MetaTimedMetric> list = new ArrayList<>();
|
||||
|
||||
txnMain.collect(reset, list);
|
||||
txnReadOnly.collect(reset, list);
|
||||
public void visitMetrics(MetricVisitor visitor) {
|
||||
txnMain.visit(visitor);
|
||||
txnReadOnly.visit(visitor);
|
||||
txnNamed.visit(visitor);
|
||||
for (TimedProfileLocation timedLocation : TimedProfileLocationRegistry.registered()) {
|
||||
timedLocation.collect(reset, list);
|
||||
timedLocation.visit(visitor);
|
||||
}
|
||||
txnNamed.collect(reset, list);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -562,7 +554,7 @@ public class TransactionManager implements SpiTransactionManager {
|
||||
if (st instanceof ScopedTransaction) {
|
||||
// can be null for Supports as that can start as a 'No Transaction' and then
|
||||
// effectively be replaced by transactions inside the scope
|
||||
((ScopedTransaction)st).complete(returnOrThrowable, opCode);
|
||||
((ScopedTransaction) st).complete(returnOrThrowable, opCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user