Maybe the easiest way to understand the objective of this algorithm is as a generalization of two simpler instance creation algorithms that decide how many instances of a fragment to create on each node, given a set of nodes that were already chosen by previous scheduling steps: 1. Instance creation for an interior fragment (i.e. a fragment without scans) without a UNION, where we create one finst
| 793 | // b) Create the finstances, based on the computed parallelism and assign the scan |
| 794 | // ranges to it. |
| 795 | void Scheduler::CreateCollocatedAndScanInstances(const ExecutorConfig& executor_config, |
| 796 | FragmentScheduleState* fragment_state, ScheduleState* state) { |
| 797 | const TPlanFragment& fragment = fragment_state->fragment; |
| 798 | bool has_union = ContainsUnionNode(fragment.plan); |
| 799 | DCHECK(has_union || ContainsScanNode(fragment.plan)); |
| 800 | // Build a map of hosts to the num instances this fragment should have, before we take |
| 801 | // into account scan ranges. If this fragment has input fragments, we always run with |
| 802 | // at least the same num instances as the input fragment. |
| 803 | std::unordered_map<NetworkAddressPB, int> instances_per_host; |
| 804 | |
| 805 | // Add hosts of input fragments, counting the number of instances of the fragment. |
| 806 | // Only do this if there's a union - otherwise only consider the parallelism of |
| 807 | // the input scan, for consistency with the previous behaviour of only using |
| 808 | // the parallelism of the scan. |
| 809 | if (has_union) { |
| 810 | for (FragmentIdx idx : fragment_state->exchange_input_fragments) { |
| 811 | std::unordered_map<NetworkAddressPB, int> input_fragment_instances_per_host; |
| 812 | const FragmentScheduleState& input_state = *state->GetFragmentScheduleState(idx); |
| 813 | for (const FInstanceScheduleState& instance_state : input_state.instance_states) { |
| 814 | ++input_fragment_instances_per_host[instance_state.host]; |
| 815 | } |
| 816 | // Merge with the existing hosts by taking the max num instances. |
| 817 | if (instances_per_host.empty()) { |
| 818 | // Optimization for the common case of one input fragment. |
| 819 | instances_per_host = move(input_fragment_instances_per_host); |
| 820 | } else { |
| 821 | for (auto& entry : input_fragment_instances_per_host) { |
| 822 | int& num_instances = instances_per_host[entry.first]; |
| 823 | num_instances = max(num_instances, entry.second); |
| 824 | } |
| 825 | } |
| 826 | } |
| 827 | } |
| 828 | |
| 829 | // Add hosts of scan nodes. |
| 830 | vector<TPlanNodeId> scan_node_ids = FindScanNodes(fragment.plan); |
| 831 | DCHECK(has_union || scan_node_ids.size() == 1) << "This method may need revisiting " |
| 832 | << "for plans with no union and multiple scans per fragment"; |
| 833 | vector<NetworkAddressPB> scan_hosts; |
| 834 | GetScanHosts(scan_node_ids, *fragment_state, &scan_hosts); |
| 835 | if (scan_hosts.empty() && instances_per_host.empty()) { |
| 836 | // None of the scan nodes have any scan ranges and there is no input fragment feeding |
| 837 | // into this fragment; run it on a random executor. |
| 838 | // TODO TODO: the TODO below seems partially stale |
| 839 | // TODO: we'll need to revisit this strategy once we can partition joins |
| 840 | // (in which case this fragment might be executing a right outer join |
| 841 | // with a large build table) |
| 842 | vector<BackendDescriptorPB> all_executors = |
| 843 | executor_config.group.GetAllExecutorDescriptors(); |
| 844 | int idx = std::uniform_int_distribution<int>(0, all_executors.size() - 1)( |
| 845 | *state->rng()); |
| 846 | const BackendDescriptorPB& be_desc = all_executors[idx]; |
| 847 | scan_hosts.push_back(be_desc.address()); |
| 848 | } |
| 849 | for (const NetworkAddressPB& host_addr : scan_hosts) { |
| 850 | // Ensure that the num instances is at least as many as input fragments. We don't |
| 851 | // want to increment if there were already some instances from the input fragment, |
| 852 | // since that could result in too high a num_instances. |
nothing calls this directly
no test coverage detected