Coverage Report

Created: 2026-08-05 13:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/olap_server.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 <gen_cpp/Types_types.h>
19
#include <gen_cpp/olap_file.pb.h>
20
#include <glog/logging.h>
21
#include <rapidjson/prettywriter.h>
22
#include <rapidjson/stringbuffer.h>
23
#include <stdint.h>
24
#include <sys/types.h>
25
26
#include <algorithm>
27
#include <atomic>
28
// IWYU pragma: no_include <bits/chrono.h>
29
#include <gen_cpp/internal_service.pb.h>
30
31
#include <chrono> // IWYU pragma: keep
32
#include <cmath>
33
#include <condition_variable>
34
#include <cstdint>
35
#include <ctime>
36
#include <functional>
37
#include <map>
38
#include <memory>
39
#include <mutex>
40
#include <ostream>
41
#include <random>
42
#include <shared_mutex>
43
#include <string>
44
#include <thread>
45
#include <unordered_set>
46
#include <utility>
47
#include <vector>
48
49
#include "agent/utils.h"
50
#include "common/config.h"
51
#include "common/logging.h"
52
#include "common/metrics/doris_metrics.h"
53
#include "common/metrics/metrics.h"
54
#include "common/status.h"
55
#include "cpp/sync_point.h"
56
#include "io/fs/file_writer.h" // IWYU pragma: keep
57
#include "io/fs/path.h"
58
#include "load/memtable/memtable_flush_executor.h"
59
#include "runtime/memory/cache_manager.h"
60
#include "runtime/memory/global_memory_arbitrator.h"
61
#include "service/backend_options.h"
62
#include "storage/compaction/cold_data_compaction.h"
63
#include "storage/compaction/compaction_permit_limiter.h"
64
#include "storage/compaction/cumulative_compaction.h"
65
#include "storage/compaction/cumulative_compaction_policy.h"
66
#include "storage/compaction/cumulative_compaction_time_series_policy.h"
67
#include "storage/compaction_task_tracker.h"
68
#include "storage/data_dir.h"
69
#include "storage/olap_common.h"
70
#include "storage/olap_define.h"
71
#include "storage/rowset/segcompaction.h"
72
#include "storage/schema_change/schema_change.h"
73
#include "storage/storage_engine.h"
74
#include "storage/storage_policy.h"
75
#include "storage/tablet/base_tablet.h"
76
#include "storage/tablet/tablet.h"
77
#include "storage/tablet/tablet_manager.h"
78
#include "storage/tablet/tablet_meta.h"
79
#include "storage/tablet/tablet_meta_manager.h"
80
#include "storage/tablet/tablet_schema.h"
81
#include "storage/task/engine_publish_version_task.h"
82
#include "storage/task/index_builder.h"
83
#include "util/client_cache.h"
84
#include "util/countdown_latch.h"
85
#include "util/debug_points.h"
86
#include "util/mem_info.h"
87
#include "util/thread.h"
88
#include "util/threadpool.h"
89
#include "util/time.h"
90
#include "util/uid_util.h"
91
#include "util/work_thread_pool.hpp"
92
93
using std::string;
94
95
namespace doris {
96
using io::Path;
97
98
// number of running SCHEMA-CHANGE threads
99
volatile uint32_t g_schema_change_active_threads = 0;
100
bvar::Status<int64_t> g_cumu_compaction_task_num_per_round("cumu_compaction_task_num_per_round", 0);
101
bvar::Status<int64_t> g_base_compaction_task_num_per_round("base_compaction_task_num_per_round", 0);
102
103
0
CompactionSubmitRegistry::CompactionSubmitRegistry(CompactionSubmitRegistry&& r) {
104
0
    std::swap(_tablet_submitted_cumu_compaction, r._tablet_submitted_cumu_compaction);
105
0
    std::swap(_tablet_submitted_base_compaction, r._tablet_submitted_base_compaction);
106
0
    std::swap(_tablet_submitted_full_compaction, r._tablet_submitted_full_compaction);
107
0
    std::swap(_tablet_submitted_binlog_compaction, r._tablet_submitted_binlog_compaction);
108
0
}
109
110
111k
CompactionSubmitRegistry CompactionSubmitRegistry::create_snapshot() {
111
    // full compaction is not engaged in this method
112
111k
    std::unique_lock<std::mutex> l(_tablet_submitted_compaction_mutex);
113
111k
    CompactionSubmitRegistry registry;
114
111k
    registry._tablet_submitted_base_compaction = _tablet_submitted_base_compaction;
115
111k
    registry._tablet_submitted_cumu_compaction = _tablet_submitted_cumu_compaction;
116
111k
    registry._tablet_submitted_binlog_compaction = _tablet_submitted_binlog_compaction;
117
111k
    return registry;
118
111k
}
119
120
5
void CompactionSubmitRegistry::reset(const std::vector<DataDir*>& stores) {
121
    // full compaction is not engaged in this method
122
5
    std::unique_lock<std::mutex> l(_tablet_submitted_compaction_mutex);
123
8
    for (const auto& store : stores) {
124
8
        _tablet_submitted_cumu_compaction[store] = {};
125
8
        _tablet_submitted_base_compaction[store] = {};
126
8
        _tablet_submitted_binlog_compaction[store] = {};
127
8
    }
128
5
}
129
130
uint32_t CompactionSubmitRegistry::count_executing_compaction(DataDir* dir,
131
131k
                                                              CompactionType compaction_type) {
132
    // non-lock, used in snapshot
133
131k
    const auto& compaction_tasks = _get_tablet_set(dir, compaction_type);
134
131k
    return cast_set<uint32_t>(std::count_if(
135
131k
            compaction_tasks.begin(), compaction_tasks.end(),
136
131k
            [](const auto& task) { return task->compaction_stage == CompactionStage::EXECUTING; }));
137
131k
}
138
139
14.2k
uint32_t CompactionSubmitRegistry::count_executing_cumu_and_base(DataDir* dir) {
140
    // non-lock, used in snapshot
141
14.2k
    return count_executing_compaction(dir, CompactionType::BASE_COMPACTION) +
142
14.2k
           count_executing_compaction(dir, CompactionType::CUMULATIVE_COMPACTION);
143
14.2k
}
144
145
234k
bool CompactionSubmitRegistry::has_compaction_task(DataDir* dir, CompactionType compaction_type) {
146
    // non-lock, used in snapshot
147
234k
    return !_get_tablet_set(dir, compaction_type).empty();
148
234k
}
149
150
std::vector<TabletCompactionContext> CompactionSubmitRegistry::pick_topn_tablets_for_compaction(
151
        TabletManager* tablet_mgr, DataDir* data_dir, CompactionType compaction_type,
152
117k
        const CumuCompactionPolicyTable& cumu_compaction_policies, uint32_t* disk_max_score) {
153
    // non-lock, used in snapshot
154
117k
    return tablet_mgr->find_best_tablets_to_compaction(compaction_type, data_dir,
155
117k
                                                       _get_tablet_set(data_dir, compaction_type),
156
117k
                                                       disk_max_score, cumu_compaction_policies);
157
117k
}
158
159
374k
bool CompactionSubmitRegistry::insert(TabletSharedPtr tablet, CompactionType compaction_type) {
160
374k
    std::unique_lock<std::mutex> l(_tablet_submitted_compaction_mutex);
161
374k
    auto& tablet_set = _get_tablet_set(tablet->data_dir(), compaction_type);
162
374k
    bool already_exist = !(tablet_set.insert(tablet).second);
163
374k
    return already_exist;
164
374k
}
165
166
void CompactionSubmitRegistry::remove(TabletSharedPtr tablet, CompactionType compaction_type,
167
374k
                                      std::function<void()> wakeup_cb) {
168
374k
    std::unique_lock<std::mutex> l(_tablet_submitted_compaction_mutex);
169
374k
    auto& tablet_set = _get_tablet_set(tablet->data_dir(), compaction_type);
170
374k
    size_t removed = tablet_set.erase(tablet);
171
374k
    if (removed == 1) {
172
374k
        wakeup_cb();
173
374k
    }
174
374k
}
175
176
CompactionSubmitRegistry::TabletSet& CompactionSubmitRegistry::_get_tablet_set(
177
1.23M
        DataDir* dir, CompactionType compaction_type) {
178
1.23M
    switch (compaction_type) {
179
164k
    case CompactionType::BASE_COMPACTION:
180
164k
        return _tablet_submitted_base_compaction[dir];
181
859k
    case CompactionType::CUMULATIVE_COMPACTION:
182
859k
        return _tablet_submitted_cumu_compaction[dir];
183
0
    case CompactionType::FULL_COMPACTION:
184
0
        return _tablet_submitted_full_compaction[dir];
185
209k
    case CompactionType::BINLOG_COMPACTION:
186
209k
        return _tablet_submitted_binlog_compaction[dir];
187
0
    default:
188
0
        CHECK(false) << "invalid compaction type";
189
1.23M
    }
190
1.23M
}
191
192
111k
static int32_t get_cumu_compaction_threads_num(size_t data_dirs_num) {
193
111k
    int32_t threads_num = config::max_cumu_compaction_threads;
194
111k
    if (threads_num == -1) {
195
111k
        int32_t num_cores = doris::CpuInfo::num_cores();
196
111k
        threads_num = std::max(cast_set<int32_t>(data_dirs_num), num_cores / 6);
197
111k
    }
198
111k
    threads_num = threads_num <= 0 ? 1 : threads_num;
199
111k
    return threads_num;
200
111k
}
201
202
111k
static int32_t get_base_compaction_threads_num(size_t data_dirs_num) {
203
111k
    int32_t threads_num = config::max_base_compaction_threads;
204
111k
    if (threads_num == -1) {
205
0
        threads_num = cast_set<int32_t>(data_dirs_num);
206
0
    }
207
111k
    threads_num = threads_num <= 0 ? 1 : threads_num;
208
111k
    return threads_num;
209
111k
}
210
211
111k
static int32_t get_binlog_compaction_threads_num(size_t data_dirs_num) {
212
111k
    int32_t threads_num = config::max_binlog_compaction_threads;
213
111k
    if (threads_num == -1) {
214
111k
        threads_num = cast_set<int32_t>(data_dirs_num);
215
111k
    }
216
111k
    threads_num = threads_num <= 0 ? 1 : threads_num;
217
111k
    return threads_num;
218
111k
}
219
220
5
Status StorageEngine::start_bg_threads(std::shared_ptr<WorkloadGroup> wg_sptr) {
221
5
    RETURN_IF_ERROR(Thread::create(
222
5
            "StorageEngine", "unused_rowset_monitor_thread",
223
5
            [this]() { this->_unused_rowset_monitor_thread_callback(); },
224
5
            &_unused_rowset_monitor_thread));
225
5
    LOG(INFO) << "unused rowset monitor thread started";
226
227
5
    RETURN_IF_ERROR(Thread::create(
228
5
            "StorageEngine", "evict_querying_rowset_thread",
229
5
            [this]() { this->_evict_quring_rowset_thread_callback(); },
230
5
            &_evict_quering_rowset_thread));
231
5
    LOG(INFO) << "evict quering thread started";
232
233
    // start thread for monitoring the snapshot and trash folder
234
5
    RETURN_IF_ERROR(Thread::create(
235
5
            "StorageEngine", "garbage_sweeper_thread",
236
5
            [this]() { this->_garbage_sweeper_thread_callback(); }, &_garbage_sweeper_thread));
237
5
    LOG(INFO) << "garbage sweeper thread started";
238
239
    // start thread for monitoring the tablet with io error
240
5
    RETURN_IF_ERROR(Thread::create(
241
5
            "StorageEngine", "disk_stat_monitor_thread",
242
5
            [this]() { this->_disk_stat_monitor_thread_callback(); }, &_disk_stat_monitor_thread));
243
5
    LOG(INFO) << "disk stat monitor thread started";
244
245
    // convert store map to vector
246
5
    std::vector<DataDir*> data_dirs = get_stores();
247
248
5
    auto base_compaction_threads = get_base_compaction_threads_num(data_dirs.size());
249
5
    auto cumu_compaction_threads = get_cumu_compaction_threads_num(data_dirs.size());
250
5
    auto binlog_compaction_threads = get_binlog_compaction_threads_num(data_dirs.size());
251
252
5
    RETURN_IF_ERROR(ThreadPoolBuilder("BaseCompactionTaskThreadPool")
253
5
                            .set_min_threads(base_compaction_threads)
254
5
                            .set_max_threads(base_compaction_threads)
255
5
                            .build(&_base_compaction_thread_pool));
256
5
    RETURN_IF_ERROR(ThreadPoolBuilder("CumuCompactionTaskThreadPool")
257
5
                            .set_min_threads(cumu_compaction_threads)
258
5
                            .set_max_threads(cumu_compaction_threads)
259
5
                            .build(&_cumu_compaction_thread_pool));
260
5
    RETURN_IF_ERROR(ThreadPoolBuilder("BinlogCompactionTaskThreadPool")
261
5
                            .set_min_threads(binlog_compaction_threads)
262
5
                            .set_max_threads(binlog_compaction_threads)
263
5
                            .build(&_binlog_compaction_thread_pool));
264
265
5
    if (config::enable_segcompaction) {
266
5
        RETURN_IF_ERROR(ThreadPoolBuilder("SegCompactionTaskThreadPool")
267
5
                                .set_min_threads(config::segcompaction_num_threads)
268
5
                                .set_max_threads(config::segcompaction_num_threads)
269
5
                                .build(&_seg_compaction_thread_pool));
270
5
    }
271
5
    RETURN_IF_ERROR(ThreadPoolBuilder("ColdDataCompactionTaskThreadPool")
272
5
                            .set_min_threads(config::cold_data_compaction_thread_num)
273
5
                            .set_max_threads(config::cold_data_compaction_thread_num)
274
5
                            .build(&_cold_data_compaction_thread_pool));
275
276
5
    _compaction_submit_registry.reset(data_dirs);
277
278
    // compaction tasks producer thread
279
5
    RETURN_IF_ERROR(Thread::create(
280
5
            "StorageEngine", "compaction_tasks_producer_thread",
281
5
            [this]() { this->_compaction_tasks_producer_callback(); },
282
5
            &_compaction_tasks_producer_thread));
283
5
    LOG(INFO) << "compaction tasks producer thread started";
284
285
5
    RETURN_IF_ERROR(Thread::create(
286
5
            "StorageEngine", "binlog_compaction_tasks_producer_thread",
287
5
            [this]() { this->_binlog_compaction_tasks_producer_callback(); },
288
5
            &_binlog_compaction_tasks_producer_thread));
289
5
    LOG(INFO) << "binlog compaction tasks producer thread started";
290
291
5
    int32_t max_checkpoint_thread_num = config::max_meta_checkpoint_threads;
292
5
    if (max_checkpoint_thread_num < 0) {
293
5
        max_checkpoint_thread_num = cast_set<int32_t>(data_dirs.size());
294
5
    }
295
5
    RETURN_IF_ERROR(ThreadPoolBuilder("TabletMetaCheckpointTaskThreadPool")
296
5
                            .set_max_threads(max_checkpoint_thread_num)
297
5
                            .build(&_tablet_meta_checkpoint_thread_pool));
298
299
5
    RETURN_IF_ERROR(Thread::create(
300
5
            "StorageEngine", "tablet_checkpoint_tasks_producer_thread",
301
5
            [this, data_dirs]() { this->_tablet_checkpoint_callback(data_dirs); },
302
5
            &_tablet_checkpoint_tasks_producer_thread));
303
5
    LOG(INFO) << "tablet checkpoint tasks producer thread started";
304
305
5
    RETURN_IF_ERROR(Thread::create(
306
5
            "StorageEngine", "tablet_path_check_thread",
307
5
            [this]() { this->_tablet_path_check_callback(); }, &_tablet_path_check_thread));
308
5
    LOG(INFO) << "tablet path check thread started";
309
310
    // path scan and gc thread
311
5
    if (config::path_gc_check) {
312
8
        for (auto data_dir : get_stores()) {
313
8
            std::shared_ptr<Thread> path_gc_thread;
314
8
            RETURN_IF_ERROR(Thread::create(
315
8
                    "StorageEngine", "path_gc_thread",
316
8
                    [this, data_dir]() { this->_path_gc_thread_callback(data_dir); },
317
8
                    &path_gc_thread));
318
8
            _path_gc_threads.emplace_back(path_gc_thread);
319
8
        }
320
5
        LOG(INFO) << "path gc threads started. number:" << get_stores().size();
321
5
    }
322
323
5
    RETURN_IF_ERROR(ThreadPoolBuilder("CooldownTaskThreadPool")
324
5
                            .set_min_threads(config::cooldown_thread_num)
325
5
                            .set_max_threads(config::cooldown_thread_num)
326
5
                            .build(&_cooldown_thread_pool));
327
5
    LOG(INFO) << "cooldown thread pool started";
328
329
5
    RETURN_IF_ERROR(Thread::create(
330
5
            "StorageEngine", "cooldown_tasks_producer_thread",
331
5
            [this]() { this->_cooldown_tasks_producer_callback(); },
332
5
            &_cooldown_tasks_producer_thread));
333
5
    LOG(INFO) << "cooldown tasks producer thread started";
334
335
5
    RETURN_IF_ERROR(Thread::create(
336
5
            "StorageEngine", "remove_unused_remote_files_thread",
337
5
            [this]() { this->_remove_unused_remote_files_callback(); },
338
5
            &_remove_unused_remote_files_thread));
339
5
    LOG(INFO) << "remove unused remote files thread started";
340
341
5
    RETURN_IF_ERROR(Thread::create(
342
5
            "StorageEngine", "cold_data_compaction_producer_thread",
343
5
            [this]() { this->_cold_data_compaction_producer_callback(); },
344
5
            &_cold_data_compaction_producer_thread));
345
5
    LOG(INFO) << "cold data compaction producer thread started";
346
347
    // add tablet publish version thread pool
348
5
    RETURN_IF_ERROR(ThreadPoolBuilder("TabletPublishTxnThreadPool")
349
5
                            .set_min_threads(config::tablet_publish_txn_max_thread)
350
5
                            .set_max_threads(config::tablet_publish_txn_max_thread)
351
5
                            .build(&_tablet_publish_txn_thread_pool));
352
353
5
    RETURN_IF_ERROR(Thread::create(
354
5
            "StorageEngine", "async_publish_version_thread",
355
5
            [this]() { this->_async_publish_callback(); }, &_async_publish_thread));
356
5
    LOG(INFO) << "async publish thread started";
357
358
5
    RETURN_IF_ERROR(Thread::create(
359
5
            "StorageEngine", "check_tablet_delete_bitmap_score_thread",
360
5
            [this]() { this->_check_tablet_delete_bitmap_score_callback(); },
361
5
            &_check_delete_bitmap_score_thread));
362
5
    LOG(INFO) << "check tablet delete bitmap score thread started";
363
364
5
    _start_adaptive_thread_controller();
365
366
5
    LOG(INFO) << "all storage engine's background threads are started.";
367
5
    return Status::OK();
368
5
}
369
370
5
void StorageEngine::_garbage_sweeper_thread_callback() {
371
5
    uint32_t max_interval = config::max_garbage_sweep_interval;
372
5
    uint32_t min_interval = config::min_garbage_sweep_interval;
373
374
5
    if (max_interval < min_interval || min_interval <= 0) {
375
0
        LOG(WARNING) << "garbage sweep interval config is illegal: [max=" << max_interval
376
0
                     << " min=" << min_interval << "].";
377
0
        min_interval = 1;
378
0
        max_interval = max_interval >= min_interval ? max_interval : min_interval;
379
0
        LOG(INFO) << "force reset garbage sweep interval. "
380
0
                  << "max_interval=" << max_interval << ", min_interval=" << min_interval;
381
0
    }
382
383
5
    const double pi = M_PI;
384
5
    double usage = 1.0;
385
    // After the program starts, the first round of cleaning starts after min_interval.
386
5
    uint32_t curr_interval = min_interval;
387
60
    do {
388
        // Function properties:
389
        // when usage < 0.6,          ratio close to 1.(interval close to max_interval)
390
        // when usage at [0.6, 0.75], ratio is rapidly decreasing from 0.87 to 0.27.
391
        // when usage > 0.75,         ratio is slowly decreasing.
392
        // when usage > 0.8,          ratio close to min_interval.
393
        // when usage = 0.88,         ratio is approximately 0.0057.
394
60
        double ratio = (1.1 * (pi / 2 - std::atan(usage * 100 / 5 - 14)) - 0.28) / pi;
395
60
        ratio = ratio > 0 ? ratio : 0;
396
        // TODO(dx): fix it
397
60
        auto curr_interval_not_work = uint32_t(max_interval * ratio);
398
60
        curr_interval_not_work = std::max(curr_interval_not_work, min_interval);
399
60
        curr_interval_not_work = std::min(curr_interval_not_work, max_interval);
400
401
        // start clean trash and update usage.
402
60
        Status res = start_trash_sweep(&usage);
403
60
        if (res.ok() && _need_clean_trash.exchange(false, std::memory_order_relaxed)) {
404
0
            res = start_trash_sweep(&usage, true);
405
0
        }
406
407
60
        if (!res.ok()) {
408
0
            LOG(WARNING) << "one or more errors occur when sweep trash."
409
0
                         << "see previous message for detail. err code=" << res;
410
            // do nothing. continue next loop.
411
0
        }
412
60
    } while (!_stop_background_threads_latch.wait_for(std::chrono::seconds(curr_interval)));
413
5
}
414
415
5
void StorageEngine::_disk_stat_monitor_thread_callback() {
416
5
    int32_t interval = config::disk_stat_monitor_interval;
417
2.09k
    do {
418
2.09k
        _start_disk_stat_monitor();
419
420
2.09k
        interval = config::disk_stat_monitor_interval;
421
2.09k
        if (interval <= 0) {
422
0
            LOG(WARNING) << "disk_stat_monitor_interval config is illegal: " << interval
423
0
                         << ", force set to 1";
424
0
            interval = 1;
425
0
        }
426
2.09k
    } while (!_stop_background_threads_latch.wait_for(std::chrono::seconds(interval)));
427
5
}
428
429
5
void StorageEngine::_unused_rowset_monitor_thread_callback() {
430
5
    int32_t interval = config::unused_rowset_monitor_interval;
431
352
    do {
432
352
        start_delete_unused_rowset();
433
434
352
        interval = config::unused_rowset_monitor_interval;
435
352
        if (interval <= 0) {
436
0
            LOG(WARNING) << "unused_rowset_monitor_interval config is illegal: " << interval
437
0
                         << ", force set to 1";
438
0
            interval = 1;
439
0
        }
440
352
    } while (!_stop_background_threads_latch.wait_for(std::chrono::seconds(interval)));
441
5
}
442
443
2.26k
int32_t StorageEngine::_auto_get_interval_by_disk_capacity(DataDir* data_dir) {
444
2.26k
    double disk_used = data_dir->get_usage(0);
445
2.26k
    double remain_used = 1 - disk_used;
446
2.26k
    DCHECK(remain_used >= 0 && remain_used <= 1);
447
2.26k
    DCHECK(config::path_gc_check_interval_second >= 0);
448
2.26k
    int32_t ret = 0;
449
2.26k
    if (remain_used > 0.9) {
450
        // if config::path_gc_check_interval_second == 24h
451
0
        ret = config::path_gc_check_interval_second;
452
2.26k
    } else if (remain_used > 0.7) {
453
        // 12h
454
0
        ret = config::path_gc_check_interval_second / 2;
455
2.26k
    } else if (remain_used > 0.5) {
456
        // 6h
457
199
        ret = config::path_gc_check_interval_second / 4;
458
2.06k
    } else if (remain_used > 0.3) {
459
        // 4h
460
2.06k
        ret = config::path_gc_check_interval_second / 6;
461
2.06k
    } else {
462
        // 3h
463
0
        ret = config::path_gc_check_interval_second / 8;
464
0
    }
465
2.26k
    return ret;
466
2.26k
}
467
468
8
void StorageEngine::_path_gc_thread_callback(DataDir* data_dir) {
469
8
    LOG(INFO) << "try to start path gc thread!";
470
8
    time_t last_exec_time = 0;
471
2.26k
    do {
472
2.26k
        time_t current_time = time(nullptr);
473
474
2.26k
        int32_t interval = _auto_get_interval_by_disk_capacity(data_dir);
475
2.26k
        DBUG_EXECUTE_IF("_path_gc_thread_callback.interval.eq.1ms", {
476
2.26k
            LOG(INFO) << "debug point change interval eq 1ms";
477
2.26k
            interval = 1;
478
2.26k
            while (DebugPoints::instance()->is_enable("_path_gc_thread_callback.always.do")) {
479
2.26k
                data_dir->perform_path_gc();
480
2.26k
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
481
2.26k
            }
482
2.26k
        });
483
2.26k
        if (interval <= 0) {
484
2.26k
            LOG(WARNING) << "path gc thread check interval config is illegal:" << interval
485
2.26k
                         << " will be forced set to half hour";
486
2.26k
            interval = 1800; // 0.5 hour
487
2.26k
        }
488
2.26k
        if (current_time - last_exec_time >= interval) {
489
12
            LOG(INFO) << "try to perform path gc! disk remain [" << 1 - data_dir->get_usage(0)
490
12
                      << "] internal [" << interval << "]";
491
12
            data_dir->perform_path_gc();
492
12
            last_exec_time = time(nullptr);
493
12
        }
494
2.26k
    } while (!_stop_background_threads_latch.wait_for(std::chrono::seconds(5)));
495
8
    LOG(INFO) << "stop path gc thread!";
496
8
}
497
498
5
void StorageEngine::_tablet_checkpoint_callback(const std::vector<DataDir*>& data_dirs) {
499
5
    int64_t interval = config::generate_tablet_meta_checkpoint_tasks_interval_secs;
500
21
    do {
501
24
        for (auto data_dir : data_dirs) {
502
24
            LOG(INFO) << "begin to produce tablet meta checkpoint tasks, data_dir="
503
24
                      << data_dir->path();
504
24
            auto st = _tablet_meta_checkpoint_thread_pool->submit_func(
505
24
                    [data_dir, this]() { _tablet_manager->do_tablet_meta_checkpoint(data_dir); });
506
24
            if (!st.ok()) {
507
0
                LOG(WARNING) << "submit tablet checkpoint tasks failed.";
508
0
            }
509
24
        }
510
21
        interval = config::generate_tablet_meta_checkpoint_tasks_interval_secs;
511
21
    } while (!_stop_background_threads_latch.wait_for(std::chrono::seconds(interval)));
512
5
}
513
514
5
void StorageEngine::_tablet_path_check_callback() {
515
5
    struct TabletIdComparator {
516
5
        bool operator()(Tablet* a, Tablet* b) { return a->tablet_id() < b->tablet_id(); }
517
5
    };
518
519
5
    using TabletQueue = std::priority_queue<Tablet*, std::vector<Tablet*>, TabletIdComparator>;
520
521
5
    int64_t interval = config::tablet_path_check_interval_seconds;
522
5
    if (interval <= 0) {
523
5
        return;
524
5
    }
525
526
0
    int64_t last_tablet_id = 0;
527
0
    do {
528
0
        int32_t batch_size = config::tablet_path_check_batch_size;
529
0
        if (batch_size <= 0) {
530
0
            if (_stop_background_threads_latch.wait_for(std::chrono::seconds(interval))) {
531
0
                break;
532
0
            }
533
0
            continue;
534
0
        }
535
536
0
        LOG(INFO) << "start to check tablet path";
537
538
0
        auto all_tablets = _tablet_manager->get_all_tablet(
539
0
                [](Tablet* t) { return t->is_used() && t->tablet_state() == TABLET_RUNNING; });
540
541
0
        TabletQueue big_id_tablets;
542
0
        TabletQueue small_id_tablets;
543
0
        for (auto tablet : all_tablets) {
544
0
            auto tablet_id = tablet->tablet_id();
545
0
            TabletQueue* belong_tablets = nullptr;
546
0
            if (tablet_id > last_tablet_id) {
547
0
                if (big_id_tablets.size() < batch_size ||
548
0
                    big_id_tablets.top()->tablet_id() > tablet_id) {
549
0
                    belong_tablets = &big_id_tablets;
550
0
                }
551
0
            } else if (big_id_tablets.size() < batch_size) {
552
0
                if (small_id_tablets.size() < batch_size ||
553
0
                    small_id_tablets.top()->tablet_id() > tablet_id) {
554
0
                    belong_tablets = &small_id_tablets;
555
0
                }
556
0
            }
557
0
            if (belong_tablets != nullptr) {
558
0
                belong_tablets->push(tablet.get());
559
0
                if (belong_tablets->size() > batch_size) {
560
0
                    belong_tablets->pop();
561
0
                }
562
0
            }
563
0
        }
564
565
0
        int32_t need_small_id_tablet_size =
566
0
                batch_size - static_cast<int32_t>(big_id_tablets.size());
567
568
0
        if (!big_id_tablets.empty()) {
569
0
            last_tablet_id = big_id_tablets.top()->tablet_id();
570
0
        }
571
0
        while (!big_id_tablets.empty()) {
572
0
            big_id_tablets.top()->check_tablet_path_exists();
573
0
            big_id_tablets.pop();
574
0
        }
575
576
0
        if (!small_id_tablets.empty() && need_small_id_tablet_size > 0) {
577
0
            while (static_cast<int32_t>(small_id_tablets.size()) > need_small_id_tablet_size) {
578
0
                small_id_tablets.pop();
579
0
            }
580
581
0
            last_tablet_id = small_id_tablets.top()->tablet_id();
582
0
            while (!small_id_tablets.empty()) {
583
0
                small_id_tablets.top()->check_tablet_path_exists();
584
0
                small_id_tablets.pop();
585
0
            }
586
0
        }
587
588
0
    } while (!_stop_background_threads_latch.wait_for(std::chrono::seconds(interval)));
589
0
}
590
591
111k
void StorageEngine::_adjust_compaction_thread_num() {
592
111k
    TEST_SYNC_POINT_RETURN_WITH_VOID("StorageEngine::_adjust_compaction_thread_num.return_void");
593
111k
    auto base_compaction_threads_num = get_base_compaction_threads_num(_store_map.size());
594
111k
    if (_base_compaction_thread_pool->max_threads() != base_compaction_threads_num) {
595
0
        int old_max_threads = _base_compaction_thread_pool->max_threads();
596
0
        Status status = _base_compaction_thread_pool->set_max_threads(base_compaction_threads_num);
597
0
        if (status.ok()) {
598
0
            VLOG_NOTICE << "update base compaction thread pool max_threads from " << old_max_threads
599
0
                        << " to " << base_compaction_threads_num;
600
0
        }
601
0
    }
602
111k
    if (_base_compaction_thread_pool->min_threads() != base_compaction_threads_num) {
603
0
        int old_min_threads = _base_compaction_thread_pool->min_threads();
604
0
        Status status = _base_compaction_thread_pool->set_min_threads(base_compaction_threads_num);
605
0
        if (status.ok()) {
606
0
            VLOG_NOTICE << "update base compaction thread pool min_threads from " << old_min_threads
607
0
                        << " to " << base_compaction_threads_num;
608
0
        }
609
0
    }
610
611
111k
    auto cumu_compaction_threads_num = get_cumu_compaction_threads_num(_store_map.size());
612
111k
    if (_cumu_compaction_thread_pool->max_threads() != cumu_compaction_threads_num) {
613
0
        int old_max_threads = _cumu_compaction_thread_pool->max_threads();
614
0
        Status status = _cumu_compaction_thread_pool->set_max_threads(cumu_compaction_threads_num);
615
0
        if (status.ok()) {
616
0
            VLOG_NOTICE << "update cumu compaction thread pool max_threads from " << old_max_threads
617
0
                        << " to " << cumu_compaction_threads_num;
618
0
        }
619
0
    }
620
111k
    if (_cumu_compaction_thread_pool->min_threads() != cumu_compaction_threads_num) {
621
0
        int old_min_threads = _cumu_compaction_thread_pool->min_threads();
622
0
        Status status = _cumu_compaction_thread_pool->set_min_threads(cumu_compaction_threads_num);
623
0
        if (status.ok()) {
624
0
            VLOG_NOTICE << "update cumu compaction thread pool min_threads from " << old_min_threads
625
0
                        << " to " << cumu_compaction_threads_num;
626
0
        }
627
0
    }
628
629
111k
    auto binlog_compaction_threads_num = get_binlog_compaction_threads_num(_store_map.size());
630
111k
    if (_binlog_compaction_thread_pool->max_threads() != binlog_compaction_threads_num) {
631
0
        int old_max_threads = _binlog_compaction_thread_pool->max_threads();
632
0
        Status status =
633
0
                _binlog_compaction_thread_pool->set_max_threads(binlog_compaction_threads_num);
634
0
        if (status.ok()) {
635
0
            VLOG_NOTICE << "update binlog compaction thread pool max_threads from "
636
0
                        << old_max_threads << " to " << binlog_compaction_threads_num;
637
0
        }
638
0
    }
639
111k
    if (_binlog_compaction_thread_pool->min_threads() != binlog_compaction_threads_num) {
640
0
        int old_min_threads = _binlog_compaction_thread_pool->min_threads();
641
0
        Status status =
642
0
                _binlog_compaction_thread_pool->set_min_threads(binlog_compaction_threads_num);
643
0
        if (status.ok()) {
644
0
            VLOG_NOTICE << "update binlog compaction thread pool min_threads from "
645
0
                        << old_min_threads << " to " << binlog_compaction_threads_num;
646
0
        }
647
0
    }
648
111k
}
649
650
12
void StorageEngine::_compaction_tasks_producer_callback() {
651
12
    LOG(INFO) << "try to start compaction producer process!";
652
653
12
    std::vector<DataDir*> data_dirs = get_stores();
654
655
12
    int round = 0;
656
12
    CompactionType compaction_type;
657
658
    // Used to record the time when the score metric was last updated.
659
    // The update of the score metric is accompanied by the logic of selecting the tablet.
660
    // If there is no slot available, the logic of selecting the tablet will be terminated,
661
    // which causes the score metric update to be terminated.
662
    // In order to avoid this situation, we need to update the score regularly.
663
12
    int64_t last_cumulative_score_update_time = 0;
664
12
    int64_t last_base_score_update_time = 0;
665
12
    static const int64_t check_score_interval_ms = 5000; // 5 secs
666
667
12
    int64_t interval = config::generate_compaction_tasks_interval_ms;
668
12.6k
    do {
669
12.6k
        int64_t cur_time = UnixMillis();
670
12.6k
        if (!config::disable_auto_compaction &&
671
12.6k
            (!config::enable_compaction_pause_on_high_memory ||
672
12.6k
             !GlobalMemoryArbitrator::is_exceed_soft_mem_limit(GB_EXCHANGE_BYTE))) {
673
12.6k
            _adjust_compaction_thread_num();
674
675
12.6k
            bool check_score = false;
676
12.6k
            if (round < config::cumulative_compaction_rounds_for_each_base_compaction_round) {
677
11.3k
                compaction_type = CompactionType::CUMULATIVE_COMPACTION;
678
11.3k
                round++;
679
11.3k
                if (cur_time - last_cumulative_score_update_time >= check_score_interval_ms) {
680
1.64k
                    check_score = true;
681
1.64k
                    last_cumulative_score_update_time = cur_time;
682
1.64k
                }
683
11.3k
            } else {
684
1.25k
                compaction_type = CompactionType::BASE_COMPACTION;
685
1.25k
                round = 0;
686
1.25k
                if (cur_time - last_base_score_update_time >= check_score_interval_ms) {
687
1.16k
                    check_score = true;
688
1.16k
                    last_base_score_update_time = cur_time;
689
1.16k
                }
690
1.25k
            }
691
12.6k
            std::unique_ptr<ThreadPool>& thread_pool =
692
12.6k
                    (compaction_type == CompactionType::CUMULATIVE_COMPACTION)
693
12.6k
                            ? _cumu_compaction_thread_pool
694
12.6k
                            : _base_compaction_thread_pool;
695
12.6k
            bvar::Status<int64_t>& g_compaction_task_num_per_round =
696
12.6k
                    (compaction_type == CompactionType::CUMULATIVE_COMPACTION)
697
12.6k
                            ? g_cumu_compaction_task_num_per_round
698
12.6k
                            : g_base_compaction_task_num_per_round;
699
12.6k
            if (config::compaction_num_per_round != -1) {
700
0
                _compaction_num_per_round = config::compaction_num_per_round;
701
12.6k
            } else if (thread_pool->get_queue_size() == 0) {
702
                // If all tasks in the thread pool queue are executed,
703
                // double the number of tasks generated each time,
704
                // with a maximum of config::max_automatic_compaction_num_per_round tasks per generation.
705
12.1k
                if (_compaction_num_per_round < config::max_automatic_compaction_num_per_round) {
706
31
                    _compaction_num_per_round *= 2;
707
31
                    g_compaction_task_num_per_round.set_value(_compaction_num_per_round);
708
31
                }
709
12.1k
            } else if (thread_pool->get_queue_size() > _compaction_num_per_round / 2) {
710
                // If all tasks in the thread pool is greater than
711
                // half of the tasks submitted in the previous round,
712
                // reduce the number of tasks generated each time by half, with a minimum of 1.
713
3
                if (_compaction_num_per_round > 1) {
714
1
                    _compaction_num_per_round /= 2;
715
1
                    g_compaction_task_num_per_round.set_value(_compaction_num_per_round);
716
1
                }
717
3
            }
718
12.6k
            _update_cumulative_compaction_policy();
719
12.6k
            std::vector<TabletCompactionContext> tablet_compaction_contexts =
720
12.6k
                    _generate_compaction_tasks(compaction_type, data_dirs, check_score);
721
12.6k
            if (tablet_compaction_contexts.empty()) {
722
4.81k
                std::unique_lock<std::mutex> lock(_compaction_producer_sleep_mutex);
723
4.81k
                _wakeup_producer_flag = 0;
724
                // It is necessary to wake up the thread on timeout to prevent deadlock
725
                // in case of no running compaction task.
726
4.81k
                _compaction_producer_sleep_cv.wait_for(
727
4.81k
                        lock, std::chrono::milliseconds(2000),
728
9.62k
                        [this] { return _wakeup_producer_flag == 1; });
729
4.81k
                continue;
730
4.81k
            }
731
732
373k
            for (const auto& tablet_compaction_context : tablet_compaction_contexts) {
733
373k
                const auto& tablet = tablet_compaction_context.tablet;
734
373k
                if (compaction_type == CompactionType::BASE_COMPACTION) {
735
74.5k
                    tablet->set_last_base_compaction_schedule_time(UnixMillis());
736
298k
                } else if (compaction_type == CompactionType::CUMULATIVE_COMPACTION) {
737
298k
                    tablet->set_last_cumu_compaction_schedule_time(UnixMillis());
738
298k
                } else if (compaction_type == CompactionType::FULL_COMPACTION) {
739
0
                    tablet->set_last_full_compaction_schedule_time(UnixMillis());
740
0
                }
741
373k
                Status st = _submit_compaction_task(tablet, compaction_type, false);
742
373k
                if (!st.ok()) {
743
0
                    LOG(WARNING) << "failed to submit compaction task for tablet: "
744
0
                                 << tablet->tablet_id() << ", err: " << st;
745
0
                }
746
373k
            }
747
7.80k
            interval = config::generate_compaction_tasks_interval_ms;
748
7.80k
        } else {
749
1
            interval = 5000; // 5s to check disable_auto_compaction
750
1
        }
751
752
        // wait some seconds for ut test
753
7.80k
        {
754
7.80k
            std ::vector<std ::any> args {};
755
7.80k
            args.emplace_back(1);
756
7.80k
            doris ::SyncPoint ::get_instance()->process(
757
7.80k
                    "StorageEngine::_compaction_tasks_producer_callback", std ::move(args));
758
7.80k
        }
759
7.80k
        int64_t end_time = UnixMillis();
760
7.80k
        DorisMetrics::instance()->compaction_producer_callback_a_round_time->set_value(end_time -
761
7.80k
                                                                                       cur_time);
762
12.6k
    } while (!_stop_background_threads_latch.wait_for(std::chrono::milliseconds(interval)));
763
12
}
764
765
5
void StorageEngine::_binlog_compaction_tasks_producer_callback() {
766
5
    LOG(INFO) << "try to start binlog compaction producer process!";
767
768
5
    std::vector<DataDir*> data_dirs = get_stores();
769
770
5
    int64_t last_binlog_score_update_time = 0;
771
5
    static const int64_t check_score_interval_ms = 5000;
772
773
5
    int64_t interval = config::generate_compaction_tasks_interval_ms;
774
99.2k
    do {
775
99.2k
        int64_t cur_time = UnixMillis();
776
99.2k
        if (config::enable_feature_binlog && !config::disable_auto_compaction &&
777
99.2k
            (!config::enable_compaction_pause_on_high_memory ||
778
99.2k
             !GlobalMemoryArbitrator::is_exceed_soft_mem_limit(GB_EXCHANGE_BYTE))) {
779
99.2k
            _adjust_compaction_thread_num();
780
781
99.2k
            bool check_score = false;
782
99.2k
            if (cur_time - last_binlog_score_update_time >= check_score_interval_ms) {
783
2.08k
                check_score = true;
784
2.08k
                last_binlog_score_update_time = cur_time;
785
2.08k
            }
786
787
99.2k
            std::vector<TabletCompactionContext> tablet_compaction_contexts =
788
99.2k
                    _generate_compaction_tasks(CompactionType::BINLOG_COMPACTION, data_dirs,
789
99.2k
                                               check_score);
790
99.2k
            for (const auto& tablet_compaction_context : tablet_compaction_contexts) {
791
1.30k
                const auto& tablet = tablet_compaction_context.tablet;
792
1.30k
                Status st =
793
1.30k
                        _submit_compaction_task(tablet, CompactionType::BINLOG_COMPACTION, false, 0,
794
1.30k
                                                tablet_compaction_context.prefer_compaction_level);
795
1.30k
                if (!st.ok()) {
796
0
                    LOG(WARNING) << "failed to submit binlog compaction task for tablet: "
797
0
                                 << tablet->tablet_id() << ", err: " << st;
798
0
                }
799
1.30k
            }
800
99.2k
            interval = config::generate_compaction_tasks_interval_ms;
801
99.2k
        } else {
802
0
            interval = 5000;
803
0
        }
804
99.2k
    } while (!_stop_background_threads_latch.wait_for(std::chrono::milliseconds(interval)));
805
5
}
806
807
void StorageEngine::get_tablet_rowset_versions(const PGetTabletVersionsRequest* request,
808
0
                                               PGetTabletVersionsResponse* response) {
809
0
    TabletSharedPtr tablet = _tablet_manager->get_tablet(request->tablet_id());
810
0
    if (tablet == nullptr) {
811
0
        response->mutable_status()->set_status_code(TStatusCode::CANCELLED);
812
0
        return;
813
0
    }
814
0
    std::vector<Version> local_versions = tablet->get_all_local_versions();
815
0
    for (const auto& local_version : local_versions) {
816
0
        auto version = response->add_versions();
817
0
        version->set_first(local_version.first);
818
0
        version->set_second(local_version.second);
819
0
    }
820
0
    response->mutable_status()->set_status_code(0);
821
0
}
822
823
bool need_generate_compaction_tasks(int task_cnt_per_disk, int thread_per_disk,
824
234k
                                    CompactionType compaction_type, bool all_base) {
825
234k
    if (compaction_type == CompactionType::BINLOG_COMPACTION) {
826
206k
        return task_cnt_per_disk < thread_per_disk;
827
206k
    }
828
829
    // We need to reserve at least one Slot for cumulative compaction.
830
    // So when there is only one Slot, we have to judge whether there is a cumulative compaction
831
    // in the current submitted tasks.
832
    // If so, the last Slot can be assigned to Base compaction,
833
    // otherwise, this Slot needs to be reserved for cumulative compaction.
834
28.4k
    if (task_cnt_per_disk >= thread_per_disk) {
835
        // Return if no available slot
836
42
        return false;
837
28.3k
    } else if (task_cnt_per_disk >= thread_per_disk - 1) {
838
        // Only one slot left, check if it can be assigned to base compaction task.
839
94
        if (compaction_type == CompactionType::BASE_COMPACTION) {
840
14
            if (all_base) {
841
0
                return false;
842
0
            }
843
14
        }
844
94
    }
845
28.3k
    return true;
846
28.4k
}
847
848
117k
int get_concurrent_per_disk(int max_score, int thread_per_disk) {
849
117k
    if (!config::enable_compaction_priority_scheduling) {
850
0
        return thread_per_disk;
851
0
    }
852
853
117k
    double load_average = 0;
854
117k
    if (DorisMetrics::instance()->system_metrics() != nullptr) {
855
5.33k
        load_average = DorisMetrics::instance()->system_metrics()->get_load_average_1_min();
856
5.33k
    }
857
117k
    int num_cores = doris::CpuInfo::num_cores();
858
117k
    bool cpu_usage_high = load_average > num_cores * 0.8;
859
860
117k
    auto process_memory_usage = doris::GlobalMemoryArbitrator::process_memory_usage();
861
117k
    bool memory_usage_high = static_cast<double>(process_memory_usage) >
862
117k
                             static_cast<double>(MemInfo::soft_mem_limit()) * 0.8;
863
864
117k
    if (max_score <= config::low_priority_compaction_score_threshold &&
865
117k
        (cpu_usage_high || memory_usage_high)) {
866
45.0k
        return config::low_priority_compaction_task_num_per_disk;
867
45.0k
    }
868
869
72.4k
    return thread_per_disk;
870
117k
}
871
872
235k
int32_t disk_compaction_slot_num(const DataDir& data_dir, CompactionType compaction_type) {
873
235k
    if (compaction_type == CompactionType::BINLOG_COMPACTION) {
874
206k
        return config::binlog_compaction_task_num_per_disk;
875
206k
    }
876
28.4k
    return data_dir.is_ssd_disk() ? config::compaction_task_num_per_fast_disk
877
28.4k
                                  : config::compaction_task_num_per_disk;
878
235k
}
879
880
bool has_free_compaction_slot(CompactionSubmitRegistry* registry, DataDir* dir,
881
117k
                              CompactionType compaction_type, uint32_t executing_cnt) {
882
117k
    int32_t thread_per_disk = disk_compaction_slot_num(*dir, compaction_type);
883
117k
    return need_generate_compaction_tasks(
884
117k
            executing_cnt, thread_per_disk, compaction_type,
885
117k
            !registry->has_compaction_task(dir, CompactionType::CUMULATIVE_COMPACTION));
886
117k
}
887
888
std::vector<TabletCompactionContext> StorageEngine::_generate_compaction_tasks(
889
111k
        CompactionType compaction_type, std::vector<DataDir*>& data_dirs, bool check_score) {
890
111k
    TEST_SYNC_POINT_RETURN_WITH_VALUE("olap_server::_generate_compaction_tasks.return_empty",
891
111k
                                      std::vector<TabletCompactionContext> {});
892
111k
    _update_cumulative_compaction_policy();
893
111k
    auto cumulative_compaction_policies = _snapshot_cumulative_compaction_policy();
894
111k
    std::vector<TabletCompactionContext> tablet_compaction_contexts;
895
111k
    uint32_t max_compaction_score = 0;
896
897
111k
    std::random_device rd;
898
111k
    std::mt19937 g(rd());
899
111k
    std::shuffle(data_dirs.begin(), data_dirs.end(), g);
900
901
    // Copy _tablet_submitted_xxx_compaction map so that we don't need to hold _tablet_submitted_compaction_mutex
902
    // when traversing the data dir
903
111k
    auto compaction_registry_snapshot = _compaction_submit_registry.create_snapshot();
904
117k
    for (auto* data_dir : data_dirs) {
905
117k
        bool need_pick_tablet = true;
906
117k
        uint32_t executing_task_num =
907
117k
                compaction_type == CompactionType::BINLOG_COMPACTION
908
117k
                        ? compaction_registry_snapshot.count_executing_compaction(
909
103k
                                  data_dir, CompactionType::BINLOG_COMPACTION)
910
117k
                        : compaction_registry_snapshot.count_executing_cumu_and_base(data_dir);
911
117k
        need_pick_tablet = has_free_compaction_slot(&compaction_registry_snapshot, data_dir,
912
117k
                                                    compaction_type, executing_task_num);
913
117k
        if (!need_pick_tablet && !check_score) {
914
0
            continue;
915
0
        }
916
917
        // Even if need_pick_tablet is false, we still need to call find_best_tablet_to_compaction(),
918
        // So that we can update the max_compaction_score metric.
919
117k
        if (!data_dir->reach_capacity_limit(0)) {
920
117k
            uint32_t disk_max_score = 0;
921
117k
            auto tablet_contexts = compaction_registry_snapshot.pick_topn_tablets_for_compaction(
922
117k
                    _tablet_manager.get(), data_dir, compaction_type,
923
117k
                    cumulative_compaction_policies, &disk_max_score);
924
117k
            int concurrent_num = get_concurrent_per_disk(
925
117k
                    disk_max_score, disk_compaction_slot_num(*data_dir, compaction_type));
926
117k
            need_pick_tablet = need_generate_compaction_tasks(
927
117k
                    executing_task_num, concurrent_num, compaction_type,
928
117k
                    !compaction_registry_snapshot.has_compaction_task(
929
117k
                            data_dir, CompactionType::CUMULATIVE_COMPACTION));
930
376k
            for (const auto& context : tablet_contexts) {
931
376k
                if (context.tablet != nullptr) {
932
376k
                    if (need_pick_tablet) {
933
374k
                        tablet_compaction_contexts.emplace_back(context);
934
374k
                    }
935
376k
                    max_compaction_score = std::max(max_compaction_score, disk_max_score);
936
376k
                }
937
376k
            }
938
117k
        }
939
117k
    }
940
941
111k
    if (max_compaction_score > 0) {
942
7.91k
        if (compaction_type == CompactionType::BASE_COMPACTION) {
943
1.25k
            DorisMetrics::instance()->tablet_base_max_compaction_score->set_value(
944
1.25k
                    max_compaction_score);
945
6.66k
        } else if (compaction_type == CompactionType::CUMULATIVE_COMPACTION) {
946
6.58k
            DorisMetrics::instance()->tablet_cumulative_max_compaction_score->set_value(
947
6.58k
                    max_compaction_score);
948
6.58k
        } else if (compaction_type == CompactionType::BINLOG_COMPACTION) {
949
80
            DorisMetrics::instance()->tablet_binlog_max_compaction_score->set_value(
950
80
                    max_compaction_score);
951
80
        }
952
7.91k
    }
953
111k
    return tablet_compaction_contexts;
954
111k
}
955
956
124k
void StorageEngine::_update_cumulative_compaction_policy() {
957
124k
    std::lock_guard<std::mutex> lock(_cumulative_compaction_policy_mtx);
958
124k
    if (_cumulative_compaction_policies.empty()) {
959
7
        _cumulative_compaction_policies[CUMULATIVE_SIZE_BASED_POLICY] =
960
7
                CumulativeCompactionPolicyFactory::create_cumulative_compaction_policy(
961
7
                        CUMULATIVE_SIZE_BASED_POLICY);
962
7
        _cumulative_compaction_policies[CUMULATIVE_TIME_SERIES_POLICY] =
963
7
                CumulativeCompactionPolicyFactory::create_cumulative_compaction_policy(
964
7
                        CUMULATIVE_TIME_SERIES_POLICY);
965
7
    }
966
124k
}
967
968
111k
CumuCompactionPolicyTable StorageEngine::_snapshot_cumulative_compaction_policy() {
969
111k
    std::lock_guard<std::mutex> lock(_cumulative_compaction_policy_mtx);
970
111k
    return _cumulative_compaction_policies;
971
111k
}
972
973
std::shared_ptr<CumulativeCompactionPolicy> StorageEngine::_get_cumulative_compaction_policy(
974
0
        std::string_view compaction_policy) {
975
0
    std::lock_guard<std::mutex> lock(_cumulative_compaction_policy_mtx);
976
0
    return _cumulative_compaction_policies.at(compaction_policy);
977
0
}
978
979
void StorageEngine::_pop_tablet_from_submitted_compaction(TabletSharedPtr tablet,
980
374k
                                                          CompactionType compaction_type) {
981
374k
    _compaction_submit_registry.remove(tablet, compaction_type, [this]() {
982
374k
        std::unique_lock<std::mutex> lock(_compaction_producer_sleep_mutex);
983
374k
        _wakeup_producer_flag = 1;
984
374k
        _compaction_producer_sleep_cv.notify_one();
985
374k
    });
986
374k
}
987
988
Status StorageEngine::_submit_compaction_task(TabletSharedPtr tablet,
989
                                              CompactionType compaction_type, bool force,
990
374k
                                              int trigger_method, int8_t prefer_compaction_level) {
991
374k
    bool already_exist = _compaction_submit_registry.insert(tablet, compaction_type);
992
374k
    if (already_exist) {
993
0
        return Status::AlreadyExist<false>(
994
0
                "compaction task has already been submitted, tablet_id={}, compaction_type={}.",
995
0
                tablet->tablet_id(), compaction_type);
996
0
    }
997
374k
    tablet->compaction_stage = CompactionStage::PENDING;
998
374k
    std::shared_ptr<CompactionMixin> compaction;
999
374k
    int64_t permits = 0;
1000
374k
    Status st = Tablet::prepare_compaction_and_calculate_permits(
1001
374k
            compaction_type, tablet, compaction, permits, prefer_compaction_level);
1002
374k
    if (st.ok() && permits > 0) {
1003
4.55k
        if (!force) {
1004
4.55k
            if (compaction_type == CompactionType::BINLOG_COMPACTION) {
1005
0
                if (!_permit_limiter.try_request(permits, compaction_type)) {
1006
0
                    _pop_tablet_from_submitted_compaction(tablet, compaction_type);
1007
0
                    tablet->compaction_stage = CompactionStage::NOT_SCHEDULED;
1008
0
                    return Status::OK();
1009
0
                }
1010
4.55k
            } else {
1011
4.55k
                _permit_limiter.request(permits);
1012
4.55k
            }
1013
4.55k
        }
1014
        // Register task with CompactionTaskTracker as PENDING
1015
4.55k
        auto* tracker = CompactionTaskTracker::instance();
1016
4.55k
        int64_t compaction_id = compaction->compaction_id();
1017
4.55k
        {
1018
4.55k
            CompactionTaskInfo info;
1019
4.55k
            info.compaction_id = compaction_id;
1020
4.55k
            info.tablet_id = tablet->tablet_id();
1021
4.55k
            info.table_id = tablet->get_table_id();
1022
4.55k
            info.partition_id = tablet->partition_id();
1023
4.55k
            switch (compaction_type) {
1024
0
            case CompactionType::BASE_COMPACTION:
1025
0
                info.compaction_type = CompactionProfileType::BASE;
1026
0
                break;
1027
4.55k
            case CompactionType::CUMULATIVE_COMPACTION:
1028
4.55k
                info.compaction_type = CompactionProfileType::CUMULATIVE;
1029
4.55k
                break;
1030
0
            case CompactionType::FULL_COMPACTION:
1031
0
                info.compaction_type = CompactionProfileType::FULL;
1032
0
                break;
1033
0
            case CompactionType::BINLOG_COMPACTION:
1034
0
                info.compaction_type = CompactionProfileType::BINLOG;
1035
0
                break;
1036
0
            default:
1037
0
                DCHECK(false) << "invalid compaction type: " << compaction_type;
1038
4.55k
            }
1039
4.55k
            info.status = CompactionTaskStatus::PENDING;
1040
4.55k
            info.trigger_method = static_cast<TriggerMethod>(trigger_method);
1041
4.55k
            info.scheduled_time_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
1042
4.55k
                                             std::chrono::system_clock::now().time_since_epoch())
1043
4.55k
                                             .count();
1044
4.55k
            info.permits = permits;
1045
4.55k
            info.backend_id = BackendOptions::get_backend_id();
1046
4.55k
            info.compaction_score = tablet->get_real_compaction_score();
1047
4.55k
            info.input_rowsets_count = compaction->input_rowsets_count();
1048
4.55k
            info.input_row_num = compaction->input_row_num_value();
1049
4.55k
            info.input_data_size = compaction->input_rowsets_data_size();
1050
4.55k
            info.input_index_size = compaction->input_rowsets_index_size();
1051
4.55k
            info.input_total_size = compaction->input_rowsets_total_size();
1052
4.55k
            info.input_segments_num = compaction->input_segments_num_value();
1053
4.55k
            info.input_version_range = compaction->input_version_range_str();
1054
4.55k
            info.is_vertical = compaction->is_vertical();
1055
4.55k
            tracker->register_task(std::move(info));
1056
4.55k
        }
1057
0
        std::unique_ptr<ThreadPool>* thread_pool = nullptr;
1058
4.55k
        const char* compaction_type_name = "UNKNOWN";
1059
4.55k
        switch (compaction_type) {
1060
4.55k
        case CompactionType::CUMULATIVE_COMPACTION:
1061
4.55k
            thread_pool = &_cumu_compaction_thread_pool;
1062
4.55k
            compaction_type_name = "CUMU";
1063
4.55k
            break;
1064
0
        case CompactionType::BINLOG_COMPACTION:
1065
0
            thread_pool = &_binlog_compaction_thread_pool;
1066
0
            compaction_type_name = "BINLOG";
1067
0
            break;
1068
0
        case CompactionType::BASE_COMPACTION:
1069
0
        case CompactionType::FULL_COMPACTION:
1070
0
            thread_pool = &_base_compaction_thread_pool;
1071
0
            compaction_type_name = "BASE";
1072
0
            break;
1073
0
        default:
1074
0
            DCHECK(false) << "invalid compaction type: " << compaction_type;
1075
4.55k
        }
1076
4.55k
        VLOG_CRITICAL << "compaction thread pool. type: " << compaction_type_name
1077
0
                      << ", num_threads: " << thread_pool->get()->num_threads()
1078
0
                      << ", num_threads_pending_start: "
1079
0
                      << thread_pool->get()->num_threads_pending_start()
1080
0
                      << ", num_active_threads: " << thread_pool->get()->num_active_threads()
1081
0
                      << ", max_threads: " << thread_pool->get()->max_threads()
1082
0
                      << ", min_threads: " << thread_pool->get()->min_threads()
1083
0
                      << ", num_total_queued_tasks: " << thread_pool->get()->get_queue_size();
1084
4.55k
        auto status =
1085
4.55k
                thread_pool->get()->submit_func([=, compaction = std::move(compaction), this]() {
1086
4.54k
                    _handle_compaction(std::move(tablet), std::move(compaction), compaction_type,
1087
4.54k
                                       permits, force, compaction_id);
1088
4.54k
                });
1089
4.55k
        if (compaction_type == CompactionType::CUMULATIVE_COMPACTION) [[likely]] {
1090
4.55k
            DorisMetrics::instance()->cumulative_compaction_task_pending_total->set_value(
1091
4.55k
                    _cumu_compaction_thread_pool->get_queue_size());
1092
4.55k
        } else if (compaction_type == CompactionType::BASE_COMPACTION) {
1093
0
            DorisMetrics::instance()->base_compaction_task_pending_total->set_value(
1094
0
                    _base_compaction_thread_pool->get_queue_size());
1095
0
        }
1096
4.55k
        if (!status.ok()) {
1097
            // Cleanup tracker on submit failure
1098
0
            tracker->remove_task(compaction_id);
1099
0
            if (!force) {
1100
0
                _permit_limiter.release(permits, compaction_type);
1101
0
            }
1102
0
            _pop_tablet_from_submitted_compaction(tablet, compaction_type);
1103
0
            tablet->compaction_stage = CompactionStage::NOT_SCHEDULED;
1104
0
            return Status::InternalError(
1105
0
                    "failed to submit compaction task to thread pool, "
1106
0
                    "tablet_id={}, compaction_type={}.",
1107
0
                    tablet->tablet_id(), compaction_type);
1108
0
        }
1109
4.55k
        return Status::OK();
1110
370k
    } else {
1111
370k
        _pop_tablet_from_submitted_compaction(tablet, compaction_type);
1112
370k
        tablet->compaction_stage = CompactionStage::NOT_SCHEDULED;
1113
370k
        if (!st.ok()) {
1114
0
            return Status::InternalError(
1115
0
                    "failed to prepare compaction task and calculate permits, "
1116
0
                    "tablet_id={}, compaction_type={}, "
1117
0
                    "permit={}, current_permit={}, status={}",
1118
0
                    tablet->tablet_id(), compaction_type, permits, _permit_limiter.usage(),
1119
0
                    st.to_string());
1120
0
        }
1121
370k
        return st;
1122
370k
    }
1123
374k
}
1124
1125
void StorageEngine::_handle_compaction(TabletSharedPtr tablet,
1126
                                       std::shared_ptr<CompactionMixin> compaction,
1127
                                       CompactionType compaction_type, int64_t permits, bool force,
1128
4.53k
                                       int64_t compaction_id) {
1129
4.53k
    if (compaction_type == CompactionType::CUMULATIVE_COMPACTION) [[likely]] {
1130
4.53k
        DorisMetrics::instance()->cumulative_compaction_task_running_total->increment(1);
1131
4.53k
        DorisMetrics::instance()->cumulative_compaction_task_pending_total->set_value(
1132
4.53k
                _cumu_compaction_thread_pool->get_queue_size());
1133
4.53k
    } else if (compaction_type == CompactionType::BASE_COMPACTION) {
1134
0
        DorisMetrics::instance()->base_compaction_task_running_total->increment(1);
1135
0
        DorisMetrics::instance()->base_compaction_task_pending_total->set_value(
1136
0
                _base_compaction_thread_pool->get_queue_size());
1137
0
    } else if (compaction_type == CompactionType::BINLOG_COMPACTION) {
1138
0
        DorisMetrics::instance()->binlog_compaction_task_running_total->increment(1);
1139
0
        DorisMetrics::instance()->binlog_compaction_task_pending_total->set_value(
1140
0
                _binlog_compaction_thread_pool->get_queue_size());
1141
0
    }
1142
4.53k
    bool is_large_task = true;
1143
4.53k
    Defer defer {[&]() {
1144
4.53k
        DBUG_EXECUTE_IF("StorageEngine._submit_compaction_task.sleep", { sleep(5); })
1145
        // Idempotent cleanup: remove task from tracker
1146
4.53k
        CompactionTaskTracker::instance()->remove_task(compaction_id);
1147
4.54k
        if (!force) {
1148
4.54k
            _permit_limiter.release(permits, compaction_type);
1149
4.54k
        }
1150
4.53k
        _pop_tablet_from_submitted_compaction(tablet, compaction_type);
1151
4.53k
        tablet->compaction_stage = CompactionStage::NOT_SCHEDULED;
1152
4.54k
        if (compaction_type == CompactionType::CUMULATIVE_COMPACTION) {
1153
4.54k
            std::lock_guard<std::mutex> lock(_cumu_compaction_delay_mtx);
1154
4.54k
            _cumu_compaction_thread_pool_used_threads--;
1155
4.54k
            if (!is_large_task) {
1156
6
                _cumu_compaction_thread_pool_small_tasks_running--;
1157
6
            }
1158
4.54k
            DorisMetrics::instance()->cumulative_compaction_task_running_total->increment(-1);
1159
4.54k
            DorisMetrics::instance()->cumulative_compaction_task_pending_total->set_value(
1160
4.54k
                    _cumu_compaction_thread_pool->get_queue_size());
1161
18.4E
        } else if (compaction_type == CompactionType::BASE_COMPACTION) {
1162
0
            DorisMetrics::instance()->base_compaction_task_running_total->increment(-1);
1163
0
            DorisMetrics::instance()->base_compaction_task_pending_total->set_value(
1164
0
                    _base_compaction_thread_pool->get_queue_size());
1165
18.4E
        } else if (compaction_type == CompactionType::BINLOG_COMPACTION) {
1166
0
            DorisMetrics::instance()->binlog_compaction_task_running_total->increment(-1);
1167
0
            DorisMetrics::instance()->binlog_compaction_task_pending_total->set_value(
1168
0
                    _binlog_compaction_thread_pool->get_queue_size());
1169
0
        }
1170
4.53k
    }};
1171
4.53k
    do {
1172
4.53k
        if (compaction->compaction_type() == ReaderType::READER_CUMULATIVE_COMPACTION) {
1173
4.53k
            std::lock_guard<std::mutex> lock(_cumu_compaction_delay_mtx);
1174
4.53k
            _cumu_compaction_thread_pool_used_threads++;
1175
4.53k
            if (config::large_cumu_compaction_task_min_thread_num > 1 &&
1176
4.53k
                _cumu_compaction_thread_pool->max_threads() >=
1177
14
                        config::large_cumu_compaction_task_min_thread_num) {
1178
                // Determine if this is a large task based on configured thresholds
1179
6
                is_large_task = (compaction->calc_input_rowsets_total_size() >
1180
6
                                         config::large_cumu_compaction_task_bytes_threshold ||
1181
6
                                 compaction->calc_input_rowsets_row_num() >
1182
6
                                         config::large_cumu_compaction_task_row_num_threshold);
1183
1184
                // Small task. No delay needed
1185
6
                if (!is_large_task) {
1186
6
                    _cumu_compaction_thread_pool_small_tasks_running++;
1187
6
                    break;
1188
6
                }
1189
                // Deal with large task
1190
0
                if (_should_delay_large_task()) {
1191
0
                    LOG_WARNING(
1192
0
                            "failed to do CumulativeCompaction, cumu thread pool is "
1193
0
                            "intensive, delay large task.")
1194
0
                            .tag("tablet_id", tablet->tablet_id())
1195
0
                            .tag("input_rows", compaction->calc_input_rowsets_row_num())
1196
0
                            .tag("input_rowsets_total_size",
1197
0
                                 compaction->calc_input_rowsets_total_size())
1198
0
                            .tag("config::large_cumu_compaction_task_bytes_threshold",
1199
0
                                 config::large_cumu_compaction_task_bytes_threshold)
1200
0
                            .tag("config::large_cumu_compaction_task_row_num_threshold",
1201
0
                                 config::large_cumu_compaction_task_row_num_threshold)
1202
0
                            .tag("remaining threads", _cumu_compaction_thread_pool_used_threads)
1203
0
                            .tag("small_tasks_running",
1204
0
                                 _cumu_compaction_thread_pool_small_tasks_running);
1205
                    // Delay this task and sleep 5s for this tablet
1206
0
                    long now = duration_cast<std::chrono::milliseconds>(
1207
0
                                       std::chrono::system_clock::now().time_since_epoch())
1208
0
                                       .count();
1209
0
                    tablet->set_last_cumu_compaction_failure_time(now);
1210
0
                    return;
1211
0
                }
1212
0
            }
1213
4.53k
        }
1214
4.53k
    } while (false);
1215
4.53k
    if (!tablet->can_do_compaction(tablet->data_dir()->path_hash(), compaction_type)) {
1216
0
        LOG(INFO) << "Tablet state has been changed, no need to begin this compaction "
1217
0
                     "task, tablet_id="
1218
0
                  << tablet->tablet_id() << ", tablet_state=" << tablet->tablet_state();
1219
0
        return;
1220
0
    }
1221
4.53k
    tablet->compaction_stage = CompactionStage::EXECUTING;
1222
    // Update tracker to RUNNING
1223
4.53k
    {
1224
4.53k
        RunningStats rs;
1225
4.53k
        rs.start_time_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
1226
4.53k
                                   std::chrono::system_clock::now().time_since_epoch())
1227
4.53k
                                   .count();
1228
4.53k
        rs.permits = permits;
1229
4.53k
        CompactionTaskTracker::instance()->update_to_running(compaction_id, rs);
1230
4.53k
    }
1231
4.53k
    TEST_SYNC_POINT_RETURN_WITH_VOID("olap_server::execute_compaction");
1232
4.53k
    tablet->execute_compaction(*compaction);
1233
4.53k
}
1234
1235
Status StorageEngine::submit_compaction_task(TabletSharedPtr tablet, CompactionType compaction_type,
1236
0
                                             bool force, bool eager, int trigger_method) {
1237
0
    if (!eager) {
1238
0
        DCHECK(compaction_type == CompactionType::BASE_COMPACTION ||
1239
0
               compaction_type == CompactionType::CUMULATIVE_COMPACTION);
1240
0
        auto compaction_registry_snapshot = _compaction_submit_registry.create_snapshot();
1241
0
        auto stores = get_stores();
1242
1243
0
        bool is_busy = std::none_of(
1244
0
                stores.begin(), stores.end(),
1245
0
                [&compaction_registry_snapshot, compaction_type](auto* data_dir) {
1246
0
                    return has_free_compaction_slot(
1247
0
                            &compaction_registry_snapshot, data_dir, compaction_type,
1248
0
                            compaction_registry_snapshot.count_executing_cumu_and_base(data_dir));
1249
0
                });
1250
0
        if (is_busy) {
1251
0
            LOG_EVERY_N(WARNING, 100)
1252
0
                    << "Too busy to submit a compaction task, tablet=" << tablet->get_table_id();
1253
0
            return Status::OK();
1254
0
        }
1255
0
    }
1256
0
    _update_cumulative_compaction_policy();
1257
    // alter table tableName set ("compaction_policy"="time_series")
1258
    // if atler table's compaction  policy, we need to modify tablet compaction policy shared ptr
1259
0
    if (tablet->get_cumulative_compaction_policy() == nullptr ||
1260
0
        tablet->get_cumulative_compaction_policy()->name() !=
1261
0
                tablet->tablet_meta()->compaction_policy()) {
1262
0
        tablet->set_cumulative_compaction_policy(
1263
0
                _get_cumulative_compaction_policy(tablet->tablet_meta()->compaction_policy()));
1264
0
    }
1265
0
    tablet->set_skip_compaction(false);
1266
0
    int8_t prefer_compaction_level = -1;
1267
0
    if (compaction_type == CompactionType::BINLOG_COMPACTION) {
1268
0
        tablet->calc_compaction_score(compaction_type, &prefer_compaction_level);
1269
0
        if (prefer_compaction_level < 0) {
1270
0
            return Status::Error<ErrorCode::BINLOG_COMPACTION_NO_SUITABLE_VERSION>(
1271
0
                    "failed to init binlog compaction due to no suitable version");
1272
0
        }
1273
0
    }
1274
0
    return _submit_compaction_task(tablet, compaction_type, force, trigger_method,
1275
0
                                   prefer_compaction_level);
1276
0
}
1277
1278
Status StorageEngine::_handle_seg_compaction(std::shared_ptr<SegcompactionWorker> worker,
1279
                                             SegCompactionCandidatesSharedPtr segments,
1280
11
                                             uint64_t submission_time) {
1281
    // note: be aware that worker->_writer maybe released when the task is cancelled
1282
11
    uint64_t exec_queue_time = GetCurrentTimeMicros() - submission_time;
1283
11
    LOG(INFO) << "segcompaction thread pool queue time(ms): " << exec_queue_time / 1000;
1284
11
    worker->compact_segments(segments);
1285
    // return OK here. error will be reported via BetaRowsetWriter::_segcompaction_status
1286
11
    return Status::OK();
1287
11
}
1288
1289
Status StorageEngine::submit_seg_compaction_task(std::shared_ptr<SegcompactionWorker> worker,
1290
11
                                                 SegCompactionCandidatesSharedPtr segments) {
1291
11
    uint64_t submission_time = GetCurrentTimeMicros();
1292
11
    return _seg_compaction_thread_pool->submit_func([this, worker, segments, submission_time] {
1293
11
        static_cast<void>(_handle_seg_compaction(worker, segments, submission_time));
1294
11
    });
1295
11
}
1296
1297
0
Status StorageEngine::process_index_change_task(const TAlterInvertedIndexReq& request) {
1298
0
    auto tablet_id = request.tablet_id;
1299
0
    TabletSharedPtr tablet = _tablet_manager->get_tablet(tablet_id);
1300
0
    DBUG_EXECUTE_IF("StorageEngine::process_index_change_task_tablet_nullptr",
1301
0
                    { tablet = nullptr; })
1302
0
    if (tablet == nullptr) {
1303
0
        LOG(WARNING) << "tablet: " << tablet_id << " not exist";
1304
0
        return Status::InternalError("tablet not exist, tablet_id={}.", tablet_id);
1305
0
    }
1306
1307
0
    IndexBuilderSharedPtr index_builder = std::make_shared<IndexBuilder>(
1308
0
            *this, tablet, request.columns, request.alter_inverted_indexes, request.is_drop_op);
1309
0
    RETURN_IF_ERROR(_handle_index_change(index_builder));
1310
0
    return Status::OK();
1311
0
}
1312
1313
0
Status StorageEngine::_handle_index_change(IndexBuilderSharedPtr index_builder) {
1314
0
    RETURN_IF_ERROR(index_builder->init());
1315
0
    RETURN_IF_ERROR(index_builder->do_build_inverted_index());
1316
0
    return Status::OK();
1317
0
}
1318
1319
5
void StorageEngine::_cooldown_tasks_producer_callback() {
1320
5
    int64_t interval = config::generate_cooldown_task_interval_sec;
1321
    // the cooldown replica may be slow to upload it's meta file, so we should wait
1322
    // until it has done uploaded
1323
5
    int64_t skip_failed_interval = interval * 10;
1324
528
    do {
1325
        // these tables are ordered by priority desc
1326
528
        std::vector<TabletSharedPtr> tablets;
1327
528
        std::vector<RowsetSharedPtr> rowsets;
1328
        // TODO(luwei) : a more efficient way to get cooldown tablets
1329
528
        auto cur_time = time(nullptr);
1330
        // we should skip all the tablets which are not running and those pending to do cooldown
1331
        // also tablets once failed to do follow cooldown
1332
528
        auto skip_tablet = [this, skip_failed_interval,
1333
4.24M
                            cur_time](const TabletSharedPtr& tablet) -> bool {
1334
4.24M
            bool is_skip =
1335
4.24M
                    cur_time - tablet->last_failed_follow_cooldown_time() < skip_failed_interval ||
1336
4.24M
                    TABLET_RUNNING != tablet->tablet_state();
1337
4.24M
            if (is_skip) {
1338
0
                return is_skip;
1339
0
            }
1340
4.24M
            std::lock_guard<std::mutex> lock(_running_cooldown_mutex);
1341
4.24M
            return _running_cooldown_tablets.find(tablet->tablet_id()) !=
1342
4.24M
                   _running_cooldown_tablets.end();
1343
4.24M
        };
1344
528
        _tablet_manager->get_cooldown_tablets(&tablets, &rowsets, std::move(skip_tablet));
1345
528
        LOG(INFO) << "cooldown producer get tablet num: " << tablets.size();
1346
528
        int max_priority = cast_set<int>(tablets.size());
1347
528
        int index = 0;
1348
528
        for (const auto& tablet : tablets) {
1349
0
            {
1350
0
                std::lock_guard<std::mutex> lock(_running_cooldown_mutex);
1351
0
                _running_cooldown_tablets.insert(tablet->tablet_id());
1352
0
            }
1353
0
            PriorityThreadPool::Task task;
1354
0
            RowsetSharedPtr rowset = std::move(rowsets[index++]);
1355
0
            task.work_function = [tablet, rowset, task_size = tablets.size(), this]() {
1356
0
                Status st = tablet->cooldown(rowset);
1357
0
                {
1358
0
                    std::lock_guard<std::mutex> lock(_running_cooldown_mutex);
1359
0
                    _running_cooldown_tablets.erase(tablet->tablet_id());
1360
0
                }
1361
0
                if (!st.ok()) {
1362
0
                    LOG(WARNING) << "failed to cooldown, tablet: " << tablet->tablet_id()
1363
0
                                 << " err: " << st;
1364
0
                } else {
1365
0
                    LOG(INFO) << "succeed to cooldown, tablet: " << tablet->tablet_id()
1366
0
                              << " cooldown progress ("
1367
0
                              << task_size - _cooldown_thread_pool->get_queue_size() << "/"
1368
0
                              << task_size << ")";
1369
0
                }
1370
0
            };
1371
0
            task.priority = max_priority--;
1372
0
            bool submited = _cooldown_thread_pool->offer(std::move(task));
1373
1374
0
            if (!submited) {
1375
0
                LOG(INFO) << "failed to submit cooldown task";
1376
0
            }
1377
0
        }
1378
528
    } while (!_stop_background_threads_latch.wait_for(std::chrono::seconds(interval)));
1379
5
}
1380
1381
5
void StorageEngine::_remove_unused_remote_files_callback() {
1382
16
    while (!_stop_background_threads_latch.wait_for(
1383
16
            std::chrono::seconds(config::remove_unused_remote_files_interval_sec))) {
1384
11
        LOG(INFO) << "begin to remove unused remote files";
1385
11
        do_remove_unused_remote_files();
1386
11
    }
1387
5
}
1388
1389
static void collect_tablet_unused_remote_files(
1390
        Tablet* t, TConfirmUnusedRemoteFilesRequest& req,
1391
        std::unordered_map<int64_t, std::pair<StorageResource, std::vector<io::FileInfo>>>& buffer,
1392
7
        int64_t& num_files_in_buffer, PendingRowsetSet& pending_remote_rowsets) {
1393
7
    auto storage_resource = get_resource_by_storage_policy_id(t->storage_policy_id());
1394
7
    if (!storage_resource) {
1395
0
        LOG(WARNING) << "encounter error when remove unused remote files, tablet_id="
1396
0
                     << t->tablet_id() << " : " << storage_resource.error();
1397
0
        return;
1398
0
    }
1399
1400
    // TODO(plat1ko): Support path v1
1401
7
    if (storage_resource->path_version > 0) {
1402
0
        return;
1403
0
    }
1404
1405
7
    std::vector<io::FileInfo> files;
1406
    // FIXME(plat1ko): What if user reset resource in storage policy to another resource?
1407
    //  Maybe we should also list files in previously uploaded resources.
1408
7
    bool exists = true;
1409
7
    auto st = storage_resource->fs->list(storage_resource->remote_tablet_path(t->tablet_id()), true,
1410
7
                                         &files, &exists);
1411
7
    if (!st.ok()) {
1412
0
        LOG(WARNING) << "encounter error when remove unused remote files, tablet_id="
1413
0
                     << t->tablet_id() << " : " << st;
1414
0
        return;
1415
0
    }
1416
7
    if (!exists || files.empty()) {
1417
0
        return;
1418
0
    }
1419
    // get all cooldowned rowsets
1420
7
    RowsetIdUnorderedSet cooldowned_rowsets;
1421
7
    UniqueId cooldown_meta_id;
1422
7
    {
1423
7
        std::shared_lock rlock(t->get_header_lock());
1424
35
        for (const auto& [_, rs_meta] : t->tablet_meta()->all_rs_metas()) {
1425
35
            if (!rs_meta->is_local()) {
1426
35
                cooldowned_rowsets.insert(rs_meta->rowset_id());
1427
35
            }
1428
35
        }
1429
7
        if (cooldowned_rowsets.empty()) {
1430
0
            return;
1431
0
        }
1432
7
        cooldown_meta_id = t->tablet_meta()->cooldown_meta_id();
1433
7
    }
1434
0
    auto [cooldown_term, cooldown_replica_id] = t->cooldown_conf();
1435
7
    if (cooldown_replica_id != t->replica_id()) {
1436
0
        return;
1437
0
    }
1438
    // {cooldown_replica_id}.{cooldown_term}.meta
1439
7
    std::string remote_meta_path =
1440
7
            cooldown_tablet_meta_filename(cooldown_replica_id, cooldown_term);
1441
    // filter out the paths that should be reserved
1442
35
    auto filter = [&](io::FileInfo& info) {
1443
35
        std::string_view filename = info.file_name;
1444
35
        if (filename.ends_with(".meta")) {
1445
7
            return filename == remote_meta_path;
1446
7
        }
1447
28
        auto rowset_id = extract_rowset_id(filename);
1448
28
        if (rowset_id.hi == 0) {
1449
0
            return false;
1450
0
        }
1451
28
        return cooldowned_rowsets.contains(rowset_id) || pending_remote_rowsets.contains(rowset_id);
1452
28
    };
1453
7
    files.erase(std::remove_if(files.begin(), files.end(), std::move(filter)), files.end());
1454
7
    if (files.empty()) {
1455
7
        return;
1456
7
    }
1457
0
    files.shrink_to_fit();
1458
0
    num_files_in_buffer += files.size();
1459
0
    buffer.insert({t->tablet_id(), {*storage_resource, std::move(files)}});
1460
0
    auto& info = req.confirm_list.emplace_back();
1461
0
    info.__set_tablet_id(t->tablet_id());
1462
0
    info.__set_cooldown_replica_id(cooldown_replica_id);
1463
0
    info.__set_cooldown_meta_id(cooldown_meta_id.to_thrift());
1464
0
}
1465
1466
static void confirm_and_remove_unused_remote_files(
1467
        const TConfirmUnusedRemoteFilesRequest& req,
1468
        std::unordered_map<int64_t, std::pair<StorageResource, std::vector<io::FileInfo>>>& buffer,
1469
0
        const int64_t num_files_in_buffer) {
1470
0
    TConfirmUnusedRemoteFilesResult result;
1471
0
    LOG(INFO) << "begin to confirm unused remote files. num_tablets=" << buffer.size()
1472
0
              << " num_files=" << num_files_in_buffer;
1473
0
    auto st = MasterServerClient::instance()->confirm_unused_remote_files(req, &result);
1474
0
    if (!st.ok()) {
1475
0
        LOG(WARNING) << st;
1476
0
        return;
1477
0
    }
1478
0
    for (auto id : result.confirmed_tablets) {
1479
0
        if (auto it = buffer.find(id); LIKELY(it != buffer.end())) {
1480
0
            auto& storage_resource = it->second.first;
1481
0
            auto& files = it->second.second;
1482
0
            std::vector<io::Path> paths;
1483
0
            paths.reserve(files.size());
1484
            // delete unused files
1485
0
            LOG(INFO) << "delete unused files. root_path=" << storage_resource.fs->root_path()
1486
0
                      << " tablet_id=" << id;
1487
0
            io::Path dir = storage_resource.remote_tablet_path(id);
1488
0
            for (auto& file : files) {
1489
0
                auto file_path = dir / file.file_name;
1490
0
                LOG(INFO) << "delete unused file: " << file_path.native();
1491
0
                paths.push_back(std::move(file_path));
1492
0
            }
1493
0
            st = storage_resource.fs->batch_delete(paths);
1494
0
            if (!st.ok()) {
1495
0
                LOG(WARNING) << "failed to delete unused files, tablet_id=" << id << " : " << st;
1496
0
            }
1497
0
            buffer.erase(it);
1498
0
        }
1499
0
    }
1500
0
}
1501
1502
11
void StorageEngine::do_remove_unused_remote_files() {
1503
970k
    auto tablets = tablet_manager()->get_all_tablet([](Tablet* t) {
1504
970k
        return t->tablet_meta()->cooldown_meta_id().initialized() && t->is_used() &&
1505
970k
               t->tablet_state() == TABLET_RUNNING &&
1506
970k
               t->cooldown_conf_unlocked().cooldown_replica_id == t->replica_id();
1507
970k
    });
1508
11
    TConfirmUnusedRemoteFilesRequest req;
1509
11
    req.__isset.confirm_list = true;
1510
    // tablet_id -> [storage_resource, unused_remote_files]
1511
11
    using unused_remote_files_buffer_t =
1512
11
            std::unordered_map<int64_t, std::pair<StorageResource, std::vector<io::FileInfo>>>;
1513
11
    unused_remote_files_buffer_t buffer;
1514
11
    int64_t num_files_in_buffer = 0;
1515
    // assume a filename is 0.1KB, buffer size should not larger than 100MB
1516
11
    constexpr int64_t max_files_in_buffer = 1000000;
1517
1518
    // batch confirm to reduce FE's overhead
1519
11
    auto next_confirm_time = std::chrono::steady_clock::now() +
1520
11
                             std::chrono::seconds(config::confirm_unused_remote_files_interval_sec);
1521
11
    for (auto& t : tablets) {
1522
7
        if (t.use_count() <= 1 // this means tablet has been dropped
1523
7
            || t->cooldown_conf_unlocked().cooldown_replica_id != t->replica_id() ||
1524
7
            t->tablet_state() != TABLET_RUNNING) {
1525
0
            continue;
1526
0
        }
1527
7
        collect_tablet_unused_remote_files(t.get(), req, buffer, num_files_in_buffer,
1528
7
                                           _pending_remote_rowsets);
1529
7
        if (num_files_in_buffer > 0 && (num_files_in_buffer > max_files_in_buffer ||
1530
0
                                        std::chrono::steady_clock::now() > next_confirm_time)) {
1531
0
            confirm_and_remove_unused_remote_files(req, buffer, num_files_in_buffer);
1532
0
            buffer.clear();
1533
0
            req.confirm_list.clear();
1534
0
            num_files_in_buffer = 0;
1535
0
            next_confirm_time =
1536
0
                    std::chrono::steady_clock::now() +
1537
0
                    std::chrono::seconds(config::confirm_unused_remote_files_interval_sec);
1538
0
        }
1539
7
    }
1540
11
    if (num_files_in_buffer > 0) {
1541
0
        confirm_and_remove_unused_remote_files(req, buffer, num_files_in_buffer);
1542
0
    }
1543
11
}
1544
1545
5
void StorageEngine::_cold_data_compaction_producer_callback() {
1546
20
    while (!_stop_background_threads_latch.wait_for(
1547
20
            std::chrono::seconds(config::cold_data_compaction_interval_sec))) {
1548
15
        if (config::disable_auto_compaction ||
1549
15
            GlobalMemoryArbitrator::is_exceed_soft_mem_limit(GB_EXCHANGE_BYTE)) {
1550
0
            continue;
1551
0
        }
1552
1553
15
        std::unordered_set<int64_t> copied_tablet_submitted;
1554
15
        {
1555
15
            std::lock_guard lock(_cold_compaction_tablet_submitted_mtx);
1556
15
            copied_tablet_submitted = _cold_compaction_tablet_submitted;
1557
15
        }
1558
15
        int64_t n = config::cold_data_compaction_thread_num - copied_tablet_submitted.size();
1559
15
        if (n <= 0) {
1560
0
            continue;
1561
0
        }
1562
976k
        auto tablets = _tablet_manager->get_all_tablet([&copied_tablet_submitted](Tablet* t) {
1563
976k
            return t->tablet_meta()->cooldown_meta_id().initialized() && t->is_used() &&
1564
976k
                   t->tablet_state() == TABLET_RUNNING &&
1565
976k
                   !copied_tablet_submitted.contains(t->tablet_id()) &&
1566
976k
                   !t->tablet_meta()->tablet_schema()->disable_auto_compaction();
1567
976k
        });
1568
15
        std::vector<std::pair<TabletSharedPtr, int64_t>> tablet_to_compact;
1569
15
        tablet_to_compact.reserve(n + 1);
1570
15
        std::vector<std::pair<TabletSharedPtr, int64_t>> tablet_to_follow;
1571
15
        tablet_to_follow.reserve(n + 1);
1572
1573
15
        for (auto& t : tablets) {
1574
7
            if (t->replica_id() == t->cooldown_conf_unlocked().cooldown_replica_id) {
1575
7
                auto score = t->calc_cold_data_compaction_score();
1576
7
                if (score < config::cold_data_compaction_score_threshold) {
1577
7
                    continue;
1578
7
                }
1579
0
                tablet_to_compact.emplace_back(t, score);
1580
0
                if (tablet_to_compact.size() > n) {
1581
0
                    std::sort(tablet_to_compact.begin(), tablet_to_compact.end(),
1582
0
                              [](auto& a, auto& b) { return a.second > b.second; });
1583
0
                    tablet_to_compact.pop_back();
1584
0
                }
1585
0
                continue;
1586
7
            }
1587
            // else, need to follow
1588
0
            {
1589
0
                std::lock_guard lock(_running_cooldown_mutex);
1590
0
                if (_running_cooldown_tablets.contains(t->table_id())) {
1591
                    // already in cooldown queue
1592
0
                    continue;
1593
0
                }
1594
0
            }
1595
            // TODO(plat1ko): some avoidance strategy if failed to follow
1596
0
            auto score = t->calc_cold_data_compaction_score();
1597
0
            tablet_to_follow.emplace_back(t, score);
1598
1599
0
            if (tablet_to_follow.size() > n) {
1600
0
                std::sort(tablet_to_follow.begin(), tablet_to_follow.end(),
1601
0
                          [](auto& a, auto& b) { return a.second > b.second; });
1602
0
                tablet_to_follow.pop_back();
1603
0
            }
1604
0
        }
1605
1606
15
        for (auto& [tablet, score] : tablet_to_compact) {
1607
0
            LOG(INFO) << "submit cold data compaction. tablet_id=" << tablet->tablet_id()
1608
0
                      << " score=" << score;
1609
0
            static_cast<void>(
1610
0
                    _cold_data_compaction_thread_pool->submit_func([t = std::move(tablet), this]() {
1611
0
                        _handle_cold_data_compaction(std::move(t));
1612
0
                    }));
1613
0
        }
1614
1615
15
        for (auto& [tablet, score] : tablet_to_follow) {
1616
0
            LOG(INFO) << "submit to follow cooldown meta. tablet_id=" << tablet->tablet_id()
1617
0
                      << " score=" << score;
1618
0
            static_cast<void>(_cold_data_compaction_thread_pool->submit_func(
1619
0
                    [t = std::move(tablet), this]() { _follow_cooldown_meta(std::move(t)); }));
1620
0
        }
1621
15
    }
1622
5
}
1623
1624
0
void StorageEngine::_handle_cold_data_compaction(TabletSharedPtr t) {
1625
0
    auto compaction = std::make_shared<ColdDataCompaction>(*this, t);
1626
0
    {
1627
0
        std::lock_guard lock(_cold_compaction_tablet_submitted_mtx);
1628
0
        _cold_compaction_tablet_submitted.insert(t->tablet_id());
1629
0
    }
1630
0
    Defer defer {[&] {
1631
0
        std::lock_guard lock(_cold_compaction_tablet_submitted_mtx);
1632
0
        _cold_compaction_tablet_submitted.erase(t->tablet_id());
1633
0
    }};
1634
0
    std::unique_lock cold_compaction_lock(t->get_cold_compaction_lock(), std::try_to_lock);
1635
0
    if (!cold_compaction_lock.owns_lock()) {
1636
0
        LOG(WARNING) << "try cold_compaction_lock failed, tablet_id=" << t->tablet_id();
1637
0
        return;
1638
0
    }
1639
0
    _update_cumulative_compaction_policy();
1640
0
    if (t->get_cumulative_compaction_policy() == nullptr ||
1641
0
        t->get_cumulative_compaction_policy()->name() != t->tablet_meta()->compaction_policy()) {
1642
0
        t->set_cumulative_compaction_policy(
1643
0
                _get_cumulative_compaction_policy(t->tablet_meta()->compaction_policy()));
1644
0
    }
1645
1646
0
    auto st = compaction->prepare_compact();
1647
0
    if (!st.ok()) {
1648
0
        LOG(WARNING) << "failed to prepare cold data compaction. tablet_id=" << t->tablet_id()
1649
0
                     << " err=" << st;
1650
0
        return;
1651
0
    }
1652
1653
0
    st = compaction->execute_compact();
1654
0
    if (!st.ok()) {
1655
0
        LOG(WARNING) << "failed to execute cold data compaction. tablet_id=" << t->tablet_id()
1656
0
                     << " err=" << st;
1657
0
        return;
1658
0
    }
1659
0
}
1660
1661
0
void StorageEngine::_follow_cooldown_meta(TabletSharedPtr t) {
1662
0
    {
1663
0
        std::lock_guard lock(_cold_compaction_tablet_submitted_mtx);
1664
0
        _cold_compaction_tablet_submitted.insert(t->tablet_id());
1665
0
    }
1666
0
    auto st = t->cooldown();
1667
0
    {
1668
0
        std::lock_guard lock(_cold_compaction_tablet_submitted_mtx);
1669
0
        _cold_compaction_tablet_submitted.erase(t->tablet_id());
1670
0
    }
1671
0
    if (!st.ok()) {
1672
        // The cooldown of the replica may be relatively slow
1673
        // resulting in a short period of time where following cannot be successful
1674
0
        LOG_EVERY_N(WARNING, 5) << "failed to cooldown. tablet_id=" << t->tablet_id()
1675
0
                                << " err=" << st;
1676
0
    }
1677
0
}
1678
1679
void StorageEngine::add_async_publish_task(int64_t partition_id, int64_t tablet_id,
1680
                                           int64_t publish_version, int64_t transaction_id,
1681
2.05k
                                           bool is_recovery, int64_t commit_tso) {
1682
2.05k
    if (!is_recovery) {
1683
2.05k
        bool exists = false;
1684
2.05k
        {
1685
2.05k
            std::shared_lock<std::shared_mutex> rlock(_async_publish_lock);
1686
2.05k
            if (auto tablet_iter = _async_publish_tasks.find(tablet_id);
1687
2.05k
                tablet_iter != _async_publish_tasks.end()) {
1688
2.05k
                if (auto iter = tablet_iter->second.find(publish_version);
1689
2.05k
                    iter != tablet_iter->second.end()) {
1690
20
                    exists = true;
1691
20
                }
1692
2.05k
            }
1693
2.05k
        }
1694
2.05k
        if (exists) {
1695
20
            return;
1696
20
        }
1697
2.03k
        TabletSharedPtr tablet = tablet_manager()->get_tablet(tablet_id);
1698
2.03k
        if (tablet == nullptr) {
1699
0
            LOG(INFO) << "tablet may be dropped when add async publish task, tablet_id: "
1700
0
                      << tablet_id;
1701
0
            return;
1702
0
        }
1703
2.03k
        PendingPublishInfoPB pending_publish_info_pb;
1704
2.03k
        pending_publish_info_pb.set_partition_id(partition_id);
1705
2.03k
        pending_publish_info_pb.set_transaction_id(transaction_id);
1706
2.03k
        pending_publish_info_pb.set_commit_tso(commit_tso);
1707
2.03k
        static_cast<void>(TabletMetaManager::save_pending_publish_info(
1708
2.03k
                tablet->data_dir(), tablet->tablet_id(), publish_version,
1709
2.03k
                pending_publish_info_pb.SerializeAsString()));
1710
2.03k
    }
1711
2.05k
    LOG(INFO) << "add pending publish task, tablet_id: " << tablet_id
1712
2.03k
              << " version: " << publish_version << " txn_id:" << transaction_id
1713
2.03k
              << " is_recovery: " << is_recovery;
1714
2.03k
    std::unique_lock<std::shared_mutex> wlock(_async_publish_lock);
1715
2.03k
    _async_publish_tasks[tablet_id][publish_version] = {transaction_id, partition_id, commit_tso};
1716
2.03k
}
1717
1718
3
int64_t StorageEngine::get_pending_publish_min_version(int64_t tablet_id) {
1719
3
    std::shared_lock<std::shared_mutex> rlock(_async_publish_lock);
1720
3
    auto iter = _async_publish_tasks.find(tablet_id);
1721
3
    if (iter == _async_publish_tasks.end()) {
1722
0
        return INT64_MAX;
1723
0
    }
1724
3
    if (iter->second.empty()) {
1725
0
        return INT64_MAX;
1726
0
    }
1727
3
    return iter->second.begin()->first;
1728
3
}
1729
1730
349k
void StorageEngine::_process_async_publish() {
1731
    // tablet, publish_version
1732
349k
    std::vector<std::pair<TabletSharedPtr, int64_t>> need_removed_tasks;
1733
349k
    {
1734
349k
        std::unique_lock<std::shared_mutex> wlock(_async_publish_lock);
1735
349k
        for (auto tablet_iter = _async_publish_tasks.begin();
1736
349k
             tablet_iter != _async_publish_tasks.end();) {
1737
10
            if (tablet_iter->second.empty()) {
1738
1
                tablet_iter = _async_publish_tasks.erase(tablet_iter);
1739
1
                continue;
1740
1
            }
1741
9
            int64_t tablet_id = tablet_iter->first;
1742
9
            TabletSharedPtr tablet = tablet_manager()->get_tablet(tablet_id);
1743
9
            if (!tablet) {
1744
1
                LOG(WARNING) << "tablet does not exist when async publush, tablet_id: "
1745
1
                             << tablet_id;
1746
1
                tablet_iter = _async_publish_tasks.erase(tablet_iter);
1747
1
                continue;
1748
1
            }
1749
1750
8
            auto task_iter = tablet_iter->second.begin();
1751
8
            int64_t version = task_iter->first;
1752
8
            int64_t transaction_id = std::get<0>(task_iter->second);
1753
8
            int64_t partition_id = std::get<1>(task_iter->second);
1754
8
            int64_t commit_tso = std::get<2>(task_iter->second);
1755
8
            int64_t max_version = tablet->max_version().second;
1756
1757
8
            if (version <= max_version) {
1758
6
                need_removed_tasks.emplace_back(tablet, version);
1759
6
                tablet_iter->second.erase(task_iter);
1760
6
                tablet_iter++;
1761
6
                continue;
1762
6
            }
1763
2
            if (version != max_version + 1) {
1764
1
                int32_t max_version_config = tablet->max_version_config();
1765
                // Keep only the most recent versions
1766
31
                while (tablet_iter->second.size() > max_version_config) {
1767
30
                    need_removed_tasks.emplace_back(tablet, version);
1768
30
                    task_iter = tablet_iter->second.erase(task_iter);
1769
30
                    version = task_iter->first;
1770
30
                }
1771
1
                tablet_iter++;
1772
1
                continue;
1773
1
            }
1774
1775
1
            auto async_publish_task = std::make_shared<AsyncTabletPublishTask>(
1776
1
                    *this, tablet, partition_id, transaction_id, version, commit_tso);
1777
1
            static_cast<void>(_tablet_publish_txn_thread_pool->submit_func(
1778
1
                    [=]() { async_publish_task->handle(); }));
1779
1
            tablet_iter->second.erase(task_iter);
1780
1
            need_removed_tasks.emplace_back(tablet, version);
1781
1
            tablet_iter++;
1782
1
        }
1783
349k
    }
1784
349k
    for (auto& [tablet, publish_version] : need_removed_tasks) {
1785
37
        static_cast<void>(TabletMetaManager::remove_pending_publish_info(
1786
37
                tablet->data_dir(), tablet->tablet_id(), publish_version));
1787
37
    }
1788
349k
}
1789
1790
5
void StorageEngine::_async_publish_callback() {
1791
349k
    while (!_stop_background_threads_latch.wait_for(std::chrono::milliseconds(30))) {
1792
349k
        _process_async_publish();
1793
349k
    }
1794
5
}
1795
1796
5
void StorageEngine::_check_tablet_delete_bitmap_score_callback() {
1797
5
    LOG(INFO) << "try to start check tablet delete bitmap score!";
1798
37
    while (!_stop_background_threads_latch.wait_for(
1799
37
            std::chrono::seconds(config::check_tablet_delete_bitmap_interval_seconds))) {
1800
32
        if (!config::enable_check_tablet_delete_bitmap_score) {
1801
0
            return;
1802
0
        }
1803
32
        uint64_t max_delete_bitmap_score = 0;
1804
32
        uint64_t max_base_rowset_delete_bitmap_score = 0;
1805
32
        _tablet_manager->get_topn_tablet_delete_bitmap_score(&max_delete_bitmap_score,
1806
32
                                                             &max_base_rowset_delete_bitmap_score);
1807
32
        if (max_delete_bitmap_score > 0) {
1808
26
            _tablet_max_delete_bitmap_score_metrics->set_value(max_delete_bitmap_score);
1809
26
        }
1810
32
        if (max_base_rowset_delete_bitmap_score > 0) {
1811
26
            _tablet_max_base_rowset_delete_bitmap_score_metrics->set_value(
1812
26
                    max_base_rowset_delete_bitmap_score);
1813
26
        }
1814
32
    }
1815
5
}
1816
} // namespace doris