From 2a5fd7f3a993909cf9a2a8c53a3b8d302a71d582 Mon Sep 17 00:00:00 2001 From: Robin Bygrave Date: Tue, 30 Mar 2021 16:02:27 +1300 Subject: [PATCH] #2210 - Improve query plan capture - ability to change default threshold micros --- .../java/io/ebean/meta/QueryPlanRequest.java | 7 +++++- .../api/NoopQueryPlanManager.java | 5 ++++ .../ebeaninternal/api/QueryPlanManager.java | 5 ++++ .../server/core/DefaultServer.java | 3 +++ .../server/query/CQueryPlanManager.java | 9 +++++-- .../query/finder/TestCustomerFinder.java | 24 +++++++++++++++---- 6 files changed, 46 insertions(+), 7 deletions(-) diff --git a/ebean-api/src/main/java/io/ebean/meta/QueryPlanRequest.java b/ebean-api/src/main/java/io/ebean/meta/QueryPlanRequest.java index 3510b30cc..c4a69b1aa 100644 --- a/ebean-api/src/main/java/io/ebean/meta/QueryPlanRequest.java +++ b/ebean-api/src/main/java/io/ebean/meta/QueryPlanRequest.java @@ -41,6 +41,9 @@ public class QueryPlanRequest { /** * Set the maximum number of plans to capture. + *

+ * Use this to limit how much query plan capturing is done as query + * plan capture is actual database load. */ public void setMaxCount(int maxCount) { this.maxCount = maxCount; @@ -58,7 +61,9 @@ public class QueryPlanRequest { /** * Set the maximum amount of time we want to use to capture plans. *

- * Query plan collection will stop once this time is exceeded. + * Query plan collection will stop once this time is exceeded. We use + * this to ensure the query plan capture does not use excessive amount + * of time - put too much load on the database. */ public void setMaxTimeMillis(long maxTimeMillis) { this.maxTimeMillis = maxTimeMillis; diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/NoopQueryPlanManager.java b/ebean-core/src/main/java/io/ebeaninternal/api/NoopQueryPlanManager.java index e00ad57f1..813121f92 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/NoopQueryPlanManager.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/NoopQueryPlanManager.java @@ -8,6 +8,11 @@ import java.util.List; class NoopQueryPlanManager implements QueryPlanManager { + @Override + public void setDefaultThreshold(long thresholdMicros) { + // do nothing + } + @Override public SpiQueryBindCapture createBindCapture(SpiQueryPlan queryPlan) { return SpiQueryBindCapture.NOOP; diff --git a/ebean-core/src/main/java/io/ebeaninternal/api/QueryPlanManager.java b/ebean-core/src/main/java/io/ebeaninternal/api/QueryPlanManager.java index f9750c47a..da7ce2b43 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/api/QueryPlanManager.java +++ b/ebean-core/src/main/java/io/ebeaninternal/api/QueryPlanManager.java @@ -12,6 +12,11 @@ public interface QueryPlanManager { QueryPlanManager NOOP = new NoopQueryPlanManager(); + /** + * Update the global default threshold used when new query plans are created. + */ + void setDefaultThreshold(long thresholdMicros); + /** * Create the bind capture for the given query plan. */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java index 5e8db9c1c..0e4d22768 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java @@ -2329,6 +2329,9 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { } List queryPlanInit(QueryPlanInit initRequest) { + if (initRequest.isAll()) { + queryPlanManager.setDefaultThreshold(initRequest.getThresholdMicros()); + } return beanDescriptorManager.queryPlanInit(initRequest); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanManager.java index c2e6aa358..4ff62f44d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanManager.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryPlanManager.java @@ -30,14 +30,14 @@ public class CQueryPlanManager implements QueryPlanManager { private final TransactionManager transactionManager; - private final long defaultThreshold; - private final QueryPlanLogger planLogger; private final TimedMetric timeCollection; private final TimedMetric timeBindCapture; + private long defaultThreshold; + public CQueryPlanManager(TransactionManager transactionManager, long defaultThreshold, QueryPlanLogger planLogger, ExtraMetrics extraMetrics) { this.transactionManager = transactionManager; this.defaultThreshold = defaultThreshold; @@ -46,6 +46,11 @@ public class CQueryPlanManager implements QueryPlanManager { this.timeBindCapture = extraMetrics.getBindCapture(); } + @Override + public void setDefaultThreshold(long thresholdMicros) { + this.defaultThreshold = thresholdMicros; + } + @Override public SpiQueryBindCapture createBindCapture(SpiQueryPlan queryPlan) { return new CQueryBindCapture(this, queryPlan, defaultThreshold); diff --git a/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java b/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java index 48cc264c6..8446f9d5b 100644 --- a/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java +++ b/ebean-core/src/test/java/org/tests/query/finder/TestCustomerFinder.java @@ -175,17 +175,24 @@ public class TestCustomerFinder extends BaseTestCase { ResetBasicData.reset(); + // change default collect query plan threshold to 200 micros + QueryPlanInit init0 = new QueryPlanInit(); + init0.setAll(true); + init0.setThresholdMicros(200); + final List plans = server().getMetaInfoManager().queryPlanInit(init0); + assertThat(plans.size()).isGreaterThan(1); + // the server has some plans runQueries(); - // enable query plan bind capture on all plans threshold 100 micros + // change query plan threshold to 100 micros QueryPlanInit init = new QueryPlanInit(); init.setAll(true); init.setThresholdMicros(100); final List appliedToPlans = server().getMetaInfoManager().queryPlanInit(init); assertThat(appliedToPlans.size()).isGreaterThan(4); - // will collect bind captures + // run queries again runQueries(); ServerMetrics metrics = server().getMetaInfoManager().collectMetrics(); @@ -203,12 +210,21 @@ public class TestCustomerFinder extends BaseTestCase { // obtains db query plans ... QueryPlanRequest request = new QueryPlanRequest(); - List plans = server().getMetaInfoManager().queryPlanCollectNow(request); - assertThat(plans).isNotEmpty(); + // collect max 1000 plans (use something more like 10) + request.setMaxCount(1_000); + // don't collect any more plans if used 10 secs + request.setMaxTimeMillis(10_000); + List plans0 = server().getMetaInfoManager().queryPlanCollectNow(request); + assertThat(plans0).isNotEmpty(); for (MetaQueryPlan plan : plans) { + logger.info("queryplan label:{}, queryTimeMicros:{} loc:{} sql:{} bind:{} plan:{}", + plan.getLabel(), plan.getQueryTimeMicros(), plan.getProfileLocation(), + plan.getSql(), plan.getBind(), plan.getPlan()); System.out.println(plan); } + + //DB.getBackgroundExecutor().scheduleWithFixedDelay(...) } @Test