#2211 - Improve query plan capture - Add default mechanism to log captured query plans

This commit is contained in:
Robin Bygrave
2021-03-30 23:19:21 +13:00
parent bcfd9cab1d
commit ee8bb5ef75
6 changed files with 206 additions and 8 deletions
@@ -0,0 +1,25 @@
package io.ebeaninternal.server.core;
import io.ebean.config.QueryPlanCapture;
import io.ebean.config.QueryPlanListener;
import io.ebean.meta.MetaQueryPlan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class DefaultQueryPlanListener implements QueryPlanListener {
static final QueryPlanListener INSTANT = new DefaultQueryPlanListener();
private static final Logger log = LoggerFactory.getLogger("io.ebean.QUERYPLAN");
@Override
public void process(QueryPlanCapture capture) {
// better to log this in JSON form?
String dbName = capture.getDatabase().getName();
for (MetaQueryPlan plan : capture.getPlans()) {
log.info("queryPlan db:{} label:{} queryTimeMicros:{} loc:{} sql:{} bind:{} plan:{}",
dbName, plan.getLabel(), plan.getQueryTimeMicros(), plan.getProfileLocation(),
plan.getSql(), plan.getBind(), plan.getPlan());
}
}
}
@@ -45,12 +45,7 @@ import io.ebean.bean.PersistenceContext.WithOption;
import io.ebean.bean.SingleBeanLoader;
import io.ebean.cache.ServerCacheManager;
import io.ebean.common.CopyOnFirstWriteList;
import io.ebean.config.CurrentTenantProvider;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.EncryptKeyManager;
import io.ebean.config.SlowQueryEvent;
import io.ebean.config.SlowQueryListener;
import io.ebean.config.TenantMode;
import io.ebean.config.*;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.event.BeanPersistController;
import io.ebean.event.ShutdownManager;
@@ -145,6 +140,7 @@ import java.util.Optional;
import java.util.Set;
import java.util.Spliterator;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -404,6 +400,31 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
migrationRunner.loadProperties(config.getProperties());
migrationRunner.run(config.getDataSource());
}
startQueryPlanCapture();
}
private void startQueryPlanCapture() {
if (config.isQueryPlanCapture()) {
long secs = config.getQueryPlanCapturePeriodSecs();
if (secs > 10) {
logger.info("capture query plan enabled, every {}secs", secs);
backgroundExecutor.scheduleWithFixedDelay(this::collectQueryPlans, secs, secs, TimeUnit.SECONDS);
}
}
}
private void collectQueryPlans() {
QueryPlanRequest request = new QueryPlanRequest();
request.setMaxCount(config.getQueryPlanCaptureMaxCount());
request.setMaxTimeMillis(config.getQueryPlanCaptureMaxTimeMillis());
// obtains query explain plans ...
List<MetaQueryPlan> plans = metaInfoManager.queryPlanCollectNow(request);
QueryPlanListener listener = config.getQueryPlanListener();
if (listener == null) {
listener = DefaultQueryPlanListener.INSTANT;
}
listener.process(new QueryPlanCapture(this, plans));
}
@Override
@@ -76,6 +76,11 @@ public class ServerConfigTest {
props.setProperty("forUpdateNoKey", "true");
props.setProperty("defaultServer", "false");
props.setProperty("queryPlan.capture", "true");
props.setProperty("queryPlan.capturePeriodSecs", "42");
props.setProperty("queryPlan.captureMaxTimeMillis", "560");
props.setProperty("queryPlan.captureMaxCount", "7");
serverConfig.loadFromProperties(props);
assertFalse(serverConfig.isDefaultServer());
@@ -106,6 +111,11 @@ public class ServerConfigTest {
assertEquals(4, serverConfig.getBackgroundExecutorSchedulePoolSize());
assertEquals(98, serverConfig.getBackgroundExecutorShutdownSecs());
assertTrue(serverConfig.isQueryPlanCapture());
assertEquals(42, serverConfig.getQueryPlanCapturePeriodSecs());
assertEquals(560, serverConfig.getQueryPlanCaptureMaxTimeMillis());
assertEquals(7, serverConfig.getQueryPlanCaptureMaxCount());
assertThat(serverConfig.getMappingLocations()).containsExactly("classpath:/foo","bar");
serverConfig.setPersistBatch(PersistBatch.NONE);
@@ -146,6 +156,11 @@ public class ServerConfigTest {
assertTrue(serverConfig.isAutoLoadModuleInfo());
assertEquals(Long.MAX_VALUE, serverConfig.getCollectQueryPlanThresholdMicros());
assertFalse(serverConfig.isQueryPlanCapture());
assertEquals(600, serverConfig.getQueryPlanCapturePeriodSecs());
assertEquals(10000L, serverConfig.getQueryPlanCaptureMaxTimeMillis());
assertEquals(10, serverConfig.getQueryPlanCaptureMaxCount());
serverConfig.setLoadModuleInfo(false);
assertFalse(serverConfig.isAutoLoadModuleInfo());
}