From 01539091ef96d8918e6d11ab0901a571f83b0dcd Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Tue, 9 Jan 2018 13:28:57 +1300 Subject: [PATCH] #1231 - Performance: Only use CallStack when using AutoTune (automatic query tuning or profiling) --- src/main/java/io/ebean/bean/CallStack.java | 10 +++- .../java/io/ebean/config/AutoTuneConfig.java | 9 +++ .../server/core/CallStackFactory.java | 2 +- .../server/core/DefaultCallStackFactory.java | 45 ++++++++++++++- .../server/core/DefaultServer.java | 57 +++++++------------ .../server/core/NoopCallStackFactory.java | 16 ++++++ 6 files changed, 97 insertions(+), 42 deletions(-) create mode 100644 src/main/java/io/ebeaninternal/server/core/NoopCallStackFactory.java diff --git a/src/main/java/io/ebean/bean/CallStack.java b/src/main/java/io/ebean/bean/CallStack.java index 3202f5e9a..1453c4238 100644 --- a/src/main/java/io/ebean/bean/CallStack.java +++ b/src/main/java/io/ebean/bean/CallStack.java @@ -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) { diff --git a/src/main/java/io/ebean/config/AutoTuneConfig.java b/src/main/java/io/ebean/config/AutoTuneConfig.java index 1bedca17e..7f9c0d980 100644 --- a/src/main/java/io/ebean/config/AutoTuneConfig.java +++ b/src/main/java/io/ebean/config/AutoTuneConfig.java @@ -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. */ diff --git a/src/main/java/io/ebeaninternal/server/core/CallStackFactory.java b/src/main/java/io/ebeaninternal/server/core/CallStackFactory.java index e514e083a..b61c40370 100644 --- a/src/main/java/io/ebeaninternal/server/core/CallStackFactory.java +++ b/src/main/java/io/ebeaninternal/server/core/CallStackFactory.java @@ -10,5 +10,5 @@ public interface CallStackFactory { /** * Create and return the CallStack given the stack trace elements. */ - CallStack createCallStack(StackTraceElement[] finalTrace); + CallStack createCallStack(); } diff --git a/src/main/java/io/ebeaninternal/server/core/DefaultCallStackFactory.java b/src/main/java/io/ebeaninternal/server/core/DefaultCallStackFactory.java index 23ada5e8b..4b6cabdc0 100644 --- a/src/main/java/io/ebeaninternal/server/core/DefaultCallStackFactory.java +++ b/src/main/java/io/ebeaninternal/server/core/DefaultCallStackFactory.java @@ -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)); } diff --git a/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/src/main/java/io/ebeaninternal/server/core/DefaultServer.java index 3b9d78425..99fd5c3c4 100644 --- a/src/main/java/io/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/io/ebeaninternal/server/core/DefaultServer.java @@ -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 diff --git a/src/main/java/io/ebeaninternal/server/core/NoopCallStackFactory.java b/src/main/java/io/ebeaninternal/server/core/NoopCallStackFactory.java new file mode 100644 index 000000000..8e04cd414 --- /dev/null +++ b/src/main/java/io/ebeaninternal/server/core/NoopCallStackFactory.java @@ -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; + } +}