#1231 - Performance: Only use CallStack when using AutoTune (automatic query tuning or profiling)

This commit is contained in:
Rob Bygrave
2018-01-09 13:28:57 +13:00
parent 7e69f71b51
commit 01539091ef
6 changed files with 97 additions and 42 deletions
+8 -2
View File
@@ -26,14 +26,16 @@ public final class CallStack implements Serializable {
private final StackTraceElement[] callStack;
private final int hc;
public CallStack(StackTraceElement[] callStack, int zeroHash, int pathHash) {
this.callStack = callStack;
this.zeroHash = enc(zeroHash);
this.pathHash = enc(pathHash);
this.hc = computeHashCode();
}
@Override
public int hashCode() {
private int computeHashCode() {
int hc = 0;
for (StackTraceElement aCallStack : callStack) {
hc = 92821 * hc + aCallStack.hashCode();
@@ -41,6 +43,10 @@ public final class CallStack implements Serializable {
return hc;
}
public int hashCode() {
return hc;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
@@ -32,6 +32,15 @@ public class AutoTuneConfig {
public AutoTuneConfig() {
}
/**
* Return true if we are profiling or query tuning.
*
* If we are not doing either then we don't need a CallStack.
*/
public boolean isActive() {
return profiling || queryTuning;
}
/**
* Return the name of the file that holds the query tuning information.
*/
@@ -10,5 +10,5 @@ public interface CallStackFactory {
/**
* Create and return the CallStack given the stack trace elements.
*/
CallStack createCallStack(StackTraceElement[] finalTrace);
CallStack createCallStack();
}
@@ -2,13 +2,56 @@ package io.ebeaninternal.server.core;
import io.ebean.bean.CallStack;
import java.util.Arrays;
/**
* Default CallStackFactory where the Hash function for StackTraceElement includes the line number.
*/
public class DefaultCallStackFactory implements CallStackFactory {
private static final int IGNORE_LEADING_ELEMENTS = 5;
private static final String IO_EBEAN = "io.ebean";
private final int maxCallStack;
DefaultCallStackFactory(int maxCallStack) {
this.maxCallStack = maxCallStack;
}
@Override
public CallStack createCallStack(StackTraceElement[] finalTrace) {
public CallStack createCallStack() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
// ignore the first 6 as they are always avaje stack elements
int startIndex = IGNORE_LEADING_ELEMENTS;
// find the first non-avaje stackElement
for (; startIndex < stackTrace.length; startIndex++) {
if (!stackTrace[startIndex].getClassName().startsWith(IO_EBEAN)) {
break;
}
}
int stackLength = stackTrace.length - startIndex;
if (stackLength > maxCallStack) {
// maximum of maxCallStack stackTrace elements
stackLength = maxCallStack;
}
// create the 'interesting' part of the stackTrace
StackTraceElement[] finalTrace = new StackTraceElement[stackLength];
System.arraycopy(stackTrace, startIndex, finalTrace, 0, stackLength);
if (stackLength < 1) {
// this should not really happen
throw new RuntimeException("StackTraceElement size 0? stack: " + Arrays.toString(stackTrace));
}
return createCallStack(finalTrace);
}
private CallStack createCallStack(StackTraceElement[] finalTrace) {
return new CallStack(finalTrace, finalTrace[0].hashCode(), pathHash(finalTrace));
}
@@ -103,7 +103,6 @@ import javax.persistence.NonUniqueResultException;
import javax.persistence.OptimisticLockException;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
@@ -123,10 +122,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private static final Logger logger = LoggerFactory.getLogger(DefaultServer.class);
private static final int IGNORE_LEADING_ELEMENTS = 5;
private static final String IO_EBEAN = "io.ebean";
private final ServerConfig serverConfig;
private final String serverName;
@@ -139,9 +134,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private final DataTimeZone dataTimeZone;
private final CallStackFactory callStackFactory = new DefaultCallStackFactory();
private final int maxCallStack;
private final CallStackFactory callStackFactory;
/**
* Ebean defaults this to true but for EJB compatible behaviour set this to
@@ -254,7 +247,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
this.updateAllPropertiesInBatch = serverConfig.isUpdateAllPropertiesInBatch();
this.collectQueryOrigins = serverConfig.isCollectQueryOrigins();
this.collectQueryStatsByNode = serverConfig.isCollectQueryStatsByNode();
this.maxCallStack = serverConfig.getMaxCallStack();
this.callStackFactory = initCallStackFactory(serverConfig);
this.rollbackOnChecked = serverConfig.isTransactionRollbackOnChecked();
@@ -284,6 +277,17 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
ShutdownManager.registerEbeanServer(this);
}
/**
* Create the CallStackFactory depending if AutoTune is being used.
*/
private CallStackFactory 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 DefaultCallStackFactory(serverConfig.getMaxCallStack());
}
private void configureServerPlugins() {
autoTuneService.startup();
@@ -380,6 +384,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return transactionManager.getDataSource();
}
// @Override
// public DataSource getReadOnlyDataSource() {
// return transactionManager.getReadOnlyDataSource();
// }
@Override
public ReadAuditPrepare getReadAuditPrepare() {
return readAuditPrepare;
@@ -2147,35 +2156,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
*/
@Override
public CallStack createCallStack() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
// ignore the first 6 as they are always avaje stack elements
int startIndex = IGNORE_LEADING_ELEMENTS;
// find the first non-avaje stackElement
for (; startIndex < stackTrace.length; startIndex++) {
if (!stackTrace[startIndex].getClassName().startsWith(IO_EBEAN)) {
break;
}
}
int stackLength = stackTrace.length - startIndex;
if (stackLength > maxCallStack) {
// maximum of maxCallStack stackTrace elements
stackLength = maxCallStack;
}
// create the 'interesting' part of the stackTrace
StackTraceElement[] finalTrace = new StackTraceElement[stackLength];
System.arraycopy(stackTrace, startIndex, finalTrace, 0, stackLength);
if (stackLength < 1) {
// this should not really happen
throw new RuntimeException("StackTraceElement size 0? stack: " + Arrays.toString(stackTrace));
}
return callStackFactory.createCallStack(finalTrace);
return callStackFactory.createCallStack();
}
@Override
@@ -0,0 +1,16 @@
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;
}
}