| 1641 | } |
| 1642 | |
| 1643 | void Scheduler::AssignmentCtx::GetRemoteExecutorCandidates( |
| 1644 | const THdfsFileSplit* hdfs_file_split, int num_candidates, |
| 1645 | vector<IpAddr>* remote_executor_candidates) { |
| 1646 | // This should be given an empty vector |
| 1647 | DCHECK_EQ(remote_executor_candidates->size(), 0); |
| 1648 | // This function should not be called with 'num_candidates' exceeding the number of |
| 1649 | // executors. |
| 1650 | DCHECK_LE(num_candidates, executor_group_.NumExecutors()); |
| 1651 | // Two different hashes of the filename can result in the same executor. |
| 1652 | // The function should return distinct executors, so it may need to do more hashes |
| 1653 | // than 'num_candidates'. |
| 1654 | unordered_set<IpAddr> distinct_backends; |
| 1655 | distinct_backends.reserve(num_candidates); |
| 1656 | // Generate multiple hashes of the file split by using the hash as a seed to a PRNG. |
| 1657 | // Note: The hash includes the partition path hash, the filename (relative to the |
| 1658 | // partition directory), and the offset. The offset is used to allow very large files |
| 1659 | // that have multiple splits to be spread across more executors. |
| 1660 | uint32_t hash = static_cast<uint32_t>(hdfs_file_split->partition_path_hash); |
| 1661 | hash = HashUtil::Hash(hdfs_file_split->relative_path.data(), |
| 1662 | hdfs_file_split->relative_path.length(), hash); |
| 1663 | hash = HashUtil::Hash(&hdfs_file_split->offset, sizeof(hdfs_file_split->offset), hash); |
| 1664 | pcg32 prng(hash); |
| 1665 | // The function should return distinct executors, so it may need to do more hashes |
| 1666 | // than 'num_candidates'. To avoid any problem scenarios, limit the total number of |
| 1667 | // iterations. The number of iterations is set to a reasonably high level, because |
| 1668 | // on average the loop short circuits considerably earlier. Using a higher number of |
| 1669 | // iterations is useful for smaller clusters where we are using this function to get |
| 1670 | // all the backends in a consistent order rather than picking a consistent subset. |
| 1671 | // Suppose there are three nodes and the number of remote executor candidates is three. |
| 1672 | // One can calculate the probability of picking three distinct executors in at most |
| 1673 | // n iterations. For n=3, the second pick must not overlap the first (probability 2/3), |
| 1674 | // and the third pick must not be either the first or second (probability 1/3). So: |
| 1675 | // P(3) = 1*(2/3)*(1/3)=2/9 |
| 1676 | // The probability that it is done in at most n+1 steps is the probability that |
| 1677 | // it completed in n steps combined with the probability that it completes in the n+1st |
| 1678 | // step. In order to complete in the n+1st step, the previous n steps must not have |
| 1679 | // all landed on a single backend (probability (1/3)^(n-1)) and this step must not land |
| 1680 | // on the two backends already chosen (probability 1/3). So, the recursive step is: |
| 1681 | // P(n+1) = P(n) + (1 - P(n))*(1-(1/3)^(n-1))*(1/3) |
| 1682 | // Here are some example probabilities: |
| 1683 | // Probability of completing in at most 5 iterations: 0.6284 |
| 1684 | // Probability of completing in at most 10 iterations: 0.9506 |
| 1685 | // Probability of completing in at most 15 iterations: 0.9935 |
| 1686 | // Probability of completing in at most 20 iterations: 0.9991 |
| 1687 | int max_iterations = num_candidates * MAX_ITERATIONS_PER_EXECUTOR_CANDIDATE; |
| 1688 | for (int i = 0; i < max_iterations; ++i) { |
| 1689 | // Look up nearest IpAddr |
| 1690 | const IpAddr* executor_addr = executor_group_.GetHashRing()->GetNode(prng()); |
| 1691 | DCHECK(executor_addr != nullptr); |
| 1692 | auto insert_ret = distinct_backends.insert(*executor_addr); |
| 1693 | // The return type of unordered_set.insert() is a pair<iterator, bool> where the |
| 1694 | // second element is whether this was a new element. If this is a new element, |
| 1695 | // add this element to the return vector. |
| 1696 | if (insert_ret.second) { |
| 1697 | remote_executor_candidates->push_back(*executor_addr); |
| 1698 | } |
| 1699 | // Short-circuit if we reach the appropriate number of replicas |
| 1700 | if (remote_executor_candidates->size() == num_candidates) break; |
no test coverage detected