mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#1868 - Change the Metric "sqlHash" into a hash of the SQL + name + location
This commit is contained in:
@@ -28,6 +28,11 @@ public interface MetaInfoManager {
|
||||
*/
|
||||
ServerMetricsAsJson collectMetricsAsJson();
|
||||
|
||||
/**
|
||||
* Return the metrics as a list of MetricData.
|
||||
*/
|
||||
List<MetricData> collectMetricsAsData();
|
||||
|
||||
/**
|
||||
* Collect query plans.
|
||||
*/
|
||||
|
||||
@@ -12,11 +12,6 @@ public interface MetaOrmQueryMetric extends MetaQueryMetric {
|
||||
*/
|
||||
boolean isAutoTuned();
|
||||
|
||||
/**
|
||||
* Return the hash of the sql.
|
||||
*/
|
||||
String getSqlHash();
|
||||
|
||||
/**
|
||||
* Return the time of the last query executed using this plan.
|
||||
*/
|
||||
|
||||
@@ -20,4 +20,9 @@ public interface MetaQueryMetric extends MetaTimedMetric {
|
||||
*/
|
||||
String getSql();
|
||||
|
||||
/**
|
||||
* Return the hash of the plan.
|
||||
*/
|
||||
String getHash();
|
||||
|
||||
}
|
||||
|
||||
@@ -28,9 +28,9 @@ public interface MetaQueryPlan {
|
||||
String getSql();
|
||||
|
||||
/**
|
||||
* Return the hash of the sql.
|
||||
* Return the hash of the plan.
|
||||
*/
|
||||
String getSqlHash();
|
||||
String getHash();
|
||||
|
||||
/**
|
||||
* Return a description of the bind values.
|
||||
|
||||
@@ -38,7 +38,7 @@ public interface MetaTimedMetric extends MetaMetric {
|
||||
|
||||
/**
|
||||
* Return the total beans or rows processed or loaded.
|
||||
*
|
||||
* <p>
|
||||
* This will be 0 if the metric isn't a query plan (like transaction execution statistics).
|
||||
*/
|
||||
long getBeanCount();
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
/**
|
||||
* An individual metric.
|
||||
*/
|
||||
public class MetricData {
|
||||
|
||||
private String name;
|
||||
private String type;
|
||||
private String hash;
|
||||
private String loc;
|
||||
private String sql;
|
||||
|
||||
private Long count;
|
||||
private Long mean;
|
||||
private Long max;
|
||||
private Long total;
|
||||
|
||||
public MetricData(String name, String type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public MetricData() {
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getHash() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
public void setHash(String hash) {
|
||||
this.hash = hash;
|
||||
}
|
||||
|
||||
public String getLoc() {
|
||||
return loc;
|
||||
}
|
||||
|
||||
public void setLoc(String loc) {
|
||||
this.loc = loc;
|
||||
}
|
||||
|
||||
public String getSql() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
public void setSql(String sql) {
|
||||
this.sql = sql;
|
||||
}
|
||||
|
||||
public Long getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setCount(Long count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public Long getMean() {
|
||||
return mean;
|
||||
}
|
||||
|
||||
public void setMean(Long mean) {
|
||||
this.mean = mean;
|
||||
}
|
||||
|
||||
public Long getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
public void setMax(Long max) {
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public Long getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public void setTotal(Long total) {
|
||||
this.total = total;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import io.ebean.meta.MetaOrmQueryNode;
|
||||
import io.ebean.meta.MetaQueryMetric;
|
||||
import io.ebean.meta.MetaQueryPlan;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.MetricData;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.meta.QueryPlanRequest;
|
||||
import io.ebean.meta.ServerMetrics;
|
||||
@@ -48,6 +49,11 @@ public class DefaultMetaInfoManager implements MetaInfoManager {
|
||||
return new DumpMetricsJson(server);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetricData> collectMetricsAsData() {
|
||||
return new DumpMetricsData(server).data();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicMetricVisitor visitBasic() {
|
||||
BasicMetricVisitor basic = new BasicMetricVisitor();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.meta.MetaCountMetric;
|
||||
import io.ebean.meta.MetaOrmQueryMetric;
|
||||
import io.ebean.meta.MetaQueryMetric;
|
||||
@@ -131,7 +130,7 @@ class DumpMetrics {
|
||||
appendQueryName(metric, sb);
|
||||
appendCounters(metric, sb);
|
||||
if (dumpHash) {
|
||||
sb.append("\n hash:").append(metric.getSqlHash());
|
||||
sb.append("\n hash:").append(metric.getHash());
|
||||
}
|
||||
appendProfileAndSql(metric, sb);
|
||||
out(sb.toString());
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.Database;
|
||||
import io.ebean.meta.MetaCountMetric;
|
||||
import io.ebean.meta.MetaMetric;
|
||||
import io.ebean.meta.MetaOrmQueryMetric;
|
||||
import io.ebean.meta.MetaQueryMetric;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.MetricData;
|
||||
import io.ebean.meta.ServerMetrics;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Dump the metrics into a list of MetricData.
|
||||
*/
|
||||
class DumpMetricsData {
|
||||
|
||||
private final Database database;
|
||||
|
||||
private final List<MetricData> list = new ArrayList<>();
|
||||
|
||||
DumpMetricsData(Database database) {
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
List<MetricData> data() {
|
||||
collect(database.getMetaInfoManager().collectMetrics());
|
||||
return list;
|
||||
}
|
||||
|
||||
private void collect(ServerMetrics serverMetrics) {
|
||||
|
||||
final List<MetaTimedMetric> timedMetrics = serverMetrics.getTimedMetrics();
|
||||
final List<MetaCountMetric> countMetrics = serverMetrics.getCountMetrics();
|
||||
final List<MetaOrmQueryMetric> ormQueryMetrics = serverMetrics.getOrmQueryMetrics();
|
||||
final List<MetaQueryMetric> dtoQueryMetrics = serverMetrics.getDtoQueryMetrics();
|
||||
|
||||
for (MetaTimedMetric metric : timedMetrics) {
|
||||
add(metric);
|
||||
}
|
||||
for (MetaCountMetric metric : countMetrics) {
|
||||
addCount(metric);
|
||||
}
|
||||
for (MetaOrmQueryMetric metric : ormQueryMetrics) {
|
||||
addQuery(metric);
|
||||
}
|
||||
for (MetaQueryMetric metric : dtoQueryMetrics) {
|
||||
addDtoQuery(metric);
|
||||
}
|
||||
}
|
||||
|
||||
private MetricData create(MetaMetric metric) {
|
||||
MetricData data = new MetricData(metric.getName(), metric.getMetricType().name());
|
||||
list.add(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
private void add(MetaTimedMetric metric) {
|
||||
final MetricData data = create(metric);
|
||||
appendCounters(data, metric);
|
||||
data.setLoc(metric.getLocation());
|
||||
}
|
||||
|
||||
private void addCount(MetaCountMetric metric) {
|
||||
final MetricData data = create(metric);
|
||||
data.setCount(metric.getCount());
|
||||
}
|
||||
|
||||
private void addQuery(MetaOrmQueryMetric metric) {
|
||||
final MetricData data = create(metric);
|
||||
appendCounters(data, metric);
|
||||
appendLocationAndSql(data, metric);
|
||||
data.setHash(metric.getHash());
|
||||
}
|
||||
|
||||
private void addDtoQuery(MetaQueryMetric metric) {
|
||||
final MetricData data = create(metric);
|
||||
appendCounters(data, metric);
|
||||
appendLocationAndSql(data, metric);
|
||||
}
|
||||
|
||||
private void appendLocationAndSql(MetricData data, MetaQueryMetric metric) {
|
||||
data.setLoc(metric.getLocation());
|
||||
data.setSql(metric.getSql());
|
||||
}
|
||||
|
||||
private void appendCounters(MetricData data, MetaTimedMetric timedMetric) {
|
||||
data.setCount(timedMetric.getCount());
|
||||
data.setTotal(timedMetric.getTotal());
|
||||
data.setMean(timedMetric.getMean());
|
||||
data.setMax(timedMetric.getMax());
|
||||
}
|
||||
}
|
||||
@@ -192,7 +192,7 @@ class DumpMetricsJson implements ServerMetricsAsJson {
|
||||
appendCounters(metric);
|
||||
if (withHash) {
|
||||
key("hash");
|
||||
val(metric.getSqlHash());
|
||||
val(metric.getHash());
|
||||
}
|
||||
appendLocationAndSql(metric);
|
||||
metricEnd();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebeaninternal.server.util.Md5;
|
||||
|
||||
class DQueryPlanMeta {
|
||||
|
||||
@@ -9,6 +10,7 @@ class DQueryPlanMeta {
|
||||
private final ProfileLocation profileLocation;
|
||||
private final String name;
|
||||
private final String sql;
|
||||
private final String hash;
|
||||
|
||||
DQueryPlanMeta(Class<?> type, String label, ProfileLocation profileLocation, String sql) {
|
||||
this.type = type;
|
||||
@@ -20,12 +22,25 @@ class DQueryPlanMeta {
|
||||
name += "_" + label;
|
||||
}
|
||||
this.name = name;
|
||||
this.hash = initHash();
|
||||
}
|
||||
|
||||
private String initHash() {
|
||||
StringBuilder sb = new StringBuilder(sql).append("|").append(name);
|
||||
if (profileLocation != null) {
|
||||
sb.append("|").append(profileLocation.location());
|
||||
}
|
||||
return Md5.hash(sb.toString());
|
||||
}
|
||||
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getHash() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -55,6 +55,11 @@ class DQueryPlanMetric implements QueryPlanMetric {
|
||||
return meta.getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHash() {
|
||||
return meta.getHash();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return meta.getLabel();
|
||||
|
||||
@@ -60,7 +60,7 @@ class CQueryBindCapture {
|
||||
|
||||
DQueryPlanOutput queryPlan = planLogger.logQueryPlan(request.getConnection(), cQueryPlan, last);
|
||||
if (queryPlan != null) {
|
||||
queryPlan.with(queryTimeMicros, captureCount, cQueryPlan.getSqlHash());
|
||||
queryPlan.with(queryTimeMicros, captureCount, cQueryPlan.getHash());
|
||||
request.process(queryPlan);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ public class CQueryPlan {
|
||||
private final boolean rowNumberIncluded;
|
||||
|
||||
private final String sql;
|
||||
private final String sqlHash;
|
||||
private final String hash;
|
||||
|
||||
private final String logWhereSql;
|
||||
|
||||
@@ -118,7 +118,6 @@ public class CQueryPlan {
|
||||
this.autoTuned = query.isAutoTuned();
|
||||
this.asOfTableCount = query.getAsOfTableCount();
|
||||
this.sql = sqlRes.getSql();
|
||||
this.sqlHash = md5Hash(sql);
|
||||
this.rowNumberIncluded = sqlRes.isIncludesRowNumberColumn();
|
||||
this.sqlTree = sqlTree;
|
||||
this.rawSql = rawSql;
|
||||
@@ -127,6 +126,7 @@ public class CQueryPlan {
|
||||
this.stats = new CQueryPlanStats(this, server.isCollectQueryOrigins());
|
||||
this.dependentTables = sqlTree.dependentTables();
|
||||
this.bindCapture = initBindCapture(server.getServerConfig(), query);
|
||||
this.hash = md5Hash();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,7 +146,6 @@ public class CQueryPlan {
|
||||
this.autoTuned = false;
|
||||
this.asOfTableCount = 0;
|
||||
this.sql = sql;
|
||||
this.sqlHash = md5Hash(sql);
|
||||
this.sqlTree = sqlTree;
|
||||
this.rawSql = false;
|
||||
this.rowNumberIncluded = rowNumberIncluded;
|
||||
@@ -155,6 +154,7 @@ public class CQueryPlan {
|
||||
this.stats = new CQueryPlanStats(this, server.isCollectQueryOrigins());
|
||||
this.dependentTables = sqlTree.dependentTables();
|
||||
this.bindCapture = initBindCapture(server.getServerConfig(), query);
|
||||
this.hash = md5Hash();
|
||||
}
|
||||
|
||||
private String deriveName(String label, SpiQuery.Type type) {
|
||||
@@ -260,23 +260,26 @@ public class CQueryPlan {
|
||||
|
||||
private String calcAuditQueryKey() {
|
||||
// rawSql needs to include the MD5 hash of the sql
|
||||
return rawSql ? planKey.getPartialKey() + "_" + sqlHash : planKey.getPartialKey();
|
||||
return rawSql ? planKey.getPartialKey() + "_" + hash : planKey.getPartialKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the MD5 hash of the sql.
|
||||
*/
|
||||
private String md5Hash(String sql) {
|
||||
private String md5Hash() {
|
||||
StringBuilder sb = new StringBuilder(sql)
|
||||
.append("|").append(name)
|
||||
.append("|").append(location);
|
||||
try {
|
||||
return Md5.hash(sql);
|
||||
return Md5.hash(sb.toString());
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to MD5 hash the query", e);
|
||||
return "error";
|
||||
}
|
||||
}
|
||||
|
||||
String getSqlHash() {
|
||||
return sqlHash;
|
||||
String getHash() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
public String getSql() {
|
||||
|
||||
@@ -233,8 +233,8 @@ public final class CQueryPlanStats {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSqlHash() {
|
||||
return queryPlan.getSqlHash();
|
||||
public String getHash() {
|
||||
return queryPlan.getHash();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,7 +16,7 @@ class DQueryPlanOutput implements MetaQueryPlan {
|
||||
private final String bind;
|
||||
private final String plan;
|
||||
|
||||
private String sqlHash;
|
||||
private String hash;
|
||||
private long queryTimeMicros;
|
||||
private long captureCount;
|
||||
|
||||
@@ -30,8 +30,8 @@ class DQueryPlanOutput implements MetaQueryPlan {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSqlHash() {
|
||||
return sqlHash;
|
||||
public String getHash() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,15 +98,15 @@ class DQueryPlanOutput implements MetaQueryPlan {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return " BeanType:" + ((beanType == null) ? "" : beanType.getSimpleName()) + " planHash:" + sqlHash + " label:" + label + " queryTimeMicros:" + queryTimeMicros + " captureCount:" + captureCount + "\n SQL:" + sql + "\nBIND:" + bind + "\nPLAN:" + plan;
|
||||
return " BeanType:" + ((beanType == null) ? "" : beanType.getSimpleName()) + " planHash:" + hash + " label:" + label + " queryTimeMicros:" + queryTimeMicros + " captureCount:" + captureCount + "\n SQL:" + sql + "\nBIND:" + bind + "\nPLAN:" + plan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Additionally set the query execution time and the number of bind captures.
|
||||
*/
|
||||
void with(long queryTimeMicros, long captureCount, String sqlHash) {
|
||||
void with(long queryTimeMicros, long captureCount, String hash) {
|
||||
this.queryTimeMicros = queryTimeMicros;
|
||||
this.captureCount = captureCount;
|
||||
this.sqlHash = sqlHash;
|
||||
this.hash = hash;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ public final class Md5 {
|
||||
* Return the MD5 hash of the underlying sql.
|
||||
*/
|
||||
public static String hash(String content) {
|
||||
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
return digestToHex(md.digest(content.getBytes(StandardCharsets.UTF_8)));
|
||||
|
||||
@@ -32,7 +32,7 @@ public class TestDbArray_basic extends BaseTestCase {
|
||||
public void insert() throws SQLException {
|
||||
|
||||
bean.setName("some stuff");
|
||||
assertThat(bean.getStatuses()).isNotNull();
|
||||
assertThat(bean.getStatuses()).as("DbArray is auto initialised").isNotNull();
|
||||
|
||||
List<String> phNumbers = bean.getPhoneNumbers();
|
||||
phNumbers.add("4321");
|
||||
|
||||
@@ -182,7 +182,7 @@ public class TestCustomerFinder extends BaseTestCase {
|
||||
assertThat(metricsJson).contains("\"name\":\"txn.main\", \"type\":\"TXN\"");
|
||||
assertThat(metricsJson).contains("\"name\":\"Customer.findList\"");
|
||||
assertThat(metricsJson).contains("\"loc\":\"CustomerFinder.byNameStatus(CustomerFinder.java:44)\"");
|
||||
assertThat(metricsJson).contains("\"hash\":\"4d648ce0542aedfb042ad68746342730\"");
|
||||
assertThat(metricsJson).contains("\"hash\":\"f089744556e1677b57c7c6ef73f65da8\"");
|
||||
assertThat(metricsJson).contains("\"sql\":\"select t0.id, t0.status,");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user