be/src/exec/spill/spill_file_manager.cpp
Line | Count | Source |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | |
18 | | #include "exec/spill/spill_file_manager.h" |
19 | | |
20 | | #include <fmt/format.h> |
21 | | #include <glog/logging.h> |
22 | | |
23 | | #include <algorithm> |
24 | | #include <filesystem> |
25 | | #include <memory> |
26 | | #include <string> |
27 | | #include <utility> |
28 | | |
29 | | #include "common/logging.h" |
30 | | #include "common/metrics/doris_metrics.h" |
31 | | #include "exec/spill/spill_file.h" |
32 | | #include "io/fs/file_system.h" |
33 | | #include "io/fs/local_file_system.h" |
34 | | #include "storage/olap_define.h" |
35 | | #include "util/debug_points.h" |
36 | | #include "util/parse_util.h" |
37 | | #include "util/pretty_printer.h" |
38 | | #include "util/time.h" |
39 | | |
40 | | namespace doris { |
41 | | |
42 | 205 | SpillFileManager::~SpillFileManager() { |
43 | | // QueryContext destruction can still queue failed deletions after stop(), for example while |
44 | | // VDataStreamMgr is being destroyed. Retry them once more before dropping the in-memory state. |
45 | | // Any directory that still cannot be deleted remains under the active spill root and will be |
46 | | // moved to the GC root by init() after restart. |
47 | 205 | _retry_pending_query_spill_directories(); |
48 | 205 | DorisMetrics::instance()->metric_registry()->deregister_entity(_entity); |
49 | 205 | } |
50 | | |
51 | | SpillFileManager::SpillFileManager( |
52 | | std::unordered_map<std::string, std::unique_ptr<SpillDataDir>>&& spill_store_map) |
53 | 209 | : _spill_store_map(std::move(spill_store_map)), _stop_background_threads_latch(1) {} |
54 | | |
55 | 211 | void SpillFileManager::stop() { |
56 | 211 | _stop_background_threads_latch.count_down(); |
57 | 211 | if (_spill_gc_thread) { |
58 | 211 | _spill_gc_thread->join(); |
59 | 211 | } |
60 | | // The GC thread may observe the stop latch before processing a recently queued failed deletion. |
61 | | // Retry the pending directories after the thread exits; later failures get one final retry in |
62 | | // the destructor. |
63 | 211 | _retry_pending_query_spill_directories(); |
64 | 211 | } |
65 | | |
66 | 209 | Status SpillFileManager::init() { |
67 | 209 | LOG(INFO) << "init spill stream manager"; |
68 | 209 | RETURN_IF_ERROR(_init_spill_store_map()); |
69 | | |
70 | 247 | for (const auto& [path, store] : _spill_store_map) { |
71 | 247 | auto gc_dir_root_dir = store->get_spill_data_gc_path(); |
72 | 247 | bool exists = true; |
73 | 247 | RETURN_IF_ERROR(io::global_local_filesystem()->exists(gc_dir_root_dir, &exists)); |
74 | 247 | if (!exists) { |
75 | 126 | RETURN_IF_ERROR(io::global_local_filesystem()->create_directory(gc_dir_root_dir)); |
76 | 126 | } |
77 | | |
78 | 247 | auto spill_dir = store->get_spill_data_path(); |
79 | 247 | RETURN_IF_ERROR(io::global_local_filesystem()->exists(spill_dir, &exists)); |
80 | 247 | if (!exists) { |
81 | 126 | RETURN_IF_ERROR(io::global_local_filesystem()->create_directory(spill_dir)); |
82 | 126 | } else { |
83 | 121 | auto suffix = ToStringFromUnixMillis(UnixMillis()); |
84 | 121 | auto gc_dir = store->get_spill_data_gc_path(suffix); |
85 | 121 | if (std::filesystem::exists(gc_dir)) { |
86 | 0 | LOG(WARNING) << "gc dir already exists: " << gc_dir; |
87 | 0 | } |
88 | 121 | (void)io::global_local_filesystem()->rename(spill_dir, gc_dir); |
89 | 121 | RETURN_IF_ERROR(io::global_local_filesystem()->create_directory(spill_dir)); |
90 | 121 | } |
91 | 247 | } |
92 | | |
93 | 209 | RETURN_IF_ERROR(Thread::create( |
94 | 209 | "Spill", "spill_gc_thread", [this]() { this->_spill_gc_thread_callback(); }, |
95 | 209 | &_spill_gc_thread)); |
96 | 209 | LOG(INFO) << "spill gc thread started"; |
97 | | |
98 | 209 | _init_metrics(); |
99 | | |
100 | 209 | return Status::OK(); |
101 | 209 | } |
102 | | |
103 | 209 | void SpillFileManager::_init_metrics() { |
104 | 209 | _entity = DorisMetrics::instance()->metric_registry()->register_entity("spill", |
105 | 209 | {{"name", "spill"}}); |
106 | | |
107 | 209 | _spill_write_bytes_metric = std::make_unique<doris::MetricPrototype>( |
108 | 209 | doris::MetricType::COUNTER, doris::MetricUnit::BYTES, "spill_write_bytes"); |
109 | 209 | _spill_write_bytes_counter = (IntAtomicCounter*)(_entity->register_metric<IntAtomicCounter>( |
110 | 209 | _spill_write_bytes_metric.get())); |
111 | | |
112 | 209 | _spill_read_bytes_metric = std::make_unique<doris::MetricPrototype>( |
113 | 209 | doris::MetricType::COUNTER, doris::MetricUnit::BYTES, "spill_read_bytes"); |
114 | 209 | _spill_read_bytes_counter = (IntAtomicCounter*)(_entity->register_metric<IntAtomicCounter>( |
115 | 209 | _spill_read_bytes_metric.get())); |
116 | 209 | } |
117 | | |
118 | | // Retry failed query-directory deletions and clean up stale spill files. |
119 | 209 | void SpillFileManager::_spill_gc_thread_callback() { |
120 | 6.71k | while (!_stop_background_threads_latch.wait_for( |
121 | 6.71k | std::chrono::milliseconds(config::spill_gc_interval_ms))) { |
122 | 6.50k | gc(config::spill_gc_work_time_ms); |
123 | 7.08k | for (auto& [path, dir] : _spill_store_map) { |
124 | 7.08k | static_cast<void>(dir->update_capacity()); |
125 | 7.08k | } |
126 | 6.50k | } |
127 | 209 | } |
128 | | |
129 | 209 | Status SpillFileManager::_init_spill_store_map() { |
130 | 247 | for (const auto& store : _spill_store_map) { |
131 | 247 | RETURN_IF_ERROR(store.second->init()); |
132 | 247 | } |
133 | | |
134 | 209 | return Status::OK(); |
135 | 209 | } |
136 | | |
137 | | std::vector<SpillDataDir*> SpillFileManager::_get_stores_for_spill( |
138 | 626 | TStorageMedium::type storage_medium) { |
139 | 626 | std::vector<std::pair<SpillDataDir*, double>> stores_with_usage; |
140 | 654 | for (auto& [_, store] : _spill_store_map) { |
141 | 654 | if (store->storage_medium() == storage_medium && !store->reach_capacity_limit(0)) { |
142 | 327 | stores_with_usage.emplace_back(store.get(), store->_get_disk_usage(0)); |
143 | 327 | } |
144 | 654 | } |
145 | 626 | if (stores_with_usage.empty()) { |
146 | 299 | return {}; |
147 | 299 | } |
148 | | |
149 | 327 | std::ranges::sort(stores_with_usage, [](auto&& a, auto&& b) { return a.second < b.second; }); |
150 | | |
151 | 327 | std::vector<SpillDataDir*> stores; |
152 | 327 | for (const auto& [store, _] : stores_with_usage) { |
153 | 327 | stores.emplace_back(store); |
154 | 327 | } |
155 | 327 | return stores; |
156 | 626 | } |
157 | | |
158 | | Status SpillFileManager::create_spill_file(const std::string& relative_path, |
159 | 327 | SpillFileSPtr& spill_file) { |
160 | 327 | auto data_dirs = _get_stores_for_spill(TStorageMedium::type::SSD); |
161 | 327 | if (data_dirs.empty()) { |
162 | 299 | data_dirs = _get_stores_for_spill(TStorageMedium::type::HDD); |
163 | 299 | } |
164 | 327 | if (data_dirs.empty()) { |
165 | 0 | return Status::Error<ErrorCode::NO_AVAILABLE_ROOT_PATH>( |
166 | 0 | "no available disk can be used for spill."); |
167 | 0 | } |
168 | | |
169 | | // Select the first available data dir (sorted by usage ascending) |
170 | 327 | SpillDataDir* data_dir = data_dirs.front(); |
171 | 327 | spill_file = std::make_shared<SpillFile>(data_dir, relative_path); |
172 | 327 | return Status::OK(); |
173 | 327 | } |
174 | | |
175 | 140 | void SpillFileManager::delete_spill_file(SpillFileSPtr spill_file) { |
176 | 140 | if (!spill_file) { |
177 | 0 | LOG(WARNING) << "[spill][delete] null spill_file"; |
178 | 0 | return; |
179 | 0 | } |
180 | 140 | spill_file->gc(); |
181 | 140 | } |
182 | | |
183 | | void SpillFileManager::delete_query_spill_directory(const std::string& query_id, |
184 | 33 | SpillDataDir* data_dir) { |
185 | 33 | PendingQuerySpillDirectory pending_directory { |
186 | 33 | .query_dir = data_dir->get_spill_data_path(query_id), |
187 | 33 | }; |
188 | | |
189 | 33 | auto status = _try_delete_query_spill_directory(pending_directory); |
190 | 33 | if (!status.ok()) { |
191 | 4 | std::lock_guard lock(_pending_query_spill_directories_mutex); |
192 | 4 | ++pending_directory.failed_count; |
193 | 4 | _pending_query_spill_directories.emplace_back(std::move(pending_directory)); |
194 | 4 | } |
195 | 33 | } |
196 | | |
197 | | Status SpillFileManager::_try_delete_query_spill_directory( |
198 | 42 | const PendingQuerySpillDirectory& pending_directory) { |
199 | 42 | DBUG_EXECUTE_IF("fault_inject::spill_file_manager::delete_query_spill_directory", { |
200 | 42 | return Status::Error<INTERNAL_ERROR>("injected query spill directory deletion failure"); |
201 | 42 | }); |
202 | 33 | const auto& fs = io::global_local_filesystem(); |
203 | 33 | return fs->delete_directory(pending_directory.query_dir); |
204 | 42 | } |
205 | | |
206 | 6.93k | void SpillFileManager::_retry_pending_query_spill_directories() { |
207 | 6.93k | std::vector<PendingQuerySpillDirectory> pending_directories; |
208 | 6.93k | { |
209 | 6.93k | std::lock_guard lock(_pending_query_spill_directories_mutex); |
210 | 6.93k | pending_directories.swap(_pending_query_spill_directories); |
211 | 6.93k | } |
212 | 6.93k | DBUG_EXECUTE_IF( |
213 | 6.93k | "fault_inject::spill_file_manager::retry_pending_query_spill_directories_after_drain", |
214 | 6.93k | { DBUG_RUN_CALLBACK(); }); |
215 | | |
216 | | // Limit repeated warnings for a persistently unavailable directory while retaining it for |
217 | | // every subsequent retry. |
218 | 6.93k | constexpr int log_interval = 5; |
219 | 6.93k | std::vector<PendingQuerySpillDirectory> failed_directories; |
220 | 6.93k | for (auto& pending_directory : pending_directories) { |
221 | 9 | auto status = _try_delete_query_spill_directory(pending_directory); |
222 | 9 | if (status.ok()) { |
223 | 4 | continue; |
224 | 4 | } |
225 | | |
226 | 5 | ++pending_directory.failed_count; |
227 | 5 | if (pending_directory.failed_count % log_interval == 0) { |
228 | 1 | LOG(WARNING) << fmt::format( |
229 | 1 | "failed to retry deleting spill query directory, dir {}, error: {}", |
230 | 1 | pending_directory.query_dir, status.to_string()); |
231 | 1 | } |
232 | 5 | failed_directories.emplace_back(std::move(pending_directory)); |
233 | 5 | } |
234 | | |
235 | 6.93k | if (!failed_directories.empty()) { |
236 | 5 | std::lock_guard lock(_pending_query_spill_directories_mutex); |
237 | 5 | for (auto& pending_directory : failed_directories) { |
238 | 5 | _pending_query_spill_directories.emplace_back(std::move(pending_directory)); |
239 | 5 | } |
240 | 5 | } |
241 | 6.93k | } |
242 | | |
243 | 6.51k | void SpillFileManager::gc(int32_t max_work_time_ms) { |
244 | 6.51k | bool exists = true; |
245 | 6.51k | bool has_work = false; |
246 | 6.51k | int64_t max_work_time_ns = max_work_time_ms * 1000L * 1000L; |
247 | 6.51k | MonotonicStopWatch watch; |
248 | 6.51k | watch.start(); |
249 | 6.51k | Defer defer {[&]() { |
250 | 6.51k | if (has_work) { |
251 | 7 | std::string msg( |
252 | 7 | fmt::format("spill gc time: {}", |
253 | 7 | PrettyPrinter::print(watch.elapsed_time(), TUnit::TIME_NS))); |
254 | 7 | msg += ", spill storage:\n"; |
255 | 11 | for (const auto& [path, store_dir] : _spill_store_map) { |
256 | 11 | msg += " " + store_dir->debug_string(); |
257 | 11 | msg += "\n"; |
258 | 11 | } |
259 | 7 | LOG(INFO) << msg; |
260 | 7 | } |
261 | 6.51k | }}; |
262 | 6.51k | _retry_pending_query_spill_directories(); |
263 | 7.09k | for (const auto& [path, store_dir] : _spill_store_map) { |
264 | 7.09k | std::string gc_root_dir = store_dir->get_spill_data_gc_path(); |
265 | | |
266 | 7.09k | std::error_code ec; |
267 | 7.09k | exists = std::filesystem::exists(gc_root_dir, ec); |
268 | 7.09k | if (ec || !exists) { |
269 | 0 | continue; |
270 | 0 | } |
271 | | // dirs of queries |
272 | 7.09k | std::vector<io::FileInfo> dirs; |
273 | 7.09k | auto st = io::global_local_filesystem()->list(gc_root_dir, false, &dirs, &exists); |
274 | 7.09k | if (!st.ok()) { |
275 | 0 | continue; |
276 | 0 | } |
277 | | |
278 | 7.09k | for (const auto& dir : dirs) { |
279 | 171 | has_work = true; |
280 | 171 | if (dir.is_file) { |
281 | 0 | continue; |
282 | 0 | } |
283 | 171 | std::string abs_dir = fmt::format("{}/{}", gc_root_dir, dir.file_name); |
284 | | // operator spill sub dirs of a query |
285 | 171 | std::vector<io::FileInfo> files; |
286 | 171 | st = io::global_local_filesystem()->list(abs_dir, false, &files, &exists); |
287 | 171 | if (!st.ok()) { |
288 | 0 | continue; |
289 | 0 | } |
290 | 171 | if (files.empty()) { |
291 | 121 | static_cast<void>(io::global_local_filesystem()->delete_directory(abs_dir)); |
292 | 121 | continue; |
293 | 121 | } |
294 | | |
295 | 50 | for (const auto& file : files) { |
296 | 50 | auto abs_file_path = fmt::format("{}/{}", abs_dir, file.file_name); |
297 | 50 | if (file.is_file) { |
298 | 0 | static_cast<void>(io::global_local_filesystem()->delete_file(abs_file_path)); |
299 | 50 | } else { |
300 | 50 | static_cast<void>( |
301 | 50 | io::global_local_filesystem()->delete_directory(abs_file_path)); |
302 | 50 | } |
303 | 50 | if (watch.elapsed_time() > max_work_time_ns) { |
304 | 0 | break; |
305 | 0 | } |
306 | 50 | } |
307 | 50 | } |
308 | 7.09k | } |
309 | 6.51k | } |
310 | | |
311 | | DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(spill_disk_capacity, MetricUnit::BYTES); |
312 | | DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(spill_disk_limit, MetricUnit::BYTES); |
313 | | DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(spill_disk_avail_capacity, MetricUnit::BYTES); |
314 | | DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(spill_disk_data_size, MetricUnit::BYTES); |
315 | | DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(spill_disk_has_spill_data, MetricUnit::BYTES); |
316 | | DEFINE_GAUGE_METRIC_PROTOTYPE_2ARG(spill_disk_has_spill_gc_data, MetricUnit::BYTES); |
317 | | |
318 | | SpillDataDir::SpillDataDir(std::string path, int64_t capacity_bytes, |
319 | | TStorageMedium::type storage_medium) |
320 | 247 | : _path(std::move(path)), |
321 | 247 | _disk_capacity_bytes(capacity_bytes), |
322 | 247 | _storage_medium(storage_medium) { |
323 | 247 | spill_data_dir_metric_entity = DorisMetrics::instance()->metric_registry()->register_entity( |
324 | 247 | std::string("spill_data_dir.") + _path, {{"path", _path + "/" + SPILL_DIR_PREFIX}}); |
325 | 247 | INT_GAUGE_METRIC_REGISTER(spill_data_dir_metric_entity, spill_disk_capacity); |
326 | 247 | INT_GAUGE_METRIC_REGISTER(spill_data_dir_metric_entity, spill_disk_limit); |
327 | 247 | INT_GAUGE_METRIC_REGISTER(spill_data_dir_metric_entity, spill_disk_avail_capacity); |
328 | 247 | INT_GAUGE_METRIC_REGISTER(spill_data_dir_metric_entity, spill_disk_data_size); |
329 | 247 | INT_GAUGE_METRIC_REGISTER(spill_data_dir_metric_entity, spill_disk_has_spill_data); |
330 | 247 | INT_GAUGE_METRIC_REGISTER(spill_data_dir_metric_entity, spill_disk_has_spill_gc_data); |
331 | 247 | } |
332 | | |
333 | 14.6k | bool is_directory_empty(const std::filesystem::path& dir) { |
334 | | // Spill cleanup may delete the directory while the iterator is constructed or advanced. Treat |
335 | | // that race as empty for these presence metrics. |
336 | 14.6k | try { |
337 | 14.6k | return std::filesystem::is_directory(dir) && |
338 | 14.6k | std::filesystem::directory_iterator(dir) == |
339 | 14.4k | std::filesystem::end(std::filesystem::directory_iterator {}); |
340 | 14.6k | } catch (const std::filesystem::filesystem_error&) { |
341 | 0 | return true; |
342 | 0 | } |
343 | 14.6k | } |
344 | | |
345 | 247 | Status SpillDataDir::init() { |
346 | 247 | bool exists = false; |
347 | 247 | RETURN_IF_ERROR(io::global_local_filesystem()->exists(_path, &exists)); |
348 | 247 | if (!exists) { |
349 | 0 | RETURN_NOT_OK_STATUS_WITH_WARN(Status::IOError("opendir failed, path={}", _path), |
350 | 0 | "check file exist failed"); |
351 | 0 | } |
352 | 247 | RETURN_IF_ERROR(update_capacity()); |
353 | 247 | LOG(INFO) << fmt::format( |
354 | 247 | "spill storage path: {}, capacity: {}, limit: {}, available: " |
355 | 247 | "{}", |
356 | 247 | _path, PrettyPrinter::print_bytes(_disk_capacity_bytes), |
357 | 247 | PrettyPrinter::print_bytes(_spill_data_limit_bytes), |
358 | 247 | PrettyPrinter::print_bytes(_available_bytes)); |
359 | 247 | return Status::OK(); |
360 | 247 | } |
361 | | |
362 | 7.96k | std::string SpillDataDir::get_spill_data_path(const std::string& query_id) const { |
363 | 7.96k | auto dir = fmt::format("{}/{}", _path, SPILL_DIR_PREFIX); |
364 | 7.96k | if (!query_id.empty()) { |
365 | 47 | dir = fmt::format("{}/{}", dir, query_id); |
366 | 47 | } |
367 | 7.96k | return dir; |
368 | 7.96k | } |
369 | | |
370 | 14.8k | std::string SpillDataDir::get_spill_data_gc_path(const std::string& sub_dir_name) const { |
371 | 14.8k | auto dir = fmt::format("{}/{}", _path, SPILL_GC_DIR_PREFIX); |
372 | 14.8k | if (!sub_dir_name.empty()) { |
373 | 121 | dir = fmt::format("{}/{}", dir, sub_dir_name); |
374 | 121 | } |
375 | 14.8k | return dir; |
376 | 14.8k | } |
377 | | |
378 | 7.32k | Status SpillDataDir::update_capacity() { |
379 | 7.32k | std::lock_guard<std::mutex> l(_mutex); |
380 | 7.32k | RETURN_IF_ERROR(io::global_local_filesystem()->get_space_info(_path, &_disk_capacity_bytes, |
381 | 7.32k | &_available_bytes)); |
382 | 7.32k | spill_disk_capacity->set_value(_disk_capacity_bytes); |
383 | 7.32k | spill_disk_avail_capacity->set_value(_available_bytes); |
384 | 7.32k | auto disk_use_max_bytes = |
385 | 7.32k | (int64_t)(_disk_capacity_bytes * config::storage_flood_stage_usage_percent / 100); |
386 | 7.32k | bool is_percent = true; |
387 | 7.32k | _spill_data_limit_bytes = ParseUtil::parse_mem_spec(config::spill_storage_limit, -1, |
388 | 7.32k | _disk_capacity_bytes, &is_percent); |
389 | 7.32k | if (_spill_data_limit_bytes <= 0) { |
390 | 0 | spill_disk_limit->set_value(_spill_data_limit_bytes); |
391 | 0 | auto err_msg = fmt::format("Failed to parse spill storage limit from '{}'", |
392 | 0 | config::spill_storage_limit); |
393 | 0 | LOG(WARNING) << err_msg; |
394 | 0 | return Status::InvalidArgument(err_msg); |
395 | 0 | } |
396 | 7.32k | if (is_percent) { |
397 | 7.32k | _spill_data_limit_bytes = (int64_t)(_spill_data_limit_bytes * |
398 | 7.32k | config::storage_flood_stage_usage_percent / 100); |
399 | 7.32k | } |
400 | 7.32k | _spill_data_limit_bytes = std::min(_spill_data_limit_bytes, disk_use_max_bytes); |
401 | 7.32k | spill_disk_limit->set_value(_spill_data_limit_bytes); |
402 | | |
403 | 7.32k | std::string spill_root_dir = get_spill_data_path(); |
404 | 7.32k | std::string spill_gc_root_dir = get_spill_data_gc_path(); |
405 | 7.32k | spill_disk_has_spill_data->set_value(is_directory_empty(spill_root_dir) ? 0 : 1); |
406 | 7.32k | spill_disk_has_spill_gc_data->set_value(is_directory_empty(spill_gc_root_dir) ? 0 : 1); |
407 | | |
408 | 7.32k | return Status::OK(); |
409 | 7.32k | } |
410 | | |
411 | 779 | bool SpillDataDir::_reach_disk_capacity_limit(int64_t incoming_data_size) { |
412 | 779 | double used_pct = _get_disk_usage(incoming_data_size); |
413 | 779 | int64_t left_bytes = _available_bytes - incoming_data_size; |
414 | 779 | if (used_pct >= config::storage_flood_stage_usage_percent / 100.0 && |
415 | 779 | left_bytes <= config::storage_flood_stage_left_capacity_bytes) { |
416 | 0 | LOG(WARNING) << "reach capacity limit. used pct: " << used_pct |
417 | 0 | << ", left bytes: " << left_bytes << ", path: " << _path; |
418 | 0 | return true; |
419 | 0 | } |
420 | 779 | return false; |
421 | 779 | } |
422 | 779 | bool SpillDataDir::reach_capacity_limit(int64_t incoming_data_size) { |
423 | 779 | std::lock_guard<std::mutex> l(_mutex); |
424 | 779 | if (_reach_disk_capacity_limit(incoming_data_size)) { |
425 | 0 | return true; |
426 | 0 | } |
427 | 779 | if (_spill_data_bytes + incoming_data_size > _spill_data_limit_bytes) { |
428 | 0 | LOG_EVERY_T(WARNING, 1) << fmt::format( |
429 | 0 | "spill data reach limit, path: {}, capacity: {}, limit: {}, used: {}, " |
430 | 0 | "available: " |
431 | 0 | "{}, " |
432 | 0 | "incoming " |
433 | 0 | "bytes: {}", |
434 | 0 | _path, PrettyPrinter::print_bytes(_disk_capacity_bytes), |
435 | 0 | PrettyPrinter::print_bytes(_spill_data_limit_bytes), |
436 | 0 | PrettyPrinter::print_bytes(_spill_data_bytes), |
437 | 0 | PrettyPrinter::print_bytes(_available_bytes), |
438 | 0 | PrettyPrinter::print_bytes(incoming_data_size)); |
439 | 0 | return true; |
440 | 0 | } |
441 | 779 | return false; |
442 | 779 | } |
443 | 11 | std::string SpillDataDir::debug_string() { |
444 | 11 | return fmt::format( |
445 | 11 | "path: {}, capacity: {}, limit: {}, used: {}, available: " |
446 | 11 | "{}", |
447 | 11 | _path, PrettyPrinter::print_bytes(_disk_capacity_bytes), |
448 | 11 | PrettyPrinter::print_bytes(_spill_data_limit_bytes), |
449 | 11 | PrettyPrinter::print_bytes(_spill_data_bytes), |
450 | 11 | PrettyPrinter::print_bytes(_available_bytes)); |
451 | 11 | } |
452 | | } // namespace doris |