#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