| 11 | } |
| 12 | |
| 13 | GetPriorityForLoadBalancing::Func |
| 14 | GetPriorityForLoadBalancing::getPriorityFunc(LoadBalancing load_balance, size_t offset, size_t pool_size) const |
| 15 | { |
| 16 | std::function<Priority(size_t index)> get_priority; |
| 17 | switch (load_balance) |
| 18 | { |
| 19 | case LoadBalancing::NEAREST_HOSTNAME: |
| 20 | if (hostname_prefix_distance.empty()) |
| 21 | throw Exception(ErrorCodes::LOGICAL_ERROR, "It's a bug: hostname_prefix_distance is not initialized"); |
| 22 | get_priority = [this](size_t i) { return Priority{static_cast<Int64>(hostname_prefix_distance[i])}; }; |
| 23 | break; |
| 24 | case LoadBalancing::HOSTNAME_LEVENSHTEIN_DISTANCE: |
| 25 | if (hostname_levenshtein_distance.empty()) |
| 26 | throw Exception(ErrorCodes::LOGICAL_ERROR, "It's a bug: hostname_levenshtein_distance is not initialized"); |
| 27 | get_priority = [this](size_t i) { return Priority{static_cast<Int64>(hostname_levenshtein_distance[i])}; }; |
| 28 | break; |
| 29 | case LoadBalancing::HOSTNAME_LONGEST_COMMON_PREFIX: |
| 30 | if (hostname_longest_common_prefix.empty()) |
| 31 | throw Exception(ErrorCodes::LOGICAL_ERROR, "It's a bug: hostname_longest_common_prefix is not initialized"); |
| 32 | /// `Priority` is "lower value means higher priority", and the other hostname strategies store a |
| 33 | /// *distance* (smaller == more similar == preferred) that is returned verbatim. Here we instead |
| 34 | /// store a *similarity* (the common prefix length, where larger == more similar == preferred), so |
| 35 | /// we negate it to turn it into a priority. We deliberately do not store a synthetic distance such |
| 36 | /// as `BIG - length`: negating here keeps the stored vector semantically honest (it really is the |
| 37 | /// common prefix length) and avoids an arbitrary magic constant. |
| 38 | get_priority = [this](size_t i) { return Priority{-static_cast<Int64>(hostname_longest_common_prefix[i])}; }; |
| 39 | break; |
| 40 | case LoadBalancing::HOSTNAME_LONGEST_COMMON_SUFFIX: |
| 41 | if (hostname_longest_common_suffix.empty()) |
| 42 | throw Exception(ErrorCodes::LOGICAL_ERROR, "It's a bug: hostname_longest_common_suffix is not initialized"); |
| 43 | /// See the comment for HOSTNAME_LONGEST_COMMON_PREFIX: the stored value is a similarity (common |
| 44 | /// suffix length, larger == preferred), so we negate it to obtain a priority (lower == preferred). |
| 45 | get_priority = [this](size_t i) { return Priority{-static_cast<Int64>(hostname_longest_common_suffix[i])}; }; |
| 46 | break; |
| 47 | case LoadBalancing::IN_ORDER: |
| 48 | get_priority = [](size_t i) { return Priority{static_cast<Int64>(i)}; }; |
| 49 | break; |
| 50 | case LoadBalancing::RANDOM: |
| 51 | break; |
| 52 | case LoadBalancing::FIRST_OR_RANDOM: |
| 53 | get_priority = [offset](size_t i) { return i != offset ? Priority{1} : Priority{0}; }; |
| 54 | break; |
| 55 | case LoadBalancing::ROUND_ROBIN: |
| 56 | /// `last_used` is `std::atomic<size_t>`. Use relaxed ordering: this counter |
| 57 | /// is only used to derive a rotation index, and there is no happens-before |
| 58 | /// relationship that needs to be observed by other threads. We compute |
| 59 | /// `local_last_used` from the pre-increment value so that two concurrent |
| 60 | /// callers receive distinct rotations of the round-robin sequence. |
| 61 | auto local_last_used = last_used.fetch_add(1, std::memory_order_relaxed) % pool_size; |
| 62 | |
| 63 | // Example: pool_size = 5 |
| 64 | // | local_last_used | i=0 | i=1 | i=2 | i=3 | i=4 | |
| 65 | // | 0 | 4 | 0 | 1 | 2 | 3 | |
| 66 | // | 1 | 3 | 4 | 0 | 1 | 2 | |
| 67 | // | 2 | 2 | 3 | 4 | 0 | 1 | |
| 68 | // | 3 | 1 | 2 | 3 | 4 | 0 | |
| 69 | // | 4 | 0 | 1 | 2 | 3 | 4 | |
| 70 | |