From 0fb77dcbf57a169b1e3426e32b14b713dfa93ddd Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Thu, 31 Jan 2019 15:44:57 -0800 Subject: [PATCH 01/18] Add query id to NoSuchElementException Co-authored-by: Dain Sundstrom Co-authored-by: Raghav Sethi --- .../main/java/com/facebook/presto/execution/QueryTracker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryTracker.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryTracker.java index 11384fb6ecaf5..6979cb820b9c8 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/QueryTracker.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryTracker.java @@ -165,7 +165,7 @@ public T getQuery(QueryId queryId) throws NoSuchElementException { return tryGetQuery(queryId) - .orElseThrow(NoSuchElementException::new); + .orElseThrow(() -> new NoSuchElementException(queryId.toString())); } public Optional tryGetQuery(QueryId queryId) From 9c42121260e04e0907c5821ec25422a8256035bf Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Mon, 13 May 2019 11:23:02 -0700 Subject: [PATCH 02/18] Remove system startup minimum worker requirement The normal minimum worker requirement applied to all queries is sufficient to cover this case. Co-authored-by: Dain Sundstrom Co-authored-by: Raghav Sethi --- .../presto/execution/ClusterSizeMonitor.java | 30 +------- .../presto/execution/QueryManagerConfig.java | 31 -------- .../presto/execution/SqlQueryManager.java | 7 +- .../execution/TestQueryManagerConfig.java | 6 -- .../tests/TestMinWorkerRequirement.java | 71 ------------------- 5 files changed, 2 insertions(+), 143 deletions(-) diff --git a/presto-main/src/main/java/com/facebook/presto/execution/ClusterSizeMonitor.java b/presto-main/src/main/java/com/facebook/presto/execution/ClusterSizeMonitor.java index 83bcb446f9ff9..80ec65e6a1675 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/ClusterSizeMonitor.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/ClusterSizeMonitor.java @@ -36,10 +36,8 @@ import static com.facebook.airlift.concurrent.Threads.threadsNamed; import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INSUFFICIENT_RESOURCES; -import static com.facebook.presto.spi.StandardErrorCode.SERVER_STARTING_UP; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.util.concurrent.Futures.immediateFuture; -import static io.airlift.units.Duration.nanosSince; import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; @@ -49,14 +47,10 @@ public class ClusterSizeMonitor { private final InternalNodeManager nodeManager; private final boolean includeCoordinator; - private final int initializationMinCount; - private final Duration initializationMaxWait; private final int executionMinCount; private final Duration executionMaxWait; private final ScheduledExecutorService executor; - private final long createNanos = System.nanoTime(); - private final Consumer listener = this::updateAllNodes; @GuardedBy("this") @@ -65,34 +59,24 @@ public class ClusterSizeMonitor @GuardedBy("this") private final List> futures = new ArrayList<>(); - @GuardedBy("this") - private boolean minimumWorkerRequirementMet; - @Inject public ClusterSizeMonitor(InternalNodeManager nodeManager, NodeSchedulerConfig nodeSchedulerConfig, QueryManagerConfig queryManagerConfig) { this( nodeManager, requireNonNull(nodeSchedulerConfig, "nodeSchedulerConfig is null").isIncludeCoordinator(), - requireNonNull(queryManagerConfig, "queryManagerConfig is null").getInitializationRequiredWorkers(), - queryManagerConfig.getInitializationTimeout(), - queryManagerConfig.getRequiredWorkers(), + requireNonNull(queryManagerConfig, "queryManagerConfig is null").getRequiredWorkers(), queryManagerConfig.getRequiredWorkersMaxWait()); } public ClusterSizeMonitor( InternalNodeManager nodeManager, boolean includeCoordinator, - int initializationMinCount, - Duration initializationMaxWait, int executionMinCount, Duration executionMaxWait) { this.nodeManager = requireNonNull(nodeManager, "nodeManager is null"); this.includeCoordinator = includeCoordinator; - checkArgument(initializationMinCount >= 0, "initializationMinCount is negative"); - this.initializationMinCount = initializationMinCount; - this.initializationMaxWait = requireNonNull(initializationMaxWait, "initializationMaxWait is null"); checkArgument(executionMinCount >= 0, "executionMinCount is negative"); this.executionMinCount = executionMinCount; this.executionMaxWait = requireNonNull(executionMaxWait, "executionMaxWait is null"); @@ -112,18 +96,6 @@ public void stop() nodeManager.removeNodeChangeListener(listener); } - public synchronized void verifyInitialMinimumWorkersRequirement() - { - if (minimumWorkerRequirementMet) { - return; - } - - if (currentCount < initializationMinCount && nanosSince(createNanos).compareTo(initializationMaxWait) < 0) { - throw new PrestoException(SERVER_STARTING_UP, format("Cluster is still initializing, there are insufficient active worker nodes (%s) to run query", currentCount)); - } - minimumWorkerRequirementMet = true; - } - /** * Returns a listener that completes when the minimum number of workers for the cluster has been met. * Note: caller should not add a listener using the direct executor, as this can delay the diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryManagerConfig.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryManagerConfig.java index 461a9cd4d62ed..1b7dfe6e35c46 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/QueryManagerConfig.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryManagerConfig.java @@ -64,9 +64,6 @@ public class QueryManagerConfig private Duration queryMaxExecutionTime = new Duration(100, TimeUnit.DAYS); private Duration queryMaxCpuTime = new Duration(1_000_000_000, TimeUnit.DAYS); - private int initializationRequiredWorkers = 1; - private Duration initializationTimeout = new Duration(5, TimeUnit.MINUTES); - private int requiredWorkers = 1; private Duration requiredWorkersMaxWait = new Duration(5, TimeUnit.MINUTES); @@ -386,34 +383,6 @@ public QueryManagerConfig setQueryExecutionPolicy(String queryExecutionPolicy) return this; } - @Min(1) - public int getInitializationRequiredWorkers() - { - return initializationRequiredWorkers; - } - - @Config("query-manager.initialization-required-workers") - @ConfigDescription("Minimum number of workers that must be available before the cluster will accept queries") - public QueryManagerConfig setInitializationRequiredWorkers(int initializationRequiredWorkers) - { - this.initializationRequiredWorkers = initializationRequiredWorkers; - return this; - } - - @NotNull - public Duration getInitializationTimeout() - { - return initializationTimeout; - } - - @Config("query-manager.initialization-timeout") - @ConfigDescription("After this time, the cluster will accept queries even if the minimum required workers are not available") - public QueryManagerConfig setInitializationTimeout(Duration initializationTimeout) - { - this.initializationTimeout = initializationTimeout; - return this; - } - @Min(1) public int getRequiredWorkers() { diff --git a/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java b/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java index 33e311dee44cd..be03b27a77e8f 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java @@ -120,8 +120,6 @@ public class SqlQueryManager private final SessionSupplier sessionSupplier; private final SessionPropertyDefaults sessionPropertyDefaults; - private final ClusterSizeMonitor clusterSizeMonitor; - private final Map, QueryExecutionFactory> executionFactories; private final SqlQueryManagerStats stats = new SqlQueryManagerStats(); @@ -143,7 +141,6 @@ public SqlQueryManager( QueryIdGenerator queryIdGenerator, SessionSupplier sessionSupplier, SessionPropertyDefaults sessionPropertyDefaults, - ClusterSizeMonitor clusterSizeMonitor, Map, QueryExecutionFactory> executionFactories, WarningCollectorFactory warningCollectorFactory) { @@ -172,7 +169,7 @@ public SqlQueryManager( this.sessionSupplier = requireNonNull(sessionSupplier, "sessionSupplier is null"); this.sessionPropertyDefaults = requireNonNull(sessionPropertyDefaults, "sessionPropertyDefaults is null"); - this.clusterSizeMonitor = requireNonNull(clusterSizeMonitor, "clusterSizeMonitor is null"); + this.path = sqlEnvironmentConfig.getPath(); this.maxQueryLength = queryManagerConfig.getMaxQueryLength(); this.maxQueryCpuTime = queryManagerConfig.getQueryMaxCpuTime(); @@ -325,8 +322,6 @@ private void createQueryInternal(QueryId queryId, SessionContext sessionCont PreparedQuery preparedQuery; Optional queryType = Optional.empty(); try { - clusterSizeMonitor.verifyInitialMinimumWorkersRequirement(); - if (query.length() > maxQueryLength) { int queryLength = query.length(); query = query.substring(0, maxQueryLength); diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestQueryManagerConfig.java b/presto-main/src/test/java/com/facebook/presto/execution/TestQueryManagerConfig.java index d4c1d844841b2..d0bb97c68d134 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TestQueryManagerConfig.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestQueryManagerConfig.java @@ -51,8 +51,6 @@ public void testDefaults() .setQueryMaxRunTime(new Duration(100, TimeUnit.DAYS)) .setQueryMaxExecutionTime(new Duration(100, TimeUnit.DAYS)) .setQueryMaxCpuTime(new Duration(1_000_000_000, TimeUnit.DAYS)) - .setInitializationRequiredWorkers(1) - .setInitializationTimeout(new Duration(5, TimeUnit.MINUTES)) .setRequiredWorkers(1) .setRequiredWorkersMaxWait(new Duration(5, TimeUnit.MINUTES)) .setQuerySubmissionMaxThreads(Runtime.getRuntime().availableProcessors() * 2)); @@ -85,8 +83,6 @@ public void testExplicitPropertyMappings() .put("query.max-run-time", "2h") .put("query.max-execution-time", "3h") .put("query.max-cpu-time", "2d") - .put("query-manager.initialization-required-workers", "200") - .put("query-manager.initialization-timeout", "1m") .put("query-manager.required-workers", "333") .put("query-manager.required-workers-max-wait", "33m") .put("query-manager.query-submission-max-threads", "5") @@ -116,8 +112,6 @@ public void testExplicitPropertyMappings() .setQueryMaxRunTime(new Duration(2, TimeUnit.HOURS)) .setQueryMaxExecutionTime(new Duration(3, TimeUnit.HOURS)) .setQueryMaxCpuTime(new Duration(2, TimeUnit.DAYS)) - .setInitializationRequiredWorkers(200) - .setInitializationTimeout(new Duration(1, TimeUnit.MINUTES)) .setRequiredWorkers(333) .setRequiredWorkersMaxWait(new Duration(33, TimeUnit.MINUTES)) .setQuerySubmissionMaxThreads(5); diff --git a/presto-tests/src/test/java/com/facebook/presto/tests/TestMinWorkerRequirement.java b/presto-tests/src/test/java/com/facebook/presto/tests/TestMinWorkerRequirement.java index cb98d48f569eb..5e764ee0121ba 100644 --- a/presto-tests/src/test/java/com/facebook/presto/tests/TestMinWorkerRequirement.java +++ b/presto-tests/src/test/java/com/facebook/presto/tests/TestMinWorkerRequirement.java @@ -25,77 +25,6 @@ @Test(singleThreaded = true) public class TestMinWorkerRequirement { - @Test(expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = "Cluster is still initializing, there are insufficient active worker nodes \\(4\\) to run query") - public void testInsufficientInitialWorkerNodes() - throws Exception - { - try (DistributedQueryRunner queryRunner = TpchQueryRunnerBuilder.builder() - .setCoordinatorProperties(ImmutableMap.builder() - .put("query-manager.initialization-required-workers", "5") - .put("failure-detector.enabled", "false") - .build()) - .setNodeCount(4) - .build()) { - queryRunner.execute("SELECT 1"); - fail("Expected exception due to insufficient active worker nodes"); - } - } - - @Test(expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = "Cluster is still initializing, there are insufficient active worker nodes \\(3\\) to run query") - public void testInsufficientInitialWorkerNodesWithCoordinatorExcluded() - throws Exception - { - try (DistributedQueryRunner queryRunner = TpchQueryRunnerBuilder.builder() - .setSingleExtraProperty("node-scheduler.include-coordinator", "false") - .setCoordinatorProperties(ImmutableMap.builder() - .put("query-manager.initialization-required-workers", "4") - .put("failure-detector.enabled", "false") - .build()) - .setNodeCount(4) - .build()) { - queryRunner.execute("SELECT 1"); - fail("Expected exception due to insufficient active worker nodes"); - } - } - - @Test - public void testSufficientInitialWorkerNodes() - throws Exception - { - try (DistributedQueryRunner queryRunner = TpchQueryRunnerBuilder.builder() - .setCoordinatorProperties(ImmutableMap.builder() - .put("query-manager.initialization-required-workers", "4") - .put("failure-detector.enabled", "false") - .build()) - .setNodeCount(4) - .build()) { - queryRunner.execute("SELECT 1"); - assertEquals(queryRunner.getCoordinator().refreshNodes().getActiveNodes().size(), 4); - - // Query should still be allowed to run if active workers drop down below the minimum required nodes - queryRunner.getServers().get(0).close(); - assertEquals(queryRunner.getCoordinator().refreshNodes().getActiveNodes().size(), 3); - queryRunner.execute("SELECT 1"); - } - } - - @Test - public void testInitializationTimeout() - throws Exception - { - try (DistributedQueryRunner queryRunner = TpchQueryRunnerBuilder.builder() - .setCoordinatorProperties(ImmutableMap.builder() - .put("query-manager.initialization-required-workers", "5") - .put("query-manager.initialization-timeout", "1ns") - .put("failure-detector.enabled", "false") - .build()) - .setNodeCount(4) - .build()) { - queryRunner.execute("SELECT 1"); - assertEquals(queryRunner.getCoordinator().refreshNodes().getActiveNodes().size(), 4); - } - } - @Test(expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = "Insufficient active worker nodes. Waited 1.00ns for at least 5 workers, but only 4 workers are active") public void testInsufficientWorkerNodes() throws Exception From efcdeb234581bb9181fc42d7730f57f542f25c76 Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Sun, 4 Nov 2018 16:31:03 -0800 Subject: [PATCH 03/18] Add DISPATCHING query states A query will be in the DISPATCHING state during handoff to a query execution coordinator. Co-authored-by: Dain Sundstrom Co-authored-by: Raghav Sethi --- .../facebook/presto/execution/QueryState.java | 4 ++ .../presto/execution/QueryStateMachine.java | 9 +++++ .../presto/execution/QueryStateTimer.java | 23 +++++++++++- .../facebook/presto/execution/QueryStats.java | 12 +++++- .../presto/execution/MockQueryExecution.java | 1 + .../execution/TestQueryStateMachine.java | 37 ++++++++++++++++--- .../presto/execution/TestQueryStats.java | 3 ++ .../presto/server/TestBasicQueryInfo.java | 1 + .../presto/server/TestQueryStateInfo.java | 1 + 9 files changed, 83 insertions(+), 8 deletions(-) diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryState.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryState.java index 59775e4022ec3..32fcd09264e05 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/QueryState.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryState.java @@ -28,6 +28,10 @@ public enum QueryState * Query is waiting for the required resources (beta). */ WAITING_FOR_RESOURCES(false), + /** + * Query is being dispatched to a coordinator. + */ + DISPATCHING(false), /** * Query is being planned. */ diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryStateMachine.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryStateMachine.java index 9992b4a2e0241..99aed6599693b 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/QueryStateMachine.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryStateMachine.java @@ -71,6 +71,7 @@ import java.util.function.Predicate; import static com.facebook.presto.execution.BasicStageExecutionStats.EMPTY_STAGE_STATS; +import static com.facebook.presto.execution.QueryState.DISPATCHING; import static com.facebook.presto.execution.QueryState.FINISHED; import static com.facebook.presto.execution.QueryState.FINISHING; import static com.facebook.presto.execution.QueryState.PLANNING; @@ -570,6 +571,7 @@ private QueryStats getQueryStats(Optional rootStage) queryStateTimer.getElapsedTime(), queryStateTimer.getQueuedTime(), queryStateTimer.getResourceWaitingTime(), + queryStateTimer.getDispatchingTime(), queryStateTimer.getExecutionTime(), queryStateTimer.getAnalysisTime(), queryStateTimer.getPlanningTime(), @@ -765,6 +767,12 @@ public boolean transitionToWaitingForResources() return queryState.setIf(WAITING_FOR_RESOURCES, currentState -> currentState.ordinal() < WAITING_FOR_RESOURCES.ordinal()); } + public boolean transitionToDispatching() + { + queryStateTimer.beginDispatching(); + return queryState.setIf(DISPATCHING, currentState -> currentState.ordinal() < DISPATCHING.ordinal()); + } + public boolean transitionToPlanning() { queryStateTimer.beginPlanning(); @@ -1072,6 +1080,7 @@ private static QueryStats pruneQueryStats(QueryStats queryStats) queryStats.getElapsedTime(), queryStats.getQueuedTime(), queryStats.getResourceWaitingTime(), + queryStats.getDispatchingTime(), queryStats.getExecutionTime(), queryStats.getAnalysisTime(), queryStats.getTotalPlanningTime(), diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryStateTimer.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryStateTimer.java index 763e509dded71..fd04b28c742c9 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/QueryStateTimer.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryStateTimer.java @@ -34,12 +34,14 @@ class QueryStateTimer private final long createNanos; private final AtomicReference beginResourceWaitingNanos = new AtomicReference<>(); + private final AtomicReference beginDispatchingNanos = new AtomicReference<>(); private final AtomicReference beginPlanningNanos = new AtomicReference<>(); private final AtomicReference beginFinishingNanos = new AtomicReference<>(); private final AtomicReference endNanos = new AtomicReference<>(); private final AtomicReference queuedTime = new AtomicReference<>(); private final AtomicReference resourceWaitingTime = new AtomicReference<>(); + private final AtomicReference dispatchingTime = new AtomicReference<>(); private final AtomicReference executionTime = new AtomicReference<>(); private final AtomicReference planningTime = new AtomicReference<>(); private final AtomicReference finishingTime = new AtomicReference<>(); @@ -71,6 +73,18 @@ private void beginWaitingForResources(long now) beginResourceWaitingNanos.compareAndSet(null, now); } + public void beginDispatching() + { + beginDispatching(tickerNanos()); + } + + private void beginDispatching(long now) + { + beginWaitingForResources(now); + resourceWaitingTime.compareAndSet(null, nanosSince(beginResourceWaitingNanos, now)); + beginDispatchingNanos.compareAndSet(null, now); + } + public void beginPlanning() { beginPlanning(tickerNanos()); @@ -78,8 +92,8 @@ public void beginPlanning() private void beginPlanning(long now) { - beginWaitingForResources(now); - resourceWaitingTime.compareAndSet(null, nanosSince(beginResourceWaitingNanos, now)); + beginDispatching(now); + dispatchingTime.compareAndSet(null, nanosSince(beginDispatchingNanos, now)); beginPlanningNanos.compareAndSet(null, now); } @@ -185,6 +199,11 @@ public Duration getResourceWaitingTime() return getDuration(resourceWaitingTime, beginResourceWaitingNanos); } + public Duration getDispatchingTime() + { + return getDuration(dispatchingTime, beginDispatchingNanos); + } + public Duration getPlanningTime() { return getDuration(planningTime, beginPlanningNanos); diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryStats.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryStats.java index ca3beff91a186..84f7a509a4837 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/QueryStats.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryStats.java @@ -47,8 +47,9 @@ public class QueryStats private final Duration elapsedTime; private final Duration queuedTime; - private final Duration executionTime; private final Duration resourceWaitingTime; + private final Duration dispatchingTime; + private final Duration executionTime; private final Duration analysisTime; private final Duration totalPlanningTime; private final Duration finishingTime; @@ -111,6 +112,7 @@ public QueryStats( @JsonProperty("elapsedTime") Duration elapsedTime, @JsonProperty("queuedTime") Duration queuedTime, @JsonProperty("resourceWaitingTime") Duration resourceWaitingTime, + @JsonProperty("dispatchingTime") Duration dispatchingTime, @JsonProperty("executionTime") Duration executionTime, @JsonProperty("analysisTime") Duration analysisTime, @JsonProperty("totalPlanningTime") Duration totalPlanningTime, @@ -172,6 +174,7 @@ public QueryStats( this.elapsedTime = requireNonNull(elapsedTime, "elapsedTime is null"); this.queuedTime = requireNonNull(queuedTime, "queuedTime is null"); this.resourceWaitingTime = requireNonNull(resourceWaitingTime, "resourceWaitingTime is null"); + this.dispatchingTime = requireNonNull(dispatchingTime, "dispatchingTime is null"); this.executionTime = requireNonNull(executionTime, "executionTime is null"); this.analysisTime = requireNonNull(analysisTime, "analysisTime is null"); this.totalPlanningTime = requireNonNull(totalPlanningTime, "totalPlanningTime is null"); @@ -252,6 +255,7 @@ public static QueryStats immediateFailureQueryStats() new Duration(0, MILLISECONDS), new Duration(0, MILLISECONDS), new Duration(0, MILLISECONDS), + new Duration(0, MILLISECONDS), 0, 0, 0, @@ -327,6 +331,12 @@ public Duration getResourceWaitingTime() return resourceWaitingTime; } + @JsonProperty + public Duration getDispatchingTime() + { + return dispatchingTime; + } + @JsonProperty public Duration getQueuedTime() { diff --git a/presto-main/src/test/java/com/facebook/presto/execution/MockQueryExecution.java b/presto-main/src/test/java/com/facebook/presto/execution/MockQueryExecution.java index e4bd9768f1656..6537503e54383 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/MockQueryExecution.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/MockQueryExecution.java @@ -113,6 +113,7 @@ public QueryInfo getQueryInfo() new Duration(6, NANOSECONDS), new Duration(5, NANOSECONDS), new Duration(31, NANOSECONDS), + new Duration(32, NANOSECONDS), new Duration(41, NANOSECONDS), new Duration(7, NANOSECONDS), new Duration(8, NANOSECONDS), diff --git a/presto-main/src/test/java/com/facebook/presto/execution/TestQueryStateMachine.java b/presto-main/src/test/java/com/facebook/presto/execution/TestQueryStateMachine.java index 141942ff3886e..182225c9913b2 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/TestQueryStateMachine.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/TestQueryStateMachine.java @@ -52,6 +52,7 @@ import static com.facebook.airlift.concurrent.MoreFutures.tryGetFutureValue; import static com.facebook.presto.SessionTestUtils.TEST_SESSION; +import static com.facebook.presto.execution.QueryState.DISPATCHING; import static com.facebook.presto.execution.QueryState.FAILED; import static com.facebook.presto.execution.QueryState.FINISHED; import static com.facebook.presto.execution.QueryState.FINISHING; @@ -107,6 +108,9 @@ public void testBasicStateChanges() QueryStateMachine stateMachine = createQueryStateMachine(); assertState(stateMachine, QUEUED); + assertTrue(stateMachine.transitionToDispatching()); + assertState(stateMachine, DISPATCHING); + assertTrue(stateMachine.transitionToPlanning()); assertState(stateMachine, PLANNING); @@ -130,6 +134,9 @@ public void testStateChangesWithResourceWaiting() assertTrue(stateMachine.transitionToWaitingForResources()); assertState(stateMachine, WAITING_FOR_RESOURCES); + assertTrue(stateMachine.transitionToDispatching()); + assertState(stateMachine, DISPATCHING); + assertTrue(stateMachine.transitionToPlanning()); assertState(stateMachine, PLANNING); @@ -150,6 +157,7 @@ public void testQueued() // all time before the first state transition is accounted to queueing assertAllTimeSpentInQueueing(QUEUED, queryStateMachine -> {}); assertAllTimeSpentInQueueing(WAITING_FOR_RESOURCES, QueryStateMachine::transitionToWaitingForResources); + assertAllTimeSpentInQueueing(DISPATCHING, QueryStateMachine::transitionToDispatching); assertAllTimeSpentInQueueing(PLANNING, QueryStateMachine::transitionToPlanning); assertAllTimeSpentInQueueing(STARTING, QueryStateMachine::transitionToStarting); assertAllTimeSpentInQueueing(RUNNING, QueryStateMachine::transitionToRunning); @@ -174,6 +182,7 @@ private void assertAllTimeSpentInQueueing(QueryState expectedState, Consumer Date: Sun, 14 Oct 2018 16:05:06 -0700 Subject: [PATCH 04/18] Split out queued phase from QueryManager Co-authored-by: Dain Sundstrom Co-authored-by: Raghav Sethi --- .../dispatcher/CoordinatorLocation.java | 54 +++ .../presto/dispatcher/DispatchInfo.java | 57 +++ .../presto/dispatcher/DispatchManager.java | 323 +++++++++++++ .../presto/dispatcher/DispatchQuery.java | 30 ++ .../dispatcher/DispatchQueryFactory.java | 36 ++ .../FailedDispatchQuery.java} | 169 +++---- .../FailedDispatchQueryFactory.java | 64 +++ .../presto/dispatcher/LocalDispatchQuery.java | 259 +++++++++++ .../dispatcher/LocalDispatchQueryFactory.java | 138 ++++++ .../dispatcher/QueuedStatementResource.java | 436 ++++++++++++++++++ .../execution/DataDefinitionExecution.java | 52 +-- .../execution/ManagedQueryExecution.java | 2 +- .../presto/execution/QueryExecution.java | 26 +- .../facebook/presto/execution/QueryInfo.java | 48 -- .../presto/execution/QueryManager.java | 22 +- .../presto/execution/QueryStateMachine.java | 2 +- .../facebook/presto/execution/QueryStats.java | 56 --- .../presto/execution/SqlQueryExecution.java | 87 +--- .../presto/execution/SqlQueryManager.java | 299 ++---------- .../execution/SqlQueryManagerStats.java | 38 +- .../resourceGroups/InternalResourceGroup.java | 2 +- .../presto/server/BasicQueryInfo.java | 23 + .../presto/server/BasicQueryStats.java | 29 ++ .../presto/server/CoordinatorModule.java | 14 +- .../facebook/presto/server/QueryResource.java | 8 +- .../presto/server/QueryStateInfoResource.java | 12 +- ...e.java => ExecutingStatementResource.java} | 170 +++---- .../server/protocol/PurgeQueriesRunnable.java | 71 --- .../presto/server/protocol/Query.java | 194 ++------ .../server/testing/TestingPrestoServer.java | 22 +- .../execution/MockManagedQueryExecution.java | 194 ++++++++ .../presto/execution/MockQueryExecution.java | 357 -------------- .../BenchmarkResourceGroup.java | 4 +- .../resourceGroups/TestResourceGroups.java | 110 ++--- .../single_query_info_response.json | 1 + .../tests/AbstractTestDistributedQueries.java | 14 +- .../presto/execution/TestEventListener.java | 4 +- .../presto/execution/TestQueryRunnerUtil.java | 15 +- .../facebook/presto/execution/TestQueues.java | 5 +- .../resourceGroups/db/TestQueuesDb.java | 20 +- .../presto/tests/TestMetadataManager.java | 17 +- .../presto/tests/TestQueryManager.java | 11 +- 42 files changed, 2096 insertions(+), 1399 deletions(-) create mode 100644 presto-main/src/main/java/com/facebook/presto/dispatcher/CoordinatorLocation.java create mode 100644 presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchInfo.java create mode 100644 presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchManager.java create mode 100644 presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchQuery.java create mode 100644 presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchQueryFactory.java rename presto-main/src/main/java/com/facebook/presto/{execution/FailedQueryExecution.java => dispatcher/FailedDispatchQuery.java} (55%) create mode 100644 presto-main/src/main/java/com/facebook/presto/dispatcher/FailedDispatchQueryFactory.java create mode 100644 presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java create mode 100644 presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQueryFactory.java create mode 100644 presto-main/src/main/java/com/facebook/presto/dispatcher/QueuedStatementResource.java rename presto-main/src/main/java/com/facebook/presto/server/protocol/{StatementResource.java => ExecutingStatementResource.java} (68%) delete mode 100644 presto-main/src/main/java/com/facebook/presto/server/protocol/PurgeQueriesRunnable.java create mode 100644 presto-main/src/test/java/com/facebook/presto/execution/MockManagedQueryExecution.java delete mode 100644 presto-main/src/test/java/com/facebook/presto/execution/MockQueryExecution.java diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/CoordinatorLocation.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/CoordinatorLocation.java new file mode 100644 index 0000000000000..45d1dd65b491f --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/CoordinatorLocation.java @@ -0,0 +1,54 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.dispatcher; + +import java.net.URI; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; + +public class CoordinatorLocation +{ + private final Optional httpUri; + private final Optional httpsUri; + + public CoordinatorLocation(Optional httpUri, Optional httpsUri) + { + this.httpUri = requireNonNull(httpUri, "httpUri is null"); + this.httpsUri = requireNonNull(httpsUri, "httpsUri is null"); + checkArgument(httpUri.isPresent() || httpsUri.isPresent(), "Coordinator must have a HTTP or HTTPS port"); + } + + public URI getUri(String preferredScheme) + { + if ("https".equalsIgnoreCase(preferredScheme)) { + return httpsUri.orElseGet(httpUri::get); + } + else { + return httpUri.orElseGet(httpsUri::get); + } + } + + @Override + public String toString() + { + return toStringHelper(this) + .omitNullValues() + .add("httpUri", httpUri.orElse(null)) + .add("httpsUri", httpsUri.orElse(null)) + .toString(); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchInfo.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchInfo.java new file mode 100644 index 0000000000000..6b15369673fd3 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchInfo.java @@ -0,0 +1,57 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.dispatcher; + +import com.facebook.presto.execution.ExecutionFailureInfo; +import io.airlift.units.Duration; + +import java.util.Optional; + +import static java.util.Objects.requireNonNull; + +public class DispatchInfo +{ + private final Optional coordinatorLocation; + private final Optional failureInfo; + private final Duration elapsedTime; + private final Duration queuedTime; + + public DispatchInfo(Optional coordinatorLocation, Optional failureInfo, Duration elapsedTime, Duration queuedTime) + { + this.coordinatorLocation = requireNonNull(coordinatorLocation, "coordinatorLocation is null"); + this.failureInfo = requireNonNull(failureInfo, "failureInfo is null"); + this.elapsedTime = requireNonNull(elapsedTime, "elapsedTime is null"); + this.queuedTime = requireNonNull(queuedTime, "queuedTime is null"); + } + + public Optional getCoordinatorLocation() + { + return coordinatorLocation; + } + + public Optional getFailureInfo() + { + return failureInfo; + } + + public Duration getElapsedTime() + { + return elapsedTime; + } + + public Duration getQueuedTime() + { + return queuedTime; + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchManager.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchManager.java new file mode 100644 index 0000000000000..613ac9204d80f --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchManager.java @@ -0,0 +1,323 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.dispatcher; + +import com.facebook.presto.Session; +import com.facebook.presto.execution.QueryIdGenerator; +import com.facebook.presto.execution.QueryInfo; +import com.facebook.presto.execution.QueryManagerConfig; +import com.facebook.presto.execution.QueryPreparer; +import com.facebook.presto.execution.QueryPreparer.PreparedQuery; +import com.facebook.presto.execution.QueryTracker; +import com.facebook.presto.execution.SqlQueryManagerStats; +import com.facebook.presto.execution.resourceGroups.ResourceGroupManager; +import com.facebook.presto.execution.warnings.WarningCollector; +import com.facebook.presto.execution.warnings.WarningCollectorFactory; +import com.facebook.presto.metadata.SessionPropertyManager; +import com.facebook.presto.security.AccessControl; +import com.facebook.presto.server.BasicQueryInfo; +import com.facebook.presto.server.SessionContext; +import com.facebook.presto.server.SessionPropertyDefaults; +import com.facebook.presto.server.SessionSupplier; +import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.QueryId; +import com.facebook.presto.spi.resourceGroups.QueryType; +import com.facebook.presto.spi.resourceGroups.SelectionContext; +import com.facebook.presto.spi.resourceGroups.SelectionCriteria; +import com.facebook.presto.sql.SqlPath; +import com.facebook.presto.transaction.TransactionManager; +import com.google.common.util.concurrent.AbstractFuture; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningScheduledExecutorService; +import io.airlift.concurrent.ThreadPoolExecutorMBean; +import org.weakref.jmx.Flatten; +import org.weakref.jmx.Managed; +import org.weakref.jmx.Nested; + +import javax.inject.Inject; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadPoolExecutor; + +import static com.facebook.presto.spi.StandardErrorCode.QUERY_TEXT_TOO_LARGE; +import static com.facebook.presto.util.StatementUtils.getQueryType; +import static com.facebook.presto.util.StatementUtils.isTransactionControlStatement; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.util.concurrent.Futures.immediateFuture; +import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator; +import static io.airlift.concurrent.Threads.threadsNamed; +import static java.lang.String.format; +import static java.util.Objects.requireNonNull; +import static java.util.concurrent.Executors.newScheduledThreadPool; + +public class DispatchManager +{ + private final QueryIdGenerator queryIdGenerator; + private final QueryPreparer queryPreparer; + private final ResourceGroupManager resourceGroupManager; + private final WarningCollector warningCollector; + private final DispatchQueryFactory dispatchQueryFactory; + private final FailedDispatchQueryFactory failedDispatchQueryFactory; + private final TransactionManager transactionManager; + private final AccessControl accessControl; + private final SessionSupplier sessionSupplier; + private final SessionPropertyDefaults sessionPropertyDefaults; + + private final int maxQueryLength; + + private final ListeningScheduledExecutorService queryManagementExecutor; + private final ThreadPoolExecutorMBean queryManagementExecutorMBean; + + private final QueryTracker queryTracker; + + private final SqlQueryManagerStats stats = new SqlQueryManagerStats(); + + @Inject + public DispatchManager( + QueryIdGenerator queryIdGenerator, + QueryPreparer queryPreparer, + @SuppressWarnings("rawtypes") ResourceGroupManager resourceGroupManager, + WarningCollectorFactory warningCollectorFactory, + DispatchQueryFactory dispatchQueryFactory, + FailedDispatchQueryFactory failedDispatchQueryFactory, + TransactionManager transactionManager, + AccessControl accessControl, + SessionSupplier sessionSupplier, + SessionPropertyDefaults sessionPropertyDefaults, + QueryManagerConfig queryManagerConfig) + { + this.queryIdGenerator = requireNonNull(queryIdGenerator, "queryIdGenerator is null"); + this.queryPreparer = requireNonNull(queryPreparer, "queryPreparer is null"); + this.resourceGroupManager = requireNonNull(resourceGroupManager, "resourceGroupManager is null"); + this.warningCollector = warningCollectorFactory.create(); + this.dispatchQueryFactory = requireNonNull(dispatchQueryFactory, "dispatchQueryFactory is null"); + this.failedDispatchQueryFactory = requireNonNull(failedDispatchQueryFactory, "failedDispatchQueryFactory is null"); + this.transactionManager = requireNonNull(transactionManager, "transactionManager is null"); + this.accessControl = requireNonNull(accessControl, "accessControl is null"); + this.sessionSupplier = requireNonNull(sessionSupplier, "sessionSupplier is null"); + this.sessionPropertyDefaults = requireNonNull(sessionPropertyDefaults, "sessionPropertyDefaults is null"); + + this.maxQueryLength = queryManagerConfig.getMaxQueryLength(); + + ScheduledExecutorService scheduledExecutorService = newScheduledThreadPool(queryManagerConfig.getQueryManagerExecutorPoolSize(), threadsNamed("query-dispatch-%s")); + queryManagementExecutor = listeningDecorator(scheduledExecutorService); + queryManagementExecutorMBean = new ThreadPoolExecutorMBean((ThreadPoolExecutor) scheduledExecutorService); + + this.queryTracker = new QueryTracker<>(queryManagerConfig, queryManagementExecutor); + } + + @Managed(description = "Query dispatch executor") + @Nested + public ThreadPoolExecutorMBean getExecutor() + { + return queryManagementExecutorMBean; + } + + @Managed + @Flatten + public SqlQueryManagerStats getStats() + { + return stats; + } + + public QueryId createQueryId() + { + return queryIdGenerator.createNextQueryId(); + } + + public ListenableFuture createQuery(QueryId queryId, String slug, SessionContext sessionContext, String query) + { + DispatchQueryCreationFuture queryCreationFuture = new DispatchQueryCreationFuture(); + queryManagementExecutor.submit(() -> { + try { + createQueryInternal(queryId, slug, sessionContext, query, resourceGroupManager); + queryCreationFuture.set(null); + } + catch (Throwable e) { + queryCreationFuture.setException(e); + } + }); + return queryCreationFuture; + } + + private void createQueryInternal(QueryId queryId, String slug, SessionContext sessionContext, String query, ResourceGroupManager resourceGroupManager) + { + requireNonNull(queryId, "queryId is null"); + requireNonNull(sessionContext, "sessionFactory is null"); + requireNonNull(query, "query is null"); + checkArgument(!query.isEmpty(), "query must not be empty string"); + checkArgument(!queryTracker.tryGetQuery(queryId).isPresent(), "query %s already exists", queryId); + + Session session = null; + SelectionContext selectionContext = null; + PreparedQuery preparedQuery; + DispatchQuery dispatchQuery; + try { + if (query.length() > maxQueryLength) { + int queryLength = query.length(); + query = query.substring(0, maxQueryLength); + throw new PrestoException(QUERY_TEXT_TOO_LARGE, format("Query text length (%s) exceeds the maximum length (%s)", queryLength, maxQueryLength)); + } + + // decode session + session = sessionSupplier.createSession(queryId, sessionContext); + + // prepare query + preparedQuery = queryPreparer.prepareQuery(session, query, warningCollector); + + // select resource group + Optional queryType = getQueryType(preparedQuery.getStatement().getClass()); + selectionContext = resourceGroupManager.selectGroup(new SelectionCriteria( + sessionContext.getIdentity().getPrincipal().isPresent(), + sessionContext.getIdentity().getUser(), + Optional.ofNullable(sessionContext.getSource()), + sessionContext.getClientTags(), + sessionContext.getResourceEstimates(), + queryType.map(Enum::name))); + + // apply system default session properties (does not override user set properties) + session = sessionPropertyDefaults.newSessionWithDefaultProperties(session, queryType.map(Enum::name), selectionContext.getResourceGroupId()); + + // mark existing transaction as active + transactionManager.activateTransaction(session, isTransactionControlStatement(preparedQuery.getStatement()), accessControl); + + dispatchQuery = dispatchQueryFactory.createDispatchQuery(session, query, preparedQuery, slug, selectionContext.getResourceGroupId(), queryType, warningCollector, queryManagementExecutor); + } + catch (RuntimeException e) { + // This is intentionally not a method, since after the state change listener is registered + // it's not safe to do any of this, and we had bugs before where people reused this code in a method + // if session creation failed, create a minimal session object + if (session == null) { + session = Session.builder(new SessionPropertyManager()) + .setQueryId(queryId) + .setIdentity(sessionContext.getIdentity()) + .setSource(sessionContext.getSource()) + .setPath(new SqlPath(Optional.empty())) + .build(); + } + + // create and immediately fail the query + DispatchQuery failedDispatchQuery = failedDispatchQueryFactory.createFailedDispatchQuery( + session, + query, + Optional.ofNullable(selectionContext).map(SelectionContext::getResourceGroupId), + e); + + try { + queryCreated(failedDispatchQuery); + } + finally { + handleQueryFailure(failedDispatchQuery); + } + return; + } + + try { + queryCreated(dispatchQuery); + dispatchQuery.addStateChangeListener(newState -> { + if (newState.isDone()) { + stats.queryFinished(dispatchQuery.getBasicQueryInfo()); + } + }); + + resourceGroupManager.submit(preparedQuery.getStatement(), dispatchQuery, selectionContext, queryManagementExecutor); + } + catch (RuntimeException e) { + dispatchQuery.fail(e); + } + } + + private void queryCreated(DispatchQuery dispatchQuery) + { + queryTracker.addQuery(dispatchQuery); + stats.queryQueued(); + } + + private void handleQueryFailure(DispatchQuery dispatchQuery) + { + try { + stats.queryStarted(); + stats.queryStopped(); + BasicQueryInfo queryInfo = dispatchQuery.getBasicQueryInfo(); + stats.queuedQueryFailed(queryInfo.getQueryStats().getQueuedTime(), Optional.ofNullable(queryInfo.getErrorCode())); + } + finally { + // execution MUST be added to the expiration queue or there will be a leak + queryTracker.expireQuery(dispatchQuery.getQueryId()); + } + } + + public ListenableFuture waitForDispatched(QueryId queryId) + { + return queryTracker.tryGetQuery(queryId) + .map(dispatchQuery -> { + dispatchQuery.recordHeartbeat(); + return dispatchQuery.getDispatchedFuture(); + }) + .orElseGet(() -> immediateFuture(null)); + } + + public List getQueries() + { + return queryTracker.getAllQueries().stream() + .map(DispatchQuery::getBasicQueryInfo) + .collect(toImmutableList()); + } + + public BasicQueryInfo getQueryInfo(QueryId queryId) + { + return queryTracker.getQuery(queryId).getBasicQueryInfo(); + } + + public Optional getDispatchInfo(QueryId queryId) + { + return queryTracker.tryGetQuery(queryId) + .map(dispatchQuery -> { + dispatchQuery.recordHeartbeat(); + return dispatchQuery.getDispatchInfo(); + }); + } + + public void cancelQuery(QueryId queryId) + { + queryTracker.tryGetQuery(queryId) + .ifPresent(DispatchQuery::cancel); + } + + private static class DispatchQueryCreationFuture + extends AbstractFuture + { + @Override + protected boolean set(QueryInfo value) + { + return super.set(value); + } + + @Override + protected boolean setException(Throwable throwable) + { + return super.setException(throwable); + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) + { + // query submission can not be canceled + return false; + } + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchQuery.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchQuery.java new file mode 100644 index 0000000000000..cedc15a2c12ea --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchQuery.java @@ -0,0 +1,30 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.dispatcher; + +import com.facebook.presto.execution.ManagedQueryExecution; +import com.facebook.presto.execution.QueryTracker.TrackedQuery; +import com.google.common.util.concurrent.ListenableFuture; + +public interface DispatchQuery + extends TrackedQuery, ManagedQueryExecution +{ + void recordHeartbeat(); + + ListenableFuture getDispatchedFuture(); + + DispatchInfo getDispatchInfo(); + + void cancel(); +} diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchQueryFactory.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchQueryFactory.java new file mode 100644 index 0000000000000..342f8ac81399a --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchQueryFactory.java @@ -0,0 +1,36 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.dispatcher; + +import com.facebook.presto.Session; +import com.facebook.presto.execution.QueryPreparer.PreparedQuery; +import com.facebook.presto.execution.warnings.WarningCollector; +import com.facebook.presto.spi.resourceGroups.QueryType; +import com.facebook.presto.spi.resourceGroups.ResourceGroupId; + +import java.util.Optional; +import java.util.concurrent.ExecutorService; + +public interface DispatchQueryFactory +{ + DispatchQuery createDispatchQuery( + Session session, + String query, + PreparedQuery preparedQuery, + String slug, + ResourceGroupId resourceGroup, + Optional queryType, + WarningCollector warningCollector, + ExecutorService queryExecutor); +} diff --git a/presto-main/src/main/java/com/facebook/presto/execution/FailedQueryExecution.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/FailedDispatchQuery.java similarity index 55% rename from presto-main/src/main/java/com/facebook/presto/execution/FailedQueryExecution.java rename to presto-main/src/main/java/com/facebook/presto/dispatcher/FailedDispatchQuery.java index b75e04a866a21..0016e619500f2 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/FailedQueryExecution.java +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/FailedDispatchQuery.java @@ -11,17 +11,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.facebook.presto.execution; +package com.facebook.presto.dispatcher; import com.facebook.presto.Session; +import com.facebook.presto.execution.ExecutionFailureInfo; +import com.facebook.presto.execution.QueryState; import com.facebook.presto.execution.StateMachine.StateChangeListener; -import com.facebook.presto.memory.VersionedMemoryPoolId; import com.facebook.presto.server.BasicQueryInfo; import com.facebook.presto.spi.ErrorCode; import com.facebook.presto.spi.QueryId; -import com.facebook.presto.spi.resourceGroups.QueryType; import com.facebook.presto.spi.resourceGroups.ResourceGroupId; -import com.facebook.presto.sql.planner.Plan; import com.google.common.util.concurrent.ListenableFuture; import io.airlift.units.DataSize; import io.airlift.units.Duration; @@ -30,126 +29,110 @@ import java.net.URI; import java.util.Optional; import java.util.concurrent.Executor; -import java.util.function.Consumer; -import static com.facebook.presto.execution.QueryInfo.immediateFailureQueryInfo; import static com.facebook.presto.execution.QueryState.FAILED; -import static com.facebook.presto.memory.LocalMemoryManager.GENERAL_POOL; +import static com.facebook.presto.server.BasicQueryInfo.immediateFailureQueryInfo; import static com.google.common.util.concurrent.Futures.immediateFuture; import static io.airlift.units.DataSize.Unit.BYTE; import static java.util.Objects.requireNonNull; -import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static java.util.concurrent.TimeUnit.MILLISECONDS; -public class FailedQueryExecution - implements QueryExecution +public class FailedDispatchQuery + implements DispatchQuery { - private final QueryInfo queryInfo; + private final BasicQueryInfo queryInfo; private final Session session; private final Executor executor; - - public FailedQueryExecution(Session session, String query, URI self, Optional resourceGroup, Optional queryType, Executor executor, Throwable cause) - { - requireNonNull(cause, "cause is null"); + private final DispatchInfo dispatchInfo; + + public FailedDispatchQuery( + Session session, + String query, + URI self, + Optional resourceGroup, + ExecutionFailureInfo failure, + Executor executor) + { + requireNonNull(session, "session is null"); + requireNonNull(query, "query is null"); + requireNonNull(self, "self is null"); + requireNonNull(resourceGroup, "resourceGroup is null"); + requireNonNull(failure, "failure is null"); + requireNonNull(executor, "executor is null"); + + this.queryInfo = immediateFailureQueryInfo(session, query, self, resourceGroup, failure.getErrorCode()); this.session = requireNonNull(session, "session is null"); this.executor = requireNonNull(executor, "executor is null"); - this.queryInfo = immediateFailureQueryInfo(session, query, self, resourceGroup, queryType, cause); - } - @Override - public QueryId getQueryId() - { - return queryInfo.getQueryId(); + this.dispatchInfo = new DispatchInfo( + Optional.empty(), + Optional.of(failure), + queryInfo.getQueryStats().getElapsedTime(), + queryInfo.getQueryStats().getQueuedTime()); } @Override - public QueryInfo getQueryInfo() + public BasicQueryInfo getBasicQueryInfo() { return queryInfo; } @Override - public QueryState getState() - { - return queryInfo.getState(); - } - - @Override - public Plan getQueryPlan() - { - throw new UnsupportedOperationException(); - } - - @Override - public VersionedMemoryPoolId getMemoryPool() + public Session getSession() { - return new VersionedMemoryPoolId(GENERAL_POOL, 0); + return session; } @Override - public void setMemoryPool(VersionedMemoryPoolId poolId) + public ListenableFuture getDispatchedFuture() { - // no-op + return immediateFuture(null); } @Override - public DataSize getUserMemoryReservation() + public DispatchInfo getDispatchInfo() { - return new DataSize(0, BYTE); + return dispatchInfo; } @Override - public DataSize getTotalMemoryReservation() + public void addStateChangeListener(StateChangeListener stateChangeListener) { - return new DataSize(0, BYTE); + executor.execute(() -> stateChangeListener.stateChanged(FAILED)); } @Override - public Duration getTotalCpuTime() - { - return new Duration(0, NANOSECONDS); - } + public void startWaitingForResources() {} @Override - public Session getSession() - { - return session; - } + public void fail(Throwable throwable) {} @Override - public DateTime getCreateTime() - { - return queryInfo.getQueryStats().getCreateTime(); - } + public void cancel() {} @Override - public Optional getExecutionStartTime() - { - return Optional.ofNullable(queryInfo.getQueryStats().getExecutionStartTime()); - } + public void pruneInfo() {} @Override - public DateTime getLastHeartbeat() + public QueryId getQueryId() { - return queryInfo.getQueryStats().getLastHeartbeat(); + return queryInfo.getQueryId(); } @Override - public Optional getEndTime() + public boolean isDone() { - return Optional.ofNullable(queryInfo.getQueryStats().getEndTime()); + return true; } @Override public Optional getErrorCode() { - return Optional.ofNullable(getQueryInfo().getFailureInfo()).map(ExecutionFailureInfo::getErrorCode); + return Optional.ofNullable(queryInfo.getErrorCode()); } @Override - public BasicQueryInfo getBasicQueryInfo() - { - return new BasicQueryInfo(getQueryInfo()); - } + public void recordHeartbeat() {} @Override public int getRunningTaskCount() @@ -158,68 +141,44 @@ public int getRunningTaskCount() } @Override - public void start() - { - // no-op - } - - @Override - public void addOutputInfoListener(Consumer listener) - { - // no-op - } - - @Override - public ListenableFuture getStateChange(QueryState currentState) - { - return immediateFuture(queryInfo.getState()); - } - - @Override - public void addStateChangeListener(StateChangeListener stateChangeListener) - { - executor.execute(() -> stateChangeListener.stateChanged(FAILED)); - } - - @Override - public void addFinalQueryInfoListener(StateChangeListener stateChangeListener) + public DateTime getLastHeartbeat() { - executor.execute(() -> stateChangeListener.stateChanged(queryInfo)); + return queryInfo.getQueryStats().getEndTime(); } @Override - public void fail(Throwable cause) + public DateTime getCreateTime() { - // no-op + return queryInfo.getQueryStats().getCreateTime(); } @Override - public boolean isDone() + public Optional getExecutionStartTime() { - return getState().isDone(); + return getEndTime(); } @Override - public void cancelQuery() + public Optional getEndTime() { - // no-op + return Optional.ofNullable(queryInfo.getQueryStats().getEndTime()); } @Override - public void cancelStage(StageId stageId) + public Duration getTotalCpuTime() { - // no-op + return new Duration(0, MILLISECONDS); } @Override - public void recordHeartbeat() + public DataSize getTotalMemoryReservation() { - // no-op + return new DataSize(0, BYTE); } @Override - public void pruneInfo() + public DataSize getUserMemoryReservation() { - // no-op + return new DataSize(0, BYTE); } } diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/FailedDispatchQueryFactory.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/FailedDispatchQueryFactory.java new file mode 100644 index 0000000000000..88cf63fa6d4bc --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/FailedDispatchQueryFactory.java @@ -0,0 +1,64 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.dispatcher; + +import com.facebook.presto.Session; +import com.facebook.presto.event.QueryMonitor; +import com.facebook.presto.execution.ExecutionFailureInfo; +import com.facebook.presto.execution.ForQueryExecution; +import com.facebook.presto.execution.LocationFactory; +import com.facebook.presto.server.BasicQueryInfo; +import com.facebook.presto.spi.resourceGroups.ResourceGroupId; + +import javax.inject.Inject; + +import java.util.Optional; +import java.util.concurrent.ExecutorService; + +import static com.facebook.presto.util.Failures.toFailure; +import static java.util.Objects.requireNonNull; + +public class FailedDispatchQueryFactory +{ + private final QueryMonitor queryMonitor; + private final LocationFactory locationFactory; + private final ExecutorService executor; + + @Inject + public FailedDispatchQueryFactory(QueryMonitor queryMonitor, LocationFactory locationFactory, @ForQueryExecution ExecutorService executor) + { + this.queryMonitor = requireNonNull(queryMonitor, "queryMonitor is null"); + this.locationFactory = requireNonNull(locationFactory, "locationFactory is null"); + this.executor = requireNonNull(executor, "executor is null"); + } + + public FailedDispatchQuery createFailedDispatchQuery(Session session, String query, Optional resourceGroup, Throwable throwable) + { + ExecutionFailureInfo failure = toFailure(throwable); + FailedDispatchQuery failedDispatchQuery = new FailedDispatchQuery( + session, + query, + locationFactory.createQueryLocation(session.getQueryId()), + resourceGroup, + failure, + executor); + + BasicQueryInfo queryInfo = failedDispatchQuery.getBasicQueryInfo(); + + queryMonitor.queryCreatedEvent(queryInfo); + queryMonitor.queryImmediateFailureEvent(queryInfo, failure); + + return failedDispatchQuery; + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java new file mode 100644 index 0000000000000..847078a63a777 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java @@ -0,0 +1,259 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.dispatcher; + +import com.facebook.airlift.log.Logger; +import com.facebook.presto.Session; +import com.facebook.presto.execution.ClusterSizeMonitor; +import com.facebook.presto.execution.ExecutionFailureInfo; +import com.facebook.presto.execution.QueryExecution; +import com.facebook.presto.execution.QueryState; +import com.facebook.presto.execution.QueryStateMachine; +import com.facebook.presto.execution.StateMachine.StateChangeListener; +import com.facebook.presto.server.BasicQueryInfo; +import com.facebook.presto.spi.ErrorCode; +import com.facebook.presto.spi.QueryId; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import io.airlift.units.DataSize; +import io.airlift.units.Duration; +import org.joda.time.DateTime; + +import java.util.Optional; +import java.util.concurrent.ExecutorService; +import java.util.function.Function; + +import static com.facebook.airlift.concurrent.MoreFutures.addExceptionCallback; +import static com.facebook.airlift.concurrent.MoreFutures.addSuccessCallback; +import static com.facebook.airlift.concurrent.MoreFutures.tryGetFutureValue; +import static com.facebook.presto.execution.QueryState.FAILED; +import static com.facebook.presto.execution.QueryState.PLANNING; +import static com.google.common.util.concurrent.Futures.immediateFuture; +import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static io.airlift.units.DataSize.Unit.BYTE; +import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +public class LocalDispatchQuery + implements DispatchQuery +{ + private final QueryStateMachine stateMachine; + private final ListenableFuture queryExecutionFuture; + + private final CoordinatorLocation coordinatorLocation; + + private final ClusterSizeMonitor clusterSizeMonitor; + + private final ExecutorService queryExecutor; + + private final Function> querySubmitter; + + public LocalDispatchQuery( + QueryStateMachine stateMachine, + ListenableFuture queryExecutionFuture, + CoordinatorLocation coordinatorLocation, + ClusterSizeMonitor clusterSizeMonitor, + ExecutorService queryExecutor, + Function> querySubmitter) + { + this.stateMachine = requireNonNull(stateMachine, "stateMachine is null"); + this.queryExecutionFuture = requireNonNull(queryExecutionFuture, "queryExecutionFuture is null"); + this.coordinatorLocation = requireNonNull(coordinatorLocation, "coordinatorLocation is null"); + this.clusterSizeMonitor = requireNonNull(clusterSizeMonitor, "clusterSizeMonitor is null"); + this.queryExecutor = requireNonNull(queryExecutor, "queryExecutor is null"); + this.querySubmitter = requireNonNull(querySubmitter, "querySubmitter is null"); + + addExceptionCallback(queryExecutionFuture, stateMachine::transitionToFailed); + } + + @Override + public void startWaitingForResources() + { + if (stateMachine.transitionToWaitingForResources()) { + waitForMinimumWorkers(); + } + } + + private void waitForMinimumWorkers() + { + ListenableFuture minimumWorkerFuture = clusterSizeMonitor.waitForMinimumWorkers(); + // when worker requirement is met, wait for query execution to finish construction and then start the execution + addSuccessCallback(minimumWorkerFuture, () -> addSuccessCallback(queryExecutionFuture, this::startExecution)); + addExceptionCallback(minimumWorkerFuture, throwable -> queryExecutor.submit(() -> stateMachine.transitionToFailed(throwable))); + } + + private void startExecution(QueryExecution queryExecution) + { + queryExecutor.submit(() -> { + if (stateMachine.transitionToDispatching()) { + querySubmitter.apply(queryExecution); + } + }); + } + + @Override + public void recordHeartbeat() + { + stateMachine.recordHeartbeat(); + } + + @Override + public DateTime getLastHeartbeat() + { + return stateMachine.getLastHeartbeat(); + } + + @Override + public ListenableFuture getDispatchedFuture() + { + return queryDispatchFuture(stateMachine.getQueryState()); + } + + private ListenableFuture queryDispatchFuture(QueryState currentState) + { + if (currentState.ordinal() >= PLANNING.ordinal()) { + return immediateFuture(null); + } + return Futures.transformAsync(stateMachine.getStateChange(currentState), this::queryDispatchFuture, directExecutor()); + } + + @Override + public DispatchInfo getDispatchInfo() + { + BasicQueryInfo queryInfo = stateMachine.getBasicQueryInfo(Optional.empty()); + Optional coordinator = Optional.empty(); + if (queryInfo.getState().ordinal() >= PLANNING.ordinal()) { + coordinator = Optional.of(coordinatorLocation); + } + Optional failureInfo = Optional.empty(); + if (queryInfo.getState() == FAILED) { + failureInfo = stateMachine.getFailureInfo(); + } + return new DispatchInfo(coordinator, failureInfo, queryInfo.getQueryStats().getElapsedTime(), queryInfo.getQueryStats().getQueuedTime()); + } + + @Override + public QueryId getQueryId() + { + return stateMachine.getQueryId(); + } + + @Override + public boolean isDone() + { + return stateMachine.getQueryState().isDone(); + } + + @Override + public DateTime getCreateTime() + { + return stateMachine.getCreateTime(); + } + + @Override + public Optional getExecutionStartTime() + { + return stateMachine.getExecutionStartTime(); + } + + @Override + public Optional getEndTime() + { + return stateMachine.getEndTime(); + } + + @Override + public int getRunningTaskCount() + { + return stateMachine.getCurrentRunningTaskCount(); + } + + @Override + public Duration getTotalCpuTime() + { + return tryGetQueryExecution() + .map(QueryExecution::getTotalCpuTime) + .orElse(new Duration(0, MILLISECONDS)); + } + + @Override + public DataSize getTotalMemoryReservation() + { + return tryGetQueryExecution() + .map(QueryExecution::getTotalMemoryReservation) + .orElse(new DataSize(0, BYTE)); + } + + @Override + public DataSize getUserMemoryReservation() + { + return tryGetQueryExecution() + .map(QueryExecution::getUserMemoryReservation) + .orElse(new DataSize(0, BYTE)); + } + + @Override + public BasicQueryInfo getBasicQueryInfo() + { + return tryGetQueryExecution() + .map(QueryExecution::getBasicQueryInfo) + .orElse(stateMachine.getBasicQueryInfo(Optional.empty())); + } + + @Override + public Session getSession() + { + return stateMachine.getSession(); + } + + @Override + public void fail(Throwable throwable) + { + stateMachine.transitionToFailed(throwable); + } + + @Override + public void cancel() + { + stateMachine.transitionToCanceled(); + } + + @Override + public void pruneInfo() + { + stateMachine.pruneQueryInfo(); + } + + @Override + public Optional getErrorCode() + { + return stateMachine.getFailureInfo().map(ExecutionFailureInfo::getErrorCode); + } + + @Override + public void addStateChangeListener(StateChangeListener stateChangeListener) + { + stateMachine.addStateChangeListener(stateChangeListener); + } + + private Optional tryGetQueryExecution() + { + try { + return tryGetFutureValue(queryExecutionFuture); + } + catch (Exception ignored) { + return Optional.empty(); + } + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQueryFactory.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQueryFactory.java new file mode 100644 index 0000000000000..1d82b2d800871 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQueryFactory.java @@ -0,0 +1,138 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.dispatcher; + +import com.facebook.presto.Session; +import com.facebook.presto.event.QueryMonitor; +import com.facebook.presto.execution.ClusterSizeMonitor; +import com.facebook.presto.execution.ForQueryExecution; +import com.facebook.presto.execution.LocationFactory; +import com.facebook.presto.execution.QueryExecution; +import com.facebook.presto.execution.QueryExecution.QueryExecutionFactory; +import com.facebook.presto.execution.QueryManager; +import com.facebook.presto.execution.QueryPreparer.PreparedQuery; +import com.facebook.presto.execution.QueryStateMachine; +import com.facebook.presto.execution.warnings.WarningCollector; +import com.facebook.presto.metadata.InternalNodeManager; +import com.facebook.presto.metadata.Metadata; +import com.facebook.presto.security.AccessControl; +import com.facebook.presto.spi.Node; +import com.facebook.presto.spi.PrestoException; +import com.facebook.presto.spi.resourceGroups.QueryType; +import com.facebook.presto.spi.resourceGroups.ResourceGroupId; +import com.facebook.presto.sql.tree.Statement; +import com.facebook.presto.transaction.TransactionManager; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; + +import javax.inject.Inject; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ExecutorService; + +import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; +import static com.facebook.presto.util.StatementUtils.isTransactionControlStatement; +import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator; +import static java.util.Objects.requireNonNull; + +public class LocalDispatchQueryFactory + implements DispatchQueryFactory +{ + private final QueryManager queryManager; + private final TransactionManager transactionManager; + private final AccessControl accessControl; + private final Metadata metadata; + private final QueryMonitor queryMonitor; + private final LocationFactory locationFactory; + + private final CoordinatorLocation coordinatorLocation; + private final ClusterSizeMonitor clusterSizeMonitor; + + private final Map, QueryExecutionFactory> executionFactories; + private final ListeningExecutorService executorService; + + @Inject + public LocalDispatchQueryFactory( + QueryManager queryManager, + TransactionManager transactionManager, + AccessControl accessControl, + Metadata metadata, + QueryMonitor queryMonitor, + LocationFactory locationFactory, + Map, QueryExecutionFactory> executionFactories, + InternalNodeManager internalNodeManager, + ClusterSizeMonitor clusterSizeMonitor, + @ForQueryExecution ExecutorService executorService) + + { + this.queryManager = requireNonNull(queryManager, "queryManager is null"); + this.transactionManager = requireNonNull(transactionManager, "transactionManager is null"); + this.accessControl = requireNonNull(accessControl, "accessControl is null"); + this.metadata = requireNonNull(metadata, "metadata is null"); + this.queryMonitor = requireNonNull(queryMonitor, "queryMonitor is null"); + this.locationFactory = requireNonNull(locationFactory, "locationFactory is null"); + this.executionFactories = requireNonNull(executionFactories, "executionFactories is null"); + + Node currentNode = requireNonNull(internalNodeManager, "internalNodeManager is null").getCurrentNode(); + this.coordinatorLocation = new CoordinatorLocation(Optional.of(currentNode.getHttpUri()), Optional.of(currentNode.getHttpUri())); + this.clusterSizeMonitor = requireNonNull(clusterSizeMonitor, "clusterSizeMonitor is null"); + + this.executorService = listeningDecorator(requireNonNull(executorService, "executorService is null")); + } + + @Override + public DispatchQuery createDispatchQuery( + Session session, + String query, + PreparedQuery preparedQuery, + String slug, + ResourceGroupId resourceGroup, + Optional queryType, + WarningCollector warningCollector, + ExecutorService queryExecutor) + { + QueryStateMachine stateMachine = QueryStateMachine.begin( + query, + session, + locationFactory.createQueryLocation(session.getQueryId()), + resourceGroup, + queryType, + isTransactionControlStatement(preparedQuery.getStatement()), + transactionManager, + accessControl, + executorService, + metadata, + warningCollector); + + queryMonitor.queryCreatedEvent(stateMachine.getBasicQueryInfo(Optional.empty())); + + ListenableFuture queryExecutionFuture = executorService.submit(() -> { + QueryExecutionFactory queryExecutionFactory = executionFactories.get(preparedQuery.getStatement().getClass()); + if (queryExecutionFactory == null) { + throw new PrestoException(NOT_SUPPORTED, "Unsupported statement type: " + preparedQuery.getStatement().getClass().getSimpleName()); + } + + return queryExecutionFactory.createQueryExecution(preparedQuery, stateMachine, slug, warningCollector, queryType); + }); + + return new LocalDispatchQuery( + stateMachine, + queryExecutionFuture, + coordinatorLocation, + clusterSizeMonitor, + queryExecutor, + queryExecution -> executorService.submit(() -> queryManager.createQuery(queryExecution))); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/QueuedStatementResource.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/QueuedStatementResource.java new file mode 100644 index 0000000000000..bf90dfd3ad76f --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/QueuedStatementResource.java @@ -0,0 +1,436 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.dispatcher; + +import com.facebook.airlift.log.Logger; +import com.facebook.presto.client.QueryError; +import com.facebook.presto.client.QueryResults; +import com.facebook.presto.client.StatementStats; +import com.facebook.presto.execution.ExecutionFailureInfo; +import com.facebook.presto.execution.QueryState; +import com.facebook.presto.server.ForStatementResource; +import com.facebook.presto.server.HttpRequestSessionContext; +import com.facebook.presto.server.SessionContext; +import com.facebook.presto.spi.ErrorCode; +import com.facebook.presto.spi.QueryId; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Ordering; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import io.airlift.units.Duration; + +import javax.annotation.PreDestroy; +import javax.annotation.concurrent.GuardedBy; +import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.container.AsyncResponse; +import javax.ws.rs.container.Suspended; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriInfo; + +import java.net.URI; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.atomic.AtomicLong; + +import static com.facebook.airlift.concurrent.MoreFutures.addTimeout; +import static com.facebook.airlift.concurrent.Threads.threadsNamed; +import static com.facebook.airlift.http.client.HttpUriBuilder.uriBuilderFrom; +import static com.facebook.airlift.http.server.AsyncResponseHandler.bindAsyncResponse; +import static com.facebook.presto.execution.QueryState.FAILED; +import static com.facebook.presto.execution.QueryState.QUEUED; +import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR; +import static com.google.common.base.MoreObjects.firstNonNull; +import static com.google.common.base.Strings.isNullOrEmpty; +import static com.google.common.net.HttpHeaders.X_FORWARDED_PROTO; +import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static java.util.Locale.ENGLISH; +import static java.util.Objects.requireNonNull; +import static java.util.UUID.randomUUID; +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static javax.ws.rs.core.MediaType.TEXT_PLAIN_TYPE; +import static javax.ws.rs.core.Response.Status.BAD_REQUEST; +import static javax.ws.rs.core.Response.Status.NOT_FOUND; + +@Path("/") +public class QueuedStatementResource +{ + private static final Logger log = Logger.get(QueuedStatementResource.class); + private static final Duration MAX_WAIT_TIME = new Duration(1, SECONDS); + private static final Ordering> WAIT_ORDERING = Ordering.natural().nullsLast(); + private static final Duration NO_DURATION = new Duration(0, MILLISECONDS); + + private final DispatchManager dispatchManager; + + private final BoundedExecutor responseExecutor; + private final ScheduledExecutorService timeoutExecutor; + + private final ConcurrentMap queries = new ConcurrentHashMap<>(); + private final ScheduledExecutorService queryPurger = newSingleThreadScheduledExecutor(threadsNamed("query-purger")); + + @Inject + public QueuedStatementResource( + DispatchManager dispatchManager, + @ForStatementResource BoundedExecutor responseExecutor, + @ForStatementResource ScheduledExecutorService timeoutExecutor) + { + this.dispatchManager = requireNonNull(dispatchManager, "dispatchManager is null"); + + this.responseExecutor = requireNonNull(responseExecutor, "responseExecutor is null"); + this.timeoutExecutor = requireNonNull(timeoutExecutor, "timeoutExecutor is null"); + + queryPurger.scheduleWithFixedDelay( + () -> { + try { + // snapshot the queries before checking states to avoid registration race + for (Entry entry : ImmutableSet.copyOf(queries.entrySet())) { + if (!entry.getValue().isSubmissionFinished()) { + continue; + } + + // forget about this query if the query manager is no longer tracking it + if (!dispatchManager.getDispatchInfo(entry.getKey()).isPresent()) { + queries.remove(entry.getKey()); + } + } + } + catch (Throwable e) { + log.warn(e, "Error removing old queries"); + } + }, + 200, + 200, + MILLISECONDS); + } + + @PreDestroy + public void stop() + { + queryPurger.shutdownNow(); + } + + @POST + @Path("/v1/statement") + @Produces(APPLICATION_JSON) + public Response postStatement( + String statement, + @HeaderParam(X_FORWARDED_PROTO) String xForwardedProto, + @Context HttpServletRequest servletRequest, + @Context UriInfo uriInfo) + { + if (isNullOrEmpty(statement)) { + throw badRequest(BAD_REQUEST, "SQL statement is empty"); + } + + SessionContext sessionContext = new HttpRequestSessionContext(servletRequest); + Query query = new Query(statement, sessionContext, dispatchManager); + queries.put(query.getQueryId(), query); + + return Response.ok(query.getQueryResults(query.getLastToken(), uriInfo, xForwardedProto)).build(); + } + + @GET + @Path("/v1/statement/queued/{queryId}/{token}") + @Produces(APPLICATION_JSON) + public void getStatus( + @PathParam("queryId") QueryId queryId, + @PathParam("token") long token, + @QueryParam("slug") String slug, + @QueryParam("maxWait") Duration maxWait, + @HeaderParam(X_FORWARDED_PROTO) String xForwardedProto, + @Context UriInfo uriInfo, + @Suspended AsyncResponse asyncResponse) + { + Query query = getQuery(queryId, slug); + + // wait for query to be dispatched, up to the wait timeout + ListenableFuture futureStateChange = addTimeout( + query.waitForDispatched(), + () -> null, + WAIT_ORDERING.min(MAX_WAIT_TIME, maxWait), + timeoutExecutor); + + // when state changes, fetch the next result + ListenableFuture queryResultsFuture = Futures.transform( + futureStateChange, + ignored -> query.getQueryResults(token, uriInfo, xForwardedProto), + responseExecutor); + + // transform to Response + ListenableFuture response = Futures.transform( + queryResultsFuture, + queryResults -> Response.ok(queryResults).build(), + directExecutor()); + bindAsyncResponse(asyncResponse, response, responseExecutor); + } + + @DELETE + @Path("/v1/statement/queued/{queryId}/{token}") + @Produces(APPLICATION_JSON) + public Response cancelQuery( + @PathParam("queryId") QueryId queryId, + @PathParam("token") long token, + @QueryParam("slug") String slug) + { + getQuery(queryId, slug) + .cancel(); + return Response.noContent().build(); + } + + private Query getQuery(QueryId queryId, String slug) + { + Query query = queries.get(queryId); + if (query == null || !query.getSlug().equals(slug)) { + throw badRequest(NOT_FOUND, "Query not found"); + } + return query; + } + + private static URI getQueryHtmlUri(QueryId queryId, UriInfo uriInfo, String xForwardedProto) + { + return uriInfo.getRequestUriBuilder() + .scheme(getScheme(xForwardedProto, uriInfo)) + .replacePath("ui/query.html") + .replaceQuery(queryId.toString()) + .build(); + } + + private static URI getQueuedUri(QueryId queryId, String slug, long token, UriInfo uriInfo, String xForwardedProto) + { + return uriInfo.getBaseUriBuilder() + .scheme(getScheme(xForwardedProto, uriInfo)) + .replacePath("/v1/statement/queued/") + .path(queryId.toString()) + .path(String.valueOf(token)) + .replaceQuery("") + .queryParam("slug", slug) + .build(); + } + + private static String getScheme(String xForwardedProto, @Context UriInfo uriInfo) + { + return isNullOrEmpty(xForwardedProto) ? uriInfo.getRequestUri().getScheme() : xForwardedProto; + } + + private static QueryResults createQueryResults( + QueryId queryId, + URI nextUri, + Optional queryError, + UriInfo uriInfo, + String xForwardedProto, + Duration elapsedTime, + Duration queuedTime) + { + QueryState state = queryError.map(error -> FAILED).orElse(QUEUED); + return new QueryResults( + queryId.toString(), + getQueryHtmlUri(queryId, uriInfo, xForwardedProto), + null, + nextUri, + null, + null, + StatementStats.builder() + .setState(state.toString()) + .setQueued(state == QUEUED) + .setElapsedTimeMillis(elapsedTime.toMillis()) + .setQueuedTimeMillis(queuedTime.toMillis()) + .build(), + queryError.orElse(null), + ImmutableList.of(), + null, + null); + } + + private static WebApplicationException badRequest(Status status, String message) + { + throw new WebApplicationException( + Response.status(status) + .type(TEXT_PLAIN_TYPE) + .entity(message) + .build()); + } + + private static final class Query + { + private final String query; + private final SessionContext sessionContext; + private final DispatchManager dispatchManager; + private final QueryId queryId; + private final String slug = "x" + randomUUID().toString().toLowerCase(ENGLISH).replace("-", ""); + private final AtomicLong lastToken = new AtomicLong(); + + @GuardedBy("this") + private ListenableFuture querySubmissionFuture; + + public Query(String query, SessionContext sessionContext, DispatchManager dispatchManager) + { + this.query = requireNonNull(query, "query is null"); + this.sessionContext = requireNonNull(sessionContext, "sessionContext is null"); + this.dispatchManager = requireNonNull(dispatchManager, "dispatchManager is null"); + this.queryId = dispatchManager.createQueryId(); + } + + public QueryId getQueryId() + { + return queryId; + } + + public String getSlug() + { + return slug; + } + + public long getLastToken() + { + return lastToken.get(); + } + + public synchronized boolean isSubmissionFinished() + { + return querySubmissionFuture != null && querySubmissionFuture.isDone(); + } + + private ListenableFuture waitForDispatched() + { + // if query query submission has not finished, wait for it to finish + synchronized (this) { + if (querySubmissionFuture == null) { + querySubmissionFuture = dispatchManager.createQuery(queryId, slug, sessionContext, query); + } + if (!querySubmissionFuture.isDone()) { + return querySubmissionFuture; + } + } + + // otherwise, wait for the query to finish + return dispatchManager.waitForDispatched(queryId); + } + + public QueryResults getQueryResults(long token, UriInfo uriInfo, String xForwardedProto) + { + long lastToken = this.lastToken.get(); + // token should be the last token or the next token + if (token != lastToken && token != lastToken + 1) { + throw new WebApplicationException(Response.Status.GONE); + } + // advance (or stay at) the token + this.lastToken.compareAndSet(lastToken, token); + + synchronized (this) { + // if query submission has not finished, return simple empty result + if (querySubmissionFuture == null || !querySubmissionFuture.isDone()) { + return createQueryResults( + token + 1, + uriInfo, + xForwardedProto, + new DispatchInfo(Optional.empty(), Optional.empty(), NO_DURATION, NO_DURATION)); + } + } + + Optional dispatchInfo = dispatchManager.getDispatchInfo(queryId); + if (!dispatchInfo.isPresent()) { + // query should always be found, but it may have just been determined to be abandoned + throw new WebApplicationException(Response + .status(NOT_FOUND) + .build()); + } + + return createQueryResults(token + 1, uriInfo, xForwardedProto, dispatchInfo.get()); + } + + public synchronized void cancel() + { + querySubmissionFuture.addListener(() -> dispatchManager.cancelQuery(queryId), directExecutor()); + } + + private QueryResults createQueryResults(long token, UriInfo uriInfo, String xForwardedProto, DispatchInfo dispatchInfo) + { + URI nextUri = getNextUri(token, uriInfo, xForwardedProto, dispatchInfo); + + Optional queryError = dispatchInfo.getFailureInfo() + .map(this::toQueryError); + + return QueuedStatementResource.createQueryResults( + queryId, + nextUri, + queryError, + uriInfo, + xForwardedProto, + dispatchInfo.getElapsedTime(), + dispatchInfo.getQueuedTime()); + } + + private URI getNextUri(long token, UriInfo uriInfo, String xForwardedProto, DispatchInfo dispatchInfo) + { + // if failed, query is complete + if (dispatchInfo.getFailureInfo().isPresent()) { + return null; + } + // if dispatched, redirect to new uri + return dispatchInfo.getCoordinatorLocation() + .map(coordinatorLocation -> getRedirectUri(coordinatorLocation, uriInfo, xForwardedProto)) + .orElseGet(() -> getQueuedUri(queryId, slug, token, uriInfo, xForwardedProto)); + } + + private URI getRedirectUri(CoordinatorLocation coordinatorLocation, UriInfo uriInfo, String xForwardedProto) + { + URI coordinatorUri = coordinatorLocation.getUri(getScheme(xForwardedProto, uriInfo)); + return uriBuilderFrom(coordinatorUri) + .appendPath("/v1/statement/executing") + .appendPath(queryId.toString()) + .appendPath("0") + .addParameter("slug", slug) + .build(); + } + + private QueryError toQueryError(ExecutionFailureInfo executionFailureInfo) + { + ErrorCode errorCode; + if (executionFailureInfo.getErrorCode() != null) { + errorCode = executionFailureInfo.getErrorCode(); + } + else { + errorCode = GENERIC_INTERNAL_ERROR.toErrorCode(); + log.warn("Failed query %s has no error code", queryId); + } + + return new QueryError( + firstNonNull(executionFailureInfo.getMessage(), "Internal error"), + null, + errorCode.getCode(), + errorCode.getName(), + errorCode.getType().toString(), + executionFailureInfo.getErrorLocation(), + executionFailureInfo.toFailureInfo()); + } + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/execution/DataDefinitionExecution.java b/presto-main/src/main/java/com/facebook/presto/execution/DataDefinitionExecution.java index e10a7ed4ae32f..b69c0040fe3e7 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/DataDefinitionExecution.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/DataDefinitionExecution.java @@ -22,10 +22,8 @@ import com.facebook.presto.metadata.MetadataManager; import com.facebook.presto.security.AccessControl; import com.facebook.presto.server.BasicQueryInfo; -import com.facebook.presto.spi.ErrorCode; import com.facebook.presto.spi.QueryId; import com.facebook.presto.spi.resourceGroups.QueryType; -import com.facebook.presto.spi.resourceGroups.ResourceGroupId; import com.facebook.presto.sql.planner.Plan; import com.facebook.presto.sql.tree.Expression; import com.facebook.presto.sql.tree.Statement; @@ -43,7 +41,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.concurrent.ExecutorService; import java.util.function.Consumer; import static com.google.common.base.Preconditions.checkArgument; @@ -58,6 +55,7 @@ public class DataDefinitionExecution { private final DataDefinitionTask task; private final T statement; + private final String slug; private final TransactionManager transactionManager; private final Metadata metadata; private final AccessControl accessControl; @@ -67,6 +65,7 @@ public class DataDefinitionExecution private DataDefinitionExecution( DataDefinitionTask task, T statement, + String slug, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, @@ -75,6 +74,7 @@ private DataDefinitionExecution( { this.task = requireNonNull(task, "task is null"); this.statement = requireNonNull(statement, "statement is null"); + this.slug = requireNonNull(slug, "slug is null"); this.transactionManager = requireNonNull(transactionManager, "transactionManager is null"); this.metadata = requireNonNull(metadata, "metadata is null"); this.accessControl = requireNonNull(accessControl, "accessControl is null"); @@ -82,6 +82,12 @@ private DataDefinitionExecution( this.parameters = parameters; } + @Override + public String getSlug() + { + return slug; + } + @Override public VersionedMemoryPoolId getMemoryPool() { @@ -100,12 +106,6 @@ public Session getSession() return stateMachine.getSession(); } - @Override - public Optional getErrorCode() - { - return stateMachine.getFailureInfo().map(ExecutionFailureInfo::getErrorCode); - } - @Override public DataSize getUserMemoryReservation() { @@ -286,69 +286,47 @@ public List getParameters() public static class DataDefinitionExecutionFactory implements QueryExecutionFactory> { - private final LocationFactory locationFactory; private final TransactionManager transactionManager; private final Metadata metadata; private final AccessControl accessControl; - private final ExecutorService executor; private final Map, DataDefinitionTask> tasks; @Inject public DataDefinitionExecutionFactory( - LocationFactory locationFactory, TransactionManager transactionManager, MetadataManager metadata, AccessControl accessControl, - @ForQueryExecution ExecutorService executor, Map, DataDefinitionTask> tasks) { - this.locationFactory = requireNonNull(locationFactory, "locationFactory is null"); this.transactionManager = requireNonNull(transactionManager, "transactionManager is null"); this.metadata = requireNonNull(metadata, "metadata is null"); this.accessControl = requireNonNull(accessControl, "accessControl is null"); - this.executor = requireNonNull(executor, "executor is null"); this.tasks = requireNonNull(tasks, "tasks is null"); } @Override public DataDefinitionExecution createQueryExecution( - String query, - Session session, PreparedQuery preparedQuery, - ResourceGroupId resourceGroup, + QueryStateMachine stateMachine, + String slug, WarningCollector warningCollector, Optional queryType) { - return createDataDefinitionExecution(query, session, resourceGroup, preparedQuery.getStatement(), preparedQuery.getParameters(), warningCollector, queryType); + return createDataDefinitionExecution(preparedQuery.getStatement(), preparedQuery.getParameters(), stateMachine, slug); } private DataDefinitionExecution createDataDefinitionExecution( - String query, - Session session, - ResourceGroupId resourceGroup, T statement, List parameters, - WarningCollector warningCollector, - Optional queryType) + QueryStateMachine stateMachine, + String slug) { @SuppressWarnings("unchecked") DataDefinitionTask task = (DataDefinitionTask) tasks.get(statement.getClass()); checkArgument(task != null, "no task for statement: %s", statement.getClass().getSimpleName()); - QueryStateMachine stateMachine = QueryStateMachine.begin( - query, - session, - locationFactory.createQueryLocation(session.getQueryId()), - resourceGroup, - queryType, - task.isTransactionControl(), - transactionManager, - accessControl, - executor, - metadata, - warningCollector); stateMachine.setUpdateType(task.getName()); - return new DataDefinitionExecution<>(task, statement, transactionManager, metadata, accessControl, stateMachine, parameters); + return new DataDefinitionExecution<>(task, statement, slug, transactionManager, metadata, accessControl, stateMachine, parameters); } } } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/ManagedQueryExecution.java b/presto-main/src/main/java/com/facebook/presto/execution/ManagedQueryExecution.java index c116110e39c3c..b23b834c32dcb 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/ManagedQueryExecution.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/ManagedQueryExecution.java @@ -24,7 +24,7 @@ public interface ManagedQueryExecution { - void start(); + void startWaitingForResources(); void fail(Throwable cause); diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryExecution.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryExecution.java index ad9369675a696..e8d26144c8290 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/QueryExecution.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryExecution.java @@ -13,19 +13,20 @@ */ package com.facebook.presto.execution; -import com.facebook.presto.Session; import com.facebook.presto.execution.QueryPreparer.PreparedQuery; import com.facebook.presto.execution.QueryTracker.TrackedQuery; import com.facebook.presto.execution.StateMachine.StateChangeListener; import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.memory.VersionedMemoryPoolId; +import com.facebook.presto.server.BasicQueryInfo; import com.facebook.presto.spi.resourceGroups.QueryType; -import com.facebook.presto.spi.resourceGroups.ResourceGroupId; import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.planner.Plan; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.ListenableFuture; +import io.airlift.units.DataSize; +import io.airlift.units.Duration; import java.net.URI; import java.util.List; @@ -36,22 +37,36 @@ import static java.util.Objects.requireNonNull; public interface QueryExecution - extends ManagedQueryExecution, TrackedQuery + extends TrackedQuery { QueryState getState(); ListenableFuture getStateChange(QueryState currentState); + void addStateChangeListener(StateChangeListener stateChangeListener); + void addOutputInfoListener(Consumer listener); Plan getQueryPlan(); + BasicQueryInfo getBasicQueryInfo(); + QueryInfo getQueryInfo(); + String getSlug(); + + Duration getTotalCpuTime(); + + DataSize getUserMemoryReservation(); + + DataSize getTotalMemoryReservation(); + VersionedMemoryPoolId getMemoryPool(); void setMemoryPool(VersionedMemoryPoolId poolId); + void start(); + void cancelQuery(); void cancelStage(StageId stageId); @@ -68,10 +83,9 @@ public interface QueryExecution interface QueryExecutionFactory { T createQueryExecution( - String query, - Session session, PreparedQuery preparedQuery, - ResourceGroupId resourceGroup, + QueryStateMachine stateMachine, + String slug, WarningCollector warningCollector, Optional queryType); } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryInfo.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryInfo.java index c4a8fdaf5c6ac..4d56762c1dd04 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/QueryInfo.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryInfo.java @@ -13,7 +13,6 @@ */ package com.facebook.presto.execution; -import com.facebook.presto.Session; import com.facebook.presto.SessionRepresentation; import com.facebook.presto.spi.ErrorCode; import com.facebook.presto.spi.ErrorType; @@ -39,11 +38,7 @@ import java.util.Optional; import java.util.Set; -import static com.facebook.presto.execution.QueryState.FAILED; -import static com.facebook.presto.execution.QueryStats.immediateFailureQueryStats; import static com.facebook.presto.execution.StageInfo.getAllStages; -import static com.facebook.presto.memory.LocalMemoryManager.GENERAL_POOL; -import static com.facebook.presto.util.Failures.toFailure; import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; @@ -168,49 +163,6 @@ public QueryInfo( this.failedTasks = failedTasks; } - public static QueryInfo immediateFailureQueryInfo( - Session session, - String query, - URI self, - Optional resourceGroupId, - Optional queryType, - Throwable throwable) - { - ExecutionFailureInfo failureCause = toFailure(throwable); - QueryInfo queryInfo = new QueryInfo( - session.getQueryId(), - session.toSessionRepresentation(), - FAILED, - GENERAL_POOL, - false, - self, - ImmutableList.of(), - query, - immediateFailureQueryStats(), - Optional.empty(), - Optional.empty(), - ImmutableMap.of(), - ImmutableSet.of(), - ImmutableMap.of(), - ImmutableMap.of(), - ImmutableSet.of(), - Optional.empty(), - false, - null, - Optional.empty(), - failureCause, - failureCause.getErrorCode(), - ImmutableList.of(), - ImmutableSet.of(), - Optional.empty(), - true, - resourceGroupId, - queryType, - Optional.empty()); - - return queryInfo; - } - @JsonProperty public QueryId getQueryId() { diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryManager.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryManager.java index c498ac0638b6e..e08705f678d25 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/QueryManager.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryManager.java @@ -13,10 +13,9 @@ */ package com.facebook.presto.execution; -import com.facebook.presto.execution.QueryExecution.QueryOutputInfo; +import com.facebook.presto.Session; import com.facebook.presto.execution.StateMachine.StateChangeListener; import com.facebook.presto.server.BasicQueryInfo; -import com.facebook.presto.server.SessionContext; import com.facebook.presto.spi.QueryId; import com.google.common.util.concurrent.ListenableFuture; @@ -33,7 +32,7 @@ public interface QueryManager * * @throws NoSuchElementException if query does not exist */ - void addOutputInfoListener(QueryId queryId, Consumer listener) + void addOutputInfoListener(QueryId queryId, Consumer listener) throws NoSuchElementException; /** @@ -66,6 +65,16 @@ BasicQueryInfo getQueryInfo(QueryId queryId) QueryInfo getFullQueryInfo(QueryId queryId) throws NoSuchElementException; + /** + * @throws NoSuchElementException if query does not exist + */ + Session getQuerySession(QueryId queryId); + + /** + * @throws NoSuchElementException if query does not exist + */ + boolean isQuerySlugValid(QueryId queryId, String slug); + /** * @throws NoSuchElementException if query does not exist */ @@ -78,13 +87,10 @@ QueryState getQueryState(QueryId queryId) */ void recordHeartbeat(QueryId queryId); - QueryId createQueryId(); - /** - * Creates a new query. This method may be called multiple times for the same query id. The - * the first call will be accepted, and the other calls will be ignored. + * Creates a new query using the specified query execution. */ - ListenableFuture createQuery(QueryId queryId, SessionContext sessionContext, String query); + void createQuery(QueryExecution execution); /** * Attempts to fail the query for the specified reason. If the query is already in a final diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryStateMachine.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryStateMachine.java index 99aed6599693b..44ad78a399b9a 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/QueryStateMachine.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryStateMachine.java @@ -97,7 +97,7 @@ @ThreadSafe public class QueryStateMachine { - public static final Logger QUERY_STATE_LOG = Logger.get(QueryStateMachine.class); + private static final Logger QUERY_STATE_LOG = Logger.get(QueryStateMachine.class); private final QueryId queryId; private final String query; diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryStats.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryStats.java index 84f7a509a4837..7db1488b50567 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/QueryStats.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryStats.java @@ -31,11 +31,9 @@ import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; -import static io.airlift.units.DataSize.Unit.BYTE; import static io.airlift.units.DataSize.succinctBytes; import static java.lang.Math.min; import static java.util.Objects.requireNonNull; -import static java.util.concurrent.TimeUnit.MILLISECONDS; public class QueryStats { @@ -240,60 +238,6 @@ public QueryStats( this.operatorSummaries = ImmutableList.copyOf(requireNonNull(operatorSummaries, "operatorSummaries is null")); } - public static QueryStats immediateFailureQueryStats() - { - DateTime now = DateTime.now(); - return new QueryStats( - now, - now, - now, - now, - new Duration(0, MILLISECONDS), - new Duration(0, MILLISECONDS), - new Duration(0, MILLISECONDS), - new Duration(0, MILLISECONDS), - new Duration(0, MILLISECONDS), - new Duration(0, MILLISECONDS), - new Duration(0, MILLISECONDS), - new Duration(0, MILLISECONDS), - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - new DataSize(0, BYTE), - new DataSize(0, BYTE), - new DataSize(0, BYTE), - new DataSize(0, BYTE), - new DataSize(0, BYTE), - new DataSize(0, BYTE), - false, - new Duration(0, MILLISECONDS), - new Duration(0, MILLISECONDS), - new Duration(0, MILLISECONDS), - new Duration(0, MILLISECONDS), - false, - ImmutableSet.of(), - new DataSize(0, BYTE), - new DataSize(0, BYTE), - 0, - new DataSize(0, BYTE), - 0, - new DataSize(0, BYTE), - 0, - 0, - new DataSize(0, BYTE), - new DataSize(0, BYTE), - new DataSize(0, BYTE), - ImmutableList.of(), - ImmutableList.of()); - } - @JsonProperty public DateTime getCreateTime() { diff --git a/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryExecution.java b/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryExecution.java index 462d2acb855cd..0c3bcb26d300d 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryExecution.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryExecution.java @@ -35,13 +35,11 @@ import com.facebook.presto.security.AccessControl; import com.facebook.presto.server.BasicQueryInfo; import com.facebook.presto.spi.ConnectorId; -import com.facebook.presto.spi.ErrorCode; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.QueryId; import com.facebook.presto.spi.TableHandle; import com.facebook.presto.spi.plan.PlanNodeIdAllocator; import com.facebook.presto.spi.resourceGroups.QueryType; -import com.facebook.presto.spi.resourceGroups.ResourceGroupId; import com.facebook.presto.split.CloseableSplitSourceProvider; import com.facebook.presto.split.SplitManager; import com.facebook.presto.sql.analyzer.Analysis; @@ -60,7 +58,6 @@ import com.facebook.presto.sql.planner.optimizations.PlanOptimizer; import com.facebook.presto.sql.planner.plan.OutputNode; import com.facebook.presto.sql.tree.Explain; -import com.facebook.presto.transaction.TransactionManager; import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.ListenableFuture; import io.airlift.units.DataSize; @@ -70,7 +67,6 @@ import javax.annotation.concurrent.ThreadSafe; import javax.inject.Inject; -import java.net.URI; import java.util.List; import java.util.Map; import java.util.Optional; @@ -79,8 +75,6 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; -import static com.facebook.airlift.concurrent.MoreFutures.addExceptionCallback; -import static com.facebook.airlift.concurrent.MoreFutures.addSuccessCallback; import static com.facebook.presto.SystemSessionProperties.isUseLegacyScheduler; import static com.facebook.presto.execution.buffer.OutputBuffers.BROADCAST_PARTITION_ID; import static com.facebook.presto.execution.buffer.OutputBuffers.createInitialEmptyOutputBuffers; @@ -99,8 +93,7 @@ public class SqlQueryExecution private static final OutputBufferId OUTPUT_BUFFER_ID = new OutputBufferId(0); private final QueryStateMachine stateMachine; - - private final ClusterSizeMonitor clusterSizeMonitor; + private final String slug; private final Metadata metadata; private final SqlParser sqlParser; private final SplitManager splitManager; @@ -121,14 +114,9 @@ public class SqlQueryExecution private final CostCalculator costCalculator; private SqlQueryExecution( - String query, - Session session, - URI self, - ResourceGroupId resourceGroup, - Optional queryType, PreparedQuery preparedQuery, - ClusterSizeMonitor clusterSizeMonitor, - TransactionManager transactionManager, + QueryStateMachine stateMachine, + String slug, Metadata metadata, AccessControl accessControl, SqlParser sqlParser, @@ -147,8 +135,8 @@ private SqlQueryExecution( CostCalculator costCalculator, WarningCollector warningCollector) { - try (SetThreadName ignored = new SetThreadName("Query-%s", session.getQueryId())) { - this.clusterSizeMonitor = requireNonNull(clusterSizeMonitor, "clusterSizeMonitor is null"); + try (SetThreadName ignored = new SetThreadName("Query-%s", stateMachine.getQueryId())) { + this.slug = requireNonNull(slug, "slug is null"); this.metadata = requireNonNull(metadata, "metadata is null"); this.sqlParser = requireNonNull(sqlParser, "sqlParser is null"); this.splitManager = requireNonNull(splitManager, "splitManager is null"); @@ -162,22 +150,7 @@ private SqlQueryExecution( this.schedulerStats = requireNonNull(schedulerStats, "schedulerStats is null"); this.statsCalculator = requireNonNull(statsCalculator, "statsCalculator is null"); this.costCalculator = requireNonNull(costCalculator, "costCalculator is null"); - - requireNonNull(query, "query is null"); - requireNonNull(session, "session is null"); - requireNonNull(self, "self is null"); - this.stateMachine = QueryStateMachine.begin( - query, - session, - self, - resourceGroup, - queryType, - false, - transactionManager, - accessControl, - queryExecutor, - metadata, - warningCollector); + this.stateMachine = requireNonNull(stateMachine, "stateMachine is null"); // analyze query requireNonNull(preparedQuery, "preparedQuery is null"); @@ -218,6 +191,12 @@ private SqlQueryExecution( } } + @Override + public String getSlug() + { + return slug; + } + @Override public VersionedMemoryPoolId getMemoryPool() { @@ -318,20 +297,6 @@ public int getRunningTaskCount() @Override public void start() - { - if (stateMachine.transitionToWaitingForResources()) { - waitForMinimumWorkers(); - } - } - - private void waitForMinimumWorkers() - { - ListenableFuture minimumWorkerFuture = clusterSizeMonitor.waitForMinimumWorkers(); - addSuccessCallback(minimumWorkerFuture, () -> queryExecutor.submit(this::startExecution)); - addExceptionCallback(minimumWorkerFuture, throwable -> queryExecutor.submit(() -> stateMachine.transitionToFailed(throwable))); - } - - private void startExecution() { try (SetThreadName ignored = new SetThreadName("Query-%s", stateMachine.getQueryId())) { try { @@ -383,12 +348,6 @@ public Session getSession() return stateMachine.getSession(); } - @Override - public Optional getErrorCode() - { - return stateMachine.getFailureInfo().map(ExecutionFailureInfo::getErrorCode); - } - @Override public void addFinalQueryInfoListener(StateChangeListener stateChangeListener) { @@ -660,14 +619,12 @@ public static class SqlQueryExecutionFactory private final List planOptimizers; private final PlanFragmenter planFragmenter; private final RemoteTaskFactory remoteTaskFactory; - private final TransactionManager transactionManager; private final QueryExplainer queryExplainer; private final LocationFactory locationFactory; private final ExecutorService queryExecutor; private final SectionExecutionFactory sectionExecutionFactory; private final InternalNodeManager internalNodeManager; private final Map executionPolicies; - private final ClusterSizeMonitor clusterSizeMonitor; private final StatsCalculator statsCalculator; private final CostCalculator costCalculator; @@ -681,14 +638,12 @@ public static class SqlQueryExecutionFactory PlanOptimizers planOptimizers, PlanFragmenter planFragmenter, RemoteTaskFactory remoteTaskFactory, - TransactionManager transactionManager, @ForQueryExecution ExecutorService queryExecutor, SectionExecutionFactory sectionExecutionFactory, InternalNodeManager internalNodeManager, QueryExplainer queryExplainer, Map executionPolicies, SplitSchedulerStats schedulerStats, - ClusterSizeMonitor clusterSizeMonitor, StatsCalculator statsCalculator, CostCalculator costCalculator) { @@ -702,13 +657,11 @@ public static class SqlQueryExecutionFactory requireNonNull(planOptimizers, "planOptimizers is null"); this.planFragmenter = requireNonNull(planFragmenter, "planFragmenter is null"); this.remoteTaskFactory = requireNonNull(remoteTaskFactory, "remoteTaskFactory is null"); - this.transactionManager = requireNonNull(transactionManager, "transactionManager is null"); this.queryExecutor = requireNonNull(queryExecutor, "queryExecutor is null"); this.sectionExecutionFactory = requireNonNull(sectionExecutionFactory, "sectionExecutionFactory is null"); this.internalNodeManager = requireNonNull(internalNodeManager, "internalNodeManager is null"); this.queryExplainer = requireNonNull(queryExplainer, "queryExplainer is null"); this.executionPolicies = requireNonNull(executionPolicies, "schedulerPolicies is null"); - this.clusterSizeMonitor = requireNonNull(clusterSizeMonitor, "clusterSizeMonitor is null"); this.planOptimizers = planOptimizers.get(); this.statsCalculator = requireNonNull(statsCalculator, "statsCalculator is null"); this.costCalculator = requireNonNull(costCalculator, "costCalculator is null"); @@ -716,26 +669,20 @@ public static class SqlQueryExecutionFactory @Override public QueryExecution createQueryExecution( - String query, - Session session, PreparedQuery preparedQuery, - ResourceGroupId resourceGroup, + QueryStateMachine stateMachine, + String slug, WarningCollector warningCollector, Optional queryType) { - String executionPolicyName = SystemSessionProperties.getExecutionPolicy(session); + String executionPolicyName = SystemSessionProperties.getExecutionPolicy(stateMachine.getSession()); ExecutionPolicy executionPolicy = executionPolicies.get(executionPolicyName); checkArgument(executionPolicy != null, "No execution policy %s", executionPolicy); SqlQueryExecution execution = new SqlQueryExecution( - query, - session, - locationFactory.createQueryLocation(session.getQueryId()), - resourceGroup, - queryType, preparedQuery, - clusterSizeMonitor, - transactionManager, + stateMachine, + slug, metadata, accessControl, sqlParser, diff --git a/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java b/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java index be03b27a77e8f..6929cce310396 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java @@ -13,38 +13,21 @@ */ package com.facebook.presto.execution; -import com.facebook.airlift.concurrent.BoundedExecutor; import com.facebook.airlift.concurrent.ThreadPoolExecutorMBean; import com.facebook.airlift.log.Logger; import com.facebook.presto.ExceededCpuLimitException; import com.facebook.presto.Session; import com.facebook.presto.event.QueryMonitor; -import com.facebook.presto.execution.QueryExecution.QueryExecutionFactory; import com.facebook.presto.execution.QueryExecution.QueryOutputInfo; -import com.facebook.presto.execution.QueryPreparer.PreparedQuery; import com.facebook.presto.execution.StateMachine.StateChangeListener; -import com.facebook.presto.execution.resourceGroups.ResourceGroupManager; -import com.facebook.presto.execution.scheduler.NodeSchedulerConfig; -import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.execution.warnings.WarningCollectorFactory; import com.facebook.presto.memory.ClusterMemoryManager; -import com.facebook.presto.metadata.SessionPropertyManager; -import com.facebook.presto.security.AccessControl; import com.facebook.presto.server.BasicQueryInfo; -import com.facebook.presto.server.SessionContext; -import com.facebook.presto.server.SessionPropertyDefaults; -import com.facebook.presto.server.SessionSupplier; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.QueryId; -import com.facebook.presto.spi.resourceGroups.QueryType; -import com.facebook.presto.spi.resourceGroups.SelectionContext; -import com.facebook.presto.spi.resourceGroups.SelectionCriteria; import com.facebook.presto.sql.planner.Plan; -import com.facebook.presto.sql.tree.Statement; -import com.facebook.presto.transaction.TransactionManager; import com.facebook.presto.version.EmbedVersion; import com.google.common.collect.Ordering; -import com.google.common.util.concurrent.AbstractFuture; import com.google.common.util.concurrent.ListenableFuture; import io.airlift.units.Duration; import org.weakref.jmx.Flatten; @@ -57,12 +40,8 @@ import javax.inject.Inject; import java.util.List; -import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadPoolExecutor; @@ -73,18 +52,11 @@ import static com.facebook.airlift.concurrent.Threads.threadsNamed; import static com.facebook.presto.SystemSessionProperties.getQueryMaxCpuTime; import static com.facebook.presto.execution.QueryState.RUNNING; -import static com.facebook.presto.execution.QueryStateMachine.QUERY_STATE_LOG; -import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; -import static com.facebook.presto.spi.StandardErrorCode.QUERY_TEXT_TOO_LARGE; -import static com.facebook.presto.util.Failures.toFailure; -import static com.facebook.presto.util.StatementUtils.getQueryType; -import static com.facebook.presto.util.StatementUtils.isTransactionControlStatement; -import static com.google.common.base.Preconditions.checkArgument; +import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.util.concurrent.Futures.immediateFailedFuture; import static java.lang.String.format; import static java.util.Objects.requireNonNull; -import static java.util.concurrent.Executors.newCachedThreadPool; @ThreadSafe public class SqlQueryManager @@ -92,92 +64,29 @@ public class SqlQueryManager { private static final Logger log = Logger.get(SqlQueryManager.class); - private final QueryPreparer queryPreparer; - - private final EmbedVersion embedVersion; - private final ExecutorService unboundedExecutorService; - private final Executor boundedExecutor; - private final ThreadPoolExecutorMBean queryExecutorMBean; - private final ResourceGroupManager resourceGroupManager; private final ClusterMemoryManager memoryManager; + private final QueryMonitor queryMonitor; + private final EmbedVersion embedVersion; + private final QueryTracker queryTracker; - private final int maxQueryLength; private final Duration maxQueryCpuTime; - private final QueryTracker queryTracker; - private final ScheduledExecutorService queryManagementExecutor; private final ThreadPoolExecutorMBean queryManagementExecutorMBean; - private final QueryMonitor queryMonitor; - private final LocationFactory locationFactory; - - private final TransactionManager transactionManager; - private final AccessControl accessControl; - - private final QueryIdGenerator queryIdGenerator; - - private final SessionSupplier sessionSupplier; - private final SessionPropertyDefaults sessionPropertyDefaults; - - private final Map, QueryExecutionFactory> executionFactories; - private final SqlQueryManagerStats stats = new SqlQueryManagerStats(); - private final WarningCollectorFactory warningCollectorFactory; - @Inject - public SqlQueryManager( - QueryPreparer queryPreparer, - EmbedVersion embedVersion, - NodeSchedulerConfig nodeSchedulerConfig, - QueryManagerConfig queryManagerConfig, - QueryMonitor queryMonitor, - ResourceGroupManager resourceGroupManager, - ClusterMemoryManager memoryManager, - LocationFactory locationFactory, - TransactionManager transactionManager, - AccessControl accessControl, - QueryIdGenerator queryIdGenerator, - SessionSupplier sessionSupplier, - SessionPropertyDefaults sessionPropertyDefaults, - Map, QueryExecutionFactory> executionFactories, - WarningCollectorFactory warningCollectorFactory) + public SqlQueryManager(ClusterMemoryManager memoryManager, QueryMonitor queryMonitor, EmbedVersion embedVersion, QueryManagerConfig queryManagerConfig, WarningCollectorFactory warningCollectorFactory) { - this.queryPreparer = requireNonNull(queryPreparer, "queryPreparer is null"); - - this.embedVersion = requireNonNull(embedVersion, "embedVersion is null"); - this.executionFactories = requireNonNull(executionFactories, "executionFactories is null"); - - this.unboundedExecutorService = newCachedThreadPool(threadsNamed("query-scheduler-%s")); - this.boundedExecutor = new BoundedExecutor(unboundedExecutorService, queryManagerConfig.getQuerySubmissionMaxThreads()); - this.queryExecutorMBean = new ThreadPoolExecutorMBean((ThreadPoolExecutor) unboundedExecutorService); - - requireNonNull(nodeSchedulerConfig, "nodeSchedulerConfig is null"); - requireNonNull(queryManagerConfig, "queryManagerConfig is null"); - this.resourceGroupManager = requireNonNull(resourceGroupManager, "resourceGroupManager is null"); this.memoryManager = requireNonNull(memoryManager, "memoryManager is null"); - this.queryMonitor = requireNonNull(queryMonitor, "queryMonitor is null"); - this.locationFactory = requireNonNull(locationFactory, "locationFactory is null"); - - this.transactionManager = requireNonNull(transactionManager, "transactionManager is null"); - this.accessControl = requireNonNull(accessControl, "accessControl is null"); - - this.queryIdGenerator = requireNonNull(queryIdGenerator, "queryIdGenerator is null"); - - this.sessionSupplier = requireNonNull(sessionSupplier, "sessionSupplier is null"); - this.sessionPropertyDefaults = requireNonNull(sessionPropertyDefaults, "sessionPropertyDefaults is null"); - - this.path = sqlEnvironmentConfig.getPath(); + this.embedVersion = requireNonNull(embedVersion, "embedVersion is null"); - this.maxQueryLength = queryManagerConfig.getMaxQueryLength(); this.maxQueryCpuTime = queryManagerConfig.getQueryMaxCpuTime(); - this.warningCollectorFactory = requireNonNull(warningCollectorFactory, "warningCollectorFactory is null"); - - queryManagementExecutor = Executors.newScheduledThreadPool(queryManagerConfig.getQueryManagerExecutorPoolSize(), threadsNamed("query-management-%s")); - queryManagementExecutorMBean = new ThreadPoolExecutorMBean((ThreadPoolExecutor) queryManagementExecutor); + this.queryManagementExecutor = Executors.newScheduledThreadPool(queryManagerConfig.getQueryManagerExecutorPoolSize(), threadsNamed("query-management-%s")); + this.queryManagementExecutorMBean = new ThreadPoolExecutorMBean((ThreadPoolExecutor) queryManagementExecutor); this.queryTracker = new QueryTracker<>(queryManagerConfig, queryManagementExecutor); } @@ -208,7 +117,6 @@ public void stop() { queryTracker.stop(); queryManagementExecutor.shutdownNow(); - unboundedExecutorService.shutdownNow(); } @Override @@ -259,10 +167,24 @@ public BasicQueryInfo getQueryInfo(QueryId queryId) @Override public QueryInfo getFullQueryInfo(QueryId queryId) + throws NoSuchElementException { return queryTracker.getQuery(queryId).getQueryInfo(); } + @Override + public Session getQuerySession(QueryId queryId) + throws NoSuchElementException + { + return queryTracker.getQuery(queryId).getSession(); + } + + @Override + public boolean isQuerySlugValid(QueryId queryId, String slug) + { + return queryTracker.getQuery(queryId).getSlug().equals(slug); + } + public Plan getQueryPlan(QueryId queryId) { return queryTracker.getQuery(queryId).getQueryPlan(); @@ -287,156 +209,28 @@ public void recordHeartbeat(QueryId queryId) } @Override - public QueryId createQueryId() - { - return queryIdGenerator.createNextQueryId(); - } - - @Override - public ListenableFuture createQuery(QueryId queryId, SessionContext sessionContext, String query) + public void createQuery(QueryExecution queryExecution) { - QueryCreationFuture queryCreationFuture = new QueryCreationFuture(); - boundedExecutor.execute(embedVersion.embedVersion(() -> { - try { - createQueryInternal(queryId, sessionContext, query, resourceGroupManager); - queryCreationFuture.set(null); - } - catch (Throwable e) { - queryCreationFuture.setException(e); - } - })); - return queryCreationFuture; - } + requireNonNull(queryExecution, "queryExecution is null"); - private void createQueryInternal(QueryId queryId, SessionContext sessionContext, String query, ResourceGroupManager resourceGroupManager) - { - requireNonNull(queryId, "queryId is null"); - requireNonNull(sessionContext, "sessionFactory is null"); - requireNonNull(query, "query is null"); - checkArgument(!query.isEmpty(), "query must not be empty string"); - checkArgument(!queryTracker.tryGetQuery(queryId).isPresent(), "query %s already exists", queryId); - - Session session = null; - SelectionContext selectionContext = null; - QueryExecution queryExecution; - PreparedQuery preparedQuery; - Optional queryType = Optional.empty(); - try { - if (query.length() > maxQueryLength) { - int queryLength = query.length(); - query = query.substring(0, maxQueryLength); - throw new PrestoException(QUERY_TEXT_TOO_LARGE, format("Query text length (%s) exceeds the maximum length (%s)", queryLength, maxQueryLength)); - } - - // decode session - session = sessionSupplier.createSession(queryId, sessionContext); - accessControl.checkQueryIntegrity(session.getIdentity(), query); - - WarningCollector warningCollector = warningCollectorFactory.create(); - - // prepare query - preparedQuery = queryPreparer.prepareQuery(session, query, warningCollector); - - // select resource group - queryType = getQueryType(preparedQuery.getStatement().getClass()); - selectionContext = resourceGroupManager.selectGroup(new SelectionCriteria( - sessionContext.getIdentity().getPrincipal().isPresent(), - sessionContext.getIdentity().getUser(), - Optional.ofNullable(sessionContext.getSource()), - sessionContext.getClientTags(), - sessionContext.getResourceEstimates(), - queryType.map(Enum::name))); - - // apply system defaults for query - session = sessionPropertyDefaults.newSessionWithDefaultProperties(session, queryType.map(Enum::name), selectionContext.getResourceGroupId()); - - // mark existing transaction as active - transactionManager.activateTransaction(session, isTransactionControlStatement(preparedQuery.getStatement()), accessControl); - - // create query execution - QueryExecutionFactory queryExecutionFactory = executionFactories.get(preparedQuery.getStatement().getClass()); - if (queryExecutionFactory == null) { - throw new PrestoException(NOT_SUPPORTED, "Unsupported statement type: " + preparedQuery.getStatement().getClass().getSimpleName()); - } - queryExecution = queryExecutionFactory.createQueryExecution( - query, - session, - preparedQuery, - selectionContext.getResourceGroupId(), - warningCollector, - queryType); - } - catch (RuntimeException e) { - // This is intentionally not a method, since after the state change listener is registered - // it's not safe to do any of this, and we had bugs before where people reused this code in a method - - // if session creation failed, create a minimal session object - if (session == null) { - session = Session.builder(new SessionPropertyManager()) - .setQueryId(queryId) - .setIdentity(sessionContext.getIdentity()) - .build(); - } - QUERY_STATE_LOG.debug(e, "Query %s failed", session.getQueryId()); - - // query failure fails the transaction - session.getTransactionId().ifPresent(transactionManager::fail); - - QueryExecution execution = new FailedQueryExecution( - session, - query, - locationFactory.createQueryLocation(queryId), - Optional.ofNullable(selectionContext).map(SelectionContext::getResourceGroupId), - queryType, - unboundedExecutorService, - e); - - try { - queryTracker.addQuery(execution); - - BasicQueryInfo queryInfo = execution.getBasicQueryInfo(); - queryMonitor.queryCreatedEvent(queryInfo); - queryMonitor.queryImmediateFailureEvent(queryInfo, toFailure(e)); - stats.queryQueued(); - stats.queryStarted(); - stats.queryStopped(); - stats.queryFinished(execution.getQueryInfo()); - } - finally { - // execution MUST be added to the expiration queue or there will be a leak - queryTracker.expireQuery(queryId); - } - - return; + if (!queryTracker.addQuery(queryExecution)) { + throw new PrestoException(GENERIC_INTERNAL_ERROR, format("Query %s already registered", queryExecution.getQueryId())); } - queryMonitor.queryCreatedEvent(queryExecution.getBasicQueryInfo()); - queryExecution.addFinalQueryInfoListener(finalQueryInfo -> { try { - stats.queryFinished(finalQueryInfo); + stats.queryFinished(new BasicQueryInfo(finalQueryInfo)); queryMonitor.queryCompletedEvent(finalQueryInfo); } finally { // execution MUST be added to the expiration queue or there will be a leak - queryTracker.expireQuery(queryId); + queryTracker.expireQuery(queryExecution.getQueryId()); } }); addStatsListeners(queryExecution); - if (!queryTracker.addQuery(queryExecution)) { - // query already created, so just exit - return; - } - - // start the query in the background - try { - resourceGroupManager.submit(preparedQuery.getStatement(), queryExecution, selectionContext, unboundedExecutorService); - } - catch (Throwable e) { - failQuery(queryId, e); - } + embedVersion.embedVersion(queryExecution::start).run(); } @Override @@ -476,14 +270,7 @@ public SqlQueryManagerStats getStats() return stats; } - @Managed(description = "Query scheduler executor") - @Nested - public ThreadPoolExecutorMBean getExecutor() - { - return queryExecutorMBean; - } - - @Managed(description = "Query query management executor") + @Managed(description = "Query management executor") @Nested public ThreadPoolExecutorMBean getManagementExecutor() { @@ -532,9 +319,6 @@ private void addStatsListeners(QueryExecution queryExecution) { Object lock = new Object(); - // QUEUED is the initial state, the counter can be incremented immediately - stats.queryQueued(); - AtomicBoolean started = new AtomicBoolean(); queryExecution.addStateChangeListener(newValue -> { synchronized (lock) { @@ -553,27 +337,4 @@ private void addStatsListeners(QueryExecution queryExecution) } }); } - - private static class QueryCreationFuture - extends AbstractFuture - { - @Override - protected boolean set(QueryInfo value) - { - return super.set(value); - } - - @Override - protected boolean setException(Throwable throwable) - { - return super.setException(throwable); - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) - { - // query submission can not be canceled - return false; - } - } } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManagerStats.java b/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManagerStats.java index a8d2ee3a516fa..31edec02c0c27 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManagerStats.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManagerStats.java @@ -16,9 +16,13 @@ import com.facebook.airlift.stats.CounterStat; import com.facebook.airlift.stats.DistributionStat; import com.facebook.airlift.stats.TimeStat; +import com.facebook.presto.server.BasicQueryInfo; +import com.facebook.presto.spi.ErrorCode; +import io.airlift.units.Duration; import org.weakref.jmx.Managed; import org.weakref.jmx.Nested; +import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; import static com.facebook.presto.spi.StandardErrorCode.ABANDONED_QUERY; @@ -67,7 +71,7 @@ public void queryStopped() runningQueries.decrementAndGet(); } - public void queryFinished(QueryInfo info) + public void queryFinished(BasicQueryInfo info) { completedQueries.update(1); @@ -120,6 +124,38 @@ else if (info.getErrorCode().getCode() == USER_CANCELED.toErrorCode().getCode()) } } + public void queuedQueryFailed(Duration queuedTime, Optional errorCode) + { + completedQueries.update(1); + failedQueries.update(1); + + this.queuedTime.add(queuedTime); + + errorCode.ifPresent(error -> { + switch (error.getType()) { + case USER_ERROR: + userErrorFailures.update(1); + break; + case INTERNAL_ERROR: + internalFailures.update(1); + break; + case INSUFFICIENT_RESOURCES: + insufficientResourcesFailures.update(1); + break; + case EXTERNAL: + externalFailures.update(1); + break; + } + + if (error.getCode() == ABANDONED_QUERY.toErrorCode().getCode()) { + abandonedQueries.update(1); + } + else if (error.getCode() == USER_CANCELED.toErrorCode().getCode()) { + canceledQueries.update(1); + } + }); + } + @Managed public long getRunningQueries() { diff --git a/presto-main/src/main/java/com/facebook/presto/execution/resourceGroups/InternalResourceGroup.java b/presto-main/src/main/java/com/facebook/presto/execution/resourceGroups/InternalResourceGroup.java index 2b33104e0e9fa..f598b7c300bbd 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/resourceGroups/InternalResourceGroup.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/resourceGroups/InternalResourceGroup.java @@ -662,7 +662,7 @@ private void startInBackground(ManagedQueryExecution query) group = group.parent.get(); } updateEligibility(); - executor.execute(query::start); + executor.execute(query::startWaitingForResources); } } diff --git a/presto-main/src/main/java/com/facebook/presto/server/BasicQueryInfo.java b/presto-main/src/main/java/com/facebook/presto/server/BasicQueryInfo.java index 0927bb175e117..6ecb19bbb7512 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/BasicQueryInfo.java +++ b/presto-main/src/main/java/com/facebook/presto/server/BasicQueryInfo.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.server; +import com.facebook.presto.Session; import com.facebook.presto.SessionRepresentation; import com.facebook.presto.execution.QueryInfo; import com.facebook.presto.execution.QueryState; @@ -25,6 +26,7 @@ import com.facebook.presto.spi.resourceGroups.ResourceGroupId; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.collect.ImmutableList; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -33,6 +35,9 @@ import java.util.List; import java.util.Optional; +import static com.facebook.presto.execution.QueryState.FAILED; +import static com.facebook.presto.memory.LocalMemoryManager.GENERAL_POOL; +import static com.facebook.presto.server.BasicQueryStats.immediateFailureQueryStats; import static com.google.common.base.MoreObjects.toStringHelper; import static java.util.Objects.requireNonNull; @@ -105,6 +110,24 @@ public BasicQueryInfo(QueryInfo queryInfo) queryInfo.getWarnings()); } + public static BasicQueryInfo immediateFailureQueryInfo(Session session, String query, URI self, Optional resourceGroupId, ErrorCode errorCode) + { + return new BasicQueryInfo( + session.getQueryId(), + session.toSessionRepresentation(), + resourceGroupId, + FAILED, + GENERAL_POOL, + false, + self, + query, + immediateFailureQueryStats(), + errorCode == null ? null : errorCode.getType(), + errorCode, + Optional.empty(), + ImmutableList.of()); + } + @JsonProperty public QueryId getQueryId() { diff --git a/presto-main/src/main/java/com/facebook/presto/server/BasicQueryStats.java b/presto-main/src/main/java/com/facebook/presto/server/BasicQueryStats.java index ed6d17926f5ad..cd5a3cc341ef9 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/BasicQueryStats.java +++ b/presto-main/src/main/java/com/facebook/presto/server/BasicQueryStats.java @@ -28,7 +28,9 @@ import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; +import static io.airlift.units.DataSize.Unit.BYTE; import static java.util.Objects.requireNonNull; +import static java.util.concurrent.TimeUnit.MILLISECONDS; /** * Lightweight version of QueryStats. Parts of the web UI depend on the fields @@ -149,6 +151,33 @@ public BasicQueryStats(QueryStats queryStats) queryStats.getProgressPercentage()); } + public static BasicQueryStats immediateFailureQueryStats() + { + DateTime now = DateTime.now(); + return new BasicQueryStats( + now, + now, + new Duration(0, MILLISECONDS), + new Duration(0, MILLISECONDS), + new Duration(0, MILLISECONDS), + 0, + 0, + 0, + 0, + new DataSize(0, BYTE), + 0, + 0, + new DataSize(0, BYTE), + new DataSize(0, BYTE), + new DataSize(0, BYTE), + new Duration(0, MILLISECONDS), + new Duration(0, MILLISECONDS), + false, + ImmutableSet.of(), + new DataSize(0, BYTE), + OptionalDouble.empty()); + } + @JsonProperty public DateTime getCreateTime() { diff --git a/presto-main/src/main/java/com/facebook/presto/server/CoordinatorModule.java b/presto-main/src/main/java/com/facebook/presto/server/CoordinatorModule.java index 142970bc88a48..aae1b4943bd5e 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/CoordinatorModule.java +++ b/presto-main/src/main/java/com/facebook/presto/server/CoordinatorModule.java @@ -24,6 +24,11 @@ import com.facebook.presto.cost.CostComparator; import com.facebook.presto.cost.StatsCalculatorModule; import com.facebook.presto.cost.TaskCountEstimator; +import com.facebook.presto.dispatcher.DispatchManager; +import com.facebook.presto.dispatcher.DispatchQueryFactory; +import com.facebook.presto.dispatcher.FailedDispatchQueryFactory; +import com.facebook.presto.dispatcher.LocalDispatchQueryFactory; +import com.facebook.presto.dispatcher.QueuedStatementResource; import com.facebook.presto.event.QueryMonitor; import com.facebook.presto.event.QueryMonitorConfig; import com.facebook.presto.execution.AddColumnTask; @@ -90,7 +95,7 @@ import com.facebook.presto.memory.TotalReservationOnBlockedNodesLowMemoryKiller; import com.facebook.presto.metadata.CatalogManager; import com.facebook.presto.operator.ForScheduler; -import com.facebook.presto.server.protocol.StatementResource; +import com.facebook.presto.server.protocol.ExecutingStatementResource; import com.facebook.presto.server.remotetask.HttpRemoteTaskFactory; import com.facebook.presto.server.remotetask.RemoteTaskStats; import com.facebook.presto.spi.memory.ClusterMemoryPoolManager; @@ -192,8 +197,8 @@ protected void setup(Binder binder) jsonCodecBinder(binder).bindJsonCodec(TaskInfo.class); jsonCodecBinder(binder).bindJsonCodec(QueryResults.class); jsonCodecBinder(binder).bindJsonCodec(SelectedRole.class); - jaxrsBinder(binder).bind(StatementResource.class); - newExporter(binder).export(StatementResource.class).withGeneratedName(); + jaxrsBinder(binder).bind(QueuedStatementResource.class); + jaxrsBinder(binder).bind(ExecutingStatementResource.class); binder.bind(StatementHttpExecutionMBean.class).in(Scopes.SINGLETON); newExporter(binder).export(StatementHttpExecutionMBean.class).withGeneratedName(); @@ -222,6 +227,9 @@ protected void setup(Binder binder) binder.bind(InternalResourceGroupManager.class).in(Scopes.SINGLETON); newExporter(binder).export(InternalResourceGroupManager.class).withGeneratedName(); binder.bind(ResourceGroupManager.class).to(InternalResourceGroupManager.class); + binder.bind(DispatchManager.class).in(Scopes.SINGLETON); + binder.bind(FailedDispatchQueryFactory.class).in(Scopes.SINGLETON); + binder.bind(DispatchQueryFactory.class).to(LocalDispatchQueryFactory.class); binder.bind(LegacyResourceGroupConfigurationManager.class).in(Scopes.SINGLETON); newExporter(binder).export(QueryManager.class).withGeneratedName(); diff --git a/presto-main/src/main/java/com/facebook/presto/server/QueryResource.java b/presto-main/src/main/java/com/facebook/presto/server/QueryResource.java index adc0ff78f2969..b3f56789657bc 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/QueryResource.java +++ b/presto-main/src/main/java/com/facebook/presto/server/QueryResource.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.server; +import com.facebook.presto.dispatcher.DispatchManager; import com.facebook.presto.execution.QueryInfo; import com.facebook.presto.execution.QueryManager; import com.facebook.presto.execution.QueryState; @@ -45,11 +46,14 @@ @Path("/v1/query") public class QueryResource { + // TODO There should be a combined interface for this + private final DispatchManager dispatchManager; private final QueryManager queryManager; @Inject - public QueryResource(QueryManager queryManager) + public QueryResource(DispatchManager dispatchManager, QueryManager queryManager) { + this.dispatchManager = requireNonNull(dispatchManager, "dispatchManager is null"); this.queryManager = requireNonNull(queryManager, "queryManager is null"); } @@ -58,7 +62,7 @@ public List getAllQueryInfo(@QueryParam("state") String stateFil { QueryState expectedState = stateFilter == null ? null : QueryState.valueOf(stateFilter.toUpperCase(Locale.ENGLISH)); ImmutableList.Builder builder = new ImmutableList.Builder<>(); - for (BasicQueryInfo queryInfo : queryManager.getQueries()) { + for (BasicQueryInfo queryInfo : dispatchManager.getQueries()) { if (stateFilter == null || queryInfo.getState() == expectedState) { builder.add(queryInfo); } diff --git a/presto-main/src/main/java/com/facebook/presto/server/QueryStateInfoResource.java b/presto-main/src/main/java/com/facebook/presto/server/QueryStateInfoResource.java index 1f38c8d932b62..38e21b8fe0af8 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/QueryStateInfoResource.java +++ b/presto-main/src/main/java/com/facebook/presto/server/QueryStateInfoResource.java @@ -13,7 +13,7 @@ */ package com.facebook.presto.server; -import com.facebook.presto.execution.QueryManager; +import com.facebook.presto.dispatcher.DispatchManager; import com.facebook.presto.execution.resourceGroups.ResourceGroupManager; import com.facebook.presto.spi.QueryId; import com.facebook.presto.spi.resourceGroups.ResourceGroupId; @@ -43,15 +43,15 @@ @Path("/v1/queryState") public class QueryStateInfoResource { - private final QueryManager queryManager; + private final DispatchManager dispatchManager; private final ResourceGroupManager resourceGroupManager; @Inject public QueryStateInfoResource( - QueryManager queryManager, + DispatchManager dispatchManager, ResourceGroupManager resourceGroupManager) { - this.queryManager = requireNonNull(queryManager, "queryManager is null"); + this.dispatchManager = requireNonNull(dispatchManager, "dispatchManager is null"); this.resourceGroupManager = requireNonNull(resourceGroupManager, "resourceGroupManager is null"); } @@ -59,7 +59,7 @@ public QueryStateInfoResource( @Produces(MediaType.APPLICATION_JSON) public List getQueryStateInfos(@QueryParam("user") String user) { - List queryInfos = queryManager.getQueries(); + List queryInfos = dispatchManager.getQueries(); if (!isNullOrEmpty(user)) { queryInfos = queryInfos.stream() @@ -92,7 +92,7 @@ public QueryStateInfo getQueryStateInfo(@PathParam("queryId") String queryId) throws WebApplicationException { try { - return getQueryStateInfo(queryManager.getQueryInfo(new QueryId(queryId))); + return getQueryStateInfo(dispatchManager.getQueryInfo(new QueryId(queryId))); } catch (NoSuchElementException e) { throw new WebApplicationException(NOT_FOUND); diff --git a/presto-main/src/main/java/com/facebook/presto/server/protocol/StatementResource.java b/presto-main/src/main/java/com/facebook/presto/server/protocol/ExecutingStatementResource.java similarity index 68% rename from presto-main/src/main/java/com/facebook/presto/server/protocol/StatementResource.java rename to presto-main/src/main/java/com/facebook/presto/server/protocol/ExecutingStatementResource.java index 9ef4fd145ee1b..8616cf4a9e771 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/protocol/StatementResource.java +++ b/presto-main/src/main/java/com/facebook/presto/server/protocol/ExecutingStatementResource.java @@ -14,16 +14,13 @@ package com.facebook.presto.server.protocol; import com.facebook.airlift.concurrent.BoundedExecutor; -import com.facebook.airlift.stats.CounterStat; +import com.facebook.presto.Session; import com.facebook.presto.client.QueryResults; import com.facebook.presto.execution.QueryManager; import com.facebook.presto.memory.context.SimpleLocalMemoryContext; -import com.facebook.presto.metadata.SessionPropertyManager; import com.facebook.presto.operator.ExchangeClient; import com.facebook.presto.operator.ExchangeClientSupplier; import com.facebook.presto.server.ForStatementResource; -import com.facebook.presto.server.HttpRequestSessionContext; -import com.facebook.presto.server.SessionContext; import com.facebook.presto.spi.QueryId; import com.facebook.presto.spi.block.BlockEncodingSerde; import com.google.common.collect.Ordering; @@ -31,17 +28,11 @@ import com.google.common.util.concurrent.ListenableFuture; import io.airlift.units.DataSize; import io.airlift.units.Duration; -import org.weakref.jmx.Managed; -import org.weakref.jmx.Nested; -import javax.annotation.Nullable; -import javax.annotation.PreDestroy; import javax.inject.Inject; -import javax.servlet.http.HttpServletRequest; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; -import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; @@ -59,12 +50,11 @@ import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Map.Entry; -import java.util.OptionalLong; +import java.util.NoSuchElementException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ScheduledExecutorService; -import static com.facebook.airlift.concurrent.Threads.threadsNamed; import static com.facebook.airlift.http.server.AsyncResponseHandler.bindAsyncResponse; import static com.facebook.presto.client.PrestoHeaders.PRESTO_ADDED_PREPARE; import static com.facebook.presto.client.PrestoHeaders.PRESTO_CLEAR_SESSION; @@ -81,12 +71,11 @@ import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static io.airlift.units.DataSize.Unit.MEGABYTE; import static java.util.Objects.requireNonNull; -import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; -import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; +import static javax.ws.rs.core.MediaType.TEXT_PLAIN_TYPE; -@Path("/v1/statement") -public class StatementResource +@Path("/") +public class ExecutingStatementResource { private static final Duration MAX_WAIT_TIME = new Duration(1, SECONDS); private static final Ordering> WAIT_ORDERING = Ordering.natural().nullsLast(); @@ -95,83 +84,30 @@ public class StatementResource private static final DataSize MAX_TARGET_RESULT_SIZE = new DataSize(128, MEGABYTE); private final QueryManager queryManager; - private final SessionPropertyManager sessionPropertyManager; private final ExchangeClientSupplier exchangeClientSupplier; private final BlockEncodingSerde blockEncodingSerde; private final BoundedExecutor responseExecutor; private final ScheduledExecutorService timeoutExecutor; private final ConcurrentMap queries = new ConcurrentHashMap<>(); - private final ScheduledExecutorService queryPurger = newSingleThreadScheduledExecutor(threadsNamed("query-purger")); - - private final CounterStat createQueryRequests = new CounterStat(); @Inject - public StatementResource( + public ExecutingStatementResource( QueryManager queryManager, - SessionPropertyManager sessionPropertyManager, ExchangeClientSupplier exchangeClientSupplier, BlockEncodingSerde blockEncodingSerde, @ForStatementResource BoundedExecutor responseExecutor, @ForStatementResource ScheduledExecutorService timeoutExecutor) { this.queryManager = requireNonNull(queryManager, "queryManager is null"); - this.sessionPropertyManager = requireNonNull(sessionPropertyManager, "sessionPropertyManager is null"); this.exchangeClientSupplier = requireNonNull(exchangeClientSupplier, "exchangeClientSupplier is null"); this.blockEncodingSerde = requireNonNull(blockEncodingSerde, "blockEncodingSerde is null"); this.responseExecutor = requireNonNull(responseExecutor, "responseExecutor is null"); this.timeoutExecutor = requireNonNull(timeoutExecutor, "timeoutExecutor is null"); - - queryPurger.scheduleWithFixedDelay(new PurgeQueriesRunnable(queries, queryManager), 200, 200, MILLISECONDS); - } - - @PreDestroy - public void stop() - { - queryPurger.shutdownNow(); - } - - @POST - @Produces(MediaType.APPLICATION_JSON) - public Response createQuery( - String statement, - @HeaderParam(X_FORWARDED_PROTO) String proto, - @Context HttpServletRequest servletRequest, - @Context UriInfo uriInfo) - { - createQueryRequests.update(1); - - if (isNullOrEmpty(statement)) { - throw new WebApplicationException(Response - .status(Status.BAD_REQUEST) - .type(MediaType.TEXT_PLAIN) - .entity("SQL statement is empty") - .build()); - } - if (isNullOrEmpty(proto)) { - proto = uriInfo.getRequestUri().getScheme(); - } - - SessionContext sessionContext = new HttpRequestSessionContext(servletRequest); - - ExchangeClient exchangeClient = exchangeClientSupplier.get(new SimpleLocalMemoryContext(newSimpleAggregatedMemoryContext(), StatementResource.class.getSimpleName())); - Query query = Query.create( - sessionContext, - statement, - queryManager, - sessionPropertyManager, - exchangeClient, - responseExecutor, - timeoutExecutor, - blockEncodingSerde); - queries.put(query.getQueryId(), query); - - QueryResults queryResults = query.getNextResult(OptionalLong.empty(), uriInfo, proto, DEFAULT_TARGET_RESULT_SIZE); - return toResponse(query, queryResults); } @GET - @Path("{queryId}/{token}") + @Path("/v1/statement/executing/{queryId}/{token}") @Produces(MediaType.APPLICATION_JSON) public void getQueryResults( @PathParam("queryId") QueryId queryId, @@ -184,30 +120,52 @@ public void getQueryResults( @Suspended AsyncResponse asyncResponse) { Query query = getQuery(queryId, slug); - if (query == null) { - asyncResponse.resume(Response.status(Status.NOT_FOUND).build()); - return; - } if (isNullOrEmpty(proto)) { proto = uriInfo.getRequestUri().getScheme(); } - asyncQueryResults(query, OptionalLong.of(token), maxWait, targetResultSize, uriInfo, proto, asyncResponse); + asyncQueryResults(query, token, maxWait, targetResultSize, uriInfo, proto, asyncResponse); } - @Nullable - private Query getQuery(QueryId queryId, String slug) + protected Query getQuery(QueryId queryId, String slug) { Query query = queries.get(queryId); - if (query != null && query.isSlugValid(slug)) { + if (query != null) { + if (!query.isSlugValid(slug)) { + throw notFound("Query not found"); + } return query; } - return null; + + // this is the first time the query has been accessed on this coordinator + Session session; + try { + if (!queryManager.isQuerySlugValid(queryId, slug)) { + throw notFound("Query not found"); + } + session = queryManager.getQuerySession(queryId); + } + catch (NoSuchElementException e) { + throw notFound("Query not found"); + } + + query = queries.computeIfAbsent(queryId, id -> { + ExchangeClient exchangeClient = exchangeClientSupplier.get(new SimpleLocalMemoryContext(newSimpleAggregatedMemoryContext(), ExecutingStatementResource.class.getSimpleName())); + return Query.create( + session, + slug, + queryManager, + exchangeClient, + responseExecutor, + timeoutExecutor, + blockEncodingSerde); + }); + return query; } private void asyncQueryResults( Query query, - OptionalLong token, + long token, Duration maxWait, DataSize targetResultSize, UriInfo uriInfo, @@ -237,16 +195,16 @@ private static Response toResponse(Query query, QueryResults queryResults) query.getSetSchema().ifPresent(schema -> response.header(PRESTO_SET_SCHEMA, schema)); // add set session properties - query.getSetSessionProperties().entrySet() - .forEach(entry -> response.header(PRESTO_SET_SESSION, entry.getKey() + '=' + entry.getValue())); + query.getSetSessionProperties() + .forEach((key, value) -> response.header(PRESTO_SET_SESSION, key + '=' + urlEncode(value))); // add clear session properties query.getResetSessionProperties() .forEach(name -> response.header(PRESTO_CLEAR_SESSION, name)); // add set roles - query.getSetRoles().entrySet() - .forEach(entry -> response.header(PRESTO_SET_ROLE, entry.getKey() + '=' + urlEncode(entry.getValue().toString()))); + query.getSetRoles() + .forEach((key, value) -> response.header(PRESTO_SET_ROLE, key + '=' + urlEncode(value.toString()))); // add added prepare statements for (Entry entry : query.getAddedPreparedStatements().entrySet()) { @@ -273,19 +231,42 @@ private static Response toResponse(Query query, QueryResults queryResults) } @DELETE - @Path("{queryId}/{token}") + @Path("/v1/statement/executing/{queryId}/{token}") @Produces(MediaType.APPLICATION_JSON) public Response cancelQuery( @PathParam("queryId") QueryId queryId, @PathParam("token") long token, @QueryParam("slug") String slug) { - Query query = getQuery(queryId, slug); - if (query == null) { - return Response.status(Status.NOT_FOUND).build(); + Query query = queries.get(queryId); + if (query != null) { + if (!query.isSlugValid(slug)) { + throw notFound("Query not found"); + } + query.cancel(); + return Response.noContent().build(); + } + + // cancel the query execution directly instead of creating the statement client + try { + if (!queryManager.isQuerySlugValid(queryId, slug)) { + throw notFound("Query not found"); + } + queryManager.cancelQuery(queryId); + return Response.noContent().build(); + } + catch (NoSuchElementException e) { + throw notFound("Query not found"); } - query.cancel(); - return Response.noContent().build(); + } + + private static WebApplicationException notFound(String message) + { + throw new WebApplicationException( + Response.status(Status.NOT_FOUND) + .type(TEXT_PLAIN_TYPE) + .entity(message) + .build()); } private static String urlEncode(String value) @@ -297,11 +278,4 @@ private static String urlEncode(String value) throw new AssertionError(e); } } - - @Managed - @Nested - public CounterStat getCreateQueryRequests() - { - return createQueryRequests; - } } diff --git a/presto-main/src/main/java/com/facebook/presto/server/protocol/PurgeQueriesRunnable.java b/presto-main/src/main/java/com/facebook/presto/server/protocol/PurgeQueriesRunnable.java deleted file mode 100644 index 5ae544198bdf9..0000000000000 --- a/presto-main/src/main/java/com/facebook/presto/server/protocol/PurgeQueriesRunnable.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.server.protocol; - -import com.facebook.airlift.log.Logger; -import com.facebook.presto.execution.QueryManager; -import com.facebook.presto.execution.QueryState; -import com.facebook.presto.spi.QueryId; -import com.google.common.collect.ImmutableSet; - -import java.util.NoSuchElementException; -import java.util.concurrent.ConcurrentMap; - -class PurgeQueriesRunnable - implements Runnable -{ - private static final Logger log = Logger.get(PurgeQueriesRunnable.class); - - private final ConcurrentMap queries; - private final QueryManager queryManager; - - public PurgeQueriesRunnable(ConcurrentMap queries, QueryManager queryManager) - { - this.queries = queries; - this.queryManager = queryManager; - } - - @Override - public void run() - { - try { - // Queries are added to the query manager before being recorded in queryIds set. - // Therefore, we take a snapshot if queryIds before getting the live queries - // from the query manager. Then we remove only the queries in the snapshot and - // not live queries set. If we did this in the other order, a query could be - // registered between fetching the live queries and inspecting the queryIds set. - for (QueryId queryId : ImmutableSet.copyOf(queries.keySet())) { - Query query = queries.get(queryId); - if (!query.isSubmissionFinished()) { - continue; - } - try { - // free up resources if the query completed - if (queryManager.getQueryState(queryId) == QueryState.FAILED) { - query.dispose(); - } - } - catch (NoSuchElementException e) { - // make sure all resource are released - query.dispose(); - // forget about this query if the query manager is no longer tracking it - queries.remove(queryId); - } - } - } - catch (Throwable e) { - log.warn(e, "Error removing old queries"); - } - } -} diff --git a/presto-main/src/main/java/com/facebook/presto/server/protocol/Query.java b/presto-main/src/main/java/com/facebook/presto/server/protocol/Query.java index 63670f5cb6d5c..6b9c4cf3597e4 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/protocol/Query.java +++ b/presto-main/src/main/java/com/facebook/presto/server/protocol/Query.java @@ -33,9 +33,7 @@ import com.facebook.presto.execution.buffer.PagesSerde; import com.facebook.presto.execution.buffer.PagesSerdeFactory; import com.facebook.presto.execution.buffer.SerializedPage; -import com.facebook.presto.metadata.SessionPropertyManager; import com.facebook.presto.operator.ExchangeClient; -import com.facebook.presto.server.SessionContext; import com.facebook.presto.spi.ErrorCode; import com.facebook.presto.spi.Page; import com.facebook.presto.spi.QueryId; @@ -50,8 +48,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; -import com.google.common.util.concurrent.AbstractFuture; -import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import io.airlift.units.DataSize; @@ -70,13 +66,11 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.Optional; -import java.util.OptionalLong; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicLong; -import static com.facebook.airlift.concurrent.MoreFutures.addSuccessCallback; import static com.facebook.airlift.concurrent.MoreFutures.addTimeout; import static com.facebook.presto.SystemSessionProperties.isExchangeCompressionEnabled; import static com.facebook.presto.execution.QueryState.FAILED; @@ -87,9 +81,7 @@ import static com.google.common.util.concurrent.Futures.immediateFuture; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static java.lang.String.format; -import static java.util.Locale.ENGLISH; import static java.util.Objects.requireNonNull; -import static java.util.UUID.randomUUID; @ThreadSafe class Query @@ -98,7 +90,8 @@ class Query private final QueryManager queryManager; private final QueryId queryId; - private final String slug = "x" + randomUUID().toString().toLowerCase(ENGLISH).replace("-", ""); + private final Session session; + private final String slug; @GuardedBy("this") private final ExchangeClient exchangeClient; @@ -106,18 +99,10 @@ class Query private final Executor resultsProcessorExecutor; private final ScheduledExecutorService timeoutExecutor; - @GuardedBy("this") - private PagesSerde serde; + private final PagesSerde serde; private final AtomicLong resultId = new AtomicLong(); - private final QuerySubmissionFuture submissionFuture; - private final SessionPropertyManager sessionPropertyManager; - private final BlockEncodingSerde blockEncodingSerde; - - @GuardedBy("this") - private Session session; - @GuardedBy("this") private QueryResults lastResult; @@ -161,76 +146,60 @@ class Query private Long updateCount; public static Query create( - SessionContext sessionContext, - String query, + Session session, + String slug, QueryManager queryManager, - SessionPropertyManager sessionPropertyManager, ExchangeClient exchangeClient, Executor dataProcessorExecutor, ScheduledExecutorService timeoutExecutor, BlockEncodingSerde blockEncodingSerde) { - Query result = new Query(sessionContext, query, queryManager, sessionPropertyManager, exchangeClient, dataProcessorExecutor, timeoutExecutor, blockEncodingSerde); + Query result = new Query(session, slug, queryManager, exchangeClient, dataProcessorExecutor, timeoutExecutor, blockEncodingSerde); - // register listeners after submission finishes - addSuccessCallback(result.submissionFuture, () -> { - result.queryManager.addOutputInfoListener(result.getQueryId(), result::setQueryOutputInfo); + result.queryManager.addOutputInfoListener(result.getQueryId(), result::setQueryOutputInfo); - result.queryManager.addStateChangeListener(result.getQueryId(), state -> { - if (state.isDone()) { - QueryInfo queryInfo = queryManager.getFullQueryInfo(result.getQueryId()); - result.closeExchangeClientIfNecessary(queryInfo); - } - }); + result.queryManager.addStateChangeListener(result.getQueryId(), state -> { + if (state.isDone()) { + QueryInfo queryInfo = queryManager.getFullQueryInfo(result.getQueryId()); + result.closeExchangeClientIfNecessary(queryInfo); + } }); return result; } private Query( - SessionContext sessionContext, - String query, + Session session, + String slug, QueryManager queryManager, - SessionPropertyManager sessionPropertyManager, ExchangeClient exchangeClient, Executor resultsProcessorExecutor, ScheduledExecutorService timeoutExecutor, BlockEncodingSerde blockEncodingSerde) { - requireNonNull(sessionContext, "sessionContext is null"); - requireNonNull(query, "query is null"); + requireNonNull(session, "session is null"); + requireNonNull(slug, "slug is null"); requireNonNull(queryManager, "queryManager is null"); - requireNonNull(sessionPropertyManager, "sessionPropertyManager is null"); requireNonNull(exchangeClient, "exchangeClient is null"); requireNonNull(resultsProcessorExecutor, "resultsProcessorExecutor is null"); requireNonNull(timeoutExecutor, "timeoutExecutor is null"); requireNonNull(blockEncodingSerde, "serde is null"); this.queryManager = queryManager; - this.sessionPropertyManager = sessionPropertyManager; - queryId = queryManager.createQueryId(); - submissionFuture = new QuerySubmissionFuture(queryId, query, sessionContext, queryManager); + this.queryId = session.getQueryId(); + this.session = session; + this.slug = slug; this.exchangeClient = exchangeClient; this.resultsProcessorExecutor = resultsProcessorExecutor; this.timeoutExecutor = timeoutExecutor; - this.blockEncodingSerde = blockEncodingSerde; - } - public boolean isSubmissionFinished() - { - return submissionFuture.isDone(); + serde = new PagesSerdeFactory(blockEncodingSerde, isExchangeCompressionEnabled(session)).createPagesSerde(); } public void cancel() { - // if submission is not finished, send cancel after it is finished - if (submissionFuture.isDone()) { - submissionFuture.addListener(() -> queryManager.cancelQuery(queryId), resultsProcessorExecutor); - } - else { - queryManager.cancelQuery(queryId); - } + queryManager.cancelQuery(queryId); dispose(); } @@ -294,14 +263,12 @@ public synchronized boolean isClearTransactionId() return clearTransactionId; } - public synchronized ListenableFuture waitForResults(OptionalLong token, UriInfo uriInfo, String scheme, Duration wait, DataSize targetResultSize) + public synchronized ListenableFuture waitForResults(long token, UriInfo uriInfo, String scheme, Duration wait, DataSize targetResultSize) { // before waiting, check if this request has already been processed and cached - if (token.isPresent()) { - Optional cachedResult = getCachedResult(token.getAsLong(), uriInfo); - if (cachedResult.isPresent()) { - return immediateFuture(cachedResult.get()); - } + Optional cachedResult = getCachedResult(token, uriInfo); + if (cachedResult.isPresent()) { + return immediateFuture(cachedResult.get()); } // wait for a results data or query to finish, up to the wait timeout @@ -317,14 +284,6 @@ public synchronized ListenableFuture waitForResults(OptionalLong t private synchronized ListenableFuture getFutureStateChange() { - // ensure the query has been submitted - submissionFuture.submitQuery(); - - // if query query submission has not finished, wait for it to finish - if (!submissionFuture.isDone()) { - return submissionFuture; - } - // if the exchange client is open, wait for data if (!exchangeClient.isClosed()) { return exchangeClient.isBlocked(); @@ -342,13 +301,16 @@ private synchronized ListenableFuture getFutureStateChange() private synchronized Optional getCachedResult(long token, UriInfo uriInfo) { + // is this the first request? + if (lastResult == null) { + return Optional.empty(); + } + // is the a repeated request for the last results? String requestedPath = uriInfo.getAbsolutePath().getPath(); if (requestedPath.equals(lastResultPath)) { - if (submissionFuture.isDone()) { - // tell query manager we are still interested in the query - queryManager.recordHeartbeat(queryId); - } + // tell query manager we are still interested in the query + queryManager.recordHeartbeat(queryId); return Optional.of(lastResult); } @@ -365,14 +327,12 @@ private synchronized Optional getCachedResult(long token, UriInfo return Optional.empty(); } - public synchronized QueryResults getNextResult(OptionalLong token, UriInfo uriInfo, String scheme, DataSize targetResultSize) + private synchronized QueryResults getNextResult(long token, UriInfo uriInfo, String scheme, DataSize targetResultSize) { // check if the result for the token have already been created - if (token.isPresent()) { - Optional cachedResult = getCachedResult(token.getAsLong(), uriInfo); - if (cachedResult.isPresent()) { - return cachedResult.get(); - } + Optional cachedResult = getCachedResult(token, uriInfo); + if (cachedResult.isPresent()) { + return cachedResult.get(); } URI queryHtmlUri = uriInfo.getRequestUriBuilder() @@ -381,34 +341,6 @@ public synchronized QueryResults getNextResult(OptionalLong token, UriInfo uriIn .replaceQuery(queryId.toString()) .build(); - // if query query submission has not finished, return simple empty result - if (!submissionFuture.isDone()) { - QueryResults queryResults = new QueryResults( - queryId.toString(), - queryHtmlUri, - null, - createNextResultsUri(scheme, uriInfo), - null, - null, - StatementStats.builder() - .setState(QueryState.QUEUED.toString()) - .setQueued(true) - .setScheduled(false) - .build(), - null, - ImmutableList.of(), - null, - null); - - cacheLastResults(queryResults); - return queryResults; - } - - if (session == null) { - session = queryManager.getFullQueryInfo(queryId).getSession().toSession(sessionPropertyManager); - serde = new PagesSerdeFactory(blockEncodingSerde, isExchangeCompressionEnabled(session)).createPagesSerde(); - } - // Remove as many pages as possible from the exchange until just greater than DESIRED_RESULT_BYTES // NOTE: it is critical that query results are created for the pages removed from the exchange // client while holding the lock because the query may transition to the finished state when the @@ -570,9 +502,9 @@ private synchronized URI createNextResultsUri(String scheme, UriInfo uriInfo) { return uriInfo.getBaseUriBuilder() .scheme(scheme) - .replacePath("/v1/statement") + .replacePath("/v1/statement/executing") .path(queryId.toString()) - .path(String.valueOf(resultId.incrementAndGet())) + .path(String.valueOf(resultId.getAndIncrement())) .replaceQuery("") .queryParam("slug", slug) .build(); @@ -719,54 +651,4 @@ private static QueryError toQueryError(QueryInfo queryInfo) failure.getErrorLocation(), failure); } - - private static class QuerySubmissionFuture - extends AbstractFuture - { - private final QueryId queryId; - private final String query; - private final SessionContext sessionContext; - private final QueryManager queryManager; - - @GuardedBy("this") - private ListenableFuture querySubmissionFuture; - - public QuerySubmissionFuture(QueryId queryId, String query, SessionContext sessionContext, QueryManager queryManager) - { - this.queryId = requireNonNull(queryId, "queryId is null"); - this.query = requireNonNull(query, "query is null"); - this.sessionContext = requireNonNull(sessionContext, "sessionContext is null"); - this.queryManager = requireNonNull(queryManager, "queryManager is null"); - } - - private synchronized void submitQuery() - { - if (querySubmissionFuture != null) { - return; - } - - querySubmissionFuture = queryManager.createQuery(queryId, sessionContext, this.query); - Futures.addCallback(querySubmissionFuture, new FutureCallback() - { - @Override - public void onSuccess(Object result) - { - set(null); - } - - @Override - public void onFailure(Throwable t) - { - setException(t); - } - }, directExecutor()); - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) - { - // query submission can not be canceled - return false; - } - } } diff --git a/presto-main/src/main/java/com/facebook/presto/server/testing/TestingPrestoServer.java b/presto-main/src/main/java/com/facebook/presto/server/testing/TestingPrestoServer.java index edbac0afb7eba..83bb640be8bee 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/testing/TestingPrestoServer.java +++ b/presto-main/src/main/java/com/facebook/presto/server/testing/TestingPrestoServer.java @@ -33,6 +33,7 @@ import com.facebook.drift.transport.netty.server.DriftNettyServerTransport; import com.facebook.presto.connector.ConnectorManager; import com.facebook.presto.cost.StatsCalculator; +import com.facebook.presto.dispatcher.DispatchManager; import com.facebook.presto.eventlistener.EventListenerManager; import com.facebook.presto.execution.QueryInfo; import com.facebook.presto.execution.QueryManager; @@ -135,7 +136,7 @@ public class TestingPrestoServer private final StatsCalculator statsCalculator; private final TestingAccessControlManager accessControl; private final ProcedureTester procedureTester; - private final Optional resourceGroupManager; + private final Optional> resourceGroupManager; private final SplitManager splitManager; private final PageSourceManager pageSourceManager; private final NodePartitioningManager nodePartitioningManager; @@ -145,6 +146,7 @@ public class TestingPrestoServer private final InternalNodeManager nodeManager; private final ServiceSelectorManager serviceSelectorManager; private final Announcer announcer; + private final DispatchManager dispatchManager; private final SqlQueryManager queryManager; private final TaskManager taskManager; private final GracefulShutdownHandler gracefulShutdownHandler; @@ -293,13 +295,6 @@ public TestingPrestoServer( lifeCycleManager = injector.getInstance(LifeCycleManager.class); - if (coordinator) { - queryManager = (SqlQueryManager) injector.getInstance(QueryManager.class); - } - else { - queryManager = null; - } - pluginManager = injector.getInstance(PluginManager.class); connectorManager = injector.getInstance(ConnectorManager.class); @@ -313,6 +308,8 @@ public TestingPrestoServer( splitManager = injector.getInstance(SplitManager.class); pageSourceManager = injector.getInstance(PageSourceManager.class); if (coordinator) { + dispatchManager = injector.getInstance(DispatchManager.class); + queryManager = (SqlQueryManager) injector.getInstance(QueryManager.class); resourceGroupManager = Optional.of(injector.getInstance(InternalResourceGroupManager.class)); nodePartitioningManager = injector.getInstance(NodePartitioningManager.class); planOptimizerManager = injector.getInstance(ConnectorPlanOptimizerManager.class); @@ -320,6 +317,8 @@ public TestingPrestoServer( statsCalculator = injector.getInstance(StatsCalculator.class); } else { + dispatchManager = null; + queryManager = null; resourceGroupManager = Optional.empty(); nodePartitioningManager = null; planOptimizerManager = null; @@ -370,6 +369,11 @@ public void installPlugin(Plugin plugin) pluginManager.installPlugin(plugin); } + public DispatchManager getDispatchManager() + { + return dispatchManager; + } + public QueryManager getQueryManager() { return queryManager; @@ -464,7 +468,7 @@ public PageSourceManager getPageSourceManager() return pageSourceManager; } - public Optional getResourceGroupManager() + public Optional> getResourceGroupManager() { return resourceGroupManager; } diff --git a/presto-main/src/test/java/com/facebook/presto/execution/MockManagedQueryExecution.java b/presto-main/src/test/java/com/facebook/presto/execution/MockManagedQueryExecution.java new file mode 100644 index 0000000000000..5ac036cab8eb9 --- /dev/null +++ b/presto-main/src/test/java/com/facebook/presto/execution/MockManagedQueryExecution.java @@ -0,0 +1,194 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.execution; + +import com.facebook.presto.Session; +import com.facebook.presto.execution.StateMachine.StateChangeListener; +import com.facebook.presto.server.BasicQueryInfo; +import com.facebook.presto.server.BasicQueryStats; +import com.facebook.presto.spi.ErrorCode; +import com.facebook.presto.spi.QueryId; +import com.facebook.presto.spi.memory.MemoryPoolId; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import io.airlift.units.DataSize; +import io.airlift.units.Duration; +import org.joda.time.DateTime; + +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.OptionalDouble; + +import static com.facebook.presto.SystemSessionProperties.QUERY_PRIORITY; +import static com.facebook.presto.execution.QueryState.FAILED; +import static com.facebook.presto.execution.QueryState.FINISHED; +import static com.facebook.presto.execution.QueryState.QUEUED; +import static com.facebook.presto.execution.QueryState.RUNNING; +import static com.facebook.presto.testing.TestingSession.testSessionBuilder; +import static io.airlift.units.DataSize.Unit.BYTE; +import static io.airlift.units.DataSize.succinctBytes; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.NANOSECONDS; + +public class MockManagedQueryExecution + implements ManagedQueryExecution +{ + private final List> listeners = new ArrayList<>(); + private final DataSize memoryUsage; + private final Duration cpuUsage; + private final Session session; + private QueryState state = QUEUED; + private Throwable failureCause; + + public MockManagedQueryExecution(long memoryUsage) + { + this(memoryUsage, "query_id", 1); + } + + public MockManagedQueryExecution(long memoryUsage, String queryId, int priority) + { + this(memoryUsage, queryId, priority, new Duration(0, MILLISECONDS)); + } + + public MockManagedQueryExecution(long memoryUsage, String queryId, int priority, Duration cpuUsage) + { + this.memoryUsage = succinctBytes(memoryUsage); + this.cpuUsage = cpuUsage; + this.session = testSessionBuilder() + .setSystemProperty(QUERY_PRIORITY, String.valueOf(priority)) + .build(); + } + + public void complete() + { + state = FINISHED; + fireStateChange(); + } + + public Throwable getThrowable() + { + return failureCause; + } + + @Override + public Session getSession() + { + return session; + } + + @Override + public Optional getErrorCode() + { + return Optional.empty(); + } + + @Override + public BasicQueryInfo getBasicQueryInfo() + { + return new BasicQueryInfo( + new QueryId("test"), + session.toSessionRepresentation(), + Optional.empty(), + state, + new MemoryPoolId("test"), + !state.isDone(), + URI.create("http://test"), + "SELECT 1", + new BasicQueryStats( + new DateTime(1), + new DateTime(2), + new Duration(3, NANOSECONDS), + new Duration(4, NANOSECONDS), + new Duration(5, NANOSECONDS), + 6, + 7, + 8, + 9, + new DataSize(14, BYTE), + 15, + 16.0, + new DataSize(17, BYTE), + new DataSize(18, BYTE), + new DataSize(19, BYTE), + new Duration(21, NANOSECONDS), + new Duration(22, NANOSECONDS), + false, + ImmutableSet.of(), + new DataSize(22, BYTE), + OptionalDouble.empty()), + null, + null, + Optional.empty(), + ImmutableList.of()); + } + + @Override + public DataSize getUserMemoryReservation() + { + return memoryUsage; + } + + @Override + public DataSize getTotalMemoryReservation() + { + return memoryUsage; + } + + @Override + public Duration getTotalCpuTime() + { + return cpuUsage; + } + + public QueryState getState() + { + return state; + } + + @Override + public void startWaitingForResources() + { + state = RUNNING; + fireStateChange(); + } + + @Override + public void fail(Throwable cause) + { + state = FAILED; + failureCause = cause; + fireStateChange(); + } + + @Override + public boolean isDone() + { + return getState().isDone(); + } + + @Override + public void addStateChangeListener(StateChangeListener stateChangeListener) + { + listeners.add(stateChangeListener); + } + + private void fireStateChange() + { + for (StateChangeListener listener : listeners) { + listener.stateChanged(state); + } + } +} diff --git a/presto-main/src/test/java/com/facebook/presto/execution/MockQueryExecution.java b/presto-main/src/test/java/com/facebook/presto/execution/MockQueryExecution.java deleted file mode 100644 index 6537503e54383..0000000000000 --- a/presto-main/src/test/java/com/facebook/presto/execution/MockQueryExecution.java +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.facebook.presto.execution; - -import com.facebook.presto.Session; -import com.facebook.presto.execution.StateMachine.StateChangeListener; -import com.facebook.presto.memory.VersionedMemoryPoolId; -import com.facebook.presto.server.BasicQueryInfo; -import com.facebook.presto.spi.ErrorCode; -import com.facebook.presto.spi.QueryId; -import com.facebook.presto.spi.memory.MemoryPoolId; -import com.facebook.presto.spi.resourceGroups.ResourceGroupId; -import com.facebook.presto.sql.planner.Plan; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.util.concurrent.ListenableFuture; -import io.airlift.units.DataSize; -import io.airlift.units.Duration; -import org.joda.time.DateTime; - -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.function.Consumer; - -import static com.facebook.presto.SystemSessionProperties.QUERY_PRIORITY; -import static com.facebook.presto.execution.QueryState.FAILED; -import static com.facebook.presto.execution.QueryState.FINISHED; -import static com.facebook.presto.execution.QueryState.QUEUED; -import static com.facebook.presto.execution.QueryState.RUNNING; -import static com.facebook.presto.testing.TestingSession.testSessionBuilder; -import static com.google.common.util.concurrent.Futures.immediateFuture; -import static io.airlift.units.DataSize.Unit.BYTE; -import static io.airlift.units.DataSize.succinctBytes; -import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static java.util.concurrent.TimeUnit.NANOSECONDS; - -public class MockQueryExecution - implements QueryExecution -{ - private final List> listeners = new ArrayList<>(); - private final DataSize memoryUsage; - private final Duration cpuUsage; - private final Session session; - private final QueryId queryId; - private QueryState state = QUEUED; - private Throwable failureCause; - private Optional resourceGroupId; - - public MockQueryExecution(long memoryUsage) - { - this(memoryUsage, "query_id", 1); - } - - public MockQueryExecution(long memoryUsage, String queryId, int priority) - { - this(memoryUsage, queryId, priority, new Duration(0, MILLISECONDS)); - } - - public MockQueryExecution(long memoryUsage, String queryId, int priority, Duration cpuUsage) - { - this.memoryUsage = succinctBytes(memoryUsage); - this.cpuUsage = cpuUsage; - this.session = testSessionBuilder() - .setSystemProperty(QUERY_PRIORITY, String.valueOf(priority)) - .build(); - this.resourceGroupId = Optional.empty(); - this.queryId = new QueryId(queryId); - } - - public void complete() - { - state = FINISHED; - fireStateChange(); - } - - @Override - public QueryId getQueryId() - { - return queryId; - } - - @Override - public QueryInfo getQueryInfo() - { - return new QueryInfo( - new QueryId("test"), - session.toSessionRepresentation(), - state, - new MemoryPoolId("test"), - !state.isDone(), - URI.create("http://test"), - ImmutableList.of(), - "SELECT 1", - new QueryStats( - new DateTime(1), - new DateTime(2), - new DateTime(3), - new DateTime(4), - new Duration(6, NANOSECONDS), - new Duration(5, NANOSECONDS), - new Duration(31, NANOSECONDS), - new Duration(32, NANOSECONDS), - new Duration(41, NANOSECONDS), - new Duration(7, NANOSECONDS), - new Duration(8, NANOSECONDS), - - new Duration(100, NANOSECONDS), - - 9, - 10, - 11, - 11, - - 12, - 13, - 15, - 30, - 16, - - 17.0, - new DataSize(18, BYTE), - new DataSize(19, BYTE), - new DataSize(20, BYTE), - new DataSize(21, BYTE), - new DataSize(22, BYTE), - new DataSize(23, BYTE), - - true, - new Duration(20, NANOSECONDS), - new Duration(21, NANOSECONDS), - new Duration(0, NANOSECONDS), - new Duration(23, NANOSECONDS), - false, - ImmutableSet.of(), - - new DataSize(123, BYTE), - - new DataSize(24, BYTE), - 25, - - new DataSize(26, BYTE), - 27, - - new DataSize(28, BYTE), - 29, - 30, - new DataSize(31, BYTE), - new DataSize(32, BYTE), - new DataSize(33, BYTE), - ImmutableList.of(), - ImmutableList.of()), - Optional.empty(), - Optional.empty(), - ImmutableMap.of(), - ImmutableSet.of(), - ImmutableMap.of(), - ImmutableMap.of(), - ImmutableSet.of(), - Optional.empty(), - false, - "", - Optional.empty(), - null, - null, - ImmutableList.of(), - ImmutableSet.of(), - Optional.empty(), - state.isDone(), - Optional.empty(), - Optional.empty(), - Optional.empty()); - } - - @Override - public QueryState getState() - { - return state; - } - - @Override - public Plan getQueryPlan() - { - throw new UnsupportedOperationException(); - } - - public Throwable getThrowable() - { - return failureCause; - } - - @Override - public void addOutputInfoListener(Consumer listener) - { - // no-op - } - - @Override - public ListenableFuture getStateChange(QueryState currentState) - { - return immediateFuture(state); - } - - @Override - public VersionedMemoryPoolId getMemoryPool() - { - throw new UnsupportedOperationException(); - } - - @Override - public void setMemoryPool(VersionedMemoryPoolId poolId) - { - throw new UnsupportedOperationException(); - } - - @Override - public Session getSession() - { - return session; - } - - @Override - public DateTime getCreateTime() - { - return getQueryInfo().getQueryStats().getCreateTime(); - } - - @Override - public Optional getExecutionStartTime() - { - return Optional.ofNullable(getQueryInfo().getQueryStats().getExecutionStartTime()); - } - - @Override - public DateTime getLastHeartbeat() - { - return getQueryInfo().getQueryStats().getLastHeartbeat(); - } - - @Override - public Optional getEndTime() - { - return Optional.ofNullable(getQueryInfo().getQueryStats().getEndTime()); - } - - @Override - public Optional getErrorCode() - { - return Optional.ofNullable(getQueryInfo().getFailureInfo()).map(ExecutionFailureInfo::getErrorCode); - } - - @Override - public BasicQueryInfo getBasicQueryInfo() - { - return new BasicQueryInfo(getQueryInfo()); - } - - @Override - public int getRunningTaskCount() - { - return getQueryInfo().getQueryStats().getRunningTasks(); - } - - @Override - public DataSize getUserMemoryReservation() - { - return memoryUsage; - } - - @Override - public DataSize getTotalMemoryReservation() - { - return memoryUsage; - } - - @Override - public Duration getTotalCpuTime() - { - return cpuUsage; - } - - @Override - public void start() - { - state = RUNNING; - fireStateChange(); - } - - @Override - public void fail(Throwable cause) - { - state = FAILED; - failureCause = cause; - fireStateChange(); - } - - @Override - public boolean isDone() - { - return getState().isDone(); - } - - @Override - public void cancelQuery() - { - state = FAILED; - fireStateChange(); - } - - @Override - public void cancelStage(StageId stageId) - { - throw new UnsupportedOperationException(); - } - - @Override - public void recordHeartbeat() - { - } - - @Override - public void pruneInfo() - { - } - - @Override - public void addStateChangeListener(StateChangeListener stateChangeListener) - { - listeners.add(stateChangeListener); - } - - @Override - public void addFinalQueryInfoListener(StateChangeListener stateChangeListener) - { - throw new UnsupportedOperationException(); - } - - private void fireStateChange() - { - for (StateChangeListener listener : listeners) { - listener.stateChanged(state); - } - } -} diff --git a/presto-main/src/test/java/com/facebook/presto/execution/resourceGroups/BenchmarkResourceGroup.java b/presto-main/src/test/java/com/facebook/presto/execution/resourceGroups/BenchmarkResourceGroup.java index e20f540f95ffc..6ed0ae33390e7 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/resourceGroups/BenchmarkResourceGroup.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/resourceGroups/BenchmarkResourceGroup.java @@ -13,7 +13,7 @@ */ package com.facebook.presto.execution.resourceGroups; -import com.facebook.presto.execution.MockQueryExecution; +import com.facebook.presto.execution.MockManagedQueryExecution; import com.facebook.presto.execution.resourceGroups.InternalResourceGroup.RootInternalResourceGroup; import io.airlift.units.DataSize; import org.openjdk.jmh.annotations.Benchmark; @@ -83,7 +83,7 @@ public void setup() group.setHardConcurrencyLimit(queries); } for (int i = 0; i < queries; i++) { - group.run(new MockQueryExecution(10)); + group.run(new MockManagedQueryExecution(10)); } } diff --git a/presto-main/src/test/java/com/facebook/presto/execution/resourceGroups/TestResourceGroups.java b/presto-main/src/test/java/com/facebook/presto/execution/resourceGroups/TestResourceGroups.java index 651399c618252..00283844dbab7 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/resourceGroups/TestResourceGroups.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/resourceGroups/TestResourceGroups.java @@ -13,7 +13,7 @@ */ package com.facebook.presto.execution.resourceGroups; -import com.facebook.presto.execution.MockQueryExecution; +import com.facebook.presto.execution.MockManagedQueryExecution; import com.facebook.presto.execution.resourceGroups.InternalResourceGroup.RootInternalResourceGroup; import com.facebook.presto.server.QueryStateInfo; import com.facebook.presto.server.ResourceGroupInfo; @@ -64,13 +64,13 @@ public void testQueueFull() root.setSoftMemoryLimit(new DataSize(1, MEGABYTE)); root.setMaxQueuedQueries(1); root.setHardConcurrencyLimit(1); - MockQueryExecution query1 = new MockQueryExecution(0); + MockManagedQueryExecution query1 = new MockManagedQueryExecution(0); root.run(query1); assertEquals(query1.getState(), RUNNING); - MockQueryExecution query2 = new MockQueryExecution(0); + MockManagedQueryExecution query2 = new MockManagedQueryExecution(0); root.run(query2); assertEquals(query2.getState(), QUEUED); - MockQueryExecution query3 = new MockQueryExecution(0); + MockManagedQueryExecution query3 = new MockManagedQueryExecution(0); root.run(query3); assertEquals(query3.getState(), FAILED); assertEquals(query3.getThrowable().getMessage(), "Too many queued queries for \"root\""); @@ -95,19 +95,19 @@ public void testFairEligibility() group3.setSoftMemoryLimit(new DataSize(1, MEGABYTE)); group3.setMaxQueuedQueries(4); group3.setHardConcurrencyLimit(1); - MockQueryExecution query1a = new MockQueryExecution(0); + MockManagedQueryExecution query1a = new MockManagedQueryExecution(0); group1.run(query1a); assertEquals(query1a.getState(), RUNNING); - MockQueryExecution query1b = new MockQueryExecution(0); + MockManagedQueryExecution query1b = new MockManagedQueryExecution(0); group1.run(query1b); assertEquals(query1b.getState(), QUEUED); - MockQueryExecution query2a = new MockQueryExecution(0); + MockManagedQueryExecution query2a = new MockManagedQueryExecution(0); group2.run(query2a); assertEquals(query2a.getState(), QUEUED); - MockQueryExecution query2b = new MockQueryExecution(0); + MockManagedQueryExecution query2b = new MockManagedQueryExecution(0); group2.run(query2b); assertEquals(query2b.getState(), QUEUED); - MockQueryExecution query3a = new MockQueryExecution(0); + MockManagedQueryExecution query3a = new MockManagedQueryExecution(0); group3.run(query3a); assertEquals(query3a.getState(), QUEUED); @@ -146,16 +146,16 @@ public void testSetSchedulingPolicy() group2.setSoftMemoryLimit(new DataSize(1, MEGABYTE)); group2.setMaxQueuedQueries(4); group2.setHardConcurrencyLimit(2); - MockQueryExecution query1a = new MockQueryExecution(0); + MockManagedQueryExecution query1a = new MockManagedQueryExecution(0); group1.run(query1a); assertEquals(query1a.getState(), RUNNING); - MockQueryExecution query1b = new MockQueryExecution(0); + MockManagedQueryExecution query1b = new MockManagedQueryExecution(0); group1.run(query1b); assertEquals(query1b.getState(), QUEUED); - MockQueryExecution query1c = new MockQueryExecution(0); + MockManagedQueryExecution query1c = new MockManagedQueryExecution(0); group1.run(query1c); assertEquals(query1c.getState(), QUEUED); - MockQueryExecution query2a = new MockQueryExecution(0); + MockManagedQueryExecution query2a = new MockManagedQueryExecution(0); group2.run(query2a); assertEquals(query2a.getState(), QUEUED); @@ -188,16 +188,16 @@ public void testFairQueuing() group2.setSoftMemoryLimit(new DataSize(1, MEGABYTE)); group2.setMaxQueuedQueries(4); group2.setHardConcurrencyLimit(2); - MockQueryExecution query1a = new MockQueryExecution(0); + MockManagedQueryExecution query1a = new MockManagedQueryExecution(0); group1.run(query1a); assertEquals(query1a.getState(), RUNNING); - MockQueryExecution query1b = new MockQueryExecution(0); + MockManagedQueryExecution query1b = new MockManagedQueryExecution(0); group1.run(query1b); assertEquals(query1b.getState(), QUEUED); - MockQueryExecution query1c = new MockQueryExecution(0); + MockManagedQueryExecution query1c = new MockManagedQueryExecution(0); group1.run(query1c); assertEquals(query1c.getState(), QUEUED); - MockQueryExecution query2a = new MockQueryExecution(0); + MockManagedQueryExecution query2a = new MockManagedQueryExecution(0); group2.run(query2a); assertEquals(query2a.getState(), QUEUED); @@ -222,15 +222,15 @@ public void testMemoryLimit() root.setSoftMemoryLimit(new DataSize(1, BYTE)); root.setMaxQueuedQueries(4); root.setHardConcurrencyLimit(3); - MockQueryExecution query1 = new MockQueryExecution(2); + MockManagedQueryExecution query1 = new MockManagedQueryExecution(2); root.run(query1); // Process the group to refresh stats root.processQueuedQueries(); assertEquals(query1.getState(), RUNNING); - MockQueryExecution query2 = new MockQueryExecution(0); + MockManagedQueryExecution query2 = new MockManagedQueryExecution(0); root.run(query2); assertEquals(query2.getState(), QUEUED); - MockQueryExecution query3 = new MockQueryExecution(0); + MockManagedQueryExecution query3 = new MockManagedQueryExecution(0); root.run(query3); assertEquals(query3.getState(), QUEUED); @@ -252,15 +252,15 @@ public void testSubgroupMemoryLimit() subgroup.setMaxQueuedQueries(4); subgroup.setHardConcurrencyLimit(3); - MockQueryExecution query1 = new MockQueryExecution(2); + MockManagedQueryExecution query1 = new MockManagedQueryExecution(2); subgroup.run(query1); // Process the group to refresh stats root.processQueuedQueries(); assertEquals(query1.getState(), RUNNING); - MockQueryExecution query2 = new MockQueryExecution(0); + MockManagedQueryExecution query2 = new MockManagedQueryExecution(0); subgroup.run(query2); assertEquals(query2.getState(), QUEUED); - MockQueryExecution query3 = new MockQueryExecution(0); + MockManagedQueryExecution query3 = new MockManagedQueryExecution(0); subgroup.run(query3); assertEquals(query3.getState(), QUEUED); @@ -281,15 +281,15 @@ public void testSoftCpuLimit() root.setMaxQueuedQueries(1); root.setHardConcurrencyLimit(2); - MockQueryExecution query1 = new MockQueryExecution(1, "query_id", 1, new Duration(1, SECONDS)); + MockManagedQueryExecution query1 = new MockManagedQueryExecution(1, "query_id", 1, new Duration(1, SECONDS)); root.run(query1); assertEquals(query1.getState(), RUNNING); - MockQueryExecution query2 = new MockQueryExecution(0); + MockManagedQueryExecution query2 = new MockManagedQueryExecution(0); root.run(query2); assertEquals(query2.getState(), RUNNING); - MockQueryExecution query3 = new MockQueryExecution(0); + MockManagedQueryExecution query3 = new MockManagedQueryExecution(0); root.run(query3); assertEquals(query3.getState(), QUEUED); @@ -313,10 +313,10 @@ public void testHardCpuLimit() root.setCpuQuotaGenerationMillisPerSecond(2000); root.setMaxQueuedQueries(1); root.setHardConcurrencyLimit(1); - MockQueryExecution query1 = new MockQueryExecution(1, "query_id", 1, new Duration(2, SECONDS)); + MockManagedQueryExecution query1 = new MockManagedQueryExecution(1, "query_id", 1, new Duration(2, SECONDS)); root.run(query1); assertEquals(query1.getState(), RUNNING); - MockQueryExecution query2 = new MockQueryExecution(0); + MockManagedQueryExecution query2 = new MockManagedQueryExecution(0); root.run(query2); assertEquals(query2.getState(), QUEUED); @@ -347,7 +347,7 @@ public void testPriorityScheduling() group2.setMaxQueuedQueries(100); group2.setHardConcurrencyLimit(1); - SortedMap queries = new TreeMap<>(); + SortedMap queries = new TreeMap<>(); Random random = new Random(); for (int i = 0; i < 100; i++) { @@ -357,7 +357,7 @@ public void testPriorityScheduling() } while (queries.containsKey(priority)); - MockQueryExecution query = new MockQueryExecution(0, "query_id", priority); + MockManagedQueryExecution query = new MockManagedQueryExecution(0, "query_id", priority); if (random.nextBoolean()) { group1.run(query); } @@ -369,10 +369,10 @@ public void testPriorityScheduling() root.setHardConcurrencyLimit(1); - List orderedQueries = new ArrayList<>(queries.values()); + List orderedQueries = new ArrayList<>(queries.values()); reverse(orderedQueries); - for (MockQueryExecution query : orderedQueries) { + for (MockManagedQueryExecution query : orderedQueries) { root.processQueuedQueries(); assertEquals(query.getState(), RUNNING); query.complete(); @@ -400,14 +400,14 @@ public void testWeightedScheduling() group2.setSoftConcurrencyLimit(2); group2.setSchedulingWeight(2); - Set group1Queries = fillGroupTo(group1, ImmutableSet.of(), 2); - Set group2Queries = fillGroupTo(group2, ImmutableSet.of(), 2); + Set group1Queries = fillGroupTo(group1, ImmutableSet.of(), 2); + Set group2Queries = fillGroupTo(group2, ImmutableSet.of(), 2); root.setHardConcurrencyLimit(1); int group2Ran = 0; for (int i = 0; i < 1000; i++) { - for (Iterator iterator = group1Queries.iterator(); iterator.hasNext(); ) { - MockQueryExecution query = iterator.next(); + for (Iterator iterator = group1Queries.iterator(); iterator.hasNext(); ) { + MockManagedQueryExecution query = iterator.next(); if (query.getState() == RUNNING) { query.complete(); iterator.remove(); @@ -452,8 +452,8 @@ public void testWeightedFairScheduling() group2.setSoftConcurrencyLimit(2); group2.setSchedulingWeight(2); - Set group1Queries = fillGroupTo(group1, ImmutableSet.of(), 4); - Set group2Queries = fillGroupTo(group2, ImmutableSet.of(), 4); + Set group1Queries = fillGroupTo(group1, ImmutableSet.of(), 4); + Set group2Queries = fillGroupTo(group2, ImmutableSet.of(), 4); root.setHardConcurrencyLimit(3); int group1Ran = 0; @@ -502,9 +502,9 @@ public void testWeightedFairSchedulingEqualWeights() group3.setSoftConcurrencyLimit(2); group3.setSchedulingWeight(2); - Set group1Queries = fillGroupTo(group1, ImmutableSet.of(), 4); - Set group2Queries = fillGroupTo(group2, ImmutableSet.of(), 4); - Set group3Queries = fillGroupTo(group3, ImmutableSet.of(), 4); + Set group1Queries = fillGroupTo(group1, ImmutableSet.of(), 4); + Set group2Queries = fillGroupTo(group2, ImmutableSet.of(), 4); + Set group3Queries = fillGroupTo(group3, ImmutableSet.of(), 4); root.setHardConcurrencyLimit(4); int group1Ran = 0; @@ -554,8 +554,8 @@ public void testWeightedFairSchedulingNoStarvation() group2.setSoftConcurrencyLimit(2); group2.setSchedulingWeight(2); - Set group1Queries = fillGroupTo(group1, ImmutableSet.of(), 4); - Set group2Queries = fillGroupTo(group2, ImmutableSet.of(), 4); + Set group1Queries = fillGroupTo(group1, ImmutableSet.of(), 4); + Set group2Queries = fillGroupTo(group2, ImmutableSet.of(), 4); root.setHardConcurrencyLimit(1); int group1Ran = 0; @@ -614,7 +614,7 @@ public void testGetInfo() rootBY.setHardConcurrencyLimit(10); // Queue 40 queries (= maxQueuedQueries (40) + maxRunningQueries (0)) - Set queries = fillGroupTo(rootAX, ImmutableSet.of(), 10, false); + Set queries = fillGroupTo(rootAX, ImmutableSet.of(), 10, false); queries.addAll(fillGroupTo(rootAY, ImmutableSet.of(), 10, false)); queries.addAll(fillGroupTo(rootBX, ImmutableSet.of(), 10, true)); queries.addAll(fillGroupTo(rootBY, ImmutableSet.of(), 10, true)); @@ -631,9 +631,9 @@ public void testGetInfo() assertEquals(info.getNumQueuedQueries(), 36); // Complete running queries - Iterator iterator = queries.iterator(); + Iterator iterator = queries.iterator(); while (iterator.hasNext()) { - MockQueryExecution query = iterator.next(); + MockManagedQueryExecution query = iterator.next(); if (query.getState() == RUNNING) { query.complete(); iterator.remove(); @@ -692,7 +692,7 @@ public void testGetResourceGroupStateInfo() rootAY.setMaxQueuedQueries(10); rootAY.setHardConcurrencyLimit(10); - Set queries = fillGroupTo(rootAX, ImmutableSet.of(), 5, false); + Set queries = fillGroupTo(rootAX, ImmutableSet.of(), 5, false); queries.addAll(fillGroupTo(rootAY, ImmutableSet.of(), 5, false)); queries.addAll(fillGroupTo(rootB, ImmutableSet.of(), 10, true)); @@ -769,7 +769,7 @@ public void testGetBlockedQueuedQueries() rootBY.setHardConcurrencyLimit(5); // Queue 40 queries (= maxQueuedQueries (40) + maxRunningQueries (0)) - Set queries = fillGroupTo(rootAX, ImmutableSet.of(), 10, false); + Set queries = fillGroupTo(rootAX, ImmutableSet.of(), 10, false); queries.addAll(fillGroupTo(rootAY, ImmutableSet.of(), 10, false)); queries.addAll(fillGroupTo(rootBX, ImmutableSet.of(), 10, true)); queries.addAll(fillGroupTo(rootBY, ImmutableSet.of(), 10, true)); @@ -793,11 +793,11 @@ public void testGetBlockedQueuedQueries() assertEquals(rootBY.getWaitingQueuedQueries(), 6); } - private static int completeGroupQueries(Set groupQueries) + private static int completeGroupQueries(Set groupQueries) { int groupRan = 0; - for (Iterator iterator = groupQueries.iterator(); iterator.hasNext(); ) { - MockQueryExecution query = iterator.next(); + for (Iterator iterator = groupQueries.iterator(); iterator.hasNext(); ) { + MockManagedQueryExecution query = iterator.next(); if (query.getState() == RUNNING) { query.complete(); iterator.remove(); @@ -807,17 +807,17 @@ private static int completeGroupQueries(Set groupQueries) return groupRan; } - private static Set fillGroupTo(InternalResourceGroup group, Set existingQueries, int count) + private static Set fillGroupTo(InternalResourceGroup group, Set existingQueries, int count) { return fillGroupTo(group, existingQueries, count, false); } - private static Set fillGroupTo(InternalResourceGroup group, Set existingQueries, int count, boolean queryPriority) + private static Set fillGroupTo(InternalResourceGroup group, Set existingQueries, int count, boolean queryPriority) { int existingCount = existingQueries.size(); - Set queries = new HashSet<>(existingQueries); + Set queries = new HashSet<>(existingQueries); for (int i = 0; i < count - existingCount; i++) { - MockQueryExecution query = new MockQueryExecution(0, group.getId().toString().replace(".", "") + Integer.toString(i), queryPriority ? i + 1 : 1); + MockManagedQueryExecution query = new MockManagedQueryExecution(0, group.getId().toString().replace(".", "") + Integer.toString(i), queryPriority ? i + 1 : 1); queries.add(query); group.run(query); } diff --git a/presto-product-tests/src/test/resources/com/facebook/presto/tests/querystats/single_query_info_response.json b/presto-product-tests/src/test/resources/com/facebook/presto/tests/querystats/single_query_info_response.json index 95d55899839a7..782d37c9ab34f 100644 --- a/presto-product-tests/src/test/resources/com/facebook/presto/tests/querystats/single_query_info_response.json +++ b/presto-product-tests/src/test/resources/com/facebook/presto/tests/querystats/single_query_info_response.json @@ -29,6 +29,7 @@ "elapsedTime": "134.00ms", "resourceWaitingTime": "11.00ms", "queuedTime": "2.11ms", + "dispatchingTime": "1111.00ms", "executionTime": "13.00ms", "analysisTime": "7.47ms", "totalPlanningTime": "9.99ms", diff --git a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestDistributedQueries.java b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestDistributedQueries.java index c82f7850aa330..4e0762a7d143b 100644 --- a/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestDistributedQueries.java +++ b/presto-tests/src/main/java/com/facebook/presto/tests/AbstractTestDistributedQueries.java @@ -16,6 +16,7 @@ import com.facebook.airlift.testing.Assertions; import com.facebook.presto.Session; import com.facebook.presto.SystemSessionProperties; +import com.facebook.presto.dispatcher.DispatchManager; import com.facebook.presto.execution.QueryInfo; import com.facebook.presto.execution.QueryManager; import com.facebook.presto.server.BasicQueryInfo; @@ -817,8 +818,9 @@ public void testQueryLoggingCount() // The completed queries counter is updated in a final query info listener, which is called eventually. // Therefore, here we wait until the value of this counter gets stable. - long beforeCompletedQueriesCount = waitUntilStable(() -> queryManager.getStats().getCompletedQueries().getTotalCount(), new Duration(5, SECONDS)); - long beforeSubmittedQueriesCount = queryManager.getStats().getSubmittedQueries().getTotalCount(); + DispatchManager dispatchManager = ((DistributedQueryRunner) getQueryRunner()).getCoordinator().getDispatchManager(); + long beforeCompletedQueriesCount = waitUntilStable(() -> dispatchManager.getStats().getCompletedQueries().getTotalCount(), new Duration(5, SECONDS)); + long beforeSubmittedQueriesCount = dispatchManager.getStats().getSubmittedQueries().getTotalCount(); assertUpdate("CREATE TABLE test_query_logging_count AS SELECT 1 foo_1, 2 foo_2_4", 1); assertQuery("SELECT foo_1, foo_2_4 FROM test_query_logging_count", "SELECT 1, 2"); assertUpdate("DROP TABLE test_query_logging_count"); @@ -826,9 +828,9 @@ public void testQueryLoggingCount() // TODO: Figure out a better way of synchronization assertUntilTimeout( - () -> assertEquals(queryManager.getStats().getCompletedQueries().getTotalCount() - beforeCompletedQueriesCount, 4), + () -> assertEquals(dispatchManager.getStats().getCompletedQueries().getTotalCount() - beforeCompletedQueriesCount, 4), new Duration(1, MINUTES)); - assertEquals(queryManager.getStats().getSubmittedQueries().getTotalCount() - beforeSubmittedQueriesCount, 4); + assertEquals(dispatchManager.getStats().getSubmittedQueries().getTotalCount() - beforeSubmittedQueriesCount, 4); }); } @@ -1016,7 +1018,7 @@ public void testWrittenStats() String sql = "CREATE TABLE test_written_stats AS SELECT * FROM nation"; DistributedQueryRunner distributedQueryRunner = (DistributedQueryRunner) getQueryRunner(); ResultWithQueryId resultResultWithQueryId = distributedQueryRunner.executeWithQueryId(getSession(), sql); - QueryInfo queryInfo = distributedQueryRunner.getQueryInfo(resultResultWithQueryId.getQueryId()); + QueryInfo queryInfo = distributedQueryRunner.getCoordinator().getQueryManager().getFullQueryInfo(resultResultWithQueryId.getQueryId()); assertEquals(queryInfo.getQueryStats().getOutputPositions(), 1L); assertEquals(queryInfo.getQueryStats().getWrittenOutputPositions(), 25L); @@ -1024,7 +1026,7 @@ public void testWrittenStats() sql = "INSERT INTO test_written_stats SELECT * FROM nation LIMIT 10"; resultResultWithQueryId = distributedQueryRunner.executeWithQueryId(getSession(), sql); - queryInfo = distributedQueryRunner.getQueryInfo(resultResultWithQueryId.getQueryId()); + queryInfo = distributedQueryRunner.getCoordinator().getQueryManager().getFullQueryInfo(resultResultWithQueryId.getQueryId()); assertEquals(queryInfo.getQueryStats().getOutputPositions(), 1L); assertEquals(queryInfo.getQueryStats().getWrittenOutputPositions(), 10L); diff --git a/presto-tests/src/test/java/com/facebook/presto/execution/TestEventListener.java b/presto-tests/src/test/java/com/facebook/presto/execution/TestEventListener.java index 4b761b34cb09a..7fff08236239e 100644 --- a/presto-tests/src/test/java/com/facebook/presto/execution/TestEventListener.java +++ b/presto-tests/src/test/java/com/facebook/presto/execution/TestEventListener.java @@ -178,7 +178,7 @@ public void testOutputStats() MaterializedResult result = runQueryAndWaitForEvents("SELECT 1 FROM lineitem", expectedEvents); QueryCreatedEvent queryCreatedEvent = getOnlyElement(generatedEvents.getQueryCreatedEvents()); QueryCompletedEvent queryCompletedEvent = getOnlyElement(generatedEvents.getQueryCompletedEvents()); - QueryStats queryStats = queryRunner.getQueryInfo(new QueryId(queryCreatedEvent.getMetadata().getQueryId())).getQueryStats(); + QueryStats queryStats = queryRunner.getCoordinator().getQueryManager().getFullQueryInfo(new QueryId(queryCreatedEvent.getMetadata().getQueryId())).getQueryStats(); assertTrue(queryStats.getOutputDataSize().toBytes() > 0L); assertTrue(queryCompletedEvent.getStatistics().getOutputBytes() > 0L); @@ -188,7 +188,7 @@ public void testOutputStats() runQueryAndWaitForEvents("SELECT COUNT(1) FROM lineitem", expectedEvents); queryCreatedEvent = getOnlyElement(generatedEvents.getQueryCreatedEvents()); queryCompletedEvent = getOnlyElement(generatedEvents.getQueryCompletedEvents()); - queryStats = queryRunner.getQueryInfo(new QueryId(queryCreatedEvent.getMetadata().getQueryId())).getQueryStats(); + queryStats = queryRunner.getCoordinator().getQueryManager().getFullQueryInfo(new QueryId(queryCreatedEvent.getMetadata().getQueryId())).getQueryStats(); assertTrue(queryStats.getOutputDataSize().toBytes() > 0L); assertTrue(queryCompletedEvent.getStatistics().getOutputBytes() > 0L); diff --git a/presto-tests/src/test/java/com/facebook/presto/execution/TestQueryRunnerUtil.java b/presto-tests/src/test/java/com/facebook/presto/execution/TestQueryRunnerUtil.java index 516c3cb086fec..2dcc82a1cb2bf 100644 --- a/presto-tests/src/test/java/com/facebook/presto/execution/TestQueryRunnerUtil.java +++ b/presto-tests/src/test/java/com/facebook/presto/execution/TestQueryRunnerUtil.java @@ -14,6 +14,7 @@ package com.facebook.presto.execution; import com.facebook.presto.Session; +import com.facebook.presto.dispatcher.DispatchManager; import com.facebook.presto.server.BasicQueryInfo; import com.facebook.presto.spi.QueryId; import com.facebook.presto.tests.DistributedQueryRunner; @@ -35,14 +36,14 @@ private TestQueryRunnerUtil() {} public static QueryId createQuery(DistributedQueryRunner queryRunner, Session session, String sql) { - QueryManager queryManager = queryRunner.getCoordinator().getQueryManager(); - getFutureValue(queryManager.createQuery(session.getQueryId(), new TestingSessionContext(session), sql)); + DispatchManager dispatchManager = queryRunner.getCoordinator().getDispatchManager(); + getFutureValue(dispatchManager.createQuery(session.getQueryId(), "slug", new TestingSessionContext(session), sql)); return session.getQueryId(); } public static void cancelQuery(DistributedQueryRunner queryRunner, QueryId queryId) { - queryRunner.getCoordinator().getQueryManager().cancelQuery(queryId); + queryRunner.getCoordinator().getDispatchManager().cancelQuery(queryId); } public static void waitForQueryState(DistributedQueryRunner queryRunner, QueryId queryId, QueryState expectedQueryState) @@ -54,17 +55,17 @@ public static void waitForQueryState(DistributedQueryRunner queryRunner, QueryId public static void waitForQueryState(DistributedQueryRunner queryRunner, QueryId queryId, Set expectedQueryStates) throws InterruptedException { - QueryManager queryManager = queryRunner.getCoordinator().getQueryManager(); + DispatchManager dispatchManager = queryRunner.getCoordinator().getDispatchManager(); do { // Heartbeat all the running queries, so they don't die while we're waiting - for (BasicQueryInfo queryInfo : queryManager.getQueries()) { + for (BasicQueryInfo queryInfo : dispatchManager.getQueries()) { if (queryInfo.getState() == RUNNING) { - queryManager.recordHeartbeat(queryInfo.getQueryId()); + dispatchManager.getQueryInfo(queryInfo.getQueryId()); } } MILLISECONDS.sleep(500); } - while (!expectedQueryStates.contains(queryManager.getQueryState(queryId))); + while (!expectedQueryStates.contains(dispatchManager.getQueryInfo(queryId).getState())); } public static DistributedQueryRunner createQueryRunner() diff --git a/presto-tests/src/test/java/com/facebook/presto/execution/TestQueues.java b/presto-tests/src/test/java/com/facebook/presto/execution/TestQueues.java index 82e2dbc0a8f6b..df2a16559d3d3 100644 --- a/presto-tests/src/test/java/com/facebook/presto/execution/TestQueues.java +++ b/presto-tests/src/test/java/com/facebook/presto/execution/TestQueues.java @@ -14,6 +14,7 @@ package com.facebook.presto.execution; import com.facebook.presto.Session; +import com.facebook.presto.dispatcher.DispatchManager; import com.facebook.presto.resourceGroups.ResourceGroupManagerPlugin; import com.facebook.presto.spi.QueryId; import com.facebook.presto.spi.resourceGroups.ResourceGroupId; @@ -309,8 +310,8 @@ private void testRejection() QueryId queryId = createQuery(queryRunner, newRejectionSession(), LONG_LASTING_QUERY); waitForQueryState(queryRunner, queryId, FAILED); - QueryManager queryManager = queryRunner.getCoordinator().getQueryManager(); - assertEquals(queryManager.getQueryInfo(queryId).getErrorCode(), QUERY_REJECTED.toErrorCode()); + DispatchManager dispatchManager = queryRunner.getCoordinator().getDispatchManager(); + assertEquals(dispatchManager.getQueryInfo(queryId).getErrorCode(), QUERY_REJECTED.toErrorCode()); } } diff --git a/presto-tests/src/test/java/com/facebook/presto/execution/resourceGroups/db/TestQueuesDb.java b/presto-tests/src/test/java/com/facebook/presto/execution/resourceGroups/db/TestQueuesDb.java index 3b5417bcd8312..0207dc42bccc6 100644 --- a/presto-tests/src/test/java/com/facebook/presto/execution/resourceGroups/db/TestQueuesDb.java +++ b/presto-tests/src/test/java/com/facebook/presto/execution/resourceGroups/db/TestQueuesDb.java @@ -14,10 +14,12 @@ package com.facebook.presto.execution.resourceGroups.db; import com.facebook.presto.Session; +import com.facebook.presto.dispatcher.DispatchManager; import com.facebook.presto.execution.QueryManager; import com.facebook.presto.execution.resourceGroups.InternalResourceGroupManager; import com.facebook.presto.resourceGroups.db.DbResourceGroupConfigurationManager; import com.facebook.presto.resourceGroups.db.H2ResourceGroupsDao; +import com.facebook.presto.server.BasicQueryInfo; import com.facebook.presto.server.ResourceGroupInfo; import com.facebook.presto.spi.QueryId; import com.facebook.presto.spi.resourceGroups.ResourceGroupId; @@ -55,6 +57,7 @@ import static com.facebook.presto.execution.resourceGroups.db.H2TestUtil.waitForRunningQueryCount; import static com.facebook.presto.spi.StandardErrorCode.EXCEEDED_TIME_LIMIT; import static com.facebook.presto.spi.StandardErrorCode.INVALID_RESOURCE_GROUP; +import static com.facebook.presto.spi.StandardErrorCode.QUERY_QUEUE_FULL; import static com.facebook.presto.spi.StandardErrorCode.QUERY_REJECTED; import static com.facebook.presto.testing.TestingSession.testSessionBuilder; import static java.lang.String.format; @@ -197,8 +200,8 @@ public void testRejection() // Verify the query cannot be submitted QueryId queryId = createQuery(queryRunner, rejectingSession(), LONG_LASTING_QUERY); waitForQueryState(queryRunner, queryId, FAILED); - QueryManager queryManager = queryRunner.getCoordinator().getQueryManager(); - assertEquals(queryManager.getQueryInfo(queryId).getErrorCode(), QUERY_REJECTED.toErrorCode()); + DispatchManager dispatchManager = queryRunner.getCoordinator().getDispatchManager(); + assertEquals(dispatchManager.getQueryInfo(queryId).getErrorCode(), QUERY_REJECTED.toErrorCode()); int selectorCount = getSelectors(queryRunner).size(); dao.insertSelector(4, 100_000, "user.*", "(?i).*reject.*", null, null, null); dbConfigurationManager.load(); @@ -250,9 +253,9 @@ public void testSelectorPriority() QueryId secondQuery = createQuery(queryRunner, dashboardSession(), LONG_LASTING_QUERY); waitForQueryState(queryRunner, secondQuery, FAILED); - resourceGroup = queryManager.getFullQueryInfo(secondQuery).getResourceGroupId(); - assertTrue(resourceGroup.isPresent()); - assertEquals(resourceGroup.get(), createResourceGroupId("global", "user-user", "reject-all-queries")); + DispatchManager dispatchManager = queryRunner.getCoordinator().getDispatchManager(); + BasicQueryInfo basicQueryInfo = dispatchManager.getQueryInfo(secondQuery); + assertEquals(basicQueryInfo.getErrorCode(), QUERY_QUEUE_FULL.toErrorCode()); } @Test(timeOut = 60_000) @@ -290,12 +293,13 @@ public void testQueryExecutionTimeLimit() waitForQueryState(queryRunner, secondQuery, QUEUED); // after a 5s wait this query should still be QUEUED, not FAILED as the max execution time should be enforced after the query starts running Thread.sleep(5_000); - assertEquals(queryManager.getQueryState(secondQuery), QUEUED); + DispatchManager dispatchManager = queryRunner.getCoordinator().getDispatchManager(); + assertEquals(dispatchManager.getQueryInfo(secondQuery).getState(), QUEUED); // reconfigure the resource group to run the second query dao.updateResourceGroup(5, "dashboard-${USER}", "1MB", 1, null, 1, null, null, null, null, null, 3L, TEST_ENVIRONMENT); dbConfigurationManager.load(); // cancel the first one and let the second one start - queryManager.cancelQuery(firstQuery); + dispatchManager.cancelQuery(firstQuery); // wait until the second one is FAILED waitForQueryState(queryRunner, secondQuery, FAILED); } @@ -350,7 +354,7 @@ public void testNonLeafGroup() // Submit a query to a non-leaf resource group QueryId invalidResourceGroupQuery = createQuery(queryRunner, session, LONG_LASTING_QUERY); waitForQueryState(queryRunner, invalidResourceGroupQuery, FAILED); - assertEquals(queryRunner.getQueryInfo(invalidResourceGroupQuery).getErrorCode(), INVALID_RESOURCE_GROUP.toErrorCode()); + assertEquals(queryRunner.getCoordinator().getDispatchManager().getQueryInfo(invalidResourceGroupQuery).getErrorCode(), INVALID_RESOURCE_GROUP.toErrorCode()); } private void assertResourceGroupWithClientTags(Set clientTags, ResourceGroupId expectedResourceGroup) diff --git a/presto-tests/src/test/java/com/facebook/presto/tests/TestMetadataManager.java b/presto-tests/src/test/java/com/facebook/presto/tests/TestMetadataManager.java index e823b7e9ba1e0..5f35cdcf83bfa 100644 --- a/presto-tests/src/test/java/com/facebook/presto/tests/TestMetadataManager.java +++ b/presto-tests/src/test/java/com/facebook/presto/tests/TestMetadataManager.java @@ -14,10 +14,10 @@ package com.facebook.presto.tests; import com.facebook.presto.connector.MockConnectorFactory; -import com.facebook.presto.execution.QueryInfo; -import com.facebook.presto.execution.QueryManager; +import com.facebook.presto.dispatcher.DispatchManager; import com.facebook.presto.execution.TestingSessionContext; import com.facebook.presto.metadata.MetadataManager; +import com.facebook.presto.server.BasicQueryInfo; import com.facebook.presto.spi.Plugin; import com.facebook.presto.spi.QueryId; import com.facebook.presto.spi.connector.ConnectorFactory; @@ -113,20 +113,21 @@ public void testMetadataIsClearedAfterQueryFailed() public void testMetadataIsClearedAfterQueryCanceled() throws Exception { - QueryManager queryManager = queryRunner.getCoordinator().getQueryManager(); - QueryId queryId = queryManager.createQueryId(); - queryManager.createQuery( + DispatchManager dispatchManager = queryRunner.getCoordinator().getDispatchManager(); + QueryId queryId = dispatchManager.createQueryId(); + dispatchManager.createQuery( queryId, + "slug", new TestingSessionContext(TEST_SESSION), "SELECT * FROM lineitem") .get(); // wait until query starts running while (true) { - QueryInfo queryInfo = queryManager.getFullQueryInfo(queryId); + BasicQueryInfo queryInfo = dispatchManager.getQueryInfo(queryId); if (queryInfo.getState().isDone()) { assertEquals(queryInfo.getState(), FAILED); - throw queryInfo.getFailureInfo().toException(); + throw dispatchManager.getDispatchInfo(queryId).get().getFailureInfo().get().toException(); } if (queryInfo.getState() == RUNNING) { break; @@ -135,7 +136,7 @@ public void testMetadataIsClearedAfterQueryCanceled() } // cancel query - queryManager.cancelQuery(queryId); + dispatchManager.cancelQuery(queryId); assertEquals(metadataManager.getCatalogsByQueryId().size(), 0); } diff --git a/presto-tests/src/test/java/com/facebook/presto/tests/TestQueryManager.java b/presto-tests/src/test/java/com/facebook/presto/tests/TestQueryManager.java index f4f6d82e1d771..fdc6230997696 100644 --- a/presto-tests/src/test/java/com/facebook/presto/tests/TestQueryManager.java +++ b/presto-tests/src/test/java/com/facebook/presto/tests/TestQueryManager.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.tests; +import com.facebook.presto.dispatcher.DispatchManager; import com.facebook.presto.execution.QueryInfo; import com.facebook.presto.execution.QueryManager; import com.facebook.presto.execution.QueryState; @@ -60,17 +61,18 @@ public void tearDown() public void testFailQuery() throws Exception { - QueryManager queryManager = queryRunner.getCoordinator().getQueryManager(); - QueryId queryId = queryManager.createQueryId(); - queryManager.createQuery( + DispatchManager dispatchManager = queryRunner.getCoordinator().getDispatchManager(); + QueryId queryId = dispatchManager.createQueryId(); + dispatchManager.createQuery( queryId, + "slug", new TestingSessionContext(TEST_SESSION), "SELECT * FROM lineitem") .get(); // wait until query starts running while (true) { - QueryState state = queryManager.getQueryState(queryId); + QueryState state = dispatchManager.getQueryInfo(queryId).getState(); if (state.isDone()) { fail("unexpected query state: " + state); } @@ -81,6 +83,7 @@ public void testFailQuery() } // cancel query + QueryManager queryManager = queryRunner.getCoordinator().getQueryManager(); queryManager.failQuery(queryId, new PrestoException(GENERIC_INTERNAL_ERROR, "mock exception")); QueryInfo queryInfo = queryManager.getFullQueryInfo(queryId); assertEquals(queryInfo.getState(), FAILED); From 1aedb21091ae1b0550b02de4966ab6413eab9fbb Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Thu, 13 Feb 2020 23:27:57 -0800 Subject: [PATCH 05/18] Add peak tasks to BasicQueryStats --- .../facebook/presto/execution/QueryStateMachine.java | 2 ++ .../com/facebook/presto/server/BasicQueryStats.java | 12 ++++++++++++ .../presto/execution/MockManagedQueryExecution.java | 1 + .../presto/memory/TestClusterMemoryLeakDetector.java | 1 + 4 files changed, 16 insertions(+) diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryStateMachine.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryStateMachine.java index 44ad78a399b9a..22e2f8e2eac00 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/QueryStateMachine.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryStateMachine.java @@ -345,6 +345,8 @@ public BasicQueryInfo getBasicQueryInfo(Optional rootS queryStateTimer.getElapsedTime(), queryStateTimer.getExecutionTime(), + getPeakRunningTaskCount(), + stageStats.getTotalDrivers(), stageStats.getQueuedDrivers(), stageStats.getRunningDrivers(), diff --git a/presto-main/src/main/java/com/facebook/presto/server/BasicQueryStats.java b/presto-main/src/main/java/com/facebook/presto/server/BasicQueryStats.java index cd5a3cc341ef9..f9ead79c5d18d 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/BasicQueryStats.java +++ b/presto-main/src/main/java/com/facebook/presto/server/BasicQueryStats.java @@ -46,6 +46,8 @@ public class BasicQueryStats private final Duration elapsedTime; private final Duration executionTime; + private final int peakRunningTasks; + private final int totalDrivers; private final int queuedDrivers; private final int runningDrivers; @@ -75,6 +77,7 @@ public BasicQueryStats( @JsonProperty("queuedTime") Duration queuedTime, @JsonProperty("elapsedTime") Duration elapsedTime, @JsonProperty("executionTime") Duration executionTime, + @JsonProperty("peakRunningTasks") int peakRunningTasks, @JsonProperty("totalDrivers") int totalDrivers, @JsonProperty("queuedDrivers") int queuedDrivers, @JsonProperty("runningDrivers") int runningDrivers, @@ -99,6 +102,8 @@ public BasicQueryStats( this.elapsedTime = requireNonNull(elapsedTime, "elapsedTime is null"); this.executionTime = requireNonNull(executionTime, "executionTime is null"); + this.peakRunningTasks = peakRunningTasks; + checkArgument(totalDrivers >= 0, "totalDrivers is negative"); this.totalDrivers = totalDrivers; checkArgument(queuedDrivers >= 0, "queuedDrivers is negative"); @@ -133,6 +138,7 @@ public BasicQueryStats(QueryStats queryStats) queryStats.getQueuedTime(), queryStats.getElapsedTime(), queryStats.getExecutionTime(), + queryStats.getPeakRunningTasks(), queryStats.getTotalDrivers(), queryStats.getQueuedDrivers(), queryStats.getRunningDrivers(), @@ -164,6 +170,7 @@ public static BasicQueryStats immediateFailureQueryStats() 0, 0, 0, + 0, new DataSize(0, BYTE), 0, 0, @@ -262,6 +269,11 @@ public DataSize getTotalMemoryReservation() return totalMemoryReservation; } + public int getPeakRunningTasks() + { + return peakRunningTasks; + } + @JsonProperty public DataSize getPeakUserMemoryReservation() { diff --git a/presto-main/src/test/java/com/facebook/presto/execution/MockManagedQueryExecution.java b/presto-main/src/test/java/com/facebook/presto/execution/MockManagedQueryExecution.java index 5ac036cab8eb9..053c574da1312 100644 --- a/presto-main/src/test/java/com/facebook/presto/execution/MockManagedQueryExecution.java +++ b/presto-main/src/test/java/com/facebook/presto/execution/MockManagedQueryExecution.java @@ -113,6 +113,7 @@ public BasicQueryInfo getBasicQueryInfo() new Duration(3, NANOSECONDS), new Duration(4, NANOSECONDS), new Duration(5, NANOSECONDS), + 5, 6, 7, 8, diff --git a/presto-tests/src/test/java/com/facebook/presto/memory/TestClusterMemoryLeakDetector.java b/presto-tests/src/test/java/com/facebook/presto/memory/TestClusterMemoryLeakDetector.java index f2139ba1dab63..3bb72732c1131 100644 --- a/presto-tests/src/test/java/com/facebook/presto/memory/TestClusterMemoryLeakDetector.java +++ b/presto-tests/src/test/java/com/facebook/presto/memory/TestClusterMemoryLeakDetector.java @@ -83,6 +83,7 @@ private static BasicQueryInfo createQueryInfo(String queryId, QueryState state) Duration.valueOf("8m"), Duration.valueOf("7m"), Duration.valueOf("34m"), + 12, 13, 14, 15, From 75102974fe2436e112c9ac3a47553d55eb4e197e Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Sun, 12 May 2019 14:20:40 -0700 Subject: [PATCH 06/18] Add LocalCoordinatorLocation Co-authored-by: Dain Sundstrom Co-authored-by: Raghav Sethi --- .../dispatcher/CoordinatorLocation.java | 39 ++----------------- .../dispatcher/LocalCoordinatorLocation.java | 35 +++++++++++++++++ .../presto/dispatcher/LocalDispatchQuery.java | 6 +-- .../dispatcher/LocalDispatchQueryFactory.java | 7 ---- .../dispatcher/QueuedStatementResource.java | 2 +- 5 files changed, 41 insertions(+), 48 deletions(-) create mode 100644 presto-main/src/main/java/com/facebook/presto/dispatcher/LocalCoordinatorLocation.java diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/CoordinatorLocation.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/CoordinatorLocation.java index 45d1dd65b491f..83c606210ce46 100644 --- a/presto-main/src/main/java/com/facebook/presto/dispatcher/CoordinatorLocation.java +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/CoordinatorLocation.java @@ -13,42 +13,11 @@ */ package com.facebook.presto.dispatcher; -import java.net.URI; -import java.util.Optional; +import javax.ws.rs.core.UriInfo; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; -import static java.util.Objects.requireNonNull; +import java.net.URI; -public class CoordinatorLocation +public interface CoordinatorLocation { - private final Optional httpUri; - private final Optional httpsUri; - - public CoordinatorLocation(Optional httpUri, Optional httpsUri) - { - this.httpUri = requireNonNull(httpUri, "httpUri is null"); - this.httpsUri = requireNonNull(httpsUri, "httpsUri is null"); - checkArgument(httpUri.isPresent() || httpsUri.isPresent(), "Coordinator must have a HTTP or HTTPS port"); - } - - public URI getUri(String preferredScheme) - { - if ("https".equalsIgnoreCase(preferredScheme)) { - return httpsUri.orElseGet(httpUri::get); - } - else { - return httpUri.orElseGet(httpsUri::get); - } - } - - @Override - public String toString() - { - return toStringHelper(this) - .omitNullValues() - .add("httpUri", httpUri.orElse(null)) - .add("httpsUri", httpsUri.orElse(null)) - .toString(); - } + URI getUri(UriInfo uriInfo, String xForwardedProto); } diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalCoordinatorLocation.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalCoordinatorLocation.java new file mode 100644 index 0000000000000..badc3c34bd41f --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalCoordinatorLocation.java @@ -0,0 +1,35 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.dispatcher; + +import javax.ws.rs.core.UriInfo; + +import java.net.URI; + +import static com.google.common.base.Strings.isNullOrEmpty; + +public class LocalCoordinatorLocation + implements CoordinatorLocation +{ + @Override + public URI getUri(UriInfo uriInfo, String xForwardedProto) + { + String scheme = isNullOrEmpty(xForwardedProto) ? uriInfo.getRequestUri().getScheme() : xForwardedProto; + return uriInfo.getRequestUriBuilder() + .scheme(scheme) + .replacePath("") + .replaceQuery("") + .build(); + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java index 847078a63a777..a2ba32abe5e36 100644 --- a/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java @@ -51,8 +51,6 @@ public class LocalDispatchQuery private final QueryStateMachine stateMachine; private final ListenableFuture queryExecutionFuture; - private final CoordinatorLocation coordinatorLocation; - private final ClusterSizeMonitor clusterSizeMonitor; private final ExecutorService queryExecutor; @@ -62,14 +60,12 @@ public class LocalDispatchQuery public LocalDispatchQuery( QueryStateMachine stateMachine, ListenableFuture queryExecutionFuture, - CoordinatorLocation coordinatorLocation, ClusterSizeMonitor clusterSizeMonitor, ExecutorService queryExecutor, Function> querySubmitter) { this.stateMachine = requireNonNull(stateMachine, "stateMachine is null"); this.queryExecutionFuture = requireNonNull(queryExecutionFuture, "queryExecutionFuture is null"); - this.coordinatorLocation = requireNonNull(coordinatorLocation, "coordinatorLocation is null"); this.clusterSizeMonitor = requireNonNull(clusterSizeMonitor, "clusterSizeMonitor is null"); this.queryExecutor = requireNonNull(queryExecutor, "queryExecutor is null"); this.querySubmitter = requireNonNull(querySubmitter, "querySubmitter is null"); @@ -134,7 +130,7 @@ public DispatchInfo getDispatchInfo() BasicQueryInfo queryInfo = stateMachine.getBasicQueryInfo(Optional.empty()); Optional coordinator = Optional.empty(); if (queryInfo.getState().ordinal() >= PLANNING.ordinal()) { - coordinator = Optional.of(coordinatorLocation); + coordinator = Optional.of(new LocalCoordinatorLocation()); } Optional failureInfo = Optional.empty(); if (queryInfo.getState() == FAILED) { diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQueryFactory.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQueryFactory.java index 1d82b2d800871..69dde3f64fb08 100644 --- a/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQueryFactory.java +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQueryFactory.java @@ -24,10 +24,8 @@ import com.facebook.presto.execution.QueryPreparer.PreparedQuery; import com.facebook.presto.execution.QueryStateMachine; import com.facebook.presto.execution.warnings.WarningCollector; -import com.facebook.presto.metadata.InternalNodeManager; import com.facebook.presto.metadata.Metadata; import com.facebook.presto.security.AccessControl; -import com.facebook.presto.spi.Node; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.resourceGroups.QueryType; import com.facebook.presto.spi.resourceGroups.ResourceGroupId; @@ -57,7 +55,6 @@ public class LocalDispatchQueryFactory private final QueryMonitor queryMonitor; private final LocationFactory locationFactory; - private final CoordinatorLocation coordinatorLocation; private final ClusterSizeMonitor clusterSizeMonitor; private final Map, QueryExecutionFactory> executionFactories; @@ -72,7 +69,6 @@ public LocalDispatchQueryFactory( QueryMonitor queryMonitor, LocationFactory locationFactory, Map, QueryExecutionFactory> executionFactories, - InternalNodeManager internalNodeManager, ClusterSizeMonitor clusterSizeMonitor, @ForQueryExecution ExecutorService executorService) @@ -85,8 +81,6 @@ public LocalDispatchQueryFactory( this.locationFactory = requireNonNull(locationFactory, "locationFactory is null"); this.executionFactories = requireNonNull(executionFactories, "executionFactories is null"); - Node currentNode = requireNonNull(internalNodeManager, "internalNodeManager is null").getCurrentNode(); - this.coordinatorLocation = new CoordinatorLocation(Optional.of(currentNode.getHttpUri()), Optional.of(currentNode.getHttpUri())); this.clusterSizeMonitor = requireNonNull(clusterSizeMonitor, "clusterSizeMonitor is null"); this.executorService = listeningDecorator(requireNonNull(executorService, "executorService is null")); @@ -130,7 +124,6 @@ public DispatchQuery createDispatchQuery( return new LocalDispatchQuery( stateMachine, queryExecutionFuture, - coordinatorLocation, clusterSizeMonitor, queryExecutor, queryExecution -> executorService.submit(() -> queryManager.createQuery(queryExecution))); diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/QueuedStatementResource.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/QueuedStatementResource.java index bf90dfd3ad76f..5f5d4f885734c 100644 --- a/presto-main/src/main/java/com/facebook/presto/dispatcher/QueuedStatementResource.java +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/QueuedStatementResource.java @@ -403,7 +403,7 @@ private URI getNextUri(long token, UriInfo uriInfo, String xForwardedProto, Disp private URI getRedirectUri(CoordinatorLocation coordinatorLocation, UriInfo uriInfo, String xForwardedProto) { - URI coordinatorUri = coordinatorLocation.getUri(getScheme(xForwardedProto, uriInfo)); + URI coordinatorUri = coordinatorLocation.getUri(uriInfo, xForwardedProto); return uriBuilderFrom(coordinatorUri) .appendPath("/v1/statement/executing") .appendPath(queryId.toString()) From 6329d906a15a43e531c992deaf978e99f74facb8 Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Mon, 3 Dec 2018 13:37:43 -0800 Subject: [PATCH 07/18] Improve query event stats for immediately failed queries Co-authored-by: Dain Sundstrom Co-authored-by: Raghav Sethi --- .../src/main/java/com/facebook/presto/event/QueryMonitor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/presto-main/src/main/java/com/facebook/presto/event/QueryMonitor.java b/presto-main/src/main/java/com/facebook/presto/event/QueryMonitor.java index f7f3a2b6c1137..7429b9414163b 100644 --- a/presto-main/src/main/java/com/facebook/presto/event/QueryMonitor.java +++ b/presto-main/src/main/java/com/facebook/presto/event/QueryMonitor.java @@ -151,7 +151,7 @@ public void queryImmediateFailureEvent(BasicQueryInfo queryInfo, ExecutionFailur ofMillis(0), ofMillis(0), ofMillis(0), - ofMillis(0), + ofMillis(queryInfo.getQueryStats().getQueuedTime().toMillis()), Optional.empty(), 0, 0, @@ -179,7 +179,7 @@ public void queryImmediateFailureEvent(BasicQueryInfo queryInfo, ExecutionFailur queryInfo.getQueryType(), ImmutableList.of(), ofEpochMilli(queryInfo.getQueryStats().getCreateTime().getMillis()), - ofEpochMilli(queryInfo.getQueryStats().getCreateTime().getMillis()), + ofEpochMilli(queryInfo.getQueryStats().getEndTime().getMillis()), ofEpochMilli(queryInfo.getQueryStats().getEndTime().getMillis()))); logQueryTimeline(queryInfo); From d641e7bddeab1f66bd3fc475777bc85c0e763ae7 Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Mon, 3 Dec 2018 13:38:04 -0800 Subject: [PATCH 08/18] Remove Optional from QueryStateMachine resourceGroup resourceGroup is already required in QueryStateMachine Co-authored-by: Dain Sundstrom Co-authored-by: Raghav Sethi --- .../facebook/presto/execution/QueryStateMachine.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryStateMachine.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryStateMachine.java index 22e2f8e2eac00..0671f43d8f6ef 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/QueryStateMachine.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryStateMachine.java @@ -104,7 +104,7 @@ public class QueryStateMachine private final Session session; private final URI self; private final Optional queryType; - private final Optional resourceGroup; + private final ResourceGroupId resourceGroup; private final TransactionManager transactionManager; private final Metadata metadata; private final QueryOutputManager outputManager; @@ -156,7 +156,7 @@ private QueryStateMachine( String query, Session session, URI self, - Optional resourceGroup, + ResourceGroupId resourceGroup, Optional queryType, TransactionManager transactionManager, Executor executor, @@ -236,7 +236,7 @@ static QueryStateMachine beginWithTicker( query, session, self, - Optional.of(resourceGroup), + resourceGroup, queryType, transactionManager, executor, @@ -373,7 +373,7 @@ public BasicQueryInfo getBasicQueryInfo(Optional rootS return new BasicQueryInfo( queryId, session.toSessionRepresentation(), - resourceGroup, + Optional.of(resourceGroup), state, memoryPool.get().getId(), stageStats.isScheduled(), @@ -448,7 +448,7 @@ QueryInfo getQueryInfo(Optional rootStage) inputs.get(), output.get(), completeInfo, - resourceGroup, + Optional.of(resourceGroup), queryType, failedTasks); } From 1c6a43f2b7d375a982b6c317a8c38e9f49a3543a Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Sat, 9 Mar 2019 16:01:01 -0800 Subject: [PATCH 09/18] Simplify DispatchInfo construction Co-authored-by: Dain Sundstrom Co-authored-by: Raghav Sethi --- .../presto/dispatcher/DispatchInfo.java | 19 ++++++++++++++++++- .../dispatcher/FailedDispatchQuery.java | 5 ++--- .../presto/dispatcher/LocalDispatchQuery.java | 17 ++++++++++------- .../dispatcher/QueuedStatementResource.java | 2 +- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchInfo.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchInfo.java index 6b15369673fd3..4700765798ad8 100644 --- a/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchInfo.java +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchInfo.java @@ -27,7 +27,24 @@ public class DispatchInfo private final Duration elapsedTime; private final Duration queuedTime; - public DispatchInfo(Optional coordinatorLocation, Optional failureInfo, Duration elapsedTime, Duration queuedTime) + public static DispatchInfo queued(Duration elapsedTime, Duration queuedTime) + { + return new DispatchInfo(Optional.empty(), Optional.empty(), elapsedTime, queuedTime); + } + + public static DispatchInfo dispatched(CoordinatorLocation coordinatorLocation, Duration elapsedTime, Duration queuedTime) + { + requireNonNull(coordinatorLocation, "coordinatorLocation is null"); + return new DispatchInfo(Optional.of(coordinatorLocation), Optional.empty(), elapsedTime, queuedTime); + } + + public static DispatchInfo failed(ExecutionFailureInfo failureInfo, Duration elapsedTime, Duration queuedTime) + { + requireNonNull(failureInfo, "coordinatorLocation is null"); + return new DispatchInfo(Optional.empty(), Optional.of(failureInfo), elapsedTime, queuedTime); + } + + private DispatchInfo(Optional coordinatorLocation, Optional failureInfo, Duration elapsedTime, Duration queuedTime) { this.coordinatorLocation = requireNonNull(coordinatorLocation, "coordinatorLocation is null"); this.failureInfo = requireNonNull(failureInfo, "failureInfo is null"); diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/FailedDispatchQuery.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/FailedDispatchQuery.java index 0016e619500f2..321c7b52f9816 100644 --- a/presto-main/src/main/java/com/facebook/presto/dispatcher/FailedDispatchQuery.java +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/FailedDispatchQuery.java @@ -64,9 +64,8 @@ public FailedDispatchQuery( this.session = requireNonNull(session, "session is null"); this.executor = requireNonNull(executor, "executor is null"); - this.dispatchInfo = new DispatchInfo( - Optional.empty(), - Optional.of(failure), + this.dispatchInfo = DispatchInfo.failed( + failure, queryInfo.getQueryStats().getElapsedTime(), queryInfo.getQueryStats().getQueuedTime()); } diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java index a2ba32abe5e36..6adc993793c91 100644 --- a/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java @@ -23,6 +23,7 @@ import com.facebook.presto.execution.StateMachine.StateChangeListener; import com.facebook.presto.server.BasicQueryInfo; import com.facebook.presto.spi.ErrorCode; +import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.QueryId; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -39,6 +40,8 @@ import static com.facebook.airlift.concurrent.MoreFutures.tryGetFutureValue; import static com.facebook.presto.execution.QueryState.FAILED; import static com.facebook.presto.execution.QueryState.PLANNING; +import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR; +import static com.facebook.presto.util.Failures.toFailure; import static com.google.common.util.concurrent.Futures.immediateFuture; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static io.airlift.units.DataSize.Unit.BYTE; @@ -128,15 +131,15 @@ private ListenableFuture queryDispatchFuture(QueryState currentState) public DispatchInfo getDispatchInfo() { BasicQueryInfo queryInfo = stateMachine.getBasicQueryInfo(Optional.empty()); - Optional coordinator = Optional.empty(); - if (queryInfo.getState().ordinal() >= PLANNING.ordinal()) { - coordinator = Optional.of(new LocalCoordinatorLocation()); - } - Optional failureInfo = Optional.empty(); if (queryInfo.getState() == FAILED) { - failureInfo = stateMachine.getFailureInfo(); + ExecutionFailureInfo failureInfo = stateMachine.getFailureInfo() + .orElseGet(() -> toFailure(new PrestoException(GENERIC_INTERNAL_ERROR, "Query failed for an unknown reason"))); + return DispatchInfo.failed(failureInfo, queryInfo.getQueryStats().getElapsedTime(), queryInfo.getQueryStats().getQueuedTime()); + } + if (queryInfo.getState().ordinal() >= PLANNING.ordinal()) { + return DispatchInfo.dispatched(new LocalCoordinatorLocation(), queryInfo.getQueryStats().getElapsedTime(), queryInfo.getQueryStats().getQueuedTime()); } - return new DispatchInfo(coordinator, failureInfo, queryInfo.getQueryStats().getElapsedTime(), queryInfo.getQueryStats().getQueuedTime()); + return DispatchInfo.queued(queryInfo.getQueryStats().getElapsedTime(), queryInfo.getQueryStats().getQueuedTime()); } @Override diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/QueuedStatementResource.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/QueuedStatementResource.java index 5f5d4f885734c..646f22381421e 100644 --- a/presto-main/src/main/java/com/facebook/presto/dispatcher/QueuedStatementResource.java +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/QueuedStatementResource.java @@ -352,7 +352,7 @@ public QueryResults getQueryResults(long token, UriInfo uriInfo, String xForward token + 1, uriInfo, xForwardedProto, - new DispatchInfo(Optional.empty(), Optional.empty(), NO_DURATION, NO_DURATION)); + DispatchInfo.queued(NO_DURATION, NO_DURATION)); } } From 848f23abe953f358ebee2c15e71a1963c19985b3 Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Sat, 9 Mar 2019 20:25:35 -0800 Subject: [PATCH 10/18] Rename SqlQueryManagerStats to QueryManagerStats Co-authored-by: Dain Sundstrom Co-authored-by: Raghav Sethi --- .../com/facebook/presto/dispatcher/DispatchManager.java | 6 +++--- .../java/com/facebook/presto/execution/QueryManager.java | 2 +- .../{SqlQueryManagerStats.java => QueryManagerStats.java} | 2 +- .../java/com/facebook/presto/execution/SqlQueryManager.java | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) rename presto-main/src/main/java/com/facebook/presto/execution/{SqlQueryManagerStats.java => QueryManagerStats.java} (99%) diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchManager.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchManager.java index 613ac9204d80f..79482b6c63469 100644 --- a/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchManager.java +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchManager.java @@ -17,10 +17,10 @@ import com.facebook.presto.execution.QueryIdGenerator; import com.facebook.presto.execution.QueryInfo; import com.facebook.presto.execution.QueryManagerConfig; +import com.facebook.presto.execution.QueryManagerStats; import com.facebook.presto.execution.QueryPreparer; import com.facebook.presto.execution.QueryPreparer.PreparedQuery; import com.facebook.presto.execution.QueryTracker; -import com.facebook.presto.execution.SqlQueryManagerStats; import com.facebook.presto.execution.resourceGroups.ResourceGroupManager; import com.facebook.presto.execution.warnings.WarningCollector; import com.facebook.presto.execution.warnings.WarningCollectorFactory; @@ -84,7 +84,7 @@ public class DispatchManager private final QueryTracker queryTracker; - private final SqlQueryManagerStats stats = new SqlQueryManagerStats(); + private final QueryManagerStats stats = new QueryManagerStats(); @Inject public DispatchManager( @@ -129,7 +129,7 @@ public ThreadPoolExecutorMBean getExecutor() @Managed @Flatten - public SqlQueryManagerStats getStats() + public QueryManagerStats getStats() { return stats; } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryManager.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryManager.java index e08705f678d25..efcb622ab2bc3 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/QueryManager.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryManager.java @@ -110,5 +110,5 @@ QueryState getQueryState(QueryId queryId) */ void cancelStage(StageId stageId); - SqlQueryManagerStats getStats(); + QueryManagerStats getStats(); } diff --git a/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManagerStats.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryManagerStats.java similarity index 99% rename from presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManagerStats.java rename to presto-main/src/main/java/com/facebook/presto/execution/QueryManagerStats.java index 31edec02c0c27..27b088b895a11 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManagerStats.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryManagerStats.java @@ -30,7 +30,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; -public class SqlQueryManagerStats +public class QueryManagerStats { private final AtomicInteger queuedQueries = new AtomicInteger(); private final AtomicInteger runningQueries = new AtomicInteger(); diff --git a/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java b/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java index 6929cce310396..df8965339601d 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java @@ -74,7 +74,7 @@ public class SqlQueryManager private final ScheduledExecutorService queryManagementExecutor; private final ThreadPoolExecutorMBean queryManagementExecutorMBean; - private final SqlQueryManagerStats stats = new SqlQueryManagerStats(); + private final QueryManagerStats stats = new QueryManagerStats(); @Inject public SqlQueryManager(ClusterMemoryManager memoryManager, QueryMonitor queryMonitor, EmbedVersion embedVersion, QueryManagerConfig queryManagerConfig, WarningCollectorFactory warningCollectorFactory) @@ -265,7 +265,7 @@ public void cancelStage(StageId stageId) @Override @Managed @Flatten - public SqlQueryManagerStats getStats() + public QueryManagerStats getStats() { return stats; } From a4d2f174b5314b58c0307a9924f41c928fdd4a0e Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Sat, 9 Mar 2019 19:45:20 -0800 Subject: [PATCH 11/18] Simplify query manager stats tracking Co-authored-by: Dain Sundstrom Co-authored-by: Raghav Sethi --- .../presto/dispatcher/DispatchManager.java | 34 ++----- .../presto/execution/QueryManagerStats.java | 89 ++++++++++++------- .../presto/execution/SqlQueryManager.java | 27 +----- 3 files changed, 66 insertions(+), 84 deletions(-) diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchManager.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchManager.java index 79482b6c63469..ac6710d87d6bb 100644 --- a/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchManager.java +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchManager.java @@ -217,23 +217,12 @@ private void createQueryInternal(QueryId queryId, String slug, SessionContex Optional.ofNullable(selectionContext).map(SelectionContext::getResourceGroupId), e); - try { - queryCreated(failedDispatchQuery); - } - finally { - handleQueryFailure(failedDispatchQuery); - } + queryCreated(failedDispatchQuery); return; } try { queryCreated(dispatchQuery); - dispatchQuery.addStateChangeListener(newState -> { - if (newState.isDone()) { - stats.queryFinished(dispatchQuery.getBasicQueryInfo()); - } - }); - resourceGroupManager.submit(preparedQuery.getStatement(), dispatchQuery, selectionContext, queryManagementExecutor); } catch (RuntimeException e) { @@ -244,21 +233,12 @@ private void createQueryInternal(QueryId queryId, String slug, SessionContex private void queryCreated(DispatchQuery dispatchQuery) { queryTracker.addQuery(dispatchQuery); - stats.queryQueued(); - } - - private void handleQueryFailure(DispatchQuery dispatchQuery) - { - try { - stats.queryStarted(); - stats.queryStopped(); - BasicQueryInfo queryInfo = dispatchQuery.getBasicQueryInfo(); - stats.queuedQueryFailed(queryInfo.getQueryStats().getQueuedTime(), Optional.ofNullable(queryInfo.getErrorCode())); - } - finally { - // execution MUST be added to the expiration queue or there will be a leak - queryTracker.expireQuery(dispatchQuery.getQueryId()); - } + dispatchQuery.addStateChangeListener(newState -> { + if (newState.isDone()) { + queryTracker.expireQuery(dispatchQuery.getQueryId()); + } + }); + stats.trackQueryStats(dispatchQuery); } public ListenableFuture waitForDispatched(QueryId queryId) diff --git a/presto-main/src/main/java/com/facebook/presto/execution/QueryManagerStats.java b/presto-main/src/main/java/com/facebook/presto/execution/QueryManagerStats.java index 27b088b895a11..c828cb9096b1d 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/QueryManagerStats.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/QueryManagerStats.java @@ -16,15 +16,19 @@ import com.facebook.airlift.stats.CounterStat; import com.facebook.airlift.stats.DistributionStat; import com.facebook.airlift.stats.TimeStat; +import com.facebook.presto.dispatcher.DispatchQuery; +import com.facebook.presto.execution.StateMachine.StateChangeListener; import com.facebook.presto.server.BasicQueryInfo; -import com.facebook.presto.spi.ErrorCode; -import io.airlift.units.Duration; import org.weakref.jmx.Managed; import org.weakref.jmx.Nested; +import javax.annotation.concurrent.GuardedBy; + import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import static com.facebook.presto.execution.QueryState.RUNNING; import static com.facebook.presto.spi.StandardErrorCode.ABANDONED_QUERY; import static com.facebook.presto.spi.StandardErrorCode.USER_CANCELED; import static java.util.concurrent.TimeUnit.MILLISECONDS; @@ -53,25 +57,34 @@ public class QueryManagerStats private final DistributionStat cpuInputByteRate = new DistributionStat(); private final DistributionStat peakRunningTasksStat = new DistributionStat(); - public void queryQueued() + public void trackQueryStats(DispatchQuery managedQueryExecution) { submittedQueries.update(1); queuedQueries.incrementAndGet(); + managedQueryExecution.addStateChangeListener(new StatisticsListener(managedQueryExecution)); } - public void queryStarted() + public void trackQueryStats(QueryExecution managedQueryExecution) + { + submittedQueries.update(1); + queuedQueries.incrementAndGet(); + managedQueryExecution.addStateChangeListener(new StatisticsListener()); + managedQueryExecution.addFinalQueryInfoListener(finalQueryInfo -> queryFinished(new BasicQueryInfo(finalQueryInfo))); + } + + private void queryStarted() { startedQueries.update(1); runningQueries.incrementAndGet(); queuedQueries.decrementAndGet(); } - public void queryStopped() + private void queryStopped() { runningQueries.decrementAndGet(); } - public void queryFinished(BasicQueryInfo info) + private void queryFinished(BasicQueryInfo info) { completedQueries.update(1); @@ -124,36 +137,50 @@ else if (info.getErrorCode().getCode() == USER_CANCELED.toErrorCode().getCode()) } } - public void queuedQueryFailed(Duration queuedTime, Optional errorCode) + private class StatisticsListener + implements StateChangeListener { - completedQueries.update(1); - failedQueries.update(1); + private final Supplier> finalQueryInfoSupplier; - this.queuedTime.add(queuedTime); + @GuardedBy("this") + private boolean stopped; + @GuardedBy("this") + private boolean started; - errorCode.ifPresent(error -> { - switch (error.getType()) { - case USER_ERROR: - userErrorFailures.update(1); - break; - case INTERNAL_ERROR: - internalFailures.update(1); - break; - case INSUFFICIENT_RESOURCES: - insufficientResourcesFailures.update(1); - break; - case EXTERNAL: - externalFailures.update(1); - break; - } + public StatisticsListener() + { + finalQueryInfoSupplier = Optional::empty; + } - if (error.getCode() == ABANDONED_QUERY.toErrorCode().getCode()) { - abandonedQueries.update(1); - } - else if (error.getCode() == USER_CANCELED.toErrorCode().getCode()) { - canceledQueries.update(1); + public StatisticsListener(DispatchQuery managedQueryExecution) + { + finalQueryInfoSupplier = () -> Optional.of(managedQueryExecution.getBasicQueryInfo()); + } + + @Override + public void stateChanged(QueryState newValue) + { + synchronized (this) { + if (stopped) { + return; + } + + if (newValue.isDone()) { + stopped = true; + if (started) { + queryStopped(); + } + finalQueryInfoSupplier.get() + .ifPresent(QueryManagerStats.this::queryFinished); + } + else if (newValue.ordinal() >= RUNNING.ordinal()) { + if (!started) { + started = true; + queryStarted(); + } + } } - }); + } } @Managed diff --git a/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java b/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java index df8965339601d..edde8d12a9dc6 100644 --- a/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java +++ b/presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java @@ -46,7 +46,6 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import static com.facebook.airlift.concurrent.Threads.threadsNamed; @@ -219,7 +218,6 @@ public void createQuery(QueryExecution queryExecution) queryExecution.addFinalQueryInfoListener(finalQueryInfo -> { try { - stats.queryFinished(new BasicQueryInfo(finalQueryInfo)); queryMonitor.queryCompletedEvent(finalQueryInfo); } finally { @@ -228,7 +226,7 @@ public void createQuery(QueryExecution queryExecution) } }); - addStatsListeners(queryExecution); + stats.trackQueryStats(queryExecution); embedVersion.embedVersion(queryExecution::start).run(); } @@ -314,27 +312,4 @@ private void enforceCpuLimits() } } } - - private void addStatsListeners(QueryExecution queryExecution) - { - Object lock = new Object(); - - AtomicBoolean started = new AtomicBoolean(); - queryExecution.addStateChangeListener(newValue -> { - synchronized (lock) { - if (newValue == RUNNING && !started.getAndSet(true)) { - stats.queryStarted(); - } - } - }); - - AtomicBoolean stopped = new AtomicBoolean(); - queryExecution.addStateChangeListener(newValue -> { - synchronized (lock) { - if (newValue.isDone() && !stopped.getAndSet(true) && started.get()) { - stats.queryStopped(); - } - } - }); - } } From 1b91f7a0717eb6264287462336e468ece97b4d17 Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Sat, 9 Mar 2019 17:44:34 -0800 Subject: [PATCH 12/18] Fix handling of failures during query creation Co-authored-by: Dain Sundstrom Co-authored-by: Raghav Sethi --- .../presto/dispatcher/DispatchManager.java | 99 ++++++++++--------- 1 file changed, 53 insertions(+), 46 deletions(-) diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchManager.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchManager.java index ac6710d87d6bb..a68491b0731c5 100644 --- a/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchManager.java +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchManager.java @@ -13,6 +13,7 @@ */ package com.facebook.presto.dispatcher; +import com.facebook.airlift.concurrent.ThreadPoolExecutorMBean; import com.facebook.presto.Session; import com.facebook.presto.execution.QueryIdGenerator; import com.facebook.presto.execution.QueryInfo; @@ -35,12 +36,10 @@ import com.facebook.presto.spi.resourceGroups.QueryType; import com.facebook.presto.spi.resourceGroups.SelectionContext; import com.facebook.presto.spi.resourceGroups.SelectionCriteria; -import com.facebook.presto.sql.SqlPath; import com.facebook.presto.transaction.TransactionManager; import com.google.common.util.concurrent.AbstractFuture; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningScheduledExecutorService; -import io.airlift.concurrent.ThreadPoolExecutorMBean; import org.weakref.jmx.Flatten; import org.weakref.jmx.Managed; import org.weakref.jmx.Nested; @@ -52,6 +51,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadPoolExecutor; +import static com.facebook.airlift.concurrent.Threads.threadsNamed; import static com.facebook.presto.spi.StandardErrorCode.QUERY_TEXT_TOO_LARGE; import static com.facebook.presto.util.StatementUtils.getQueryType; import static com.facebook.presto.util.StatementUtils.isTransactionControlStatement; @@ -59,7 +59,6 @@ import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.util.concurrent.Futures.immediateFuture; import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator; -import static io.airlift.concurrent.Threads.threadsNamed; import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static java.util.concurrent.Executors.newScheduledThreadPool; @@ -141,31 +140,32 @@ public QueryId createQueryId() public ListenableFuture createQuery(QueryId queryId, String slug, SessionContext sessionContext, String query) { + requireNonNull(queryId, "queryId is null"); + requireNonNull(sessionContext, "sessionFactory is null"); + requireNonNull(query, "query is null"); + checkArgument(!query.isEmpty(), "query must not be empty string"); + checkArgument(!queryTracker.tryGetQuery(queryId).isPresent(), "query %s already exists", queryId); + DispatchQueryCreationFuture queryCreationFuture = new DispatchQueryCreationFuture(); queryManagementExecutor.submit(() -> { try { createQueryInternal(queryId, slug, sessionContext, query, resourceGroupManager); - queryCreationFuture.set(null); } - catch (Throwable e) { - queryCreationFuture.setException(e); + finally { + queryCreationFuture.set(null); } }); return queryCreationFuture; } + /** + * Creates and registers a dispatch query with the query tracker. This method will never fail to register a query with the query + * tracker. If an error occurs while, creating a dispatch query a failed dispatch will be created and registered. + */ private void createQueryInternal(QueryId queryId, String slug, SessionContext sessionContext, String query, ResourceGroupManager resourceGroupManager) { - requireNonNull(queryId, "queryId is null"); - requireNonNull(sessionContext, "sessionFactory is null"); - requireNonNull(query, "query is null"); - checkArgument(!query.isEmpty(), "query must not be empty string"); - checkArgument(!queryTracker.tryGetQuery(queryId).isPresent(), "query %s already exists", queryId); - Session session = null; - SelectionContext selectionContext = null; PreparedQuery preparedQuery; - DispatchQuery dispatchQuery; try { if (query.length() > maxQueryLength) { int queryLength = query.length(); @@ -181,7 +181,7 @@ private void createQueryInternal(QueryId queryId, String slug, SessionContex // select resource group Optional queryType = getQueryType(preparedQuery.getStatement().getClass()); - selectionContext = resourceGroupManager.selectGroup(new SelectionCriteria( + SelectionContext selectionContext = resourceGroupManager.selectGroup(new SelectionCriteria( sessionContext.getIdentity().getPrincipal().isPresent(), sessionContext.getIdentity().getUser(), Optional.ofNullable(sessionContext.getSource()), @@ -195,50 +195,57 @@ private void createQueryInternal(QueryId queryId, String slug, SessionContex // mark existing transaction as active transactionManager.activateTransaction(session, isTransactionControlStatement(preparedQuery.getStatement()), accessControl); - dispatchQuery = dispatchQueryFactory.createDispatchQuery(session, query, preparedQuery, slug, selectionContext.getResourceGroupId(), queryType, warningCollector, queryManagementExecutor); + DispatchQuery dispatchQuery = dispatchQueryFactory.createDispatchQuery( + session, + query, + preparedQuery, + slug, + selectionContext.getResourceGroupId(), + queryType, + warningCollector, + queryManagementExecutor); + + boolean queryAdded = queryCreated(dispatchQuery); + if (queryAdded && !dispatchQuery.isDone()) { + try { + resourceGroupManager.submit(preparedQuery.getStatement(), dispatchQuery, selectionContext, queryManagementExecutor); + } + catch (Throwable e) { + // dispatch query has already been registered, so just fail it directly + dispatchQuery.fail(e); + } + } } - catch (RuntimeException e) { - // This is intentionally not a method, since after the state change listener is registered - // it's not safe to do any of this, and we had bugs before where people reused this code in a method - // if session creation failed, create a minimal session object + catch (Throwable throwable) { + // creation must never fail, so register a failed query in this case if (session == null) { session = Session.builder(new SessionPropertyManager()) .setQueryId(queryId) .setIdentity(sessionContext.getIdentity()) .setSource(sessionContext.getSource()) - .setPath(new SqlPath(Optional.empty())) .build(); } - - // create and immediately fail the query - DispatchQuery failedDispatchQuery = failedDispatchQueryFactory.createFailedDispatchQuery( - session, - query, - Optional.ofNullable(selectionContext).map(SelectionContext::getResourceGroupId), - e); - + DispatchQuery failedDispatchQuery = failedDispatchQueryFactory.createFailedDispatchQuery(session, query, Optional.empty(), throwable); queryCreated(failedDispatchQuery); - return; - } - - try { - queryCreated(dispatchQuery); - resourceGroupManager.submit(preparedQuery.getStatement(), dispatchQuery, selectionContext, queryManagementExecutor); - } - catch (RuntimeException e) { - dispatchQuery.fail(e); } } - private void queryCreated(DispatchQuery dispatchQuery) + private boolean queryCreated(DispatchQuery dispatchQuery) { - queryTracker.addQuery(dispatchQuery); - dispatchQuery.addStateChangeListener(newState -> { - if (newState.isDone()) { - queryTracker.expireQuery(dispatchQuery.getQueryId()); - } - }); - stats.trackQueryStats(dispatchQuery); + boolean queryAdded = queryTracker.addQuery(dispatchQuery); + + // only add state tracking if this query instance will actually be used for the execution + if (queryAdded) { + dispatchQuery.addStateChangeListener(newState -> { + if (newState.isDone()) { + // execution MUST be added to the expiration queue or there will be a leak + queryTracker.expireQuery(dispatchQuery.getQueryId()); + } + }); + stats.trackQueryStats(dispatchQuery); + } + + return queryAdded; } public ListenableFuture waitForDispatched(QueryId queryId) From eb07101d60fca16600cc7ac30f3e98dfb0625e4c Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Sun, 10 Mar 2019 15:51:36 -0700 Subject: [PATCH 13/18] Cleanup dispatcher executor management Co-authored-by: Dain Sundstrom Co-authored-by: Raghav Sethi --- .../presto/dispatcher/DispatchExecutor.java | 120 ++++++++++++++++++ .../presto/dispatcher/DispatchManager.java | 38 ++---- .../dispatcher/DispatchQueryFactory.java | 4 +- .../FailedDispatchQueryFactory.java | 5 +- .../presto/dispatcher/LocalDispatchQuery.java | 10 +- .../dispatcher/LocalDispatchQueryFactory.java | 21 ++- .../dispatcher/QueuedStatementResource.java | 12 +- .../presto/server/CoordinatorModule.java | 10 +- 8 files changed, 162 insertions(+), 58 deletions(-) create mode 100644 presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchExecutor.java diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchExecutor.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchExecutor.java new file mode 100644 index 0000000000000..5a3f7d1816d15 --- /dev/null +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchExecutor.java @@ -0,0 +1,120 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.facebook.presto.dispatcher; + +import com.facebook.airlift.concurrent.BoundedExecutor; +import com.facebook.airlift.concurrent.ThreadPoolExecutorMBean; +import com.facebook.presto.execution.QueryManagerConfig; +import com.google.common.io.Closer; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.ListeningScheduledExecutorService; +import org.weakref.jmx.Flatten; +import org.weakref.jmx.Managed; +import org.weakref.jmx.Nested; + +import javax.annotation.PreDestroy; +import javax.inject.Inject; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadPoolExecutor; + +import static com.facebook.airlift.concurrent.Threads.daemonThreadsNamed; +import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator; +import static java.util.Objects.requireNonNull; +import static java.util.concurrent.Executors.newCachedThreadPool; +import static java.util.concurrent.Executors.newScheduledThreadPool; + +public class DispatchExecutor +{ + private final Closer closer = Closer.create(); + + private final ListeningExecutorService executor; + private final BoundedExecutor boundedExecutor; + private final ListeningScheduledExecutorService scheduledExecutor; + + private final DispatchExecutorMBeans mbeans; + + @Inject + public DispatchExecutor(QueryManagerConfig config) + { + ExecutorService coreExecutor = newCachedThreadPool(daemonThreadsNamed("dispatcher-query-%s")); + closer.register(coreExecutor::shutdownNow); + executor = listeningDecorator(coreExecutor); + boundedExecutor = new BoundedExecutor(coreExecutor, config.getQuerySubmissionMaxThreads()); + + ScheduledExecutorService coreScheduledExecutor = newScheduledThreadPool(config.getQueryManagerExecutorPoolSize(), daemonThreadsNamed("dispatch-executor-%s")); + closer.register(coreScheduledExecutor::shutdownNow); + scheduledExecutor = listeningDecorator(coreScheduledExecutor); + + mbeans = new DispatchExecutorMBeans(coreExecutor, coreScheduledExecutor); + } + + public ListeningExecutorService getExecutor() + { + return executor; + } + + public BoundedExecutor getBoundedExecutor() + { + return boundedExecutor; + } + + public ListeningScheduledExecutorService getScheduledExecutor() + { + return scheduledExecutor; + } + + @Managed + @Flatten + public DispatchExecutorMBeans getMbeans() + { + return mbeans; + } + + @PreDestroy + public void shutdown() + throws Exception + { + closer.close(); + } + + public class DispatchExecutorMBeans + { + private final ThreadPoolExecutorMBean executor; + private final ThreadPoolExecutorMBean scheduledExecutor; + + public DispatchExecutorMBeans(ExecutorService coreExecutor, ScheduledExecutorService coreScheduledExecutor) + { + requireNonNull(coreExecutor, "coreExecutor is null"); + requireNonNull(coreScheduledExecutor, "coreScheduledExecutor is null"); + executor = new ThreadPoolExecutorMBean((ThreadPoolExecutor) coreExecutor); + scheduledExecutor = new ThreadPoolExecutorMBean((ThreadPoolExecutor) coreScheduledExecutor); + } + + @Managed + @Nested + public ThreadPoolExecutorMBean getExecutor() + { + return executor; + } + + @Managed + @Nested + public ThreadPoolExecutorMBean getScheduledExecutor() + { + return scheduledExecutor; + } + } +} diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchManager.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchManager.java index a68491b0731c5..803ced80c2e6f 100644 --- a/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchManager.java +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchManager.java @@ -13,7 +13,7 @@ */ package com.facebook.presto.dispatcher; -import com.facebook.airlift.concurrent.ThreadPoolExecutorMBean; +import com.facebook.airlift.concurrent.BoundedExecutor; import com.facebook.presto.Session; import com.facebook.presto.execution.QueryIdGenerator; import com.facebook.presto.execution.QueryInfo; @@ -39,29 +39,23 @@ import com.facebook.presto.transaction.TransactionManager; import com.google.common.util.concurrent.AbstractFuture; import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningScheduledExecutorService; import org.weakref.jmx.Flatten; import org.weakref.jmx.Managed; -import org.weakref.jmx.Nested; import javax.inject.Inject; import java.util.List; import java.util.Optional; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.Executor; -import static com.facebook.airlift.concurrent.Threads.threadsNamed; import static com.facebook.presto.spi.StandardErrorCode.QUERY_TEXT_TOO_LARGE; import static com.facebook.presto.util.StatementUtils.getQueryType; import static com.facebook.presto.util.StatementUtils.isTransactionControlStatement; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.util.concurrent.Futures.immediateFuture; -import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator; import static java.lang.String.format; import static java.util.Objects.requireNonNull; -import static java.util.concurrent.Executors.newScheduledThreadPool; public class DispatchManager { @@ -78,8 +72,8 @@ public class DispatchManager private final int maxQueryLength; - private final ListeningScheduledExecutorService queryManagementExecutor; - private final ThreadPoolExecutorMBean queryManagementExecutorMBean; + private final Executor queryExecutor; + private final BoundedExecutor boundedQueryExecutor; private final QueryTracker queryTracker; @@ -97,7 +91,8 @@ public DispatchManager( AccessControl accessControl, SessionSupplier sessionSupplier, SessionPropertyDefaults sessionPropertyDefaults, - QueryManagerConfig queryManagerConfig) + QueryManagerConfig queryManagerConfig, + DispatchExecutor dispatchExecutor) { this.queryIdGenerator = requireNonNull(queryIdGenerator, "queryIdGenerator is null"); this.queryPreparer = requireNonNull(queryPreparer, "queryPreparer is null"); @@ -112,18 +107,10 @@ public DispatchManager( this.maxQueryLength = queryManagerConfig.getMaxQueryLength(); - ScheduledExecutorService scheduledExecutorService = newScheduledThreadPool(queryManagerConfig.getQueryManagerExecutorPoolSize(), threadsNamed("query-dispatch-%s")); - queryManagementExecutor = listeningDecorator(scheduledExecutorService); - queryManagementExecutorMBean = new ThreadPoolExecutorMBean((ThreadPoolExecutor) scheduledExecutorService); + this.queryExecutor = requireNonNull(dispatchExecutor, "dispatchExecutor is null").getExecutor(); + this.boundedQueryExecutor = requireNonNull(dispatchExecutor, "dispatchExecutor is null").getBoundedExecutor(); - this.queryTracker = new QueryTracker<>(queryManagerConfig, queryManagementExecutor); - } - - @Managed(description = "Query dispatch executor") - @Nested - public ThreadPoolExecutorMBean getExecutor() - { - return queryManagementExecutorMBean; + this.queryTracker = new QueryTracker<>(queryManagerConfig, dispatchExecutor.getScheduledExecutor()); } @Managed @@ -147,7 +134,7 @@ public ListenableFuture createQuery(QueryId queryId, String slug, SessionCont checkArgument(!queryTracker.tryGetQuery(queryId).isPresent(), "query %s already exists", queryId); DispatchQueryCreationFuture queryCreationFuture = new DispatchQueryCreationFuture(); - queryManagementExecutor.submit(() -> { + boundedQueryExecutor.execute(() -> { try { createQueryInternal(queryId, slug, sessionContext, query, resourceGroupManager); } @@ -202,13 +189,12 @@ private void createQueryInternal(QueryId queryId, String slug, SessionContex slug, selectionContext.getResourceGroupId(), queryType, - warningCollector, - queryManagementExecutor); + warningCollector); boolean queryAdded = queryCreated(dispatchQuery); if (queryAdded && !dispatchQuery.isDone()) { try { - resourceGroupManager.submit(preparedQuery.getStatement(), dispatchQuery, selectionContext, queryManagementExecutor); + resourceGroupManager.submit(preparedQuery.getStatement(), dispatchQuery, selectionContext, queryExecutor); } catch (Throwable e) { // dispatch query has already been registered, so just fail it directly diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchQueryFactory.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchQueryFactory.java index 342f8ac81399a..41253e64c4416 100644 --- a/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchQueryFactory.java +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/DispatchQueryFactory.java @@ -20,7 +20,6 @@ import com.facebook.presto.spi.resourceGroups.ResourceGroupId; import java.util.Optional; -import java.util.concurrent.ExecutorService; public interface DispatchQueryFactory { @@ -31,6 +30,5 @@ DispatchQuery createDispatchQuery( String slug, ResourceGroupId resourceGroup, Optional queryType, - WarningCollector warningCollector, - ExecutorService queryExecutor); + WarningCollector warningCollector); } diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/FailedDispatchQueryFactory.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/FailedDispatchQueryFactory.java index 88cf63fa6d4bc..173f7f6757e74 100644 --- a/presto-main/src/main/java/com/facebook/presto/dispatcher/FailedDispatchQueryFactory.java +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/FailedDispatchQueryFactory.java @@ -16,7 +16,6 @@ import com.facebook.presto.Session; import com.facebook.presto.event.QueryMonitor; import com.facebook.presto.execution.ExecutionFailureInfo; -import com.facebook.presto.execution.ForQueryExecution; import com.facebook.presto.execution.LocationFactory; import com.facebook.presto.server.BasicQueryInfo; import com.facebook.presto.spi.resourceGroups.ResourceGroupId; @@ -36,11 +35,11 @@ public class FailedDispatchQueryFactory private final ExecutorService executor; @Inject - public FailedDispatchQueryFactory(QueryMonitor queryMonitor, LocationFactory locationFactory, @ForQueryExecution ExecutorService executor) + public FailedDispatchQueryFactory(QueryMonitor queryMonitor, LocationFactory locationFactory, DispatchExecutor dispatchExecutor) { this.queryMonitor = requireNonNull(queryMonitor, "queryMonitor is null"); this.locationFactory = requireNonNull(locationFactory, "locationFactory is null"); - this.executor = requireNonNull(executor, "executor is null"); + this.executor = requireNonNull(dispatchExecutor, "dispatchExecutor is null").getExecutor(); } public FailedDispatchQuery createFailedDispatchQuery(Session session, String query, Optional resourceGroup, Throwable throwable) diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java index 6adc993793c91..971ff47ec200f 100644 --- a/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java @@ -32,7 +32,7 @@ import org.joda.time.DateTime; import java.util.Optional; -import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executor; import java.util.function.Function; import static com.facebook.airlift.concurrent.MoreFutures.addExceptionCallback; @@ -56,7 +56,7 @@ public class LocalDispatchQuery private final ClusterSizeMonitor clusterSizeMonitor; - private final ExecutorService queryExecutor; + private final Executor queryExecutor; private final Function> querySubmitter; @@ -64,7 +64,7 @@ public LocalDispatchQuery( QueryStateMachine stateMachine, ListenableFuture queryExecutionFuture, ClusterSizeMonitor clusterSizeMonitor, - ExecutorService queryExecutor, + Executor queryExecutor, Function> querySubmitter) { this.stateMachine = requireNonNull(stateMachine, "stateMachine is null"); @@ -89,12 +89,12 @@ private void waitForMinimumWorkers() ListenableFuture minimumWorkerFuture = clusterSizeMonitor.waitForMinimumWorkers(); // when worker requirement is met, wait for query execution to finish construction and then start the execution addSuccessCallback(minimumWorkerFuture, () -> addSuccessCallback(queryExecutionFuture, this::startExecution)); - addExceptionCallback(minimumWorkerFuture, throwable -> queryExecutor.submit(() -> stateMachine.transitionToFailed(throwable))); + addExceptionCallback(minimumWorkerFuture, throwable -> queryExecutor.execute(() -> stateMachine.transitionToFailed(throwable))); } private void startExecution(QueryExecution queryExecution) { - queryExecutor.submit(() -> { + queryExecutor.execute(() -> { if (stateMachine.transitionToDispatching()) { querySubmitter.apply(queryExecution); } diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQueryFactory.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQueryFactory.java index 69dde3f64fb08..b2a21801ea414 100644 --- a/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQueryFactory.java +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQueryFactory.java @@ -16,7 +16,6 @@ import com.facebook.presto.Session; import com.facebook.presto.event.QueryMonitor; import com.facebook.presto.execution.ClusterSizeMonitor; -import com.facebook.presto.execution.ForQueryExecution; import com.facebook.presto.execution.LocationFactory; import com.facebook.presto.execution.QueryExecution; import com.facebook.presto.execution.QueryExecution.QueryExecutionFactory; @@ -38,11 +37,9 @@ import java.util.Map; import java.util.Optional; -import java.util.concurrent.ExecutorService; import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED; import static com.facebook.presto.util.StatementUtils.isTransactionControlStatement; -import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator; import static java.util.Objects.requireNonNull; public class LocalDispatchQueryFactory @@ -58,7 +55,7 @@ public class LocalDispatchQueryFactory private final ClusterSizeMonitor clusterSizeMonitor; private final Map, QueryExecutionFactory> executionFactories; - private final ListeningExecutorService executorService; + private final ListeningExecutorService executor; @Inject public LocalDispatchQueryFactory( @@ -70,8 +67,7 @@ public LocalDispatchQueryFactory( LocationFactory locationFactory, Map, QueryExecutionFactory> executionFactories, ClusterSizeMonitor clusterSizeMonitor, - @ForQueryExecution ExecutorService executorService) - + DispatchExecutor dispatchExecutor) { this.queryManager = requireNonNull(queryManager, "queryManager is null"); this.transactionManager = requireNonNull(transactionManager, "transactionManager is null"); @@ -83,7 +79,7 @@ public LocalDispatchQueryFactory( this.clusterSizeMonitor = requireNonNull(clusterSizeMonitor, "clusterSizeMonitor is null"); - this.executorService = listeningDecorator(requireNonNull(executorService, "executorService is null")); + this.executor = requireNonNull(dispatchExecutor, "executorService is null").getExecutor(); } @Override @@ -94,8 +90,7 @@ public DispatchQuery createDispatchQuery( String slug, ResourceGroupId resourceGroup, Optional queryType, - WarningCollector warningCollector, - ExecutorService queryExecutor) + WarningCollector warningCollector) { QueryStateMachine stateMachine = QueryStateMachine.begin( query, @@ -106,13 +101,13 @@ public DispatchQuery createDispatchQuery( isTransactionControlStatement(preparedQuery.getStatement()), transactionManager, accessControl, - executorService, + executor, metadata, warningCollector); queryMonitor.queryCreatedEvent(stateMachine.getBasicQueryInfo(Optional.empty())); - ListenableFuture queryExecutionFuture = executorService.submit(() -> { + ListenableFuture queryExecutionFuture = executor.submit(() -> { QueryExecutionFactory queryExecutionFactory = executionFactories.get(preparedQuery.getStatement().getClass()); if (queryExecutionFactory == null) { throw new PrestoException(NOT_SUPPORTED, "Unsupported statement type: " + preparedQuery.getStatement().getClass().getSimpleName()); @@ -125,7 +120,7 @@ public DispatchQuery createDispatchQuery( stateMachine, queryExecutionFuture, clusterSizeMonitor, - queryExecutor, - queryExecution -> executorService.submit(() -> queryManager.createQuery(queryExecution))); + executor, + queryExecution -> executor.submit(() -> queryManager.createQuery(queryExecution))); } } diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/QueuedStatementResource.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/QueuedStatementResource.java index 646f22381421e..d22e6a5de3c1e 100644 --- a/presto-main/src/main/java/com/facebook/presto/dispatcher/QueuedStatementResource.java +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/QueuedStatementResource.java @@ -19,7 +19,6 @@ import com.facebook.presto.client.StatementStats; import com.facebook.presto.execution.ExecutionFailureInfo; import com.facebook.presto.execution.QueryState; -import com.facebook.presto.server.ForStatementResource; import com.facebook.presto.server.HttpRequestSessionContext; import com.facebook.presto.server.SessionContext; import com.facebook.presto.spi.ErrorCode; @@ -56,6 +55,7 @@ import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicLong; @@ -91,7 +91,7 @@ public class QueuedStatementResource private final DispatchManager dispatchManager; - private final BoundedExecutor responseExecutor; + private final Executor responseExecutor; private final ScheduledExecutorService timeoutExecutor; private final ConcurrentMap queries = new ConcurrentHashMap<>(); @@ -100,13 +100,13 @@ public class QueuedStatementResource @Inject public QueuedStatementResource( DispatchManager dispatchManager, - @ForStatementResource BoundedExecutor responseExecutor, - @ForStatementResource ScheduledExecutorService timeoutExecutor) + DispatchExecutor executor) { this.dispatchManager = requireNonNull(dispatchManager, "dispatchManager is null"); - this.responseExecutor = requireNonNull(responseExecutor, "responseExecutor is null"); - this.timeoutExecutor = requireNonNull(timeoutExecutor, "timeoutExecutor is null"); + requireNonNull(dispatchManager, "dispatchManager is null"); + this.responseExecutor = requireNonNull(executor, "responseExecutor is null").getExecutor(); + this.timeoutExecutor = requireNonNull(executor, "timeoutExecutor is null").getScheduledExecutor(); queryPurger.scheduleWithFixedDelay( () -> { diff --git a/presto-main/src/main/java/com/facebook/presto/server/CoordinatorModule.java b/presto-main/src/main/java/com/facebook/presto/server/CoordinatorModule.java index aae1b4943bd5e..8fdf9b8f19d1a 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/CoordinatorModule.java +++ b/presto-main/src/main/java/com/facebook/presto/server/CoordinatorModule.java @@ -24,6 +24,7 @@ import com.facebook.presto.cost.CostComparator; import com.facebook.presto.cost.StatsCalculatorModule; import com.facebook.presto.cost.TaskCountEstimator; +import com.facebook.presto.dispatcher.DispatchExecutor; import com.facebook.presto.dispatcher.DispatchManager; import com.facebook.presto.dispatcher.DispatchQueryFactory; import com.facebook.presto.dispatcher.FailedDispatchQueryFactory; @@ -222,16 +223,21 @@ protected void setup(Binder binder) jaxrsBinder(binder).bind(ResourceGroupStateInfoResource.class); binder.bind(QueryIdGenerator.class).in(Scopes.SINGLETON); binder.bind(QueryManager.class).to(SqlQueryManager.class).in(Scopes.SINGLETON); + newExporter(binder).export(QueryManager.class).withGeneratedName(); binder.bind(QueryPreparer.class).in(Scopes.SINGLETON); binder.bind(SessionSupplier.class).to(QuerySessionSupplier.class).in(Scopes.SINGLETON); binder.bind(InternalResourceGroupManager.class).in(Scopes.SINGLETON); newExporter(binder).export(InternalResourceGroupManager.class).withGeneratedName(); binder.bind(ResourceGroupManager.class).to(InternalResourceGroupManager.class); + binder.bind(LegacyResourceGroupConfigurationManager.class).in(Scopes.SINGLETON); + + // dispatcher binder.bind(DispatchManager.class).in(Scopes.SINGLETON); binder.bind(FailedDispatchQueryFactory.class).in(Scopes.SINGLETON); + binder.bind(DispatchExecutor.class).in(Scopes.SINGLETON); + + // local dispatcher binder.bind(DispatchQueryFactory.class).to(LocalDispatchQueryFactory.class); - binder.bind(LegacyResourceGroupConfigurationManager.class).in(Scopes.SINGLETON); - newExporter(binder).export(QueryManager.class).withGeneratedName(); // cluster memory manager binder.bind(ClusterMemoryManager.class).in(Scopes.SINGLETON); From 73918d18636d6a3caad22edf68ead0361cea60fa Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Mon, 3 Dec 2018 13:38:04 -0800 Subject: [PATCH 14/18] Change local dispatch to finish immediately after query submission Co-authored-by: Dain Sundstrom Co-authored-by: Raghav Sethi --- .../presto/dispatcher/LocalDispatchQuery.java | 41 ++++++++++--------- .../dispatcher/LocalDispatchQueryFactory.java | 2 +- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java index 971ff47ec200f..5aa38bbc3296b 100644 --- a/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java @@ -25,26 +25,23 @@ import com.facebook.presto.spi.ErrorCode; import com.facebook.presto.spi.PrestoException; import com.facebook.presto.spi.QueryId; -import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.SettableFuture; import io.airlift.units.DataSize; import io.airlift.units.Duration; import org.joda.time.DateTime; import java.util.Optional; import java.util.concurrent.Executor; -import java.util.function.Function; +import java.util.function.Consumer; import static com.facebook.airlift.concurrent.MoreFutures.addExceptionCallback; import static com.facebook.airlift.concurrent.MoreFutures.addSuccessCallback; import static com.facebook.airlift.concurrent.MoreFutures.tryGetFutureValue; import static com.facebook.presto.execution.QueryState.FAILED; -import static com.facebook.presto.execution.QueryState.PLANNING; import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR; import static com.facebook.presto.util.Failures.toFailure; -import static com.google.common.util.concurrent.Futures.immediateFuture; -import static com.google.common.util.concurrent.MoreExecutors.directExecutor; -import static io.airlift.units.DataSize.Unit.BYTE; +import static com.google.common.util.concurrent.Futures.nonCancellationPropagating; import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.MILLISECONDS; @@ -58,14 +55,15 @@ public class LocalDispatchQuery private final Executor queryExecutor; - private final Function> querySubmitter; + private final Consumer querySubmitter; + private final SettableFuture submitted = SettableFuture.create(); public LocalDispatchQuery( QueryStateMachine stateMachine, ListenableFuture queryExecutionFuture, ClusterSizeMonitor clusterSizeMonitor, Executor queryExecutor, - Function> querySubmitter) + Consumer querySubmitter) { this.stateMachine = requireNonNull(stateMachine, "stateMachine is null"); this.queryExecutionFuture = requireNonNull(queryExecutionFuture, "queryExecutionFuture is null"); @@ -74,6 +72,11 @@ public LocalDispatchQuery( this.querySubmitter = requireNonNull(querySubmitter, "querySubmitter is null"); addExceptionCallback(queryExecutionFuture, stateMachine::transitionToFailed); + stateMachine.addStateChangeListener(state -> { + if (state.isDone()) { + submitted.set(null); + } + }); } @Override @@ -96,7 +99,12 @@ private void startExecution(QueryExecution queryExecution) { queryExecutor.execute(() -> { if (stateMachine.transitionToDispatching()) { - querySubmitter.apply(queryExecution); + try { + querySubmitter.accept(queryExecution); + } + finally { + submitted.set(null); + } } }); } @@ -116,27 +124,22 @@ public DateTime getLastHeartbeat() @Override public ListenableFuture getDispatchedFuture() { - return queryDispatchFuture(stateMachine.getQueryState()); - } - - private ListenableFuture queryDispatchFuture(QueryState currentState) - { - if (currentState.ordinal() >= PLANNING.ordinal()) { - return immediateFuture(null); - } - return Futures.transformAsync(stateMachine.getStateChange(currentState), this::queryDispatchFuture, directExecutor()); + return nonCancellationPropagating(submitted); } @Override public DispatchInfo getDispatchInfo() { + // observe submitted before getting the state, to ensure a failed query stat is visible + boolean dispatched = submitted.isDone(); BasicQueryInfo queryInfo = stateMachine.getBasicQueryInfo(Optional.empty()); + if (queryInfo.getState() == FAILED) { ExecutionFailureInfo failureInfo = stateMachine.getFailureInfo() .orElseGet(() -> toFailure(new PrestoException(GENERIC_INTERNAL_ERROR, "Query failed for an unknown reason"))); return DispatchInfo.failed(failureInfo, queryInfo.getQueryStats().getElapsedTime(), queryInfo.getQueryStats().getQueuedTime()); } - if (queryInfo.getState().ordinal() >= PLANNING.ordinal()) { + if (dispatched) { return DispatchInfo.dispatched(new LocalCoordinatorLocation(), queryInfo.getQueryStats().getElapsedTime(), queryInfo.getQueryStats().getQueuedTime()); } return DispatchInfo.queued(queryInfo.getQueryStats().getElapsedTime(), queryInfo.getQueryStats().getQueuedTime()); diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQueryFactory.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQueryFactory.java index b2a21801ea414..69f05e68bf675 100644 --- a/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQueryFactory.java +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQueryFactory.java @@ -121,6 +121,6 @@ public DispatchQuery createDispatchQuery( queryExecutionFuture, clusterSizeMonitor, executor, - queryExecution -> executor.submit(() -> queryManager.createQuery(queryExecution))); + queryManager::createQuery); } } From 1a4ef68f27f8f6a792de33020750dbe5e35ef18c Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Fri, 15 Mar 2019 16:04:33 -0700 Subject: [PATCH 15/18] Catch errors from LocalDispatchQuery querySubmitter querySubmitter should never throw, but if it does fail the query immediately Co-authored-by: Dain Sundstrom Co-authored-by: Raghav Sethi --- .../facebook/presto/dispatcher/LocalDispatchQuery.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java index 5aa38bbc3296b..d50420171a1fc 100644 --- a/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/LocalDispatchQuery.java @@ -42,12 +42,14 @@ import static com.facebook.presto.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR; import static com.facebook.presto.util.Failures.toFailure; import static com.google.common.util.concurrent.Futures.nonCancellationPropagating; +import static io.airlift.units.DataSize.Unit.BYTE; import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.MILLISECONDS; public class LocalDispatchQuery implements DispatchQuery { + private static final Logger log = Logger.get(LocalDispatchQuery.class); private final QueryStateMachine stateMachine; private final ListenableFuture queryExecutionFuture; @@ -102,6 +104,12 @@ private void startExecution(QueryExecution queryExecution) try { querySubmitter.accept(queryExecution); } + catch (Throwable t) { + // this should never happen but be safe + stateMachine.transitionToFailed(t); + log.error(t, "query submitter threw exception"); + throw t; + } finally { submitted.set(null); } From a855e13c9528a47b658ee78edfa0c5e4dce97d53 Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Mon, 18 Mar 2019 13:03:14 -0700 Subject: [PATCH 16/18] Fix result caching in protocol Query Previously, the cache was effectively disabled for the first result, so a retry on first request resulted in a 410 gone. Co-authored-by: Dain Sundstrom Co-authored-by: Raghav Sethi --- .../presto/server/protocol/Query.java | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/presto-main/src/main/java/com/facebook/presto/server/protocol/Query.java b/presto-main/src/main/java/com/facebook/presto/server/protocol/Query.java index 6b9c4cf3597e4..5dd57acb2e8b2 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/protocol/Query.java +++ b/presto-main/src/main/java/com/facebook/presto/server/protocol/Query.java @@ -266,7 +266,8 @@ public synchronized boolean isClearTransactionId() public synchronized ListenableFuture waitForResults(long token, UriInfo uriInfo, String scheme, Duration wait, DataSize targetResultSize) { // before waiting, check if this request has already been processed and cached - Optional cachedResult = getCachedResult(token, uriInfo); + String requestedPath = uriInfo.getAbsolutePath().getPath(); + Optional cachedResult = getCachedResult(token, requestedPath); if (cachedResult.isPresent()) { return immediateFuture(cachedResult.get()); } @@ -299,7 +300,7 @@ private synchronized ListenableFuture getFutureStateChange() } } - private synchronized Optional getCachedResult(long token, UriInfo uriInfo) + private synchronized Optional getCachedResult(long token, String requestedPath) { // is this the first request? if (lastResult == null) { @@ -307,7 +308,6 @@ private synchronized Optional getCachedResult(long token, UriInfo } // is the a repeated request for the last results? - String requestedPath = uriInfo.getAbsolutePath().getPath(); if (requestedPath.equals(lastResultPath)) { // tell query manager we are still interested in the query queryManager.recordHeartbeat(queryId); @@ -330,7 +330,8 @@ private synchronized Optional getCachedResult(long token, UriInfo private synchronized QueryResults getNextResult(long token, UriInfo uriInfo, String scheme, DataSize targetResultSize) { // check if the result for the token have already been created - Optional cachedResult = getCachedResult(token, uriInfo); + String requestedPath = uriInfo.getAbsolutePath().getPath(); + Optional cachedResult = getCachedResult(token, requestedPath); if (cachedResult.isPresent()) { return cachedResult.get(); } @@ -441,19 +442,13 @@ private synchronized QueryResults getNextResult(long token, UriInfo uriInfo, Str queryInfo.getUpdateType(), updateCount); - cacheLastResults(queryResults); + cacheLastResults(queryResults, requestedPath); return queryResults; } - private synchronized void cacheLastResults(QueryResults queryResults) + private synchronized void cacheLastResults(QueryResults queryResults, String requestedPath) { - // cache the last results - if (lastResult != null && lastResult.getNextUri() != null) { - lastResultPath = lastResult.getNextUri().getPath(); - } - else { - lastResultPath = null; - } + lastResultPath = requestedPath; lastResult = queryResults; } From 823a0e5048b269c285550e574afb0ccc9522ef87 Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Sat, 30 Mar 2019 14:16:02 -0700 Subject: [PATCH 17/18] Simplify token management in protocol Query Co-authored-by: Dain Sundstrom Co-authored-by: Raghav Sethi --- .../presto/server/protocol/Query.java | 60 +++++++++++-------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/presto-main/src/main/java/com/facebook/presto/server/protocol/Query.java b/presto-main/src/main/java/com/facebook/presto/server/protocol/Query.java index 5dd57acb2e8b2..8dfac27bc7413 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/protocol/Query.java +++ b/presto-main/src/main/java/com/facebook/presto/server/protocol/Query.java @@ -66,10 +66,10 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.Optional; +import java.util.OptionalLong; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.atomic.AtomicLong; import static com.facebook.airlift.concurrent.MoreFutures.addTimeout; import static com.facebook.presto.SystemSessionProperties.isExchangeCompressionEnabled; @@ -78,6 +78,7 @@ import static com.facebook.presto.util.Failures.toFailure; import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Verify.verify; import static com.google.common.util.concurrent.Futures.immediateFuture; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static java.lang.String.format; @@ -101,13 +102,14 @@ class Query private final PagesSerde serde; - private final AtomicLong resultId = new AtomicLong(); + @GuardedBy("this") + private OptionalLong nextToken = OptionalLong.of(0); @GuardedBy("this") private QueryResults lastResult; @GuardedBy("this") - private String lastResultPath; + private long lastToken = -1; @GuardedBy("this") private List columns; @@ -266,8 +268,7 @@ public synchronized boolean isClearTransactionId() public synchronized ListenableFuture waitForResults(long token, UriInfo uriInfo, String scheme, Duration wait, DataSize targetResultSize) { // before waiting, check if this request has already been processed and cached - String requestedPath = uriInfo.getAbsolutePath().getPath(); - Optional cachedResult = getCachedResult(token, requestedPath); + Optional cachedResult = getCachedResult(token); if (cachedResult.isPresent()) { return immediateFuture(cachedResult.get()); } @@ -300,7 +301,7 @@ private synchronized ListenableFuture getFutureStateChange() } } - private synchronized Optional getCachedResult(long token, String requestedPath) + private synchronized Optional getCachedResult(long token) { // is this the first request? if (lastResult == null) { @@ -308,18 +309,24 @@ private synchronized Optional getCachedResult(long token, String r } // is the a repeated request for the last results? - if (requestedPath.equals(lastResultPath)) { + if (token == lastToken) { // tell query manager we are still interested in the query queryManager.recordHeartbeat(queryId); return Optional.of(lastResult); } - if (token < resultId.get()) { + // if this is a result before the lastResult, the data is gone + if (token < lastToken) { throw new WebApplicationException(Response.Status.GONE); } + // if this is a request for a result after the end of the stream, return not found + if (!nextToken.isPresent()) { + throw new WebApplicationException(Response.Status.NOT_FOUND); + } + // if this is not a request for the next results, return not found - if (lastResult.getNextUri() == null || !requestedPath.equals(lastResult.getNextUri().getPath())) { + if (token != nextToken.getAsLong()) { // unknown token throw new WebApplicationException(Response.Status.NOT_FOUND); } @@ -330,12 +337,13 @@ private synchronized Optional getCachedResult(long token, String r private synchronized QueryResults getNextResult(long token, UriInfo uriInfo, String scheme, DataSize targetResultSize) { // check if the result for the token have already been created - String requestedPath = uriInfo.getAbsolutePath().getPath(); - Optional cachedResult = getCachedResult(token, requestedPath); + Optional cachedResult = getCachedResult(token); if (cachedResult.isPresent()) { return cachedResult.get(); } + verify(nextToken.isPresent(), "Can not generate next result when next token is not present"); + verify(token == nextToken.getAsLong(), "Expected token to equal next token"); URI queryHtmlUri = uriInfo.getRequestUriBuilder() .scheme(scheme) .replacePath("ui/query.html") @@ -399,17 +407,24 @@ private synchronized QueryResults getNextResult(long token, UriInfo uriInfo, Str data = ImmutableSet.of(ImmutableList.of(true)); } + // advance next token // only return a next if // (1) the query is not done AND the query state is not FAILED // OR // (2)there is more data to send (due to buffering) + if ((!queryInfo.isFinalQueryInfo() && queryInfo.getState() != FAILED) || !exchangeClient.isClosed()) { + nextToken = OptionalLong.of(token + 1); + } + else { + nextToken = OptionalLong.empty(); + } + URI nextResultsUri = null; - if (!queryInfo.isFinalQueryInfo() && !queryInfo.getState().equals(QueryState.FAILED) - || !exchangeClient.isClosed()) { - nextResultsUri = createNextResultsUri(scheme, uriInfo); + if (nextToken.isPresent()) { + nextResultsUri = createNextResultsUri(scheme, uriInfo, nextToken.getAsLong()); } - // update catalog and schema + // update catalog, schema, and path setCatalog = queryInfo.getSetCatalog(); setSchema = queryInfo.getSetSchema(); @@ -442,14 +457,11 @@ private synchronized QueryResults getNextResult(long token, UriInfo uriInfo, Str queryInfo.getUpdateType(), updateCount); - cacheLastResults(queryResults, requestedPath); - return queryResults; - } - - private synchronized void cacheLastResults(QueryResults queryResults, String requestedPath) - { - lastResultPath = requestedPath; + // cache the new result + lastToken = token; lastResult = queryResults; + + return queryResults; } private synchronized void closeExchangeClientIfNecessary(QueryInfo queryInfo) @@ -493,13 +505,13 @@ private ListenableFuture queryDoneFuture(QueryState currentState) return Futures.transformAsync(queryManager.getStateChange(queryId, currentState), this::queryDoneFuture, directExecutor()); } - private synchronized URI createNextResultsUri(String scheme, UriInfo uriInfo) + private synchronized URI createNextResultsUri(String scheme, UriInfo uriInfo, long nextToken) { return uriInfo.getBaseUriBuilder() .scheme(scheme) .replacePath("/v1/statement/executing") .path(queryId.toString()) - .path(String.valueOf(resultId.getAndIncrement())) + .path(String.valueOf(nextToken)) .replaceQuery("") .queryParam("slug", slug) .build(); From 95e5b4c2e21d4d1154bbb78ebaa425ca9963ac3e Mon Sep 17 00:00:00 2001 From: Tim Meehan Date: Fri, 6 Mar 2020 12:44:06 -0800 Subject: [PATCH 18/18] Add purger to ExecutingStatementResource Co-authored-by: Dain Sundstrom --- .../dispatcher/QueuedStatementResource.java | 2 +- .../protocol/ExecutingStatementResource.java | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/presto-main/src/main/java/com/facebook/presto/dispatcher/QueuedStatementResource.java b/presto-main/src/main/java/com/facebook/presto/dispatcher/QueuedStatementResource.java index d22e6a5de3c1e..79a62fd35ea95 100644 --- a/presto-main/src/main/java/com/facebook/presto/dispatcher/QueuedStatementResource.java +++ b/presto-main/src/main/java/com/facebook/presto/dispatcher/QueuedStatementResource.java @@ -95,7 +95,7 @@ public class QueuedStatementResource private final ScheduledExecutorService timeoutExecutor; private final ConcurrentMap queries = new ConcurrentHashMap<>(); - private final ScheduledExecutorService queryPurger = newSingleThreadScheduledExecutor(threadsNamed("query-purger")); + private final ScheduledExecutorService queryPurger = newSingleThreadScheduledExecutor(threadsNamed("dispatch-query-purger")); @Inject public QueuedStatementResource( diff --git a/presto-main/src/main/java/com/facebook/presto/server/protocol/ExecutingStatementResource.java b/presto-main/src/main/java/com/facebook/presto/server/protocol/ExecutingStatementResource.java index 8616cf4a9e771..5cd0181f1ecdb 100644 --- a/presto-main/src/main/java/com/facebook/presto/server/protocol/ExecutingStatementResource.java +++ b/presto-main/src/main/java/com/facebook/presto/server/protocol/ExecutingStatementResource.java @@ -14,6 +14,7 @@ package com.facebook.presto.server.protocol; import com.facebook.airlift.concurrent.BoundedExecutor; +import com.facebook.airlift.log.Logger; import com.facebook.presto.Session; import com.facebook.presto.client.QueryResults; import com.facebook.presto.execution.QueryManager; @@ -29,6 +30,7 @@ import io.airlift.units.DataSize; import io.airlift.units.Duration; +import javax.annotation.PreDestroy; import javax.inject.Inject; import javax.ws.rs.DELETE; import javax.ws.rs.GET; @@ -55,6 +57,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ScheduledExecutorService; +import static com.facebook.airlift.concurrent.Threads.threadsNamed; import static com.facebook.airlift.http.server.AsyncResponseHandler.bindAsyncResponse; import static com.facebook.presto.client.PrestoHeaders.PRESTO_ADDED_PREPARE; import static com.facebook.presto.client.PrestoHeaders.PRESTO_CLEAR_SESSION; @@ -71,12 +74,15 @@ import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static io.airlift.units.DataSize.Unit.MEGABYTE; import static java.util.Objects.requireNonNull; +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; import static javax.ws.rs.core.MediaType.TEXT_PLAIN_TYPE; @Path("/") public class ExecutingStatementResource { + private static final Logger log = Logger.get(ExecutingStatementResource.class); private static final Duration MAX_WAIT_TIME = new Duration(1, SECONDS); private static final Ordering> WAIT_ORDERING = Ordering.natural().nullsLast(); @@ -90,6 +96,7 @@ public class ExecutingStatementResource private final ScheduledExecutorService timeoutExecutor; private final ConcurrentMap queries = new ConcurrentHashMap<>(); + private final ScheduledExecutorService queryPurger = newSingleThreadScheduledExecutor(threadsNamed("execution-query-purger")); @Inject public ExecutingStatementResource( @@ -104,6 +111,34 @@ public ExecutingStatementResource( this.blockEncodingSerde = requireNonNull(blockEncodingSerde, "blockEncodingSerde is null"); this.responseExecutor = requireNonNull(responseExecutor, "responseExecutor is null"); this.timeoutExecutor = requireNonNull(timeoutExecutor, "timeoutExecutor is null"); + + queryPurger.scheduleWithFixedDelay( + () -> { + try { + for (Entry entry : queries.entrySet()) { + // forget about this query if the query manager is no longer tracking it + try { + queryManager.getQueryState(entry.getKey()); + } + catch (NoSuchElementException e) { + // query is no longer registered + queries.remove(entry.getKey()); + } + } + } + catch (Throwable e) { + log.warn(e, "Error removing old queries"); + } + }, + 200, + 200, + MILLISECONDS); + } + + @PreDestroy + public void stop() + { + queryPurger.shutdownNow(); } @GET