-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Propagate stats and cost in distributed explain plans #11268
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d5dd094
b902196
4b1ec93
15c9833
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,22 +38,29 @@ public class CachingCostProvider | |
| private final Lookup lookup; | ||
| private final Session session; | ||
| private final TypeProvider types; | ||
| private final PlanNodeSourceProvider sourceProvider; | ||
|
|
||
| private final Map<PlanNode, PlanNodeCostEstimate> cache = new IdentityHashMap<>(); | ||
|
|
||
| public CachingCostProvider(CostCalculator costCalculator, StatsProvider statsProvider, Session session, TypeProvider types) | ||
| public CachingCostProvider(CostCalculator costCalculator, StatsProvider statsProvider, Optional<Memo> memo, Lookup lookup, Session session, TypeProvider types) | ||
| { | ||
| this(costCalculator, statsProvider, Optional.empty(), noLookup(), session, types); | ||
| this(costCalculator, statsProvider, memo, lookup, session, types, PlanNode::getSources); | ||
| } | ||
|
|
||
| public CachingCostProvider(CostCalculator costCalculator, StatsProvider statsProvider, Optional<Memo> memo, Lookup lookup, Session session, TypeProvider types) | ||
| public CachingCostProvider(CostCalculator costCalculator, StatsProvider statsProvider, Session session, TypeProvider types, PlanNodeSourceProvider sourceProvider) | ||
| { | ||
| this(costCalculator, statsProvider, Optional.empty(), noLookup(), session, types, sourceProvider); | ||
| } | ||
|
|
||
| public CachingCostProvider(CostCalculator costCalculator, StatsProvider statsProvider, Optional<Memo> memo, Lookup lookup, Session session, TypeProvider types, PlanNodeSourceProvider sourceProvider) | ||
| { | ||
| this.costCalculator = requireNonNull(costCalculator, "costCalculator is null"); | ||
| this.statsProvider = requireNonNull(statsProvider, "statsProvider is null"); | ||
| this.memo = requireNonNull(memo, "memo is null"); | ||
| this.lookup = requireNonNull(lookup, "lookup is null"); | ||
| this.session = requireNonNull(session, "session is null"); | ||
| this.types = requireNonNull(types, "types is null"); | ||
| this.sourceProvider = requireNonNull(sourceProvider, "sourceProvider is null"); | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -95,7 +102,7 @@ private PlanNodeCostEstimate calculateCumulativeCost(PlanNode node) | |
| { | ||
| PlanNodeCostEstimate localCosts = costCalculator.calculateCost(node, statsProvider, lookup, session, types); | ||
|
|
||
| PlanNodeCostEstimate sourcesCost = node.getSources().stream() | ||
| PlanNodeCostEstimate sourcesCost = sourceProvider.getSources(node).stream() | ||
|
||
| .map(this::getCumulativeCost) | ||
| .reduce(ZERO_COST, PlanNodeCostEstimate::add); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| /* | ||
| * 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.cost; | ||
|
|
||
| import com.facebook.presto.Session; | ||
| import com.facebook.presto.execution.scheduler.NodeSchedulerConfig; | ||
| import com.facebook.presto.metadata.InternalNodeManager; | ||
| import com.facebook.presto.sql.planner.TypeProvider; | ||
| import com.facebook.presto.sql.planner.iterative.Lookup; | ||
| import com.facebook.presto.sql.planner.plan.ExchangeNode; | ||
| import com.facebook.presto.sql.planner.plan.PlanNode; | ||
| import com.facebook.presto.sql.planner.plan.RemoteSourceNode; | ||
|
|
||
| import java.util.function.IntSupplier; | ||
|
|
||
| import static com.facebook.presto.cost.CostCalculatorUsingExchanges.calculateExchangeCost; | ||
| import static com.facebook.presto.cost.CostCalculatorUsingExchanges.currentNumberOfWorkerNodes; | ||
| import static com.facebook.presto.sql.planner.plan.ExchangeNode.Scope.REMOTE; | ||
| import static java.util.Objects.requireNonNull; | ||
|
|
||
| public class FragmentedPlanCostCalculator | ||
| implements CostCalculator | ||
| { | ||
| private final CostCalculator delegate; | ||
| private final FragmentedPlanSourceProvider sourceProvider; | ||
| private final IntSupplier numberOfNodes; | ||
|
|
||
| public FragmentedPlanCostCalculator(FragmentedPlanSourceProvider sourceProvider, CostCalculator delegate, InternalNodeManager nodeManager, NodeSchedulerConfig nodeSchedulerConfig) | ||
| { | ||
| this(delegate, sourceProvider, currentNumberOfWorkerNodes(nodeSchedulerConfig.isIncludeCoordinator(), nodeManager)); | ||
| } | ||
|
|
||
| public FragmentedPlanCostCalculator(CostCalculator delegate, FragmentedPlanSourceProvider sourceProvider, IntSupplier numberOfNodes) | ||
| { | ||
| this.sourceProvider = requireNonNull(sourceProvider, "sourceProvider is null"); | ||
| this.delegate = requireNonNull(delegate, "delegate is null"); | ||
| this.numberOfNodes = requireNonNull(numberOfNodes, "numberOfNodes is null"); | ||
| } | ||
|
|
||
| @Override | ||
| public PlanNodeCostEstimate calculateCost(PlanNode node, StatsProvider stats, Lookup lookup, Session session, TypeProvider types) | ||
| { | ||
| if (node instanceof RemoteSourceNode) { | ||
| return calculateRemoteSourceNodeCost((RemoteSourceNode) node, stats, types); | ||
| } | ||
| else { | ||
| return delegate.calculateCost(node, stats, lookup, session, types); | ||
| } | ||
| } | ||
|
|
||
| private PlanNodeCostEstimate calculateRemoteSourceNodeCost(RemoteSourceNode node, StatsProvider stats, TypeProvider types) | ||
| { | ||
| PlanNodeCostEstimate costEstimate = PlanNodeCostEstimate.ZERO_COST; | ||
| ExchangeNode.Type exchangeType = node.getExchangeType(); | ||
| for (PlanNode source : sourceProvider.getSources(node)) { | ||
| PlanNodeCostEstimate exchangeCost = calculateExchangeCost(numberOfNodes.getAsInt(), stats.getStats(source), node.getOutputSymbols(), exchangeType, REMOTE, types); | ||
| costEstimate.add(exchangeCost); | ||
| } | ||
| return costEstimate; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| /* | ||
| * 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.cost; | ||
|
|
||
| import com.facebook.presto.sql.planner.PlanFragment; | ||
| import com.facebook.presto.sql.planner.plan.PlanFragmentId; | ||
| import com.facebook.presto.sql.planner.plan.PlanNode; | ||
| import com.facebook.presto.sql.planner.plan.RemoteSourceNode; | ||
| import com.google.common.collect.ImmutableMap; | ||
|
|
||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| import static com.google.common.base.Verify.verify; | ||
| import static com.google.common.collect.ImmutableList.toImmutableList; | ||
| import static com.google.common.collect.ImmutableMap.toImmutableMap; | ||
| import static java.util.Objects.requireNonNull; | ||
|
|
||
| public class FragmentedPlanSourceProvider | ||
| implements PlanNodeSourceProvider | ||
| { | ||
| private final Map<PlanFragmentId, PlanFragment> fragments; | ||
|
|
||
| public static FragmentedPlanSourceProvider create(List<PlanFragment> planFragments) | ||
| { | ||
| Map<PlanFragmentId, PlanFragment> fragmentIdPlanNodeMap = planFragments.stream() | ||
| .collect(toImmutableMap(PlanFragment::getId, fragment -> fragment)); | ||
| return new FragmentedPlanSourceProvider(fragmentIdPlanNodeMap); | ||
| } | ||
|
|
||
| private FragmentedPlanSourceProvider(Map<PlanFragmentId, PlanFragment> fragments) | ||
| { | ||
| this.fragments = ImmutableMap.copyOf(requireNonNull(fragments, "fragments is null")); | ||
| } | ||
|
|
||
| @Override | ||
| public List<PlanNode> getSources(PlanNode node) | ||
| { | ||
| if (node instanceof RemoteSourceNode) { | ||
| return ((RemoteSourceNode) node).getSourceFragmentIds().stream() | ||
| .map(id -> { | ||
| verify(fragments.containsKey(id), "fragment id not in map: %s", id); | ||
| return fragments.get(id).getRoot(); | ||
| }) | ||
| .collect(toImmutableList()); | ||
| } | ||
|
|
||
| return node.getSources(); | ||
| } | ||
|
|
||
| public List<PlanFragment> getSourceFragments(RemoteSourceNode node) | ||
| { | ||
| return node.getSourceFragmentIds().stream() | ||
| .map(id -> { | ||
| verify(fragments.containsKey(id), "fragment id not in map: %s", id); | ||
| return fragments.get(id); | ||
| }) | ||
| .collect(toImmutableList()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| /* | ||
| * 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.cost; | ||
|
|
||
| import com.facebook.presto.Session; | ||
| import com.facebook.presto.sql.planner.PlanFragment; | ||
| import com.facebook.presto.sql.planner.Symbol; | ||
| import com.facebook.presto.sql.planner.TypeProvider; | ||
| import com.facebook.presto.sql.planner.iterative.Lookup; | ||
| import com.facebook.presto.sql.planner.plan.PlanNode; | ||
| import com.facebook.presto.sql.planner.plan.RemoteSourceNode; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import static com.facebook.presto.cost.PlanNodeStatsEstimateMath.addStatsAndMaxDistinctValues; | ||
| import static com.google.common.base.Preconditions.checkArgument; | ||
| import static com.google.common.base.Verify.verify; | ||
| import static java.util.Objects.requireNonNull; | ||
|
|
||
| public class FragmentedPlanStatsCalculator | ||
| implements StatsCalculator | ||
| { | ||
| private final StatsCalculator delegate; | ||
| private final FragmentedPlanSourceProvider sourceProvider; | ||
|
|
||
| public FragmentedPlanStatsCalculator(StatsCalculator delegate, FragmentedPlanSourceProvider sourceProvider) | ||
|
||
| { | ||
| this.delegate = requireNonNull(delegate, "delegate is null"); | ||
| this.sourceProvider = requireNonNull(sourceProvider, "source provider is null"); | ||
| } | ||
|
|
||
| @Override | ||
| public PlanNodeStatsEstimate calculateStats(PlanNode node, StatsProvider sourceStats, Lookup lookup, Session session, TypeProvider types) | ||
| { | ||
| if (node instanceof RemoteSourceNode) { | ||
| return calculateRemoteSourceStats((RemoteSourceNode) node, sourceStats); | ||
| } | ||
| return delegate.calculateStats(node, sourceStats, lookup, session, types); | ||
| } | ||
|
|
||
| private PlanNodeStatsEstimate calculateRemoteSourceStats(RemoteSourceNode node, StatsProvider statsProvider) | ||
| { | ||
| PlanNodeStatsEstimate estimate = null; | ||
| for (PlanFragment sourceFragment : sourceProvider.getSourceFragments(node)) { | ||
| PlanNodeStatsEstimate sourceStatsWithMappedSymbols = mapToOutputSymbols(statsProvider.getStats(sourceFragment.getRoot()), sourceFragment.getPartitioningScheme().getOutputLayout(), node.getOutputSymbols()); | ||
|
||
|
|
||
| if (estimate != null) { | ||
| estimate = addStatsAndMaxDistinctValues(estimate, sourceStatsWithMappedSymbols); | ||
| } | ||
| else { | ||
| estimate = sourceStatsWithMappedSymbols; | ||
| } | ||
| } | ||
|
|
||
| verify(estimate != null, "estimate is null"); | ||
| return estimate; | ||
| } | ||
|
|
||
| private PlanNodeStatsEstimate mapToOutputSymbols(PlanNodeStatsEstimate estimate, List<Symbol> inputs, List<Symbol> outputs) | ||
| { | ||
| checkArgument(inputs.size() == outputs.size(), "Input symbols count does not match output symbols count"); | ||
| PlanNodeStatsEstimate.Builder mapped = PlanNodeStatsEstimate.builder() | ||
| .setOutputRowCount(estimate.getOutputRowCount()); | ||
|
|
||
| for (int i = 0; i < inputs.size(); i++) { | ||
| mapped.addSymbolStatistics(outputs.get(i), estimate.getSymbolStatistics(inputs.get(i))); | ||
| } | ||
|
|
||
| return mapped.build(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| /* | ||
| * 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.cost; | ||
|
|
||
| import com.facebook.presto.sql.planner.plan.PlanNode; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| @FunctionalInterface | ||
| public interface PlanNodeSourceProvider | ||
| { | ||
| List<PlanNode> getSources(PlanNode node); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you check this PR for potential conflicts with #11267?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doesn't overlap i don't think.