From 30ae435f3301e5849fe344fe3f8e35de049162f2 Mon Sep 17 00:00:00 2001 From: rob bygrave Date: Fri, 12 Apr 2019 00:45:00 +1200 Subject: [PATCH] #1672 - ENH: Add ebean.dumpMetricsOnShutdown=true ... for dumping metrics --- .../java/io/ebean/config/ServerConfig.java | 36 ++++ src/main/java/io/ebean/meta/SortMetric.java | 80 ++++++++ .../java/io/ebeaninternal/api/SpiQuery.java | 38 ++-- .../server/core/DefaultServer.java | 6 +- .../server/core/DumpMetrics.java | 172 ++++++++++++++++++ .../server/core/OrmQueryRequest.java | 14 -- .../server/core/SpiOrmQueryRequest.java | 10 - .../server/deploy/BeanDescriptor.java | 19 -- .../server/profile/DQueryPlanMeta.java | 10 + .../server/profile/DQueryPlanMetric.java | 2 +- .../server/query/CQueryPlan.java | 18 ++ .../server/query/CQueryPlanStats.java | 2 +- .../server/query/QueryPlanLogger.java | 2 +- .../server/profile/SortMetricTest.java | 67 +++++++ src/test/resources/ebean.properties | 3 + 15 files changed, 415 insertions(+), 64 deletions(-) create mode 100644 src/main/java/io/ebean/meta/SortMetric.java create mode 100644 src/main/java/io/ebeaninternal/server/core/DumpMetrics.java create mode 100644 src/test/java/io/ebeaninternal/server/profile/SortMetricTest.java diff --git a/src/main/java/io/ebean/config/ServerConfig.java b/src/main/java/io/ebean/config/ServerConfig.java index 899044d9f..9efda763d 100644 --- a/src/main/java/io/ebean/config/ServerConfig.java +++ b/src/main/java/io/ebean/config/ServerConfig.java @@ -524,6 +524,10 @@ public class ServerConfig { */ private boolean idGeneratorAutomatic = true; + private boolean dumpMetricsOnShutdown; + + private String dumpMetricsOptions; + /** * Construct a Database Configuration for programmatically creating an Database. */ @@ -2903,6 +2907,8 @@ public class ServerConfig { } loadDocStoreSettings(p); + dumpMetricsOnShutdown = p.getBoolean("dumpMetricsOnShutdown", dumpMetricsOnShutdown); + dumpMetricsOptions = p.get("dumpMetricsOptions", dumpMetricsOptions); queryPlanTTLSeconds = p.getInt("queryPlanTTLSeconds", queryPlanTTLSeconds); slowQueryMillis = p.getLong("slowQueryMillis", slowQueryMillis); collectQueryPlans = p.getBoolean("collectQueryPlans", collectQueryPlans); @@ -3287,6 +3293,36 @@ public class ServerConfig { this.collectQueryPlans = collectQueryPlans; } + /** + * Return true if metrics should be dumped when the server is shutdown. + */ + public boolean isDumpMetricsOnShutdown() { + return dumpMetricsOnShutdown; + } + + /** + * Set to true if metrics should be dumped when the server is shutdown. + */ + public void setDumpMetricsOnShutdown(boolean dumpMetricsOnShutdown) { + this.dumpMetricsOnShutdown = dumpMetricsOnShutdown; + } + + /** + * Return the options for dumping metrics. + */ + public String getDumpMetricsOptions() { + return dumpMetricsOptions; + } + + /** + * Include 'sql' or 'hash' in options such that they are included in the output. + * + * @param dumpMetricsOptions Example "sql,hash", "sql" + */ + public void setDumpMetricsOptions(String dumpMetricsOptions) { + this.dumpMetricsOptions = dumpMetricsOptions; + } + public enum UuidVersion { VERSION4, VERSION1, diff --git a/src/main/java/io/ebean/meta/SortMetric.java b/src/main/java/io/ebean/meta/SortMetric.java new file mode 100644 index 000000000..32d4161d1 --- /dev/null +++ b/src/main/java/io/ebean/meta/SortMetric.java @@ -0,0 +1,80 @@ +package io.ebean.meta; + +import java.util.Comparator; + +/** + * Comparator for timed metrics sorted by name and then count. + */ +public class SortMetric { + + public static final Comparator NAME = new Name(); + public static final Comparator COUNT = new Count(); + public static final Comparator TOTAL = new Total(); + public static final Comparator MEAN = new Mean(); + public static final Comparator MAX = new Max(); + + /** + * Sort by name. + */ + public static class Name implements Comparator { + + @Override + public int compare(MetaTimedMetric o1, MetaTimedMetric o2) { + String name = o1.getName(); + String name2 = o2.getName(); + if (name == null) { + return name2 == null ? 0 : -1; + } + if (name2 == null) { + return 1; + } + + int i = name.compareTo(name2); + return i != 0 ? i : Long.compare(o1.getCount(), o2.getCount()); + } + } + + /** + * Sort by count desc. + */ + public static class Count implements Comparator { + + @Override + public int compare(MetaTimedMetric o1, MetaTimedMetric o2) { + return Long.compare(o2.getCount(), o1.getCount()); + } + } + + /** + * Sort by total time desc. + */ + public static class Total implements Comparator { + + @Override + public int compare(MetaTimedMetric o1, MetaTimedMetric o2) { + return Long.compare(o2.getTotal(), o1.getTotal()); + } + } + + /** + * Sort by mean desc. + */ + public static class Mean implements Comparator { + + @Override + public int compare(MetaTimedMetric o1, MetaTimedMetric o2) { + return Long.compare(o2.getMean(), o1.getMean()); + } + } + + /** + * Sort by max desc. + */ + public static class Max implements Comparator { + + @Override + public int compare(MetaTimedMetric o1, MetaTimedMetric o2) { + return Long.compare(o2.getMax(), o1.getMax()); + } + } +} diff --git a/src/main/java/io/ebeaninternal/api/SpiQuery.java b/src/main/java/io/ebeaninternal/api/SpiQuery.java index 483749988..05f4c0e79 100644 --- a/src/main/java/io/ebeaninternal/api/SpiQuery.java +++ b/src/main/java/io/ebeaninternal/api/SpiQuery.java @@ -54,67 +54,69 @@ public interface SpiQuery extends Query, TxnProfileEventCodes { /** * Find by Id or unique returning a single bean. */ - BEAN(FIND_ONE), + BEAN(FIND_ONE, "byId"), /** * Find returning a List. */ - LIST(FIND_MANY), + LIST(FIND_MANY, "findList"), /** * Find returning a Set. */ - SET(FIND_MANY), + SET(FIND_MANY, "findSet"), /** * Find returning a Map. */ - MAP(FIND_MANY), + MAP(FIND_MANY, "findMap"), /** * Find iterate type query - findEach(), findIterate() etc. */ - ITERATE(FIND_ITERATE), + ITERATE(FIND_ITERATE, "findEach"), /** * Find the Id's. */ - ID_LIST(FIND_ID_LIST), + ID_LIST(FIND_ID_LIST, "findIds"), /** * Find single attribute. */ - ATTRIBUTE(FIND_ATTRIBUTE), + ATTRIBUTE(FIND_ATTRIBUTE, "findAttribute"), /** * Find rowCount. */ - COUNT(FIND_COUNT), + COUNT(FIND_COUNT, "findCount"), /** * A subquery used as part of a where clause. */ - SUBQUERY(FIND_SUBQUERY), + SUBQUERY(FIND_SUBQUERY, "subquery"), /** * Delete query. */ - DELETE(FIND_DELETE, true), + DELETE(FIND_DELETE, "delete", true), /** * Update query. */ - UPDATE(FIND_UPDATE, true); + UPDATE(FIND_UPDATE, "update", true); - boolean update; - String profileEventId; + private boolean update; + private String profileEventId; + private String label; - Type(String profileEventId) { - this(profileEventId, false); + Type(String profileEventId, String label) { + this(profileEventId, label, false); } - Type(String profileEventId, boolean update) { + Type(String profileEventId, String label, boolean update) { this.profileEventId = profileEventId; + this.label = label; this.update = update; } @@ -128,6 +130,10 @@ public interface SpiQuery extends Query, TxnProfileEventCodes { public String profileEventId() { return profileEventId; } + + public String label() { + return label; + } } enum TemporalMode { diff --git a/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/src/main/java/io/ebeaninternal/server/core/DefaultServer.java index db6e1bf0a..4606d7fde 100644 --- a/src/main/java/io/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/io/ebeaninternal/server/core/DefaultServer.java @@ -505,6 +505,10 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { private void shutdownPlugins() { + if (serverConfig.isDumpMetricsOnShutdown()) { + new DumpMetrics(this, serverConfig.getDumpMetricsOptions()).dump(); + } + for (Plugin plugin : serverPlugins) { try { plugin.shutdown(); @@ -1234,7 +1238,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { } SpiOrmQueryRequest request = createQueryRequest(spiQuery, t); - request.profileLocationById(); if (request.isUseDocStore()) { return docStore().find(request); } @@ -1588,7 +1591,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { private List findList(Query query, Transaction t, boolean findOne) { SpiOrmQueryRequest request = createQueryRequest(Type.LIST, query, t); - request.profileLocationAll(); request.resetBeanCacheAutoMode(findOne); Object result = request.getFromQueryCache(); if (result != null) { diff --git a/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java b/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java new file mode 100644 index 000000000..f60f7657c --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/core/DumpMetrics.java @@ -0,0 +1,172 @@ +package io.ebeaninternal.server.core; + +import io.ebean.ProfileLocation; +import io.ebean.meta.MetaOrmQueryMetric; +import io.ebean.meta.MetaQueryMetric; +import io.ebean.meta.MetaTimedMetric; +import io.ebean.meta.ServerMetrics; +import io.ebean.meta.SortMetric; +import io.ebeaninternal.api.SpiEbeanServer; + +import java.util.Comparator; +import java.util.List; + +class DumpMetrics { + + private final SpiEbeanServer server; + private final String options; + + private final String nameFormat; + private final String nameFormatTimed; + + private boolean dumpHash; + private boolean dumpSql; + private boolean dumpLoc; + + private Comparator sortBy = SortMetric.NAME; + + DumpMetrics(SpiEbeanServer server, String options) { + this.server = server; + this.options = options; + + int width = 0; + + if (options != null) { + dumpLoc = options.contains("loc"); + dumpSql = options.contains("sql"); + dumpHash = options.contains("hash"); + for (int i = 5; i < 10; i++) { + width = Math.max(width, optionWidth(i * 10)); + } + for (String option : new String[]{"Total", "Count", "Mean", "Max"}) { + sortOption(option); + } + } + if (width == 0) { + width = 80; + } + + nameFormat = "%1$-" + width + "s"; + nameFormatTimed = "%1$-" + (width + 6) + "s"; + } + + private int optionWidth(int check) { + return options.contains("w" + check) ? check : 0; + } + + private void sortOption(String option) { + if (options.contains("sort" + option)) { + sortBy = setSortOption(option); + } + } + + private Comparator setSortOption(String option) { + switch (option.toUpperCase()) { + case "TOTAL": + return SortMetric.TOTAL; + case "COUNT": + return SortMetric.COUNT; + case "MEAN": + return SortMetric.MEAN; + case "MAX": + return SortMetric.MAX; + } + return SortMetric.NAME; + } + + void dump() { + + out("-- Dumping metrics for " + server.getName() + " -- "); + ServerMetrics serverMetrics = server.getMetaInfoManager().collectMetrics(); + + for (MetaTimedMetric metric : serverMetrics.getTimedMetrics()) { + log(metric); + } + + List ormQueryMetrics = serverMetrics.getOrmQueryMetrics(); + if (!ormQueryMetrics.isEmpty()) { + out("\n-- ORM queries --"); + ormQueryMetrics.sort(sortBy); + for (MetaOrmQueryMetric metric : ormQueryMetrics) { + logQuery(metric); + } + } + + List dtoQueryMetrics = serverMetrics.getDtoQueryMetrics(); + if (!dtoQueryMetrics.isEmpty()) { + out("\n-- DTO queries --"); + dtoQueryMetrics.sort(sortBy); + for (MetaQueryMetric metric : dtoQueryMetrics) { + logDtoQuery(metric); + } + } + } + + private void out(String sb) { + System.out.println(sb); + } + + private void logQuery(MetaOrmQueryMetric metric) { + + StringBuilder sb = new StringBuilder(); + + sb.append("query:").append(padName(metric.getName())).append(" "); + addCounters(metric, sb); + + if (dumpHash) { + sb.append("\n hash:").append(metric.getQueryPlanHash()); + } + + ProfileLocation profileLocation = metric.getProfileLocation(); + if (dumpLoc && profileLocation != null) { + sb.append("\n loc:").append(profileLocation.shortDescription()); + } + + if (dumpSql) { + sb.append("\n\n sql:").append(metric.getSql()).append("\n\n"); + } + + out(sb.toString()); + } + + + private void logDtoQuery(MetaQueryMetric metric) { + + StringBuilder sb = new StringBuilder(); + + sb.append("query:").append(padName(metric.getName())).append(" "); + addCounters(metric, sb); + + if (dumpSql) { + sb.append(" \n\n sql:").append(metric.getSql()).append("\n\n"); + } + out(sb.toString()); + } + + private void log(MetaTimedMetric metric) { + + StringBuilder sb = new StringBuilder(); + sb.append(padNameTimed(metric.getName())).append(" "); + addCounters(metric, sb); + out(sb.toString()); + } + + private void addCounters(MetaTimedMetric timedMetric, StringBuilder sb) { + sb.append(" count:").append(pad(timedMetric.getCount())) + .append(" total:").append(pad(timedMetric.getTotal())) + .append(" mean:").append(pad(timedMetric.getMean())) + .append(" max:").append(pad(timedMetric.getMax())); + } + + private String padName(String name) { + return String.format(nameFormat, name); + } + + private String padNameTimed(String name) { + return String.format(nameFormatTimed, name); + } + + private String pad(long value) { + return String.format("%1$-8s", value); + } +} diff --git a/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java b/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java index 0f09bac26..af0f73443 100644 --- a/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java +++ b/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java @@ -102,20 +102,6 @@ public final class OrmQueryRequest extends BeanRequest implements SpiOrmQuery return queryEngine.translate(this, bindLog, sql, e); } - @Override - public void profileLocationById() { - if (query.getProfileLocation() == null) { - query.setProfileLocation(beanDescriptor.profileLocationById()); - } - } - - @Override - public void profileLocationAll() { - if (query.getProfileLocation() == null && query.isFindAll()) { - query.setProfileLocation(beanDescriptor.profileLocationAll()); - } - } - @Override public boolean isDeleteByStatement() { if (!transaction.isPersistCascade() || beanDescriptor.isDeleteByStatement()) { diff --git a/src/main/java/io/ebeaninternal/server/core/SpiOrmQueryRequest.java b/src/main/java/io/ebeaninternal/server/core/SpiOrmQueryRequest.java index c78661398..d4195701b 100644 --- a/src/main/java/io/ebeaninternal/server/core/SpiOrmQueryRequest.java +++ b/src/main/java/io/ebeaninternal/server/core/SpiOrmQueryRequest.java @@ -162,16 +162,6 @@ public interface SpiOrmQueryRequest extends BeanQueryRequest, DocQueryRequ */ boolean isUseDocStore(); - /** - * Set profile location for "find by id" if not set. - */ - void profileLocationById(); - - /** - * Set profile location for "find all" if not set. - */ - void profileLocationAll(); - /** * Return true if delete by statement is allowed for this type given cascade rules etc. */ diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java index 7d3cba9c1..ed600488a 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java @@ -1,7 +1,6 @@ package io.ebeaninternal.server.deploy; import io.ebean.PersistenceContextScope; -import io.ebean.ProfileLocation; import io.ebean.Query; import io.ebean.SqlUpdate; import io.ebean.Transaction; @@ -136,8 +135,6 @@ public class BeanDescriptor implements BeanType, STreeType { private final Map namedQuery; private final short profileBeanId; - private final ProfileLocation locationById; - private final ProfileLocation locationAll; private final boolean multiValueSupported; @@ -448,8 +445,6 @@ public class BeanDescriptor implements BeanType, STreeType { this.name = InternString.intern(deploy.getName()); this.baseTableAlias = "t0"; this.fullName = InternString.intern(deploy.getFullName()); - this.locationById = ProfileLocation.createAt(fullName + ".byId"); - this.locationAll = ProfileLocation.createAt(fullName + ".all"); this.profileBeanId = deploy.getProfileId(); this.beanType = deploy.getBeanType(); this.rootBeanType = PersistenceContextUtil.root(beanType); @@ -582,20 +577,6 @@ public class BeanDescriptor implements BeanType, STreeType { } } - /** - * Return a location for "find by id". - */ - public ProfileLocation profileLocationById() { - return locationById; - } - - /** - * Return a location for "find all". - */ - public ProfileLocation profileLocationAll() { - return locationAll; - } - /** * Return the id used in profiling to identify the bean type. */ diff --git a/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java b/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java index 02c6859c4..f6d1f46fd 100644 --- a/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java +++ b/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMeta.java @@ -4,18 +4,28 @@ class DQueryPlanMeta { private final Class type; private final String label; + private final String name; private final String sql; DQueryPlanMeta(Class type, String label, String sql) { this.type = type; this.label = label; this.sql = sql; + String name = type.getSimpleName(); + if (label != null) { + name += "_" + label; + } + this.name = name; } public Class getType() { return type; } + public String getName() { + return name; + } + public String getLabel() { return label; } diff --git a/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java b/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java index 4f4240233..2490a1b5c 100644 --- a/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java +++ b/src/main/java/io/ebeaninternal/server/profile/DQueryPlanMetric.java @@ -67,7 +67,7 @@ class DQueryPlanMetric implements QueryPlanMetric { @Override public String getName() { - return stats.getName(); + return meta.getName(); } @Override diff --git a/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java b/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java index 5680b3135..e1812802b 100644 --- a/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java +++ b/src/main/java/io/ebeaninternal/server/query/CQueryPlan.java @@ -65,6 +65,8 @@ public class CQueryPlan { private final String label; + private final String name; + private final CQueryPlanKey planKey; private final boolean rawSql; @@ -112,6 +114,7 @@ public class CQueryPlan { SpiQuery query = request.getQuery(); this.profileLocation = query.getProfileLocation(); this.label = query.getPlanLabel(); + this.name = deriveName(label, query.getType()); this.location = location(); this.autoTuned = query.isAutoTuned(); this.asOfTableCount = query.getAsOfTableCount(); @@ -138,6 +141,7 @@ public class CQueryPlan { SpiQuery query = request.getQuery(); this.profileLocation = query.getProfileLocation(); this.label = query.getPlanLabel(); + this.name = deriveName(label, query.getType()); this.location = location(); this.planKey = buildPlanKey(sql, rawSql, rowNumberIncluded, logWhereSql); this.autoTuned = false; @@ -154,6 +158,16 @@ public class CQueryPlan { this.bindCapture = initBindCapture(server.getServerConfig(), query); } + private String deriveName(String label, SpiQuery.Type type) { + if (label == null) { + return beanType.getSimpleName() + "." + type.label(); + } + if (label.startsWith(beanType.getSimpleName())) { + return label; + } + return beanType.getSimpleName() + "_" + label; + } + private CQueryBindCapture initBindCapture(ServerConfig serverConfig, SpiQuery query) { if (serverConfig.isCollectQueryPlans() && !query.getType().isUpdate()) { return new CQueryBindCapture(this, PlatformQueryPlan.getLogger(serverConfig.getDatabasePlatform().getPlatform())); @@ -192,6 +206,10 @@ public class CQueryPlan { return label; } + public String getName() { + return name; + } + public String getLocation() { return location; } diff --git a/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java b/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java index 8498b3280..25455ae37 100644 --- a/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java +++ b/src/main/java/io/ebeaninternal/server/query/CQueryPlanStats.java @@ -184,7 +184,7 @@ public final class CQueryPlanStats { @Override public String getName() { - return queryPlan.getLabel(); + return queryPlan.getName(); } @Override diff --git a/src/main/java/io/ebeaninternal/server/query/QueryPlanLogger.java b/src/main/java/io/ebeaninternal/server/query/QueryPlanLogger.java index 8f51e0e67..f3bbb267c 100644 --- a/src/main/java/io/ebeaninternal/server/query/QueryPlanLogger.java +++ b/src/main/java/io/ebeaninternal/server/query/QueryPlanLogger.java @@ -26,7 +26,7 @@ public abstract class QueryPlanLogger { } protected DQueryPlanOutput createPlan(CQueryPlan plan, String bind, String planString) { - return new DQueryPlanOutput(plan.getBeanType(), plan.getLabel(), plan.getSql(), bind, planString); + return new DQueryPlanOutput(plan.getBeanType(), plan.getName(), plan.getSql(), bind, planString); } DQueryPlanOutput readQueryPlanBasic(CQueryPlan plan, BindCapture bind, ResultSet rset) throws SQLException { diff --git a/src/test/java/io/ebeaninternal/server/profile/SortMetricTest.java b/src/test/java/io/ebeaninternal/server/profile/SortMetricTest.java new file mode 100644 index 000000000..683964a2c --- /dev/null +++ b/src/test/java/io/ebeaninternal/server/profile/SortMetricTest.java @@ -0,0 +1,67 @@ +package io.ebeaninternal.server.profile; + +import io.ebean.meta.MetaTimedMetric; +import io.ebean.meta.MetricType; +import io.ebean.meta.SortMetric; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertEquals; + +public class SortMetricTest { + + private Comparator sortMetric = SortMetric.NAME; + + @Test + public void compare_list() { + + List list = new ArrayList<>(); + list.add(create("d")); + list.add(create("b")); + list.add(create("c")); + list.add(create(null)); + list.add(create("a")); + list.sort(sortMetric); + + String names = list.stream().map(DTimeMetricStats::getName).collect(Collectors.joining()); + + assertEquals("nullabcd", names); + } + + @Test + public void compare_when_same() { + + assertEquals(0, sortMetric.compare(create("foo"), create("foo"))); + assertEquals(0, sortMetric.compare(create(null), create(null))); + } + + @Test + public void compare_when_less() { + + assertEquals(-1, sortMetric.compare(create("a"), create("b"))); + assertEquals(-1, sortMetric.compare(create("foo"), create("goo"))); + } + + @Test + public void compare_when_more() { + + assertEquals(1, sortMetric.compare(create("b"), create("a"))); + assertEquals(1, sortMetric.compare(create("goo"), create("foo"))); + } + + @Test + public void compare_when_nulls() { + + assertEquals(0, sortMetric.compare(create(null), create(null))); + assertEquals(1, sortMetric.compare(create("foo"), create(null))); + assertEquals(-1, sortMetric.compare(create(null), create("foo"))); + } + + private DTimeMetricStats create(String name) { + return new DTimeMetricStats(MetricType.L2, name, 0, 0, 0, 0, 0); + } +} diff --git a/src/test/resources/ebean.properties b/src/test/resources/ebean.properties index 65af4407e..9e00efeff 100644 --- a/src/test/resources/ebean.properties +++ b/src/test/resources/ebean.properties @@ -23,6 +23,9 @@ ebean.ddl.header=-- Generated by ebean ${version} at ${timestamp} ebean.packages=org.tests datasource.default=h2 +ebean.dumpMetricsOnShutdown=true +ebean.dumpMetricsOptions=sql,hash + ebean.collectQueryPlans=true ebean.autoReadOnlyDataSource=true