#1768 - Initial refactor extracting CallOrigin interface (#1791)

This commit is contained in:
Rob Bygrave
2019-08-14 17:02:57 +12:00
committed by GitHub
parent 3825dea00a
commit 1d471fee92
23 changed files with 184 additions and 164 deletions
@@ -0,0 +1,22 @@
package io.ebean.bean;
/**
* A call origin for query execution profiling to collect graph use (for query tuning).
*/
public interface CallOrigin {
/**
* Return the top element. Typically the top stack element with class and line.
*/
String getTopElement();
/**
* Return the full description of the call origin.
*/
String getFullDescription();
/**
* Compute and return an origin key based on the query hash.
*/
String getOriginKey(int queryHash);
}
+22 -59
View File
@@ -3,6 +3,8 @@ package io.ebean.bean;
import java.io.Serializable;
import java.util.Arrays;
import static io.ebean.util.EncodeB64.enc;
/**
* Represent the call stack (stack trace elements).
* <p>
@@ -17,15 +19,15 @@ import java.util.Arrays;
* Note the call stack is trimmed to remove the common ebean internal elements.
* </p>
*/
public final class CallStack implements Serializable {
public final class CallStack implements Serializable, CallOrigin {
private static final long serialVersionUID = -8590644046907438579L;
private static final String NEWLINE = "\n";
private final String zeroHash;
private final String pathHash;
private final StackTraceElement[] callStack;
private final int hc;
public CallStack(StackTraceElement[] callStack, int zeroHash, int pathHash) {
@@ -37,12 +39,17 @@ public final class CallStack implements Serializable {
private int computeHashCode() {
int hc = 0;
for (StackTraceElement aCallStack : callStack) {
hc = 92821 * hc + aCallStack.hashCode();
for (StackTraceElement element : callStack) {
hc = 92821 * hc + element.hashCode();
}
return hc;
}
@Override
public String toString() {
return zeroHash + ":" + pathHash + ":" + callStack[0];
}
@Override
public int hashCode() {
return hc;
@@ -63,73 +70,29 @@ public final class CallStack implements Serializable {
/**
* Return the first element of the call stack.
*/
public StackTraceElement getFirstStackTraceElement() {
return callStack[0];
}
/**
* Return the call stack.
*/
public StackTraceElement[] getCallStack() {
return callStack;
}
/**
* Return the hash for the first stack element.
*/
public String getZeroHash() {
return zeroHash;
}
/**
* Return the hash for the stack elements (excluding first stack element).
*/
public String getPathHash() {
return pathHash;
}
@Override
public String toString() {
return zeroHash + ":" + pathHash + ":" + callStack[0];
public String getTopElement() {
return callStack[0].toString();
}
/**
* Return the call stack lines appended with the given newLine string.
*/
public String description(String newLine) {
@Override
public String getFullDescription() {
StringBuilder sb = new StringBuilder(400);
for (StackTraceElement aCallStack : callStack) {
sb.append(aCallStack.toString()).append(newLine);
for (int i = 0; i < callStack.length; i++) {
if (i > 0) {
sb.append(NEWLINE);
}
sb.append(callStack[i].toString());
}
return sb.toString();
}
@Override
public String getOriginKey(int queryHash) {
return enc(queryHash) + "." + zeroHash + "." + pathHash;
}
private static final int radix = 1 << 6;
private static final int mask = radix - 1;
/**
* Convert the integer to unsigned base 64.
*/
public static String enc(int i) {
char[] buf = new char[32];
int charPos = 32;
do {
buf[--charPos] = intToBase64[i & mask];
i >>>= 6;
} while (i != 0);
return new String(buf, charPos, (32 - charPos));
}
private static final char intToBase64[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
};
}
@@ -16,7 +16,7 @@ public final class ObjectGraphOrigin implements Serializable {
private static final long serialVersionUID = 410937765287968708L;
private final CallStack callStack;
private final CallOrigin callOrigin;
private final String beanType;
@@ -24,11 +24,11 @@ public final class ObjectGraphOrigin implements Serializable {
private final String key;
public ObjectGraphOrigin(int queryHash, CallStack callStack, String beanType) {
this.callStack = callStack;
public ObjectGraphOrigin(int queryHash, CallOrigin callOrigin, String beanType) {
this.callOrigin = callOrigin;
this.beanType = beanType;
this.queryHash = queryHash;
this.key = callStack.getOriginKey(queryHash);
this.key = callOrigin.getOriginKey(queryHash);
}
/**
@@ -49,22 +49,22 @@ public final class ObjectGraphOrigin implements Serializable {
/**
* The call stack involved.
*/
public CallStack getCallStack() {
return callStack;
public CallOrigin getCallOrigin() {
return callOrigin;
}
public String getFirstStackElement() {
return callStack.getFirstStackTraceElement().toString();
public String getTopElement() {
return callOrigin.getTopElement();
}
@Override
public String toString() {
return "key[" + key + "] type[" + beanType + "] " + callStack.getFirstStackTraceElement() + " ";
return "key[" + key + "] type[" + beanType + "] " + callOrigin.getTopElement();
}
@Override
public int hashCode() {
int hc = 92821 * callStack.hashCode();
int hc = 92821 * callOrigin.hashCode();
hc = 92821 * hc + beanType.hashCode();
hc = 92821 * hc + queryHash;
return hc;
@@ -82,6 +82,6 @@ public final class ObjectGraphOrigin implements Serializable {
ObjectGraphOrigin e = (ObjectGraphOrigin) obj;
return e.queryHash == queryHash
&& e.beanType.equals(beanType)
&& e.callStack.equals(callStack);
&& e.callOrigin.equals(callOrigin);
}
}
@@ -2929,6 +2929,7 @@ public class ServerConfig {
}
loadDocStoreSettings(p);
maxCallStack = p.getInt("maxCallStack", maxCallStack);
dumpMetricsOnShutdown = p.getBoolean("dumpMetricsOnShutdown", dumpMetricsOnShutdown);
dumpMetricsOptions = p.get("dumpMetricsOptions", dumpMetricsOptions);
queryPlanTTLSeconds = p.getInt("queryPlanTTLSeconds", queryPlanTTLSeconds);
@@ -0,0 +1,32 @@
package io.ebean.util;
/**
* Integer to base64 encoder.
*/
public final class EncodeB64 {
private static final int radix = 1 << 6;
private static final int mask = radix - 1;
/**
* Convert the integer to unsigned base 64.
*/
public static String enc(int i) {
char[] buf = new char[32];
int charPos = 32;
do {
buf[--charPos] = intToBase64[i & mask];
i >>>= 6;
} while (i != 0);
return new String(buf, charPos, (32 - charPos));
}
private static final char intToBase64[] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
};
}
@@ -11,7 +11,7 @@ import io.ebean.Transaction;
import io.ebean.TxScope;
import io.ebean.bean.BeanCollectionLoader;
import io.ebean.bean.BeanLoader;
import io.ebean.bean.CallStack;
import io.ebean.bean.CallOrigin;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
@@ -81,7 +81,7 @@ public interface SpiEbeanServer extends ExtendedServer, EbeanServer, BeanLoader,
* graph costing.
* </p>
*/
CallStack createCallStack();
CallOrigin createCallOrigin();
/**
* Return the PersistenceContextScope to use defined at query or server level.
@@ -7,7 +7,7 @@ import io.ebean.OrderBy;
import io.ebean.PersistenceContextScope;
import io.ebean.ProfileLocation;
import io.ebean.Query;
import io.ebean.bean.CallStack;
import io.ebean.bean.CallOrigin;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.bean.PersistenceContext;
import io.ebean.event.readaudit.ReadEvent;
@@ -558,7 +558,7 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
* because the queryPlanHash is used to identify the query point.
* </p>
*/
ObjectGraphNode setOrigin(CallStack callStack);
ObjectGraphNode setOrigin(CallOrigin callOrigin);
/**
* Set the profile point of the bean or collection that is lazy loading.
@@ -149,7 +149,7 @@ public class AutoTuneDiffCollection {
origin.setKey(point.getKey());
origin.setBeanType(point.getBeanType());
origin.setDetail(entry.getDetail().toString());
origin.setCallStack(point.getCallStack().description("\n"));
origin.setCallStack(point.getCallOrigin().getFullDescription());
origin.setOriginal(query);
if (updateTuning) {
@@ -1,6 +1,6 @@
package io.ebeaninternal.server.autotune.service;
import io.ebean.bean.CallStack;
import io.ebean.bean.CallOrigin;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.config.AutoTuneConfig;
import io.ebean.config.AutoTuneMode;
@@ -97,7 +97,7 @@ public class BaseQueryTuner {
if (!useTuning(query)) {
if (profiling) {
profiling(query, server.createCallStack());
profiling(query, server.createCallOrigin());
}
return false;
}
@@ -110,8 +110,8 @@ public class BaseQueryTuner {
}
// create a query point to identify the query
CallStack stack = server.createCallStack();
ObjectGraphNode origin = query.setOrigin(stack);
CallOrigin callOrigin = server.createCallOrigin();
ObjectGraphNode origin = query.setOrigin(callOrigin);
if (profiling) {
if (profilingListener.isProfileRequest(origin, query)) {
@@ -154,10 +154,10 @@ public class BaseQueryTuner {
}
}
private void profiling(SpiQuery<?> query, CallStack stack) {
private void profiling(SpiQuery<?> query, CallOrigin call) {
// create a query point to identify the query
ObjectGraphNode origin = query.setOrigin(stack);
ObjectGraphNode origin = query.setOrigin(call);
if (profilingListener.isProfileRequest(origin, query)) {
// collect more profiling based on profiling rate etc
query.setProfilingListener(profilingListener);
@@ -0,0 +1,14 @@
package io.ebeaninternal.server.core;
import io.ebean.bean.CallOrigin;
/**
* Creates CallOrigin based on the stack trace.
*/
public interface CallOriginFactory {
/**
* Create and return the CallStack given the stack trace elements.
*/
CallOrigin createCallOrigin();
}
@@ -1,14 +0,0 @@
package io.ebeaninternal.server.core;
import io.ebean.bean.CallStack;
/**
* Creates CallStack based on the stack trace.
*/
public interface CallStackFactory {
/**
* Create and return the CallStack given the stack trace elements.
*/
CallStack createCallStack();
}
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.core;
import io.ebean.bean.CallOrigin;
import io.ebean.bean.CallStack;
import java.util.Arrays;
@@ -7,7 +8,7 @@ import java.util.Arrays;
/**
* Default CallStackFactory where the Hash function for StackTraceElement includes the line number.
*/
public class DefaultCallStackFactory implements CallStackFactory {
public class DefaultCallOriginFactory implements CallOriginFactory {
private static final int IGNORE_LEADING_ELEMENTS = 5;
@@ -15,12 +16,12 @@ public class DefaultCallStackFactory implements CallStackFactory {
private final int maxCallStack;
DefaultCallStackFactory(int maxCallStack) {
DefaultCallOriginFactory(int maxCallStack) {
this.maxCallStack = maxCallStack;
}
@Override
public CallStack createCallStack() {
public CallOrigin createCallOrigin() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
// ignore the first 6 as they are always avaje stack elements
@@ -58,7 +59,7 @@ public class DefaultCallStackFactory implements CallStackFactory {
return element.getMethodName().startsWith("_ebean_");
}
private CallStack createCallStack(StackTraceElement[] finalTrace) {
private CallOrigin createCallStack(StackTraceElement[] finalTrace) {
return new CallStack(finalTrace, finalTrace[0].hashCode(), pathHash(finalTrace));
}
@@ -35,7 +35,7 @@ import io.ebean.ValuePair;
import io.ebean.Version;
import io.ebean.annotation.TxIsolation;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.CallStack;
import io.ebean.bean.CallOrigin;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.ObjectGraphNode;
@@ -164,7 +164,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
*/
private final ClockService clockService;
private final CallStackFactory callStackFactory;
private final CallOriginFactory callStackFactory;
/**
* Handles the save, delete, updateSql CallableSql.
@@ -312,12 +312,12 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
/**
* Create the CallStackFactory depending if AutoTune is being used.
*/
private CallStackFactory initCallStackFactory(ServerConfig serverConfig) {
private CallOriginFactory initCallStackFactory(ServerConfig serverConfig) {
if (!serverConfig.getAutoTuneConfig().isActive()) {
// use a common CallStack for performance as we don't care with no AutoTune
return new NoopCallStackFactory();
return new NoopCallOriginFactory();
}
return new DefaultCallStackFactory(serverConfig.getMaxCallStack());
return new DefaultCallOriginFactory(serverConfig.getMaxCallStack());
}
private void configureServerPlugins() {
@@ -1135,7 +1135,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
}
// if determine cost and no origin for AutoTune
if (query.getParentNode() == null) {
query.setOrigin(createCallStack());
query.setOrigin(createCallOrigin());
}
return new OrmQueryRequest<>(this, queryEngine, query, (SpiTransaction) t);
@@ -2309,8 +2309,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
* </p>
*/
@Override
public CallStack createCallStack() {
return callStackFactory.createCallStack();
public CallOrigin createCallOrigin() {
return callStackFactory.createCallOrigin();
}
@Override
@@ -19,7 +19,7 @@ class DefaultSlowQueryListener implements SlowQueryListener {
String firstStack = "";
ObjectGraphNode node = event.getOriginNode();
if (node != null) {
firstStack = node.getOriginQueryPoint().getFirstStackElement();
firstStack = node.getOriginQueryPoint().getTopElement();
}
log.warn("Slow query warning - millis:{} rows:{} caller[{}] sql[{}]", event.getTimeMillis(), event.getRowCount(), firstStack, event.getSql());
}
@@ -0,0 +1,17 @@
package io.ebeaninternal.server.core;
import io.ebean.bean.CallOrigin;
import io.ebean.bean.CallStack;
/**
* A CallOriginFactory we can use when we don't use AutoTune.
*/
class NoopCallOriginFactory implements CallOriginFactory {
private final CallOrigin COMMON = new CallStack(Thread.currentThread().getStackTrace(), 0, 0);
@Override
public CallOrigin createCallOrigin() {
return COMMON;
}
}
@@ -1,16 +0,0 @@
package io.ebeaninternal.server.core;
import io.ebean.bean.CallStack;
/**
* A CallStackFactory we can use when we don't use AutoTune.
*/
class NoopCallStackFactory implements CallStackFactory {
private final CallStack COMMON = new CallStack(Thread.currentThread().getStackTrace(), 0, 0);
@Override
public CallStack createCallStack() {
return COMMON;
}
}
@@ -3,7 +3,7 @@ package io.ebeaninternal.server.loadcontext;
import io.ebean.CacheMode;
import io.ebean.ProfileLocation;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.CallStack;
import io.ebean.bean.CallOrigin;
import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.bean.ObjectGraphOrigin;
@@ -94,8 +94,8 @@ public class DLoadContext implements LoadContext {
}
private ObjectGraphOrigin initOrigin() {
CallStack callStack = ebeanServer.createCallStack();
return new ObjectGraphOrigin(0, callStack, rootDescriptor.getFullName());
CallOrigin callOrigin = ebeanServer.createCallOrigin();
return new ObjectGraphOrigin(0, callOrigin, rootDescriptor.getFullName());
}
public DLoadContext(OrmQueryRequest<?> request, SpiQuerySecondary secondaryQueries) {
@@ -24,7 +24,7 @@ import io.ebean.RawSql;
import io.ebean.Transaction;
import io.ebean.UpdateQuery;
import io.ebean.Version;
import io.ebean.bean.CallStack;
import io.ebean.bean.CallOrigin;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.bean.ObjectGraphOrigin;
import io.ebean.bean.PersistenceContext;
@@ -1073,10 +1073,10 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
@Override
public ObjectGraphNode setOrigin(CallStack callStack) {
public ObjectGraphNode setOrigin(CallOrigin callOrigin) {
// create a 'origin' which links this query to the profiling information
ObjectGraphOrigin o = new ObjectGraphOrigin(calculateOriginQueryHash(), callStack, beanType.getName());
ObjectGraphOrigin o = new ObjectGraphOrigin(calculateOriginQueryHash(), callOrigin, beanType.getName());
parentNode = new ObjectGraphNode(o, null);
return parentNode;
}