mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-10-23 05:35:54 +00:00
Merge pull request #2288 from ebean-orm/feature/sql-hash
Change query metric hash from MD5 of sql + name + loc to Checksum of sql (+ rename to sqlHash)
This commit is contained in:
@@ -21,8 +21,8 @@ public interface MetaQueryMetric extends MetaTimedMetric {
|
||||
String getSql();
|
||||
|
||||
/**
|
||||
* Return the hash of the plan.
|
||||
* Return the hash of the sql.
|
||||
*/
|
||||
String getHash();
|
||||
long getSqlHash();
|
||||
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public interface MetaQueryPlan {
|
||||
/**
|
||||
* Return the hash of the plan.
|
||||
*/
|
||||
String getHash();
|
||||
long getHash();
|
||||
|
||||
/**
|
||||
* Return a description of the bind values.
|
||||
|
||||
@@ -6,7 +6,7 @@ package io.ebean.meta;
|
||||
public class MetricData {
|
||||
|
||||
private String name;
|
||||
private String hash;
|
||||
private long sqlHash;
|
||||
private String loc;
|
||||
private String sql;
|
||||
|
||||
@@ -30,12 +30,12 @@ public class MetricData {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getHash() {
|
||||
return hash;
|
||||
public long getSqlHash() {
|
||||
return sqlHash;
|
||||
}
|
||||
|
||||
public void setHash(String hash) {
|
||||
this.hash = hash;
|
||||
public void setSqlHash(long sqlHash) {
|
||||
this.sqlHash = sqlHash;
|
||||
}
|
||||
|
||||
public String getLoc() {
|
||||
|
||||
@@ -10,7 +10,7 @@ public class QueryPlanInit {
|
||||
|
||||
private boolean all;
|
||||
|
||||
private Set<String> hashes = new HashSet<>();
|
||||
private Set<Long> hashes = new HashSet<>();
|
||||
|
||||
private long thresholdMicros;
|
||||
|
||||
@@ -47,21 +47,21 @@ public class QueryPlanInit {
|
||||
/**
|
||||
* Return true if the query plan should be initiated based on it's hash.
|
||||
*/
|
||||
public boolean includeHash(String hash) {
|
||||
return all || hashes.contains(hash);
|
||||
public boolean includeHash(long sqlHash) {
|
||||
return all || hashes.contains(sqlHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the specific hashes that we want to collect query plans on.
|
||||
*/
|
||||
public Set<String> getHashes() {
|
||||
public Set<Long> getHashes() {
|
||||
return hashes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the specific hashes that we want to collect query plans on.
|
||||
*/
|
||||
public void setHashes(Set<String> hashes) {
|
||||
public void setHashes(Set<Long> hashes) {
|
||||
this.hashes = hashes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,9 @@ public interface SpiQueryPlan {
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* The hash for the query plan.
|
||||
* The hash of the sql.
|
||||
*/
|
||||
String getHash();
|
||||
long getSqlHash();
|
||||
|
||||
/**
|
||||
* The SQL for the query plan.
|
||||
|
||||
@@ -120,7 +120,7 @@ class DumpMetrics {
|
||||
appendQueryName(metric, sb);
|
||||
appendCounters(metric, sb);
|
||||
if (dumpHash) {
|
||||
sb.append("\n hash:").append(metric.getHash());
|
||||
sb.append("\n sqlHash:").append(metric.getSqlHash());
|
||||
}
|
||||
appendProfileAndSql(metric, sb);
|
||||
out(sb.toString());
|
||||
|
||||
@@ -67,7 +67,7 @@ class DumpMetricsData {
|
||||
final MetricData data = create(metric);
|
||||
appendCounters(data, metric);
|
||||
appendLocationAndSql(data, metric);
|
||||
data.setHash(metric.getHash());
|
||||
data.setSqlHash(metric.getSqlHash());
|
||||
}
|
||||
|
||||
private void appendLocationAndSql(MetricData data, MetaQueryMetric metric) {
|
||||
|
||||
@@ -197,7 +197,7 @@ class DumpMetricsJson implements ServerMetricsAsJson {
|
||||
metricStart(metric);
|
||||
appendTiming(metric);
|
||||
if (withHash) {
|
||||
appendExtra("hash", metric.getHash());
|
||||
keyVal("sqlHash", metric.getSqlHash());
|
||||
}
|
||||
if (isIncludeDetail(metric)) {
|
||||
appendExtra("loc", metric.getLocation());
|
||||
@@ -218,13 +218,14 @@ class DumpMetricsJson implements ServerMetricsAsJson {
|
||||
}
|
||||
|
||||
private void appendTiming(MetaTimedMetric timedMetric) throws IOException {
|
||||
key("count");
|
||||
val(timedMetric.getCount());
|
||||
key("total");
|
||||
val(timedMetric.getTotal());
|
||||
key("mean");
|
||||
val(timedMetric.getMean());
|
||||
key("max");
|
||||
val(timedMetric.getMax());
|
||||
keyVal("count", timedMetric.getCount());
|
||||
keyVal("total", timedMetric.getTotal());
|
||||
keyVal("mean", timedMetric.getMean());
|
||||
keyVal("max", timedMetric.getMax());
|
||||
}
|
||||
|
||||
private void keyVal(String key, long value) throws IOException {
|
||||
key(key);
|
||||
val(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1558,7 +1558,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType, SpiBeanType {
|
||||
|
||||
void queryPlanInit(QueryPlanInit request, List<MetaQueryPlan> list) {
|
||||
for (CQueryPlan queryPlan : queryPlanCache.values()) {
|
||||
if (request.includeHash(queryPlan.getHash())) {
|
||||
if (request.includeHash(queryPlan.getSqlHash())) {
|
||||
queryPlan.queryPlanInit(request.getThresholdMicros());
|
||||
list.add(queryPlan.createMeta(null, null));
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.profile;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebeaninternal.server.util.Md5;
|
||||
import io.ebeaninternal.server.util.Checksum;
|
||||
|
||||
class DQueryPlanMeta {
|
||||
|
||||
@@ -10,7 +10,7 @@ class DQueryPlanMeta {
|
||||
private final ProfileLocation profileLocation;
|
||||
private final String name;
|
||||
private final String sql;
|
||||
private final String hash;
|
||||
private final long sqlHash;
|
||||
|
||||
DQueryPlanMeta(Class<?> type, String label, ProfileLocation profileLocation, String sql) {
|
||||
this.type = type;
|
||||
@@ -22,23 +22,15 @@ 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());
|
||||
this.sqlHash = Checksum.checksum(sql);
|
||||
}
|
||||
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getHash() {
|
||||
return hash;
|
||||
public long getSqlHash() {
|
||||
return sqlHash;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
||||
@@ -59,8 +59,8 @@ class DQueryPlanMetric implements QueryPlanMetric {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHash() {
|
||||
return meta.getHash();
|
||||
public long getSqlHash() {
|
||||
return meta.getSqlHash();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -13,12 +13,12 @@ import io.ebeaninternal.api.SpiQueryBindCapture;
|
||||
import io.ebeaninternal.api.SpiQueryPlan;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
import io.ebeaninternal.server.util.Checksum;
|
||||
import io.ebeaninternal.server.util.Str;
|
||||
import io.ebeaninternal.server.query.CQueryPlanStats.Snapshot;
|
||||
import io.ebeaninternal.server.type.DataBind;
|
||||
import io.ebeaninternal.server.type.DataBindCapture;
|
||||
import io.ebeaninternal.server.type.RsetDataReader;
|
||||
import io.ebeaninternal.server.util.Md5;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -68,7 +68,7 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
private final boolean rawSql;
|
||||
|
||||
private final String sql;
|
||||
private final String hash;
|
||||
private final long sqlHash;
|
||||
|
||||
private final String logWhereSql;
|
||||
|
||||
@@ -118,7 +118,7 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
this.stats = new CQueryPlanStats(this);
|
||||
this.dependentTables = sqlTree.dependentTables();
|
||||
this.bindCapture = initBindCapture(query);
|
||||
this.hash = md5Hash();
|
||||
this.sqlHash = Checksum.checksum(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,7 +143,7 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
this.stats = new CQueryPlanStats(this);
|
||||
this.dependentTables = sqlTree.dependentTables();
|
||||
this.bindCapture = initBindCaptureRaw(sql, query);
|
||||
this.hash = md5Hash();
|
||||
this.sqlHash = Checksum.checksum(sql);
|
||||
}
|
||||
|
||||
private String deriveName(String label, SpiQuery.Type type, String simpleName) {
|
||||
@@ -193,8 +193,8 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHash() {
|
||||
return hash;
|
||||
public long getSqlHash() {
|
||||
return sqlHash;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -226,7 +226,7 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
|
||||
@Override
|
||||
public DQueryPlanOutput createMeta(String bind, String planString) {
|
||||
return new DQueryPlanOutput(getBeanType(), name, hash, sql, profileLocation, bind, planString);
|
||||
return new DQueryPlanOutput(getBeanType(), name, sqlHash, sql, profileLocation, bind, planString);
|
||||
}
|
||||
|
||||
public DataReader createDataReader(ResultSet rset) {
|
||||
@@ -273,22 +273,7 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
|
||||
private String calcAuditQueryKey() {
|
||||
// rawSql needs to include the MD5 hash of the sql
|
||||
return rawSql ? planKey.getPartialKey() + "_" + hash : planKey.getPartialKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the MD5 hash of the sql.
|
||||
*/
|
||||
private String md5Hash() {
|
||||
StringBuilder sb = new StringBuilder(sql)
|
||||
.append("|").append(name)
|
||||
.append("|").append(location);
|
||||
try {
|
||||
return Md5.hash(sb.toString());
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to MD5 hash the query", e);
|
||||
return "error";
|
||||
}
|
||||
return rawSql ? planKey.getPartialKey() + "_" + sqlHash : planKey.getPartialKey();
|
||||
}
|
||||
|
||||
SqlTree getSqlTree() {
|
||||
|
||||
@@ -127,8 +127,8 @@ public final class CQueryPlanStats {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHash() {
|
||||
return queryPlan.getHash();
|
||||
public long getSqlHash() {
|
||||
return queryPlan.getSqlHash();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,12 +16,11 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
|
||||
private final String sql;
|
||||
private final String bind;
|
||||
private final String plan;
|
||||
|
||||
private String hash;
|
||||
private final long hash;
|
||||
private long queryTimeMicros;
|
||||
private long captureCount;
|
||||
|
||||
DQueryPlanOutput(Class<?> beanType, String label, String hash, String sql, ProfileLocation profileLocation, String bind, String plan) {
|
||||
DQueryPlanOutput(Class<?> beanType, String label, long hash, String sql, ProfileLocation profileLocation, String bind, String plan) {
|
||||
this.beanType = beanType;
|
||||
this.label = label;
|
||||
this.hash = hash;
|
||||
@@ -32,7 +31,7 @@ class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHash() {
|
||||
public long getHash() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
package io.ebeaninternal.server.util;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
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)));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("MD5 hashing failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the digest into a hex value.
|
||||
*/
|
||||
private static String digestToHex(byte[] digest) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte aDigest : digest) {
|
||||
sb.append(Integer.toString((aDigest & 0xff) + 0x100, 16).substring(1));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package io.ebeaninternal.server.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class Md5Test {
|
||||
|
||||
@Test
|
||||
public void hash() throws Exception {
|
||||
|
||||
String content = "some random content we wish to hash";
|
||||
String hash1 = Md5.hash(content);
|
||||
String hash2 = Md5.hash(content);
|
||||
assertEquals(hash1, hash2);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -216,7 +216,7 @@ public class TestCustomerFinder extends BaseTestCase {
|
||||
assertThat(plans0).isNotEmpty();
|
||||
|
||||
for (MetaQueryPlan plan : plans) {
|
||||
logger.info("queryplan label:{}, queryTimeMicros:{} loc:{} sql:{} bind:{} plan:{}",
|
||||
logger.info("queryPlan label:{}, queryTimeMicros:{} loc:{} sql:{} bind:{} plan:{}",
|
||||
plan.getLabel(), plan.getQueryTimeMicros(), plan.getProfileLocation(),
|
||||
plan.getSql(), plan.getBind(), plan.getPlan());
|
||||
System.out.println(plan);
|
||||
@@ -244,7 +244,7 @@ public class TestCustomerFinder extends BaseTestCase {
|
||||
assertThat(metricsJson).contains("\"name\":\"orm.Customer.findList\"");
|
||||
assertThat(metricsJson).contains("\"loc\":\"CustomerFinder.byNameStatus(CustomerFinder.java:44)\"");
|
||||
if (isH2() || isPostgres()) {
|
||||
assertThat(metricsJson).contains("\"hash\":\"cc20eb930403cfd418db2d0475c6e26a\"");
|
||||
assertThat(metricsJson).contains("\"sqlHash\":3634991469");
|
||||
assertThat(metricsJson).contains("\"sql\":\"select t0.id, t0.status,");
|
||||
}
|
||||
}
|
||||
@@ -267,7 +267,7 @@ public class TestCustomerFinder extends BaseTestCase {
|
||||
assertThat(metricsJson).contains("\"name\":\"txn.main\"");
|
||||
assertThat(metricsJson).contains("\"name\":\"orm.Customer.findList\"");
|
||||
assertThat(metricsJson).doesNotContain("\"loc\":");
|
||||
assertThat(metricsJson).doesNotContain("\"hash\":");
|
||||
assertThat(metricsJson).doesNotContain("\"sqlHash\":");
|
||||
assertThat(metricsJson).doesNotContain("\"sql\":");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user