Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 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 io.trino.execution;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.errorprone.annotations.concurrent.GuardedBy;
import io.airlift.concurrent.SetThreadName;
import io.airlift.units.Duration;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.trino.annotation.NotThreadSafe;
import io.trino.event.SplitMonitor;
import io.trino.execution.StateMachine.StateChangeListener;
import io.trino.execution.buffer.BufferState;
import io.trino.execution.buffer.OutputBuffer;
import io.trino.execution.executor.TaskExecutor;
import io.trino.execution.executor.TaskHandle;
import io.trino.metadata.Split;
import io.trino.operator.Driver;
import io.trino.operator.DriverContext;
import io.trino.operator.DriverFactory;
import io.trino.operator.DriverStats;
import io.trino.operator.PipelineContext;
import io.trino.operator.TaskContext;
import io.trino.spi.SplitWeight;
import io.trino.spi.TrinoException;
import io.trino.sql.planner.LocalExecutionPlanner.LocalExecutionPlan;
import io.trino.sql.planner.plan.PlanNodeId;
import io.trino.tracing.TrinoAttributes;
import jakarta.annotation.Nullable;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Verify.verify;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.util.concurrent.Futures.immediateVoidFuture;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static io.trino.SystemSessionProperties.getInitialSplitsPerNode;
import static io.trino.SystemSessionProperties.getMaxDriversPerTask;
import static io.trino.SystemSessionProperties.getSplitConcurrencyAdjustmentInterval;
import static io.trino.execution.SqlTaskExecution.SplitsState.ADDING_SPLITS;
import static io.trino.execution.SqlTaskExecution.SplitsState.FINISHED;
import static io.trino.execution.SqlTaskExecution.SplitsState.NO_MORE_SPLITS;
import static io.trino.spi.StandardErrorCode.GENERIC_INTERNAL_ERROR;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static java.util.function.Function.identity;
public class SqlTaskExecution
{
private final TaskId taskId;
private final TaskStateMachine taskStateMachine;
private final Span taskSpan;
private final TaskContext taskContext;
private final OutputBuffer outputBuffer;
private final TaskHandle taskHandle;
private final TaskExecutor taskExecutor;
private final Executor notificationExecutor;
private final SplitMonitor splitMonitor;
private final DriverAndTaskTerminationTracker driverAndTaskTerminationTracker;
private final Map driverRunnerFactoriesWithSplitLifeCycle;
private final List driverRunnerFactoriesWithTaskLifeCycle;
private final Map driverRunnerFactoriesWithRemoteSource;
private final List allDriverRunnerFactories;
@GuardedBy("this")
private final Map maxAcknowledgedSplitByPlanNode = new HashMap<>();
@GuardedBy("this")
private final List sourceStartOrder;
@GuardedBy("this")
private int schedulingPlanNodeOrdinal;
@GuardedBy("this")
private final Map pendingSplitsByPlanNode;
// number of created PrioritizedSplitRunners that haven't yet finished
private final AtomicLong remainingSplitRunners = new AtomicLong();
public SqlTaskExecution(
TaskStateMachine taskStateMachine,
TaskContext taskContext,
Span taskSpan,
OutputBuffer outputBuffer,
LocalExecutionPlan localExecutionPlan,
TaskExecutor taskExecutor,
SplitMonitor splitMonitor,
Tracer tracer,
Executor notificationExecutor)
{
this.taskStateMachine = requireNonNull(taskStateMachine, "taskStateMachine is null");
this.taskId = taskStateMachine.getTaskId();
this.taskSpan = requireNonNull(taskSpan, "taskSpan is null");
this.taskContext = requireNonNull(taskContext, "taskContext is null");
this.outputBuffer = requireNonNull(outputBuffer, "outputBuffer is null");
this.taskExecutor = requireNonNull(taskExecutor, "taskExecutor is null");
this.notificationExecutor = requireNonNull(notificationExecutor, "notificationExecutor is null");
this.splitMonitor = requireNonNull(splitMonitor, "splitMonitor is null");
this.driverAndTaskTerminationTracker = new DriverAndTaskTerminationTracker(taskStateMachine);
try (SetThreadName _ = new SetThreadName("Task-%s", taskId)) {
List driverFactories = localExecutionPlan.getDriverFactories();
// index driver factories
Set partitionedSources = ImmutableSet.copyOf(localExecutionPlan.getPartitionedSourceOrder());
ImmutableMap.Builder driverRunnerFactoriesWithSplitLifeCycle = ImmutableMap.builder();
ImmutableList.Builder driverRunnerFactoriesWithTaskLifeCycle = ImmutableList.builder();
ImmutableMap.Builder driverRunnerFactoriesWithRemoteSource = ImmutableMap.builder();
for (DriverFactory driverFactory : driverFactories) {
Optional sourceId = driverFactory.getSourceId();
if (sourceId.isPresent() && partitionedSources.contains(sourceId.get())) {
driverRunnerFactoriesWithSplitLifeCycle.put(sourceId.get(), new DriverSplitRunnerFactory(driverFactory, tracer, true));
}
else {
DriverSplitRunnerFactory runnerFactory = new DriverSplitRunnerFactory(driverFactory, tracer, false);
sourceId.ifPresent(planNodeId -> driverRunnerFactoriesWithRemoteSource.put(planNodeId, runnerFactory));
driverRunnerFactoriesWithTaskLifeCycle.add(runnerFactory);
}
}
this.driverRunnerFactoriesWithSplitLifeCycle = driverRunnerFactoriesWithSplitLifeCycle.buildOrThrow();
this.driverRunnerFactoriesWithTaskLifeCycle = driverRunnerFactoriesWithTaskLifeCycle.build();
this.driverRunnerFactoriesWithRemoteSource = driverRunnerFactoriesWithRemoteSource.buildOrThrow();
this.allDriverRunnerFactories = ImmutableList.builderWithExpectedSize(driverFactories.size())
.addAll(this.driverRunnerFactoriesWithTaskLifeCycle)
.addAll(this.driverRunnerFactoriesWithSplitLifeCycle.values())
.build();
this.pendingSplitsByPlanNode = this.driverRunnerFactoriesWithSplitLifeCycle.keySet().stream()
.collect(toImmutableMap(identity(), ignore -> new PendingSplitsForPlanNode()));
sourceStartOrder = localExecutionPlan.getPartitionedSourceOrder();
checkArgument(this.driverRunnerFactoriesWithSplitLifeCycle.keySet().equals(partitionedSources),
"Fragment is partitioned, but not all partitioned drivers were found");
// don't register the task if it is already completed (most likely failed during planning above)
if (taskStateMachine.getState().isTerminatingOrDone()) {
taskHandle = null;
driverFactories.forEach(DriverFactory::noMoreDrivers);
}
else {
taskHandle = createTaskHandle(taskStateMachine, taskContext, outputBuffer, driverFactories, taskExecutor, driverAndTaskTerminationTracker);
}
taskStateMachine.addStateChangeListener(state -> {
if (state.isDone()) {
for (DriverSplitRunnerFactory factory : allDriverRunnerFactories) {
factory.getPipelineSpan().end();
}
}
});
}
}
// this must be synchronized to prevent a concurrent call to checkTaskCompletion() from proceeding before all task lifecycle drivers are created
public synchronized void start()
{
try (SetThreadName _ = new SetThreadName("Task-%s", getTaskId())) {
// Signal immediate termination complete if task termination has started
if (taskStateMachine.getState().isTerminating()) {
taskStateMachine.terminationComplete();
}
else if (taskHandle != null) {
// The scheduleDriversForTaskLifeCycle method calls enqueueDriverSplitRunner, which registers a callback with access to this object.
// The call back is accessed from another thread, so this code cannot be placed in the constructor. This must also happen before outputBuffer
// callbacks are registered to prevent a task completion check before task lifecycle splits are created
scheduleDriversForTaskLifeCycle();
// Output buffer state change listener callback must not run in the constructor to avoid leaking a reference to "this" across to another thread
outputBuffer.addStateChangeListener(new CheckTaskCompletionOnBufferFinish(SqlTaskExecution.this));
}
}
}
// this is a separate method to ensure that the `this` reference is not leaked during construction
private static TaskHandle createTaskHandle(
TaskStateMachine taskStateMachine,
TaskContext taskContext,
OutputBuffer outputBuffer,
List driverFactories,
TaskExecutor taskExecutor,
DriverAndTaskTerminationTracker driverAndTaskTerminationTracker)
{
TaskHandle taskHandle = taskExecutor.addTask(
taskStateMachine.getTaskId(),
outputBuffer::getUtilization,
getInitialSplitsPerNode(taskContext.getSession()),
getSplitConcurrencyAdjustmentInterval(taskContext.getSession()),
getMaxDriversPerTask(taskContext.getSession()));
taskStateMachine.addStateChangeListener(state -> {
if (state.isTerminatingOrDone()) {
if (!taskHandle.isDestroyed()) {
taskExecutor.removeTask(taskHandle);
for (DriverFactory factory : driverFactories) {
factory.noMoreDrivers();
}
}
// Need to re-check the live driver count since termination may have occurred without any running
if (state.isTerminating()) {
driverAndTaskTerminationTracker.checkTaskTermination();
}
}
});
return taskHandle;
}
public TaskId getTaskId()
{
return taskId;
}
public TaskContext getTaskContext()
{
return taskContext;
}
public void addSplitAssignments(List splitAssignments)
{
requireNonNull(splitAssignments, "splitAssignments is null");
checkState(!Thread.holdsLock(this), "Cannot add split assignments while holding a lock on the %s", getClass().getSimpleName());
// Avoid accepting new splits once the task is terminating or done
if (taskStateMachine.getState().isTerminatingOrDone()) {
return;
}
try (SetThreadName _ = new SetThreadName("Task-%s", taskId)) {
// update our record of split assignments and schedule drivers for new partitioned splits
Set updatedUnpartitionedSources = updateSplitAssignments(splitAssignments);
for (PlanNodeId planNodeId : updatedUnpartitionedSources) {
DriverSplitRunnerFactory factory = driverRunnerFactoriesWithRemoteSource.get(planNodeId);
// schedule splits outside the lock
factory.scheduleSplits();
}
// we may have transitioned to no more splits, so check for completion
checkTaskCompletion();
}
}
private synchronized Set updateSplitAssignments(List splitAssignments)
{
ImmutableSet.Builder updatedUnpartitionedSources = ImmutableSet.builder();
List unacknowledgedSplitAssignment = new ArrayList<>(splitAssignments.size());
// first remove any split that was already acknowledged
for (SplitAssignment splitAssignment : splitAssignments) {
// drop assignments containing no unacknowledged splits
// the noMoreSplits signal acknowledgement is not tracked but it is okay to deliver it more than once
if (!splitAssignment.getSplits().isEmpty() || splitAssignment.isNoMoreSplits()) {
PlanNodeId planNodeId = splitAssignment.getPlanNodeId();
long currentMaxAcknowledgedSplit = maxAcknowledgedSplitByPlanNode.getOrDefault(planNodeId, Long.MIN_VALUE);
long maxAcknowledgedSplit = currentMaxAcknowledgedSplit;
ImmutableSet.Builder builder = ImmutableSet.builderWithExpectedSize(splitAssignment.getSplits().size());
for (ScheduledSplit split : splitAssignment.getSplits()) {
long sequenceId = split.getSequenceId();
// previously acknowledged splits can be included in source
if (sequenceId > currentMaxAcknowledgedSplit) {
builder.add(split);
}
if (sequenceId > maxAcknowledgedSplit) {
maxAcknowledgedSplit = sequenceId;
}
}
if (maxAcknowledgedSplit > currentMaxAcknowledgedSplit) {
maxAcknowledgedSplitByPlanNode.put(planNodeId, maxAcknowledgedSplit);
}
Set newSplits = builder.build();
// We may have filtered all splits out, so only proceed with updates if new splits are
// present or noMoreSplits is set
if (!newSplits.isEmpty() || splitAssignment.isNoMoreSplits()) {
unacknowledgedSplitAssignment.add(new SplitAssignment(splitAssignment.getPlanNodeId(), newSplits, splitAssignment.isNoMoreSplits()));
}
}
}
// update task with new sources
for (SplitAssignment splitAssignment : unacknowledgedSplitAssignment) {
if (driverRunnerFactoriesWithSplitLifeCycle.containsKey(splitAssignment.getPlanNodeId())) {
schedulePartitionedSource(splitAssignment);
}
else {
// tell existing drivers about the new splits
DriverSplitRunnerFactory factory = driverRunnerFactoriesWithRemoteSource.get(splitAssignment.getPlanNodeId());
factory.enqueueSplits(splitAssignment.getSplits(), splitAssignment.isNoMoreSplits());
updatedUnpartitionedSources.add(splitAssignment.getPlanNodeId());
}
}
return updatedUnpartitionedSources.build();
}
@GuardedBy("this")
private void mergeIntoPendingSplits(PlanNodeId planNodeId, Set scheduledSplits, boolean noMoreSplits)
{
checkHoldsLock();
DriverSplitRunnerFactory partitionedDriverFactory = driverRunnerFactoriesWithSplitLifeCycle.get(planNodeId);
PendingSplitsForPlanNode pendingSplitsForPlanNode = pendingSplitsByPlanNode.get(planNodeId);
partitionedDriverFactory.splitsAdded(scheduledSplits.size(), SplitWeight.rawValueSum(scheduledSplits, scheduledSplit -> scheduledSplit.getSplit().getSplitWeight()));
for (ScheduledSplit scheduledSplit : scheduledSplits) {
pendingSplitsForPlanNode.addSplit(scheduledSplit);
}
if (noMoreSplits) {
pendingSplitsForPlanNode.setNoMoreSplits();
}
}
private synchronized void schedulePartitionedSource(SplitAssignment splitAssignmentUpdate)
{
mergeIntoPendingSplits(splitAssignmentUpdate.getPlanNodeId(), splitAssignmentUpdate.getSplits(), splitAssignmentUpdate.isNoMoreSplits());
while (schedulingPlanNodeOrdinal < sourceStartOrder.size()) {
PlanNodeId schedulingPlanNode = sourceStartOrder.get(schedulingPlanNodeOrdinal);
DriverSplitRunnerFactory partitionedDriverRunnerFactory = driverRunnerFactoriesWithSplitLifeCycle.get(schedulingPlanNode);
PendingSplitsForPlanNode pendingSplits = pendingSplitsByPlanNode.get(schedulingPlanNode);
// Enqueue driver runners with split lifecycle for this plan node and driver life cycle combination.
Set removed = pendingSplits.removeAllSplits();
ImmutableList.Builder runners = ImmutableList.builderWithExpectedSize(removed.size());
for (ScheduledSplit scheduledSplit : removed) {
// create a new driver for the split
runners.add(partitionedDriverRunnerFactory.createPartitionedDriverRunner(scheduledSplit));
}
enqueueDriverSplitRunner(false, runners.build());
// If all driver runners have been enqueued for this plan node and driver life cycle combination,
// move on to the next plan node.
if (pendingSplits.getState() != NO_MORE_SPLITS) {
break;
}
partitionedDriverRunnerFactory.noMoreDriverRunner();
pendingSplits.markAsCleanedUp();
schedulingPlanNodeOrdinal++;
}
}
private void scheduleDriversForTaskLifeCycle()
{
// This method is called at the beginning of the task.
// It schedules drivers for all the pipelines that have task life cycle.
List runners = new ArrayList<>();
for (DriverSplitRunnerFactory driverRunnerFactory : driverRunnerFactoriesWithTaskLifeCycle) {
for (int i = 0; i < driverRunnerFactory.getDriverInstances().orElse(1); i++) {
runners.add(driverRunnerFactory.createUnpartitionedDriverRunner());
}
}
enqueueDriverSplitRunner(true, runners);
for (DriverSplitRunnerFactory driverRunnerFactory : driverRunnerFactoriesWithTaskLifeCycle) {
driverRunnerFactory.noMoreDriverRunner();
verify(driverRunnerFactory.isNoMoreDriverRunner());
}
checkTaskCompletion();
}
private synchronized void enqueueDriverSplitRunner(boolean forceRunSplit, List runners)
{
// schedule driver to be executed
List> finishedFutures = taskExecutor.enqueueSplits(taskHandle, forceRunSplit, runners);
checkState(finishedFutures.size() == runners.size(), "Expected %s futures but got %s", runners.size(), finishedFutures.size());
// record new split runners
remainingSplitRunners.addAndGet(runners.size());
// when split runner completes, update state and fire events
for (int i = 0; i < finishedFutures.size(); i++) {
ListenableFuture finishedFuture = finishedFutures.get(i);
DriverSplitRunner splitRunner = runners.get(i);
Futures.addCallback(finishedFuture, new FutureCallback