mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Refactor for query plan capture with initial threshold micros - part 2
This commit is contained in:
@@ -33,11 +33,6 @@ public interface MetaInfoManager {
|
||||
*/
|
||||
List<MetricData> collectMetricsAsData();
|
||||
|
||||
/**
|
||||
* Collect query plans.
|
||||
*/
|
||||
List<MetaQueryPlan> collectQueryPlans(QueryPlanRequest request);
|
||||
|
||||
/**
|
||||
* Visit the metrics resetting and collecting/reporting as desired.
|
||||
*/
|
||||
@@ -54,4 +49,21 @@ public interface MetaInfoManager {
|
||||
*/
|
||||
void resetAllMetrics();
|
||||
|
||||
/**
|
||||
* Initiate query plan collection by turning on "bind capture" on matching query plans.
|
||||
* <p>
|
||||
* Also refer to ServerConfig collectQueryPlans that needs to be set to true
|
||||
* and collectQueryPlanThresholdMicros which is a global defaults that can also
|
||||
* initiate query plan capture.
|
||||
*
|
||||
* @return The query plans that have had bind capture turned on by this request.
|
||||
*/
|
||||
List<MetaQueryPlan> queryPlanInit(QueryPlanInit initRequest);
|
||||
|
||||
/**
|
||||
* Collect query plans in the foreground.
|
||||
*/
|
||||
List<MetaQueryPlan> queryPlanCollectNow(QueryPlanRequest request);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Initiate query plan collection for plans by their hash or all query plans.
|
||||
*/
|
||||
public class QueryPlanInit {
|
||||
|
||||
private boolean all;
|
||||
|
||||
private Set<String> hashes = new HashSet<>();
|
||||
|
||||
private long thresholdMicros;
|
||||
|
||||
/**
|
||||
* Return true if this initiates bind collection on all query plans.
|
||||
*/
|
||||
public boolean isAll() {
|
||||
return all;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to initiate bind collection on all query plans.
|
||||
*/
|
||||
public void setAll(boolean all) {
|
||||
this.all = all;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the query execution time threshold which must be exceeded to initiate
|
||||
* query plan collection.
|
||||
*/
|
||||
public long getThresholdMicros() {
|
||||
return thresholdMicros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the query execution time threshold which must be exceeded to initiate
|
||||
* query plan collection.
|
||||
*/
|
||||
public void setThresholdMicros(long thresholdMicros) {
|
||||
this.thresholdMicros = thresholdMicros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the query plan should be initiated based on it's hash.
|
||||
*/
|
||||
public boolean includeHash(String hash) {
|
||||
return all || hashes.contains(hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the specific hashes that we want to collect query plans on.
|
||||
*/
|
||||
public Set<String> getHashes() {
|
||||
return hashes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the specific hashes that we want to collect query plans on.
|
||||
*/
|
||||
public void setHashes(Set<String> hashes) {
|
||||
this.hashes = hashes;
|
||||
}
|
||||
}
|
||||
@@ -1,97 +1,66 @@
|
||||
package io.ebean.meta;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Request used to capture query plans.
|
||||
*/
|
||||
public class QueryPlanRequest {
|
||||
|
||||
private final List<MetaQueryPlan> plans = new ArrayList<>();
|
||||
|
||||
private Connection connection;
|
||||
|
||||
private boolean store;
|
||||
|
||||
private long since;
|
||||
|
||||
private Set<Class<?>> includedBeanTypes;
|
||||
private int maxCount;
|
||||
|
||||
private Set<String> includedLabels;
|
||||
|
||||
public List<MetaQueryPlan> getPlans() {
|
||||
return plans;
|
||||
}
|
||||
private long maxTimeMillis;
|
||||
|
||||
/**
|
||||
* Return the connection to use to capture the query plans.
|
||||
*/
|
||||
public Connection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the connection to use to capture the query plans.
|
||||
*/
|
||||
public void setConnection(Connection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the captured query plan is stored.
|
||||
*/
|
||||
public boolean isStore() {
|
||||
return store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to true to store the captured query plan.
|
||||
*/
|
||||
public void setStore(boolean store) {
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the epoch time after which the query plan was capture (to be included).
|
||||
* Return the epoch time in millis for minimum bind capture time.
|
||||
* <p>
|
||||
* When set this ensures that the bind values used to get the query plan
|
||||
* have been around for a while (e.g. 5 mins) and so reasonably represent
|
||||
* bind values that match the slowest execution for this query plan.
|
||||
*/
|
||||
public long getSince() {
|
||||
return since;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the epoch time after which the query plan was captured.
|
||||
* <p>
|
||||
* This is used to only capture plans that have changed since a given time (like the time of last capture).
|
||||
* </p>
|
||||
* Set the epoch time (e.g. 5 mins ago) such that the query bind values
|
||||
* reasonably represent bind values that match the slowest execution for this query plan.
|
||||
*
|
||||
* @param since The time after which the query plan was captured to be included
|
||||
* @param since The minimum age of the bind values capture.
|
||||
*/
|
||||
public void setSince(long since) {
|
||||
this.since = since;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process consume the query plan.
|
||||
* Return the maximum number of plans to capture.
|
||||
*/
|
||||
public void process(MetaQueryPlan plan) {
|
||||
plans.add(plan);
|
||||
public int getMaxCount() {
|
||||
return maxCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the bean type should be included in the query plan capture.
|
||||
* Set the maximum number of plans to capture.
|
||||
*/
|
||||
public boolean includeType(Class<?> beanType) {
|
||||
return includedBeanTypes == null || includedBeanTypes.isEmpty() || includedBeanTypes.contains(beanType);
|
||||
public void setMaxCount(int maxCount) {
|
||||
this.maxCount = maxCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the label should be included in the query plan capture.
|
||||
* Return the maximum amount of time we want to use to capture plans.
|
||||
* <p>
|
||||
* Query plan collection will stop once this time is exceeded.
|
||||
*/
|
||||
public boolean includeLabel(String label) {
|
||||
return includedLabels == null || includedLabels.isEmpty() || includedLabels.contains(label);
|
||||
public long getMaxTimeMillis() {
|
||||
return maxTimeMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum amount of time we want to use to capture plans.
|
||||
* <p>
|
||||
* Query plan collection will stop once this time is exceeded.
|
||||
*/
|
||||
public void setMaxTimeMillis(long maxTimeMillis) {
|
||||
this.maxTimeMillis = maxTimeMillis;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.meta.QueryPlanRequest;
|
||||
import io.ebeaninternal.server.type.bindcapture.BindCapture;
|
||||
|
||||
class NoopQueryBindCapture implements SpiQueryBindCapture {
|
||||
@@ -16,7 +15,7 @@ class NoopQueryBindCapture implements SpiQueryBindCapture {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collectQueryPlan(QueryPlanRequest request) {
|
||||
public void queryPlanInit(long thresholdMicros) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.meta.MetaQueryPlan;
|
||||
import io.ebean.meta.QueryPlanRequest;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
class NoopQueryPlanManager implements QueryPlanManager {
|
||||
|
||||
@Override
|
||||
public SpiQueryBindCapture createBindCapture(SpiQueryPlan queryPlan) {
|
||||
return SpiQueryBindCapture.NOOP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaQueryPlan> collect(QueryPlanRequest request) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.meta.MetaQueryPlan;
|
||||
import io.ebean.meta.QueryPlanRequest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Manage query plan capture.
|
||||
*/
|
||||
@@ -11,4 +16,9 @@ public interface QueryPlanManager {
|
||||
* Create the bind capture for the given query plan.
|
||||
*/
|
||||
SpiQueryBindCapture createBindCapture(SpiQueryPlan queryPlan);
|
||||
|
||||
/**
|
||||
* Collect the database query plans.
|
||||
*/
|
||||
List<MetaQueryPlan> collect(QueryPlanRequest request);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.meta.MetaQueryPlan;
|
||||
|
||||
/**
|
||||
* Internal database query plan being capture.
|
||||
*/
|
||||
public interface SpiDbQueryPlan extends MetaQueryPlan {
|
||||
|
||||
/**
|
||||
* Extend with queryTimeMicros and captureCount.
|
||||
*/
|
||||
SpiDbQueryPlan with(long queryTimeMicros, long captureCount);
|
||||
|
||||
}
|
||||
@@ -20,12 +20,16 @@ public interface SpiQueryBindCapture {
|
||||
boolean collectFor(long timeMicros);
|
||||
|
||||
/**
|
||||
* Set the bind capture and the related query execution time.
|
||||
* Set the captured bind values that we can use later to collect a query plan.
|
||||
*
|
||||
* @param bindCapture The bind values of the query
|
||||
* @param queryTimeMicros The query execution time
|
||||
* @param startNanos The nanos start of this bind capture
|
||||
*/
|
||||
void setBind(BindCapture bindCapture, long timeMicros, long startNanos);
|
||||
void setBind(BindCapture bindCapture, long queryTimeMicros, long startNanos);
|
||||
|
||||
/**
|
||||
* Collect the query execution plan usually executing the query to do so.
|
||||
* Update the threshold micros triggering the bind capture.
|
||||
*/
|
||||
void collectQueryPlan(QueryPlanRequest request);
|
||||
void queryPlanInit(long thresholdMicros);
|
||||
}
|
||||
|
||||
@@ -31,4 +31,18 @@ public interface SpiQueryPlan {
|
||||
* The related profile location.
|
||||
*/
|
||||
ProfileLocation getProfileLocation();
|
||||
|
||||
/**
|
||||
* Initiate bind capture with the give threshold.
|
||||
*/
|
||||
void queryPlanInit(long thresholdMicros);
|
||||
|
||||
/**
|
||||
* Return as Database query plan.
|
||||
*
|
||||
* @param bind Description of the bind values used
|
||||
* @param planString The raw database query plan
|
||||
*/
|
||||
SpiDbQueryPlan createMeta(String bind, String planString);
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import io.ebean.meta.MetaQueryPlan;
|
||||
import io.ebean.meta.MetaTimedMetric;
|
||||
import io.ebean.meta.MetricData;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.meta.QueryPlanInit;
|
||||
import io.ebean.meta.QueryPlanRequest;
|
||||
import io.ebean.meta.ServerMetrics;
|
||||
import io.ebean.meta.ServerMetricsAsJson;
|
||||
@@ -27,8 +28,13 @@ public class DefaultMetaInfoManager implements MetaInfoManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaQueryPlan> collectQueryPlans(QueryPlanRequest request) {
|
||||
return server.collectQueryPlans(request);
|
||||
public List<MetaQueryPlan> queryPlanInit(QueryPlanInit initRequest) {
|
||||
return server.queryPlanInit(initRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaQueryPlan> queryPlanCollectNow(QueryPlanRequest request) {
|
||||
return server.queryPlanCollectNow(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -55,6 +55,7 @@ import io.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import io.ebean.meta.MetaInfoManager;
|
||||
import io.ebean.meta.MetaQueryPlan;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.meta.QueryPlanInit;
|
||||
import io.ebean.meta.QueryPlanRequest;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.plugin.Plugin;
|
||||
@@ -62,7 +63,6 @@ import io.ebean.plugin.Property;
|
||||
import io.ebean.plugin.SpiServer;
|
||||
import io.ebean.text.csv.CsvReader;
|
||||
import io.ebean.text.json.JsonContext;
|
||||
import io.ebean.util.JdbcClose;
|
||||
import io.ebeaninternal.api.ExtraMetrics;
|
||||
import io.ebeaninternal.api.LoadBeanRequest;
|
||||
import io.ebeaninternal.api.LoadManyRequest;
|
||||
@@ -254,17 +254,13 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
* Create the DefaultServer.
|
||||
*/
|
||||
public DefaultServer(InternalConfiguration config, ServerCacheManager cache) {
|
||||
|
||||
this.logManager = config.getLogManager();
|
||||
this.dtoBeanManager = config.getDtoBeanManager();
|
||||
this.serverConfig = config.getServerConfig();
|
||||
this.metaInfoManager = new DefaultMetaInfoManager(this);
|
||||
this.serverCacheManager = cache;
|
||||
this.databasePlatform = config.getDatabasePlatform();
|
||||
this.backgroundExecutor = config.getBackgroundExecutor();
|
||||
this.queryPlanManager = config.getQueryPlanManager();
|
||||
this.extraMetrics = config.getExtraMetrics();
|
||||
|
||||
this.serverName = serverConfig.getName();
|
||||
this.lazyLoadBatchSize = serverConfig.getLazyLoadBatchSize();
|
||||
this.queryBatchSize = serverConfig.getQueryBatchSize();
|
||||
@@ -275,10 +271,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
this.currentTenantProvider = serverConfig.getCurrentTenantProvider();
|
||||
this.slowQueryMicros = config.getSlowQueryMicros();
|
||||
this.slowQueryListener = config.getSlowQueryListener();
|
||||
|
||||
this.beanDescriptorManager = config.getBeanDescriptorManager();
|
||||
beanDescriptorManager.setEbeanServer(this);
|
||||
|
||||
this.updateAllPropertiesInBatch = serverConfig.isUpdateAllPropertiesInBatch();
|
||||
this.callStackFactory = initCallStackFactory(serverConfig);
|
||||
|
||||
@@ -299,6 +293,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
DocStoreIntegration docStoreComponents = config.createDocStoreIntegration(this);
|
||||
this.transactionManager = config.createTransactionManager(docStoreComponents.updateProcessor());
|
||||
this.documentStore = docStoreComponents.documentStore();
|
||||
this.queryPlanManager = config.initQueryPlanManager(transactionManager.getDataSource());
|
||||
this.metaInfoManager = new DefaultMetaInfoManager(this);
|
||||
|
||||
this.serverPlugins = config.getPlugins();
|
||||
this.ddlGenerator = new DdlGenerator(this, serverConfig);
|
||||
@@ -2441,23 +2437,16 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
visitor.visitEnd();
|
||||
}
|
||||
|
||||
public List<MetaQueryPlan> collectQueryPlans(QueryPlanRequest request) {
|
||||
Connection connection = null;
|
||||
try {
|
||||
connection = getDataSource().getConnection();
|
||||
request.setConnection(connection);
|
||||
beanDescriptorManager.collectQueryPlans(request);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
} finally {
|
||||
JdbcClose.close(connection);
|
||||
}
|
||||
return request.getPlans();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiQueryBindCapture createQueryBindCapture(SpiQueryPlan plan) {
|
||||
return queryPlanManager.createBindCapture(plan);
|
||||
}
|
||||
|
||||
List<MetaQueryPlan> queryPlanInit(QueryPlanInit initRequest) {
|
||||
return beanDescriptorManager.queryPlanInit(initRequest);
|
||||
}
|
||||
|
||||
List<MetaQueryPlan> queryPlanCollectNow(QueryPlanRequest request) {
|
||||
return queryPlanManager.collect(request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -651,12 +651,12 @@ public class InternalConfiguration {
|
||||
return new DefaultServerCacheManager(builder);
|
||||
}
|
||||
|
||||
public QueryPlanManager getQueryPlanManager() {
|
||||
public QueryPlanManager initQueryPlanManager(DataSource dataSource) {
|
||||
if (!serverConfig.isCollectQueryPlans()) {
|
||||
return QueryPlanManager.NOOP;
|
||||
}
|
||||
long threshold = serverConfig.getCollectQueryPlanThresholdMicros();
|
||||
return new CQueryPlanManager(threshold, queryPlanLogger(databasePlatform.getPlatform()));
|
||||
return new CQueryPlanManager(dataSource, threshold, queryPlanLogger(databasePlatform.getPlatform()), extraMetrics);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -665,13 +665,13 @@ public class InternalConfiguration {
|
||||
QueryPlanLogger queryPlanLogger(Platform platform) {
|
||||
switch (platform.base()) {
|
||||
case POSTGRES:
|
||||
return new QueryPlanLoggerPostgres(extraMetrics);
|
||||
return new QueryPlanLoggerPostgres();
|
||||
case SQLSERVER:
|
||||
return new QueryPlanLoggerSqlServer(extraMetrics);
|
||||
return new QueryPlanLoggerSqlServer();
|
||||
case ORACLE:
|
||||
return new QueryPlanLoggerOracle(extraMetrics);
|
||||
return new QueryPlanLoggerOracle();
|
||||
default:
|
||||
return new QueryPlanLoggerExplain(extraMetrics);
|
||||
return new QueryPlanLoggerExplain();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,9 @@ import io.ebean.event.changelog.ChangeType;
|
||||
import io.ebean.event.readaudit.ReadAuditLogger;
|
||||
import io.ebean.event.readaudit.ReadAuditPrepare;
|
||||
import io.ebean.event.readaudit.ReadEvent;
|
||||
import io.ebean.meta.MetaQueryPlan;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.meta.QueryPlanRequest;
|
||||
import io.ebean.meta.QueryPlanInit;
|
||||
import io.ebean.plugin.BeanDocType;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.plugin.ExpressionPath;
|
||||
@@ -1682,10 +1683,11 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return new DeployUpdateParser(this).parse(ormUpdateStatement);
|
||||
}
|
||||
|
||||
void collectQueryPlans(QueryPlanRequest request) {
|
||||
void queryPlanInit(QueryPlanInit request, List<MetaQueryPlan> list) {
|
||||
for (CQueryPlan queryPlan : queryPlanCache.values()) {
|
||||
if (request.includeLabel(queryPlan.getLabel())) {
|
||||
queryPlan.collectQueryPlan(request);
|
||||
if (request.includeHash(queryPlan.getHash())) {
|
||||
queryPlan.queryPlanInit(request.getThresholdMicros());
|
||||
list.add(queryPlan.createMeta(null, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,9 @@ import io.ebean.event.changelog.ChangeLogFilter;
|
||||
import io.ebean.event.changelog.ChangeLogListener;
|
||||
import io.ebean.event.changelog.ChangeLogPrepare;
|
||||
import io.ebean.event.changelog.ChangeLogRegister;
|
||||
import io.ebean.meta.MetaQueryPlan;
|
||||
import io.ebean.meta.MetricVisitor;
|
||||
import io.ebean.meta.QueryPlanRequest;
|
||||
import io.ebean.meta.QueryPlanInit;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.util.AnnotationUtil;
|
||||
import io.ebeaninternal.api.ConcurrencyMode;
|
||||
@@ -55,7 +56,6 @@ import io.ebeaninternal.server.persist.platform.MultiValueBind;
|
||||
import io.ebeaninternal.server.properties.BeanPropertiesReader;
|
||||
import io.ebeaninternal.server.properties.BeanPropertyAccess;
|
||||
import io.ebeaninternal.server.properties.EnhanceBeanPropertyAccess;
|
||||
import io.ebeaninternal.server.query.CQueryPlan;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
import io.ebeaninternal.server.type.ScalarTypeInteger;
|
||||
import io.ebeaninternal.server.type.TypeManager;
|
||||
@@ -1693,12 +1693,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
}
|
||||
}
|
||||
|
||||
public void collectQueryPlans(QueryPlanRequest request) {
|
||||
public List<MetaQueryPlan> queryPlanInit(QueryPlanInit request) {
|
||||
List<MetaQueryPlan> list = new ArrayList<>();
|
||||
for (BeanDescriptor<?> desc : immutableDescriptorList) {
|
||||
if (request.includeType(desc.getBeanType())) {
|
||||
desc.collectQueryPlans(request);
|
||||
}
|
||||
desc.queryPlanInit(request, list);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1710,7 +1710,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
@Override
|
||||
public int compare(BeanDescriptor<?> o1, BeanDescriptor<?> o2) {
|
||||
|
||||
return o1.getName().compareTo(o2.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.meta.QueryPlanRequest;
|
||||
import io.ebeaninternal.api.SpiDbQueryPlan;
|
||||
import io.ebeaninternal.api.SpiQueryBindCapture;
|
||||
import io.ebeaninternal.api.SpiQueryPlan;
|
||||
import io.ebeaninternal.server.type.bindcapture.BindCapture;
|
||||
@@ -9,8 +9,8 @@ class CQueryBindCapture implements SpiQueryBindCapture {
|
||||
|
||||
private static final double multiplier = 1.3d;
|
||||
|
||||
private final CQueryPlanManager manager;
|
||||
private final SpiQueryPlan queryPlan;
|
||||
private final QueryPlanLogger planLogger;
|
||||
|
||||
private BindCapture bindCapture;
|
||||
private long queryTimeMicros;
|
||||
@@ -19,9 +19,9 @@ class CQueryBindCapture implements SpiQueryBindCapture {
|
||||
|
||||
private long lastBindCapture;
|
||||
|
||||
CQueryBindCapture(SpiQueryPlan queryPlan, QueryPlanLogger planLogger, long thresholdMicros) {
|
||||
CQueryBindCapture(CQueryPlanManager manager, SpiQueryPlan queryPlan, long thresholdMicros) {
|
||||
this.manager = manager;
|
||||
this.queryPlan = queryPlan;
|
||||
this.planLogger = planLogger;
|
||||
this.thresholdMicros = thresholdMicros;
|
||||
}
|
||||
|
||||
@@ -33,13 +33,6 @@ class CQueryBindCapture implements SpiQueryBindCapture {
|
||||
return timeMicros > thresholdMicros && captureCount < 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the captured bind values that we can use later to collect a query plan.
|
||||
*
|
||||
* @param bindCapture The bind values of the query
|
||||
* @param queryTimeMicros The query execution time
|
||||
* @param startNanos The nanos start of this bind capture
|
||||
*/
|
||||
@Override
|
||||
public void setBind(BindCapture bindCapture, long queryTimeMicros, long startNanos) {
|
||||
synchronized (this) {
|
||||
@@ -48,27 +41,36 @@ class CQueryBindCapture implements SpiQueryBindCapture {
|
||||
this.bindCapture = bindCapture;
|
||||
this.queryTimeMicros = queryTimeMicros;
|
||||
lastBindCapture = System.currentTimeMillis();
|
||||
planLogger.addBindTimeSince(startNanos);
|
||||
manager.notifyBindCapture(this, startNanos);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queryPlanInit(long thresholdMicros) {
|
||||
// effective enable bind capture for this plan
|
||||
this.thresholdMicros = thresholdMicros;
|
||||
this.captureCount = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the query plan using already captured bind values.
|
||||
*/
|
||||
@Override
|
||||
public void collectQueryPlan(QueryPlanRequest request) {
|
||||
if (bindCapture == null || request.getSince() > lastBindCapture) {
|
||||
public boolean collectQueryPlan(CQueryPlanRequest request) {
|
||||
if (bindCapture == null || request.getSince() < lastBindCapture) {
|
||||
// no bind capture since the last capture
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
final BindCapture last = this.bindCapture;
|
||||
|
||||
DQueryPlanOutput queryPlan = planLogger.collectQueryPlan(request.getConnection(), this.queryPlan, last);
|
||||
SpiDbQueryPlan queryPlan = manager.collectPlan(request.getConnection(), this.queryPlan, last);
|
||||
if (queryPlan != null) {
|
||||
queryPlan.with(queryTimeMicros, captureCount, this.queryPlan.getHash());
|
||||
request.process(queryPlan);
|
||||
request.add(queryPlan.with(queryTimeMicros, captureCount));
|
||||
// effectively turn off bind capture for this plan
|
||||
thresholdMicros = Long.MAX_VALUE;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package io.ebeaninternal.server.query;
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.config.dbplatform.SqlLimitResponse;
|
||||
import io.ebean.meta.MetricType;
|
||||
import io.ebean.meta.QueryPlanRequest;
|
||||
import io.ebean.metric.MetricFactory;
|
||||
import io.ebean.metric.TimedMetric;
|
||||
import io.ebeaninternal.api.CQueryPlanKey;
|
||||
@@ -56,8 +55,6 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
|
||||
private final SpiEbeanServer server;
|
||||
|
||||
private final boolean autoTuned;
|
||||
|
||||
private final ProfileLocation profileLocation;
|
||||
|
||||
private final String location;
|
||||
@@ -114,7 +111,6 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
this.label = query.getPlanLabel();
|
||||
this.name = deriveName(label, query.getType());
|
||||
this.location = location();
|
||||
this.autoTuned = query.isAutoTuned();
|
||||
this.asOfTableCount = query.getAsOfTableCount();
|
||||
this.sql = sqlRes.getSql();
|
||||
this.rowNumberIncluded = sqlRes.isIncludesRowNumberColumn();
|
||||
@@ -141,7 +137,6 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
this.name = deriveName(label, query.getType());
|
||||
this.location = location();
|
||||
this.planKey = buildPlanKey(sql, rowNumberIncluded, logWhereSql);
|
||||
this.autoTuned = false;
|
||||
this.asOfTableCount = 0;
|
||||
this.sql = sql;
|
||||
this.sqlTree = sqlTree;
|
||||
@@ -151,7 +146,7 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
this.encryptedProps = sqlTree.getEncryptedProps();
|
||||
this.stats = new CQueryPlanStats(this);
|
||||
this.dependentTables = sqlTree.dependentTables();
|
||||
this.bindCapture = initBindCapture(query);
|
||||
this.bindCapture = initBindCaptureRaw(sql);
|
||||
this.hash = md5Hash();
|
||||
}
|
||||
|
||||
@@ -169,6 +164,10 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
return query.getType().isUpdate() ? SpiQueryBindCapture.NOOP : server.createQueryBindCapture(this);
|
||||
}
|
||||
|
||||
private SpiQueryBindCapture initBindCaptureRaw(String sql) {
|
||||
return sql.equals(RESULT_SET_BASED_RAW_SQL) ? SpiQueryBindCapture.NOOP : server.createQueryBindCapture(this);
|
||||
}
|
||||
|
||||
private String location() {
|
||||
return (profileLocation == null) ? null : profileLocation.location();
|
||||
}
|
||||
@@ -219,6 +218,16 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void queryPlanInit(long thresholdMicros) {
|
||||
bindCapture.queryPlanInit(thresholdMicros);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DQueryPlanOutput createMeta(String bind, String planString) {
|
||||
return new DQueryPlanOutput(getBeanType(), name, hash, sql, profileLocation, bind, planString);
|
||||
}
|
||||
|
||||
public DataReader createDataReader(ResultSet rset) {
|
||||
return new RsetDataReader(dataTimeZone, rset);
|
||||
}
|
||||
@@ -342,8 +351,8 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
}
|
||||
|
||||
void captureBindForQueryPlan(CQueryPredicates predicates, long executionTimeMicros) {
|
||||
final long startNanos = System.nanoTime();
|
||||
try {
|
||||
long startNanos = System.nanoTime();
|
||||
DataBindCapture capture = bindCapture();
|
||||
predicates.bind(capture);
|
||||
bindCapture.setBind(capture.bindCapture(), executionTimeMicros, startNanos);
|
||||
@@ -351,11 +360,4 @@ public class CQueryPlan implements SpiQueryPlan {
|
||||
logger.error("Error capturing bind values", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void collectQueryPlan(QueryPlanRequest request) {
|
||||
|
||||
if (!getSql().equals(RESULT_SET_BASED_RAW_SQL) && bindCapture != null) {
|
||||
bindCapture.collectQueryPlan(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,89 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.meta.MetaQueryPlan;
|
||||
import io.ebean.meta.QueryPlanRequest;
|
||||
import io.ebean.metric.TimedMetric;
|
||||
import io.ebeaninternal.api.ExtraMetrics;
|
||||
import io.ebeaninternal.api.QueryPlanManager;
|
||||
import io.ebeaninternal.api.SpiDbQueryPlan;
|
||||
import io.ebeaninternal.api.SpiQueryBindCapture;
|
||||
import io.ebeaninternal.api.SpiQueryPlan;
|
||||
import io.ebeaninternal.server.type.bindcapture.BindCapture;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
public class CQueryPlanManager implements QueryPlanManager {
|
||||
|
||||
private final long threshold;
|
||||
private static final Logger log = LoggerFactory.getLogger(CQueryPlanManager.class);
|
||||
|
||||
private static final Object dummy = new Object();
|
||||
|
||||
private final ConcurrentHashMap<CQueryBindCapture, Object> plans = new ConcurrentHashMap<>();
|
||||
|
||||
private final DataSource dataSource;
|
||||
|
||||
private final long defaultThreshold;
|
||||
|
||||
private final QueryPlanLogger planLogger;
|
||||
|
||||
public CQueryPlanManager(long threshold, QueryPlanLogger planLogger) {
|
||||
this.threshold = threshold;
|
||||
private final TimedMetric timeCollection;
|
||||
|
||||
private final TimedMetric timeBindCapture;
|
||||
|
||||
public CQueryPlanManager(DataSource dataSource, long defaultThreshold, QueryPlanLogger planLogger, ExtraMetrics extraMetrics) {
|
||||
this.dataSource = dataSource;
|
||||
this.defaultThreshold = defaultThreshold;
|
||||
this.planLogger = planLogger;
|
||||
this.timeCollection = extraMetrics.getPlanCollect();
|
||||
this.timeBindCapture = extraMetrics.getBindCapture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiQueryBindCapture createBindCapture(SpiQueryPlan queryPlan) {
|
||||
return new CQueryBindCapture(queryPlan, planLogger, threshold);
|
||||
return new CQueryBindCapture(this, queryPlan, defaultThreshold);
|
||||
}
|
||||
|
||||
public void notifyBindCapture(CQueryBindCapture planBind, long startNanos) {
|
||||
plans.put(planBind, dummy);
|
||||
timeBindCapture.addSinceNanos(startNanos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetaQueryPlan> collect(QueryPlanRequest request) {
|
||||
if (plans.isEmpty()) {
|
||||
return emptyList();
|
||||
}
|
||||
long startNanos = System.nanoTime();
|
||||
try {
|
||||
return collectPlans(request);
|
||||
} finally {
|
||||
timeCollection.addSinceNanos(startNanos);
|
||||
}
|
||||
}
|
||||
|
||||
private List<MetaQueryPlan> collectPlans(QueryPlanRequest request) {
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
CQueryPlanRequest req = new CQueryPlanRequest(connection, request, plans.keySet().iterator());
|
||||
while (req.hasNext()) {
|
||||
req.nextCapture();
|
||||
}
|
||||
return req.getPlans();
|
||||
|
||||
} catch (SQLException e) {
|
||||
log.error("Error during query plan collection", e);
|
||||
return emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public SpiDbQueryPlan collectPlan(Connection connection, SpiQueryPlan queryPlan, BindCapture last) {
|
||||
return planLogger.collectPlan(connection, queryPlan, last);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.meta.MetaQueryPlan;
|
||||
import io.ebean.meta.QueryPlanRequest;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Captures database query plans.
|
||||
*/
|
||||
class CQueryPlanRequest {
|
||||
|
||||
private final List<MetaQueryPlan> plans = new ArrayList<>();
|
||||
|
||||
private final Connection connection;
|
||||
private final long since;
|
||||
private final int maxCount;
|
||||
private final long maxTime;
|
||||
private Iterator<CQueryBindCapture> iterator;
|
||||
|
||||
CQueryPlanRequest(Connection connection, QueryPlanRequest req, Iterator<CQueryBindCapture> iterator) {
|
||||
this.connection = connection;
|
||||
this.iterator = iterator;
|
||||
this.maxCount = req.getMaxCount();
|
||||
long reqSince = req.getSince();
|
||||
this.since = (reqSince == 0) ? Long.MAX_VALUE: reqSince;
|
||||
long maxTimeMillis = req.getMaxTimeMillis();
|
||||
this.maxTime = maxTimeMillis > 0 ? System.currentTimeMillis() + maxTimeMillis : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the connection used to collect the db query plan.
|
||||
*/
|
||||
Connection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the collected query plan.
|
||||
*/
|
||||
void add(MetaQueryPlan dbQueryPlan) {
|
||||
plans.add(dbQueryPlan);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the min epoch time in millis for minimum bind capture age.
|
||||
*/
|
||||
long getSince() {
|
||||
return since;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the captured query plans.
|
||||
*/
|
||||
List<MetaQueryPlan> getPlans() {
|
||||
return plans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true to continue database query plan capture.
|
||||
*/
|
||||
boolean hasNext() {
|
||||
return moreByCount() && moreByTime() && iterator.hasNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture the next database query plan.
|
||||
*/
|
||||
void nextCapture() {
|
||||
final CQueryBindCapture next = iterator.next();
|
||||
if (next.collectQueryPlan(this)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean moreByCount() {
|
||||
return maxCount == 0 || maxCount > plans.size();
|
||||
}
|
||||
|
||||
private boolean moreByTime() {
|
||||
return maxTime == 0 || maxTime > System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,12 @@ package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.meta.MetaQueryPlan;
|
||||
import io.ebeaninternal.api.SpiDbQueryPlan;
|
||||
|
||||
/**
|
||||
* Captured query plan details.
|
||||
*/
|
||||
class DQueryPlanOutput implements MetaQueryPlan {
|
||||
class DQueryPlanOutput implements MetaQueryPlan, SpiDbQueryPlan {
|
||||
|
||||
private final Class<?> beanType;
|
||||
private final String label;
|
||||
@@ -20,13 +21,14 @@ class DQueryPlanOutput implements MetaQueryPlan {
|
||||
private long queryTimeMicros;
|
||||
private long captureCount;
|
||||
|
||||
DQueryPlanOutput(Class<?> beanType, String label, String sql, String bind, String plan, ProfileLocation profileLocation) {
|
||||
DQueryPlanOutput(Class<?> beanType, String label, String hash, String sql, ProfileLocation profileLocation, String bind, String plan) {
|
||||
this.beanType = beanType;
|
||||
this.label = label;
|
||||
this.hash = hash;
|
||||
this.sql = sql;
|
||||
this.profileLocation = profileLocation;
|
||||
this.bind = bind;
|
||||
this.plan = plan;
|
||||
this.profileLocation = profileLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -104,9 +106,10 @@ class DQueryPlanOutput implements MetaQueryPlan {
|
||||
/**
|
||||
* Additionally set the query execution time and the number of bind captures.
|
||||
*/
|
||||
void with(long queryTimeMicros, long captureCount, String hash) {
|
||||
@Override
|
||||
public DQueryPlanOutput with(long queryTimeMicros, long captureCount) {
|
||||
this.queryTimeMicros = queryTimeMicros;
|
||||
this.captureCount = captureCount;
|
||||
this.hash = hash;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.metric.TimedMetric;
|
||||
import io.ebeaninternal.api.ExtraMetrics;
|
||||
import io.ebeaninternal.api.SpiDbQueryPlan;
|
||||
import io.ebeaninternal.api.SpiQueryPlan;
|
||||
import io.ebeaninternal.server.type.bindcapture.BindCapture;
|
||||
import org.slf4j.Logger;
|
||||
@@ -15,37 +14,9 @@ public abstract class QueryPlanLogger {
|
||||
|
||||
static final Logger queryPlanLog = LoggerFactory.getLogger(QueryPlanLogger.class);
|
||||
|
||||
private final TimedMetric timeCollection;
|
||||
abstract SpiDbQueryPlan collectPlan(Connection conn, SpiQueryPlan plan, BindCapture bind);
|
||||
|
||||
private final TimedMetric timeBindCapture;
|
||||
|
||||
protected QueryPlanLogger(ExtraMetrics extraMetrics) {
|
||||
this.timeCollection = extraMetrics.getPlanCollect();
|
||||
this.timeBindCapture = extraMetrics.getBindCapture();
|
||||
}
|
||||
|
||||
abstract DQueryPlanOutput collect(Connection conn, SpiQueryPlan plan, BindCapture bind);
|
||||
|
||||
/**
|
||||
* Add timing for bind capture.
|
||||
*/
|
||||
public void addBindTimeSince(long startNanos) {
|
||||
timeBindCapture.addSinceNanos(startNanos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the DB query plan.
|
||||
*/
|
||||
public DQueryPlanOutput collectQueryPlan(Connection conn, SpiQueryPlan plan, BindCapture bind) {
|
||||
long startNanos = System.nanoTime();
|
||||
try {
|
||||
return collect(conn, plan, bind);
|
||||
} finally {
|
||||
timeCollection.addSinceNanos(startNanos);
|
||||
}
|
||||
}
|
||||
|
||||
DQueryPlanOutput readQueryPlan(SpiQueryPlan plan, BindCapture bind, ResultSet rset) throws SQLException {
|
||||
SpiDbQueryPlan readQueryPlan(SpiQueryPlan plan, BindCapture bind, ResultSet rset) throws SQLException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 1; i <= rset.getMetaData().getColumnCount(); i++) {
|
||||
sb.append(rset.getMetaData().getColumnLabel(i)).append("\t");
|
||||
@@ -56,11 +27,11 @@ public abstract class QueryPlanLogger {
|
||||
return createPlan(plan, bind.toString(), sb.toString());
|
||||
}
|
||||
|
||||
DQueryPlanOutput createPlan(SpiQueryPlan plan, String bind, String planString) {
|
||||
return new DQueryPlanOutput(plan.getBeanType(), plan.getName(), plan.getSql(), bind, planString, plan.getProfileLocation());
|
||||
SpiDbQueryPlan createPlan(SpiQueryPlan plan, String bind, String planString) {
|
||||
return plan.createMeta(bind, planString);
|
||||
}
|
||||
|
||||
DQueryPlanOutput readQueryPlanBasic(SpiQueryPlan plan, BindCapture bind, ResultSet rset) throws SQLException {
|
||||
SpiDbQueryPlan readQueryPlanBasic(SpiQueryPlan plan, BindCapture bind, ResultSet rset) throws SQLException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
readPlanData(sb, rset);
|
||||
return createPlan(plan, bind.toString(), sb.toString().trim());
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebeaninternal.api.ExtraMetrics;
|
||||
import io.ebeaninternal.api.SpiDbQueryPlan;
|
||||
import io.ebeaninternal.api.SpiQueryPlan;
|
||||
import io.ebeaninternal.server.type.bindcapture.BindCapture;
|
||||
|
||||
@@ -14,23 +14,17 @@ import java.sql.SQLException;
|
||||
*/
|
||||
public class QueryPlanLoggerExplain extends QueryPlanLogger {
|
||||
|
||||
public QueryPlanLoggerExplain(ExtraMetrics extraMetrics) {
|
||||
super(extraMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DQueryPlanOutput collect(Connection conn, SpiQueryPlan plan, BindCapture bind) {
|
||||
|
||||
public SpiDbQueryPlan collectPlan(Connection conn, SpiQueryPlan plan, BindCapture bind) {
|
||||
try (PreparedStatement explainStmt = conn.prepareStatement("EXPLAIN " + plan.getSql())) {
|
||||
bind.prepare(explainStmt, conn);
|
||||
try (ResultSet rset = explainStmt.executeQuery()) {
|
||||
return readQueryPlan(plan, bind, rset);
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
queryPlanLog.error("Could not log query plan", e);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebeaninternal.api.ExtraMetrics;
|
||||
import io.ebeaninternal.api.SpiDbQueryPlan;
|
||||
import io.ebeaninternal.api.SpiQueryPlan;
|
||||
import io.ebeaninternal.server.type.bindcapture.BindCapture;
|
||||
|
||||
@@ -18,13 +18,8 @@ import java.sql.Statement;
|
||||
*/
|
||||
public class QueryPlanLoggerOracle extends QueryPlanLogger {
|
||||
|
||||
public QueryPlanLoggerOracle(ExtraMetrics extraMetrics) {
|
||||
super(extraMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DQueryPlanOutput collect(Connection conn, SpiQueryPlan plan, BindCapture bind) {
|
||||
|
||||
public SpiDbQueryPlan collectPlan(Connection conn, SpiQueryPlan plan, BindCapture bind) {
|
||||
try (Statement stmt = conn.createStatement()) {
|
||||
try (PreparedStatement explainStmt = conn.prepareStatement("EXPLAIN PLAN FOR " + plan.getSql())) {
|
||||
bind.prepare(explainStmt, conn);
|
||||
@@ -33,11 +28,10 @@ public class QueryPlanLoggerOracle extends QueryPlanLogger {
|
||||
try (ResultSet rset = stmt.executeQuery("select plan_table_output from table(dbms_xplan.display())")) {
|
||||
return readQueryPlan(plan, bind, rset);
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
queryPlanLog.error("Could not log query plan", e);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebeaninternal.api.ExtraMetrics;
|
||||
import io.ebeaninternal.api.SpiDbQueryPlan;
|
||||
import io.ebeaninternal.api.SpiQueryPlan;
|
||||
import io.ebeaninternal.server.type.bindcapture.BindCapture;
|
||||
|
||||
@@ -14,24 +14,17 @@ import java.sql.SQLException;
|
||||
*/
|
||||
public class QueryPlanLoggerPostgres extends QueryPlanLogger {
|
||||
|
||||
public QueryPlanLoggerPostgres(ExtraMetrics extraMetrics) {
|
||||
super(extraMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DQueryPlanOutput collect(Connection conn, SpiQueryPlan plan, BindCapture bind) {
|
||||
|
||||
public SpiDbQueryPlan collectPlan(Connection conn, SpiQueryPlan plan, BindCapture bind) {
|
||||
String explain = "explain analyze " + plan.getSql();
|
||||
try (PreparedStatement explainStmt = conn.prepareStatement(explain)) {
|
||||
bind.prepare(explainStmt, conn);
|
||||
try (ResultSet rset = explainStmt.executeQuery()) {
|
||||
return readQueryPlanBasic(plan, bind, rset);
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
queryPlanLog.error("Could not log query plan: " + explain, e);
|
||||
throw new IllegalStateException("Failed to obtain explain plan: " + explain, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebeaninternal.api.ExtraMetrics;
|
||||
import io.ebeaninternal.api.SpiDbQueryPlan;
|
||||
import io.ebeaninternal.api.SpiQueryPlan;
|
||||
import io.ebeaninternal.server.type.bindcapture.BindCapture;
|
||||
|
||||
@@ -18,13 +18,8 @@ import java.sql.Statement;
|
||||
*/
|
||||
public class QueryPlanLoggerSqlServer extends QueryPlanLogger {
|
||||
|
||||
public QueryPlanLoggerSqlServer(ExtraMetrics extraMetrics) {
|
||||
super(extraMetrics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DQueryPlanOutput collect(Connection conn, SpiQueryPlan plan, BindCapture bind) {
|
||||
|
||||
public SpiDbQueryPlan collectPlan(Connection conn, SpiQueryPlan plan, BindCapture bind) {
|
||||
try (Statement stmt = conn.createStatement()) {
|
||||
stmt.execute("set statistics xml on");
|
||||
stmt.execute("begin transaction");
|
||||
@@ -47,14 +42,14 @@ public class QueryPlanLoggerSqlServer extends QueryPlanLogger {
|
||||
|
||||
} catch (SQLException e) {
|
||||
queryPlanLog.error("Could not log query plan", e);
|
||||
|
||||
return null;
|
||||
} finally {
|
||||
stmt.execute("set statistics xml off");
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
queryPlanLog.error("Could not log query plan", e);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user