| 1 | #pragma once | 
|---|
| 2 |  | 
|---|
| 3 | #include <Core/NamesAndTypes.h> | 
|---|
| 4 | #include <Storages/MergeTree/RangesInDataPart.h> | 
|---|
| 5 | #include <Storages/MergeTree/MergeTreeBlockReadUtils.h> | 
|---|
| 6 | #include <Storages/SelectQueryInfo.h> | 
|---|
| 7 | #include <mutex> | 
|---|
| 8 |  | 
|---|
| 9 |  | 
|---|
| 10 | namespace DB | 
|---|
| 11 | { | 
|---|
| 12 |  | 
|---|
| 13 | using MergeTreeReadTaskPtr = std::unique_ptr<MergeTreeReadTask>; | 
|---|
| 14 |  | 
|---|
| 15 | /**   Provides read tasks for MergeTreeThreadSelectBlockInputStream`s in fine-grained batches, allowing for more | 
|---|
| 16 | *    uniform distribution of work amongst multiple threads. All parts and their ranges are divided into `threads` | 
|---|
| 17 | *    workloads with at most `sum_marks / threads` marks. Then, threads are performing reads from these workloads | 
|---|
| 18 | *    in "sequential" manner, requesting work in small batches. As soon as some thread has exhausted | 
|---|
| 19 | *    it's workload, it either is signaled that no more work is available (`do_not_steal_tasks == false`) or | 
|---|
| 20 | *    continues taking small batches from other threads' workloads (`do_not_steal_tasks == true`). | 
|---|
| 21 | */ | 
|---|
| 22 | class MergeTreeReadPool : private boost::noncopyable | 
|---|
| 23 | { | 
|---|
| 24 | public: | 
|---|
| 25 | /** Pull could dynamically lower (backoff) number of threads, if read operation are too slow. | 
|---|
| 26 | * Settings for that backoff. | 
|---|
| 27 | */ | 
|---|
| 28 | struct BackoffSettings | 
|---|
| 29 | { | 
|---|
| 30 | /// Pay attention only to reads, that took at least this amount of time. If set to 0 - means backoff is disabled. | 
|---|
| 31 | size_t min_read_latency_ms = 1000; | 
|---|
| 32 | /// Count events, when read throughput is less than specified bytes per second. | 
|---|
| 33 | size_t max_throughput = 1048576; | 
|---|
| 34 | /// Do not pay attention to event, if not enough time passed since previous event. | 
|---|
| 35 | size_t min_interval_between_events_ms = 1000; | 
|---|
| 36 | /// Number of events to do backoff - to lower number of threads in pool. | 
|---|
| 37 | size_t min_events = 2; | 
|---|
| 38 |  | 
|---|
| 39 | /// Constants above is just an example. | 
|---|
| 40 | BackoffSettings(const Settings & settings) | 
|---|
| 41 | : min_read_latency_ms(settings.read_backoff_min_latency_ms.totalMilliseconds()), | 
|---|
| 42 | max_throughput(settings.read_backoff_max_throughput), | 
|---|
| 43 | min_interval_between_events_ms(settings.read_backoff_min_interval_between_events_ms.totalMilliseconds()), | 
|---|
| 44 | min_events(settings.read_backoff_min_events) | 
|---|
| 45 | { | 
|---|
| 46 | } | 
|---|
| 47 |  | 
|---|
| 48 | BackoffSettings() : min_read_latency_ms(0) {} | 
|---|
| 49 | }; | 
|---|
| 50 |  | 
|---|
| 51 | BackoffSettings backoff_settings; | 
|---|
| 52 |  | 
|---|
| 53 | private: | 
|---|
| 54 | /** State to track numbers of slow reads. | 
|---|
| 55 | */ | 
|---|
| 56 | struct BackoffState | 
|---|
| 57 | { | 
|---|
| 58 | size_t current_threads; | 
|---|
| 59 | Stopwatch time_since_prev_event {CLOCK_MONOTONIC_COARSE}; | 
|---|
| 60 | size_t num_events = 0; | 
|---|
| 61 |  | 
|---|
| 62 | BackoffState(size_t threads) : current_threads(threads) {} | 
|---|
| 63 | }; | 
|---|
| 64 |  | 
|---|
| 65 | BackoffState backoff_state; | 
|---|
| 66 |  | 
|---|
| 67 | public: | 
|---|
| 68 | MergeTreeReadPool( | 
|---|
| 69 | const size_t threads_, const size_t sum_marks_, const size_t min_marks_for_concurrent_read_, | 
|---|
| 70 | RangesInDataParts parts_, const MergeTreeData & data_, const PrewhereInfoPtr & prewhere_info_, | 
|---|
| 71 | const bool check_columns_, const Names & column_names_, | 
|---|
| 72 | const BackoffSettings & backoff_settings_, size_t preferred_block_size_bytes_, | 
|---|
| 73 | const bool do_not_steal_tasks_ = false); | 
|---|
| 74 |  | 
|---|
| 75 | MergeTreeReadTaskPtr getTask(const size_t min_marks_to_read, const size_t thread, const Names & ordered_names); | 
|---|
| 76 |  | 
|---|
| 77 | /** Each worker could call this method and pass information about read performance. | 
|---|
| 78 | * If read performance is too low, pool could decide to lower number of threads: do not assign more tasks to several threads. | 
|---|
| 79 | * This allows to overcome excessive load to disk subsystem, when reads are not from page cache. | 
|---|
| 80 | */ | 
|---|
| 81 | void profileFeedback(const ReadBufferFromFileBase::ProfileInfo info); | 
|---|
| 82 |  | 
|---|
| 83 | /// This method tells which mark ranges we have to read if we start from @from mark range | 
|---|
| 84 | MarkRanges getRestMarks(const MergeTreeDataPart & part, const MarkRange & from) const; | 
|---|
| 85 |  | 
|---|
| 86 | Block () const; | 
|---|
| 87 |  | 
|---|
| 88 | private: | 
|---|
| 89 | std::vector<size_t> fillPerPartInfo( | 
|---|
| 90 | RangesInDataParts & parts, const bool check_columns); | 
|---|
| 91 |  | 
|---|
| 92 | void fillPerThreadInfo( | 
|---|
| 93 | const size_t threads, const size_t sum_marks, std::vector<size_t> per_part_sum_marks, | 
|---|
| 94 | RangesInDataParts & parts, const size_t min_marks_for_concurrent_read); | 
|---|
| 95 |  | 
|---|
| 96 | std::vector<std::shared_lock<std::shared_mutex>> per_part_columns_lock; | 
|---|
| 97 | const MergeTreeData & data; | 
|---|
| 98 | Names column_names; | 
|---|
| 99 | bool do_not_steal_tasks; | 
|---|
| 100 | bool predict_block_size_bytes; | 
|---|
| 101 | std::vector<NameSet> per_part_column_name_set; | 
|---|
| 102 | std::vector<NamesAndTypesList> per_part_columns; | 
|---|
| 103 | std::vector<NamesAndTypesList> per_part_pre_columns; | 
|---|
| 104 | std::vector<char> per_part_should_reorder; | 
|---|
| 105 | std::vector<MergeTreeBlockSizePredictorPtr> per_part_size_predictor; | 
|---|
| 106 | PrewhereInfoPtr prewhere_info; | 
|---|
| 107 |  | 
|---|
| 108 | struct Part | 
|---|
| 109 | { | 
|---|
| 110 | MergeTreeData::DataPartPtr data_part; | 
|---|
| 111 | size_t part_index_in_query; | 
|---|
| 112 | }; | 
|---|
| 113 |  | 
|---|
| 114 | std::vector<Part> parts_with_idx; | 
|---|
| 115 |  | 
|---|
| 116 | struct ThreadTask | 
|---|
| 117 | { | 
|---|
| 118 | struct PartIndexAndRange | 
|---|
| 119 | { | 
|---|
| 120 | size_t part_idx; | 
|---|
| 121 | MarkRanges ranges; | 
|---|
| 122 | }; | 
|---|
| 123 |  | 
|---|
| 124 | std::vector<PartIndexAndRange> parts_and_ranges; | 
|---|
| 125 | std::vector<size_t> sum_marks_in_parts; | 
|---|
| 126 | }; | 
|---|
| 127 |  | 
|---|
| 128 | std::vector<ThreadTask> threads_tasks; | 
|---|
| 129 |  | 
|---|
| 130 | std::set<size_t> remaining_thread_tasks; | 
|---|
| 131 |  | 
|---|
| 132 | RangesInDataParts parts_ranges; | 
|---|
| 133 |  | 
|---|
| 134 | mutable std::mutex mutex; | 
|---|
| 135 |  | 
|---|
| 136 | Logger * log = &Logger::get( "MergeTreeReadPool"); | 
|---|
| 137 | }; | 
|---|
| 138 |  | 
|---|
| 139 | using MergeTreeReadPoolPtr = std::shared_ptr<MergeTreeReadPool>; | 
|---|
| 140 |  | 
|---|
| 141 | } | 
|---|
| 142 |  | 
|---|