Coverage Report

Created: 2026-08-14 09:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/cloud/cloud_storage_engine.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 "cloud/cloud_storage_engine.h"
19
20
#include <bvar/reducer.h>
21
#include <gen_cpp/PlanNodes_types.h>
22
#include <gen_cpp/cloud.pb.h>
23
#include <gen_cpp/olap_file.pb.h>
24
#include <rapidjson/document.h>
25
#include <rapidjson/encodings.h>
26
#include <rapidjson/prettywriter.h>
27
#include <rapidjson/stringbuffer.h>
28
29
#include <algorithm>
30
#include <memory>
31
#include <variant>
32
33
#include "cloud/cloud_base_compaction.h"
34
#include "cloud/cloud_compaction_stop_token.h"
35
#include "cloud/cloud_cumulative_compaction.h"
36
#include "cloud/cloud_cumulative_compaction_binlog_policy.h"
37
#include "cloud/cloud_cumulative_compaction_policy.h"
38
#include "cloud/cloud_full_compaction.h"
39
#include "cloud/cloud_index_change_compaction.h"
40
#include "cloud/cloud_meta_mgr.h"
41
#include "cloud/cloud_snapshot_mgr.h"
42
#include "cloud/cloud_tablet_hotspot.h"
43
#include "cloud/cloud_tablet_mgr.h"
44
#include "cloud/cloud_txn_delete_bitmap_cache.h"
45
#include "cloud/cloud_warm_up_manager.h"
46
#include "cloud/config.h"
47
#include "common/config.h"
48
#include "common/metrics/doris_metrics.h"
49
#include "common/signal_handler.h"
50
#include "common/status.h"
51
#include "core/assert_cast.h"
52
#include "io/cache/block_file_cache_downloader.h"
53
#include "io/cache/block_file_cache_factory.h"
54
#include "io/cache/file_cache_common.h"
55
#include "io/fs/file_system.h"
56
#include "io/fs/hdfs_file_system.h"
57
#include "io/fs/s3_file_system.h"
58
#include "io/hdfs_util.h"
59
#include "io/io_common.h"
60
#include "load/memtable/memtable_flush_executor.h"
61
#include "runtime/exec_env.h"
62
#include "runtime/memory/cache_manager.h"
63
#include "service/backend_options.h"
64
#include "storage/compaction/cumulative_compaction_binlog_policy.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/storage_policy.h"
69
#include "util/parse_util.h"
70
#include "util/time.h"
71
72
namespace doris {
73
74
using namespace std::literals;
75
76
bvar::Adder<uint64_t> g_base_compaction_running_task_count("base_compaction_running_task_count");
77
bvar::Adder<uint64_t> g_full_compaction_running_task_count("full_compaction_running_task_count");
78
bvar::Adder<uint64_t> g_cumu_compaction_running_task_count(
79
        "cumulative_compaction_running_task_count");
80
81
15.9k
int get_cumu_thread_num() {
82
15.9k
    if (config::max_cumu_compaction_threads > 0) {
83
0
        return config::max_cumu_compaction_threads;
84
0
    }
85
86
15.9k
    int num_cores = doris::CpuInfo::num_cores();
87
15.9k
    return std::min(std::max(int(num_cores * config::cumu_compaction_thread_num_factor), 2), 20);
88
15.9k
}
89
90
15.9k
int get_base_thread_num() {
91
15.9k
    if (config::max_base_compaction_threads > 0) {
92
15.9k
        return config::max_base_compaction_threads;
93
15.9k
    }
94
95
0
    int num_cores = doris::CpuInfo::num_cores();
96
0
    return std::min(std::max(int(num_cores * config::base_compaction_thread_num_factor), 1), 10);
97
15.9k
}
98
99
15.9k
int get_binlog_thread_num() {
100
15.9k
    if (config::max_binlog_compaction_threads > 0) {
101
0
        return config::max_binlog_compaction_threads;
102
0
    }
103
104
15.9k
    int num_cores = doris::CpuInfo::num_cores();
105
15.9k
    return std::min(std::max(int(num_cores * config::binlog_compaction_thread_num_factor), 1), 10);
106
15.9k
}
107
108
CloudStorageEngine::CloudStorageEngine(const EngineOptions& options)
109
247
        : BaseStorageEngine(Type::CLOUD, options.backend_uid),
110
247
          _meta_mgr(std::make_unique<cloud::CloudMetaMgr>()),
111
247
          _tablet_mgr(std::make_unique<CloudTabletMgr>(*this)),
112
247
          _options(options) {
113
247
    _cumulative_compaction_policies[CUMULATIVE_SIZE_BASED_POLICY] =
114
247
            std::make_shared<CloudSizeBasedCumulativeCompactionPolicy>();
115
247
    _cumulative_compaction_policies[CUMULATIVE_TIME_SERIES_POLICY] =
116
247
            std::make_shared<CloudTimeSeriesCumulativeCompactionPolicy>();
117
247
    _cumulative_compaction_policies[CUMULATIVE_BINLOG_POLICY] =
118
247
            std::make_shared<CloudBinlogCumulativeCompactionPolicy>();
119
247
    _startup_timepoint = std::chrono::system_clock::now();
120
247
}
121
122
246
CloudStorageEngine::~CloudStorageEngine() {
123
246
    stop();
124
246
}
125
126
static Status vault_process_error(std::string_view id,
127
0
                                  std::variant<S3Conf, cloud::HdfsVaultInfo>& vault, Status err) {
128
0
    std::stringstream ss;
129
0
    std::visit(
130
0
            [&]<typename T>(T& val) {
131
0
                if constexpr (std::is_same_v<T, S3Conf>) {
132
0
                    ss << val.to_string();
133
0
                } else if constexpr (std::is_same_v<T, cloud::HdfsVaultInfo>) {
134
0
                    val.SerializeToOstream(&ss);
135
0
                }
136
0
            },
Unexecuted instantiation: cloud_storage_engine.cpp:_ZZN5dorisL19vault_process_errorESt17basic_string_viewIcSt11char_traitsIcEERSt7variantIJNS_6S3ConfENS_5cloud13HdfsVaultInfoEEENS_6StatusEENK3$_0clIS5_EEDaRT_
Unexecuted instantiation: cloud_storage_engine.cpp:_ZZN5dorisL19vault_process_errorESt17basic_string_viewIcSt11char_traitsIcEERSt7variantIJNS_6S3ConfENS_5cloud13HdfsVaultInfoEEENS_6StatusEENK3$_0clIS7_EEDaRT_
137
0
            vault);
138
0
    return Status::IOError("Invalid vault, id {}, err {}, detail conf {}", id, err, ss.str());
139
0
}
140
141
struct VaultCreateFSVisitor {
142
    VaultCreateFSVisitor(const std::string& id, const cloud::StorageVaultPB_PathFormat& path_format,
143
                         bool check_fs)
144
1
            : id(id), path_format(path_format), check_fs(check_fs) {}
145
1
    Status operator()(const S3Conf& s3_conf) const {
146
1
        LOG(INFO) << "get new s3 info: " << s3_conf.to_string() << " resource_id=" << id
147
1
                  << " check_fs: " << check_fs;
148
149
1
        auto fs = DORIS_TRY(io::S3FileSystem::create(s3_conf, id));
150
1
        if (check_fs && !s3_conf.client_conf.role_arn.empty()) {
151
0
            bool res = false;
152
            // just check connectivity, not care object if exist
153
0
            auto st = fs->exists("not_exist_object", &res);
154
0
            if (!st.ok()) {
155
0
                LOG(FATAL) << "failed to check s3 fs, resource_id: " << id << " st: " << st
156
0
                           << "s3_conf: " << s3_conf.to_string()
157
0
                           << "add enable_check_storage_vault=false to be.conf to skip the check";
158
0
            }
159
0
        }
160
161
1
        put_storage_resource(id, {std::move(fs), path_format}, 0);
162
1
        LOG_INFO("successfully create s3 vault, vault id {}", id);
163
1
        return Status::OK();
164
1
    }
165
166
    // TODO(ByteYue): Make sure enable_java_support is on
167
0
    Status operator()(const cloud::HdfsVaultInfo& vault) const {
168
0
        auto hdfs_params = io::to_hdfs_params(vault);
169
0
        auto fs = DORIS_TRY(io::HdfsFileSystem::create(hdfs_params, hdfs_params.fs_name, id,
170
0
                                                       nullptr, vault.prefix()));
171
0
        put_storage_resource(id, {std::move(fs), path_format}, 0);
172
0
        LOG_INFO("successfully create hdfs vault, vault id {}", id);
173
0
        return Status::OK();
174
0
    }
175
176
    const std::string& id;
177
    const cloud::StorageVaultPB_PathFormat& path_format;
178
    bool check_fs;
179
};
180
181
struct RefreshFSVaultVisitor {
182
    RefreshFSVaultVisitor(const std::string& id, io::FileSystemSPtr fs,
183
                          const cloud::StorageVaultPB_PathFormat& path_format)
184
629
            : id(id), fs(std::move(fs)), path_format(path_format) {}
185
186
629
    Status operator()(const S3Conf& s3_conf) const {
187
629
        DCHECK_EQ(fs->type(), io::FileSystemType::S3) << id;
188
629
        auto s3_fs = std::static_pointer_cast<io::S3FileSystem>(fs);
189
629
        auto client_holder = s3_fs->client_holder();
190
629
        auto st = client_holder->reset(s3_conf.client_conf);
191
629
        if (!st.ok()) {
192
0
            LOG(WARNING) << "failed to update s3 fs, resource_id=" << id << ": " << st;
193
0
        }
194
629
        return st;
195
629
    }
196
197
0
    Status operator()(const cloud::HdfsVaultInfo& vault) const {
198
0
        auto hdfs_params = io::to_hdfs_params(vault);
199
0
        auto hdfs_fs =
200
0
                DORIS_TRY(io::HdfsFileSystem::create(hdfs_params, hdfs_params.fs_name, id, nullptr,
201
0
                                                     vault.has_prefix() ? vault.prefix() : ""));
202
0
        auto hdfs = std::static_pointer_cast<io::HdfsFileSystem>(hdfs_fs);
203
0
        put_storage_resource(id, {std::move(hdfs), path_format}, 0);
204
0
        return Status::OK();
205
0
    }
206
207
    const std::string& id;
208
    io::FileSystemSPtr fs;
209
    const cloud::StorageVaultPB_PathFormat& path_format;
210
};
211
212
1
Status CloudStorageEngine::open() {
213
1
    sync_storage_vault();
214
215
    // TODO(plat1ko): DeleteBitmapTxnManager
216
217
1
    _memtable_flush_executor = std::make_unique<MemTableFlushExecutor>();
218
    // Use file cache disks number
219
1
    _memtable_flush_executor->init(
220
1
            cast_set<int32_t>(io::FileCacheFactory::instance()->get_cache_instance_size()));
221
222
1
    _calc_delete_bitmap_executor = std::make_unique<CalcDeleteBitmapExecutor>();
223
1
    _calc_delete_bitmap_executor->init("TabletCalcDeleteBitmapThreadPool",
224
1
                                       config::calc_delete_bitmap_max_thread);
225
226
1
    _calc_delete_bitmap_executor_for_load = std::make_unique<CalcDeleteBitmapExecutor>();
227
1
    _calc_delete_bitmap_executor_for_load->init(
228
1
            "LoadCalcDeleteBitmapThreadPool",
229
1
            config::calc_delete_bitmap_for_load_max_thread > 0
230
1
                    ? config::calc_delete_bitmap_for_load_max_thread
231
1
                    : std::max(1, CpuInfo::num_cores() / 2));
232
233
    // The default cache is set to 100MB, use memory limit to dynamic adjustment
234
1
    bool is_percent = false;
235
1
    int64_t delete_bitmap_agg_cache_cache_limit =
236
1
            ParseUtil::parse_mem_spec(config::delete_bitmap_dynamic_agg_cache_limit,
237
1
                                      MemInfo::mem_limit(), MemInfo::physical_mem(), &is_percent);
238
1
    _txn_delete_bitmap_cache = std::make_unique<CloudTxnDeleteBitmapCache>(
239
1
            delete_bitmap_agg_cache_cache_limit > config::delete_bitmap_agg_cache_capacity
240
1
                    ? delete_bitmap_agg_cache_cache_limit
241
1
                    : config::delete_bitmap_agg_cache_capacity);
242
1
    RETURN_IF_ERROR(_txn_delete_bitmap_cache->init());
243
244
1
    _committed_rs_mgr = std::make_unique<CloudCommittedRSMgr>();
245
1
    RETURN_IF_ERROR(_committed_rs_mgr->init());
246
247
1
    _file_cache_block_downloader = std::make_unique<io::FileCacheBlockDownloader>(*this);
248
249
1
    _cloud_warm_up_manager = std::make_shared<CloudWarmUpManager>(*this);
250
251
1
    _tablet_hotspot = std::make_unique<TabletHotspot>();
252
253
1
    _cloud_snapshot_mgr = std::make_unique<CloudSnapshotMgr>(*this);
254
255
1
    RETURN_NOT_OK_STATUS_WITH_WARN(
256
1
            init_stream_load_recorder(ExecEnv::GetInstance()->store_paths()[0].path),
257
1
            "init StreamLoadRecorder failed");
258
259
    // check cluster id
260
1
    RETURN_NOT_OK_STATUS_WITH_WARN(_check_all_root_path_cluster_id(), "fail to check cluster id");
261
262
1
    RETURN_NOT_OK_STATUS_WITH_WARN(ThreadPoolBuilder("SyncLoadForTabletsThreadPool")
263
1
                                           .set_max_threads(config::sync_load_for_tablets_thread)
264
1
                                           .set_min_threads(config::sync_load_for_tablets_thread)
265
1
                                           .build(&_sync_load_for_tablets_thread_pool),
266
1
                                   "fail to build SyncLoadForTabletsThreadPool");
267
268
1
    RETURN_NOT_OK_STATUS_WITH_WARN(ThreadPoolBuilder("WarmupCacheAsyncThreadPool")
269
1
                                           .set_max_threads(config::warmup_cache_async_thread)
270
1
                                           .set_min_threads(config::warmup_cache_async_thread)
271
1
                                           .build(&_warmup_cache_async_thread_pool),
272
1
                                   "fail to build WarmupCacheAsyncThreadPool");
273
274
1
    return Status::OK();
275
1
}
276
277
#ifdef BE_TEST
278
void CloudStorageEngine::init_calc_delete_bitmap_executor_for_UT() {
279
    if (_calc_delete_bitmap_executor == nullptr) {
280
        _calc_delete_bitmap_executor = std::make_unique<CalcDeleteBitmapExecutor>();
281
        _calc_delete_bitmap_executor->init("TabletCalcDeleteBitmapThreadPool",
282
                                           config::calc_delete_bitmap_max_thread);
283
    }
284
    if (_calc_delete_bitmap_executor_for_load == nullptr) {
285
        _calc_delete_bitmap_executor_for_load = std::make_unique<CalcDeleteBitmapExecutor>();
286
        _calc_delete_bitmap_executor_for_load->init(
287
                "LoadCalcDeleteBitmapThreadPool",
288
                config::calc_delete_bitmap_for_load_max_thread > 0
289
                        ? config::calc_delete_bitmap_for_load_max_thread
290
                        : std::max(1, CpuInfo::num_cores() / 2));
291
    }
292
}
293
#endif
294
295
246
void CloudStorageEngine::stop() {
296
246
    if (_stopped) {
297
0
        return;
298
0
    }
299
300
246
    _stopped = true;
301
246
    _stop_background_threads_latch.count_down();
302
303
246
    for (auto&& t : _bg_threads) {
304
0
        if (t) {
305
0
            t->join();
306
0
        }
307
0
    }
308
309
246
    if (_base_compaction_thread_pool) {
310
0
        _base_compaction_thread_pool->shutdown();
311
0
    }
312
246
    if (_cumu_compaction_thread_pool) {
313
0
        _cumu_compaction_thread_pool->shutdown();
314
0
    }
315
246
    if (_binlog_compaction_thread_pool) {
316
0
        _binlog_compaction_thread_pool->shutdown();
317
0
    }
318
246
    _adaptive_thread_controller.stop();
319
246
    LOG(INFO) << "Cloud storage engine is stopped.";
320
321
246
    if (_calc_tablet_delete_bitmap_task_thread_pool) {
322
0
        _calc_tablet_delete_bitmap_task_thread_pool->shutdown();
323
0
    }
324
246
    if (_sync_delete_bitmap_thread_pool) {
325
0
        _sync_delete_bitmap_thread_pool->shutdown();
326
0
    }
327
246
}
328
329
66.0k
bool CloudStorageEngine::stopped() {
330
66.0k
    return _stopped;
331
66.0k
}
332
333
#ifdef BE_TEST
334
void CloudStorageEngine::set_cloud_warm_up_manager(std::unique_ptr<CloudWarmUpManager> manager) {
335
    _cloud_warm_up_manager = std::shared_ptr<CloudWarmUpManager>(std::move(manager));
336
}
337
#endif
338
339
Result<BaseTabletSPtr> CloudStorageEngine::get_tablet(int64_t tablet_id,
340
                                                      SyncRowsetStats* sync_stats,
341
                                                      bool force_use_only_cached,
342
1.30M
                                                      bool cache_on_miss) {
343
1.30M
    return _tablet_mgr
344
1.30M
            ->get_tablet(tablet_id, false, true, sync_stats, force_use_only_cached, cache_on_miss)
345
1.30M
            .transform([](auto&& t) { return static_pointer_cast<BaseTablet>(std::move(t)); });
346
1.30M
}
347
348
Status CloudStorageEngine::get_tablet_meta(int64_t tablet_id, TabletMetaSharedPtr* tablet_meta,
349
172k
                                           bool force_use_only_cached) {
350
172k
    if (tablet_meta == nullptr) {
351
0
        return Status::InvalidArgument("tablet_meta output is null");
352
0
    }
353
354
#if 0
355
    if (_tablet_mgr && _tablet_mgr->peek_tablet_meta(tablet_id, tablet_meta)) {
356
        return Status::OK();
357
    }
358
359
    if (force_use_only_cached) {
360
        return Status::NotFound("tablet meta {} not found in cache", tablet_id);
361
    }
362
#endif
363
364
172k
    if (_meta_mgr == nullptr) {
365
0
        return Status::InternalError("cloud meta manager is not initialized");
366
0
    }
367
368
172k
    return _meta_mgr->get_tablet_meta(tablet_id, tablet_meta);
369
172k
}
370
371
1
Status CloudStorageEngine::start_bg_threads(std::shared_ptr<WorkloadGroup> wg_sptr) {
372
1
    RETURN_IF_ERROR(Thread::create(
373
1
            "CloudStorageEngine", "refresh_s3_info_thread",
374
1
            [this]() { this->_refresh_storage_vault_info_thread_callback(); },
375
1
            &_bg_threads.emplace_back()));
376
1
    LOG(INFO) << "refresh s3 info thread started";
377
378
1
    RETURN_IF_ERROR(Thread::create(
379
1
            "CloudStorageEngine", "vacuum_stale_rowsets_thread",
380
1
            [this]() { this->_vacuum_stale_rowsets_thread_callback(); },
381
1
            &_bg_threads.emplace_back()));
382
1
    LOG(INFO) << "vacuum stale rowsets thread started";
383
384
1
    RETURN_IF_ERROR(Thread::create(
385
1
            "CloudStorageEngine", "sync_tablets_thread",
386
1
            [this]() { this->_sync_tablets_thread_callback(); }, &_bg_threads.emplace_back()));
387
1
    LOG(INFO) << "sync tablets thread started";
388
389
1
    RETURN_IF_ERROR(Thread::create(
390
1
            "CloudStorageEngine", "evict_querying_rowset_thread",
391
1
            [this]() { this->_evict_quring_rowset_thread_callback(); },
392
1
            &_evict_quering_rowset_thread));
393
1
    LOG(INFO) << "evict quering thread started";
394
395
    // add calculate tablet delete bitmap task thread pool
396
1
    RETURN_IF_ERROR(ThreadPoolBuilder("TabletCalDeleteBitmapThreadPool")
397
1
                            .set_min_threads(config::calc_tablet_delete_bitmap_task_max_thread)
398
1
                            .set_max_threads(config::calc_tablet_delete_bitmap_task_max_thread)
399
1
                            .build(&_calc_tablet_delete_bitmap_task_thread_pool));
400
1
    RETURN_IF_ERROR(ThreadPoolBuilder("SyncDeleteBitmapThreadPool")
401
1
                            .set_min_threads(config::sync_delete_bitmap_task_max_thread)
402
1
                            .set_max_threads(config::sync_delete_bitmap_task_max_thread)
403
1
                            .build(&_sync_delete_bitmap_thread_pool));
404
405
    // TODO(plat1ko): check_bucket_enable_versioning_thread
406
407
    // compaction tasks producer thread
408
1
    int base_thread_num = get_base_thread_num();
409
1
    int cumu_thread_num = get_cumu_thread_num();
410
1
    int binlog_thread_num = get_binlog_thread_num();
411
412
1
    RETURN_IF_ERROR(ThreadPoolBuilder("BaseCompactionTaskThreadPool")
413
1
                            .set_min_threads(base_thread_num)
414
1
                            .set_max_threads(base_thread_num)
415
1
                            .build(&_base_compaction_thread_pool));
416
1
    RETURN_IF_ERROR(ThreadPoolBuilder("CumuCompactionTaskThreadPool")
417
1
                            .set_min_threads(cumu_thread_num)
418
1
                            .set_max_threads(cumu_thread_num)
419
1
                            .build(&_cumu_compaction_thread_pool));
420
1
    RETURN_IF_ERROR(ThreadPoolBuilder("BinlogCompactionTaskThreadPool")
421
1
                            .set_min_threads(binlog_thread_num)
422
1
                            .set_max_threads(binlog_thread_num)
423
1
                            .build(&_binlog_compaction_thread_pool));
424
1
    RETURN_IF_ERROR(Thread::create(
425
1
            "StorageEngine", "compaction_tasks_producer_thread",
426
1
            [this]() { this->_compaction_tasks_producer_callback(); },
427
1
            &_bg_threads.emplace_back()));
428
1
    RETURN_IF_ERROR(Thread::create(
429
1
            "StorageEngine", "binlog_compaction_tasks_producer_thread",
430
1
            [this]() { this->_binlog_compaction_tasks_producer_callback(); },
431
1
            &_bg_threads.emplace_back()));
432
1
    LOG(INFO) << "compaction tasks producer thread started,"
433
1
              << " base thread num " << base_thread_num << " cumu thread num " << cumu_thread_num
434
1
              << " binlog thread num " << binlog_thread_num;
435
436
1
    RETURN_IF_ERROR(Thread::create(
437
1
            "StorageEngine", "lease_compaction_thread",
438
1
            [this]() { this->_lease_compaction_thread_callback(); }, &_bg_threads.emplace_back()));
439
440
1
    LOG(INFO) << "lease compaction thread started";
441
442
1
    RETURN_IF_ERROR(Thread::create(
443
1
            "StorageEngine", "check_tablet_delete_bitmap_score_thread",
444
1
            [this]() { this->_check_tablet_delete_bitmap_score_callback(); },
445
1
            &_bg_threads.emplace_back()));
446
1
    LOG(INFO) << "check tablet delete bitmap score thread started";
447
448
1
    _start_adaptive_thread_controller();
449
450
1
    return Status::OK();
451
1
}
452
453
630
void CloudStorageEngine::sync_storage_vault() {
454
630
    cloud::StorageVaultInfos vault_infos;
455
630
    bool enable_storage_vault = false;
456
457
630
    auto st = _meta_mgr->get_storage_vault_info(&vault_infos, &enable_storage_vault);
458
630
    if (!st.ok()) {
459
0
        LOG(WARNING) << "failed to get storage vault info. err=" << st;
460
0
        return;
461
0
    }
462
463
630
    if (vault_infos.empty()) {
464
0
        LOG(WARNING) << "empty storage vault info";
465
0
        return;
466
0
    }
467
468
630
    bool check_storage_vault = false;
469
630
    bool expected = false;
470
630
    if (first_sync_storage_vault.compare_exchange_strong(expected, true)) {
471
0
        check_storage_vault = config::enable_check_storage_vault;
472
0
        LOG(INFO) << "first sync storage vault info, BE try to check iam role connectivity, "
473
0
                     "check_storage_vault="
474
0
                  << check_storage_vault;
475
0
    }
476
477
630
    for (auto& [id, vault_info, path_format] : vault_infos) {
478
627
        auto fs = get_filesystem(id);
479
627
        auto status =
480
627
                (fs == nullptr)
481
627
                        ? std::visit(VaultCreateFSVisitor {id, path_format, check_storage_vault},
482
1
                                     vault_info)
483
627
                        : std::visit(RefreshFSVaultVisitor {id, std::move(fs), path_format},
484
626
                                     vault_info);
485
627
        if (!status.ok()) [[unlikely]] {
486
0
            LOG(WARNING) << vault_process_error(id, vault_info, std::move(st));
487
0
        }
488
627
    }
489
490
630
    if (auto& id = std::get<0>(vault_infos.back());
491
630
        (latest_fs() == nullptr || latest_fs()->id() != id) && !enable_storage_vault) {
492
1
        set_latest_fs(get_filesystem(id));
493
1
    }
494
630
}
495
496
// We should enable_java_support if we want to use hdfs vault
497
1
void CloudStorageEngine::_refresh_storage_vault_info_thread_callback() {
498
53
    while (!_stop_background_threads_latch.wait_for(
499
53
            std::chrono::seconds(config::refresh_s3_info_interval_s))) {
500
52
        sync_storage_vault();
501
        // Dynamically modified s3_{get,put}_* rate limiter configs and cgroup CPU quota
502
        // changes are picked up by the daemon's s3_rate_limiter_refresh_thread, which
503
        // runs in both cloud and non-cloud mode.
504
52
    }
505
1
}
506
507
1
void CloudStorageEngine::_vacuum_stale_rowsets_thread_callback() {
508
11
    while (!_stop_background_threads_latch.wait_for(
509
11
            std::chrono::seconds(config::vacuum_stale_rowsets_interval_s))) {
510
10
        _tablet_mgr->vacuum_stale_rowsets(_stop_background_threads_latch);
511
10
    }
512
1
}
513
514
1
void CloudStorageEngine::_sync_tablets_thread_callback() {
515
6
    while (!_stop_background_threads_latch.wait_for(
516
6
            std::chrono::seconds(config::schedule_sync_tablets_interval_s))) {
517
5
        _tablet_mgr->sync_tablets(_stop_background_threads_latch);
518
5
    }
519
1
}
520
521
void CloudStorageEngine::get_cumu_compaction(
522
91.3k
        int64_t tablet_id, std::vector<std::shared_ptr<CloudCumulativeCompaction>>& res) {
523
91.3k
    std::lock_guard lock(_compaction_mtx);
524
91.3k
    if (auto it = _submitted_cumu_compactions.find(tablet_id);
525
91.3k
        it != _submitted_cumu_compactions.end()) {
526
0
        res = it->second;
527
0
    }
528
91.3k
}
529
530
15.9k
Status CloudStorageEngine::_adjust_compaction_thread_num() {
531
15.9k
    int base_thread_num = get_base_thread_num();
532
533
15.9k
    if (!_base_compaction_thread_pool || !_cumu_compaction_thread_pool ||
534
15.9k
        !_binlog_compaction_thread_pool) {
535
0
        LOG(WARNING) << "compaction thread pool is not created";
536
0
        return Status::Error<ErrorCode::INTERNAL_ERROR, false>("");
537
0
    }
538
539
15.9k
    if (_base_compaction_thread_pool->max_threads() != base_thread_num) {
540
0
        int old_max_threads = _base_compaction_thread_pool->max_threads();
541
0
        Status status = _base_compaction_thread_pool->set_max_threads(base_thread_num);
542
0
        if (status.ok()) {
543
0
            VLOG_NOTICE << "update base compaction thread pool max_threads from " << old_max_threads
544
0
                        << " to " << base_thread_num;
545
0
        }
546
0
    }
547
15.9k
    if (_base_compaction_thread_pool->min_threads() != base_thread_num) {
548
0
        int old_min_threads = _base_compaction_thread_pool->min_threads();
549
0
        Status status = _base_compaction_thread_pool->set_min_threads(base_thread_num);
550
0
        if (status.ok()) {
551
0
            VLOG_NOTICE << "update base compaction thread pool min_threads from " << old_min_threads
552
0
                        << " to " << base_thread_num;
553
0
        }
554
0
    }
555
556
15.9k
    int cumu_thread_num = get_cumu_thread_num();
557
15.9k
    if (_cumu_compaction_thread_pool->max_threads() != cumu_thread_num) {
558
0
        int old_max_threads = _cumu_compaction_thread_pool->max_threads();
559
0
        Status status = _cumu_compaction_thread_pool->set_max_threads(cumu_thread_num);
560
0
        if (status.ok()) {
561
0
            VLOG_NOTICE << "update cumu compaction thread pool max_threads from " << old_max_threads
562
0
                        << " to " << cumu_thread_num;
563
0
        }
564
0
    }
565
15.9k
    if (_cumu_compaction_thread_pool->min_threads() != cumu_thread_num) {
566
0
        int old_min_threads = _cumu_compaction_thread_pool->min_threads();
567
0
        Status status = _cumu_compaction_thread_pool->set_min_threads(cumu_thread_num);
568
0
        if (status.ok()) {
569
0
            VLOG_NOTICE << "update cumu compaction thread pool min_threads from " << old_min_threads
570
0
                        << " to " << cumu_thread_num;
571
0
        }
572
0
    }
573
574
15.9k
    int binlog_thread_num = get_binlog_thread_num();
575
15.9k
    if (_binlog_compaction_thread_pool->max_threads() != binlog_thread_num) {
576
0
        int old_max_threads = _binlog_compaction_thread_pool->max_threads();
577
0
        Status status = _binlog_compaction_thread_pool->set_max_threads(binlog_thread_num);
578
0
        if (status.ok()) {
579
0
            VLOG_NOTICE << "update binlog compaction thread pool max_threads from "
580
0
                        << old_max_threads << " to " << binlog_thread_num;
581
0
        }
582
0
    }
583
15.9k
    if (_binlog_compaction_thread_pool->min_threads() != binlog_thread_num) {
584
0
        int old_min_threads = _binlog_compaction_thread_pool->min_threads();
585
0
        Status status = _binlog_compaction_thread_pool->set_min_threads(binlog_thread_num);
586
0
        if (status.ok()) {
587
0
            VLOG_NOTICE << "update binlog compaction thread pool min_threads from "
588
0
                        << old_min_threads << " to " << binlog_thread_num;
589
0
        }
590
0
    }
591
15.9k
    return Status::OK();
592
15.9k
}
593
594
1
void CloudStorageEngine::_compaction_tasks_producer_callback() {
595
1
    LOG(INFO) << "try to start compaction producer process!";
596
597
1
    int round = 0;
598
1
    CompactionType compaction_type;
599
600
    // Used to record the time when the score metric was last updated.
601
    // The update of the score metric is accompanied by the logic of selecting the tablet.
602
    // If there is no slot available, the logic of selecting the tablet will be terminated,
603
    // which causes the score metric update to be terminated.
604
    // In order to avoid this situation, we need to update the score regularly.
605
1
    int64_t last_cumulative_score_update_time = 0;
606
1
    int64_t last_base_score_update_time = 0;
607
1
    static const int64_t check_score_interval_ms = 5000; // 5 secs
608
609
1
    int64_t interval = config::generate_compaction_tasks_interval_ms;
610
15.9k
    do {
611
15.9k
        int64_t cur_time = UnixMillis();
612
15.9k
        if (!config::disable_auto_compaction) {
613
15.9k
            Status st = _adjust_compaction_thread_num();
614
15.9k
            if (!st.ok()) {
615
0
                break;
616
0
            }
617
618
15.9k
            bool check_score = false;
619
15.9k
            if (round < config::cumulative_compaction_rounds_for_each_base_compaction_round) {
620
14.3k
                compaction_type = CompactionType::CUMULATIVE_COMPACTION;
621
14.3k
                round++;
622
14.3k
                if (cur_time - last_cumulative_score_update_time >= check_score_interval_ms) {
623
609
                    check_score = true;
624
609
                    last_cumulative_score_update_time = cur_time;
625
609
                }
626
14.3k
            } else {
627
1.59k
                compaction_type = CompactionType::BASE_COMPACTION;
628
1.59k
                round = 0;
629
1.59k
                if (cur_time - last_base_score_update_time >= check_score_interval_ms) {
630
506
                    check_score = true;
631
506
                    last_base_score_update_time = cur_time;
632
506
                }
633
1.59k
            }
634
15.9k
            std::unique_ptr<ThreadPool>& thread_pool =
635
15.9k
                    (compaction_type == CompactionType::CUMULATIVE_COMPACTION)
636
15.9k
                            ? _cumu_compaction_thread_pool
637
15.9k
                            : _base_compaction_thread_pool;
638
15.9k
            VLOG_CRITICAL << "compaction thread pool. type: "
639
0
                          << (compaction_type == CompactionType::CUMULATIVE_COMPACTION ? "CUMU"
640
0
                                                                                       : "BASE")
641
0
                          << ", num_threads: " << thread_pool->num_threads()
642
0
                          << ", num_threads_pending_start: "
643
0
                          << thread_pool->num_threads_pending_start()
644
0
                          << ", num_active_threads: " << thread_pool->num_active_threads()
645
0
                          << ", max_threads: " << thread_pool->max_threads()
646
0
                          << ", min_threads: " << thread_pool->min_threads()
647
0
                          << ", num_total_queued_tasks: " << thread_pool->get_queue_size();
648
15.9k
            std::vector<CloudTabletSPtr> tablets_compaction =
649
15.9k
                    _generate_cloud_compaction_tasks(compaction_type, check_score);
650
651
            /// Regardless of whether the tablet is submitted for compaction or not,
652
            /// we need to call 'reset_compaction' to clean up the base_compaction or cumulative_compaction objects
653
            /// in the tablet, because these two objects store the tablet's own shared_ptr.
654
            /// If it is not cleaned up, the reference count of the tablet will always be greater than 1,
655
            /// thus cannot be collected by the garbage collector. (TabletManager::start_trash_sweep)
656
93.8k
            for (const auto& tablet : tablets_compaction) {
657
93.8k
                Status status = submit_compaction_task(tablet, compaction_type);
658
93.8k
                if (status.ok()) continue;
659
84.9k
                if ((!status.is<ErrorCode::BE_NO_SUITABLE_VERSION>() &&
660
84.9k
                     !status.is<ErrorCode::CUMULATIVE_NO_SUITABLE_VERSION>()) ||
661
84.9k
                    VLOG_DEBUG_IS_ON) {
662
215
                    LOG(WARNING) << "failed to submit compaction task for tablet: "
663
215
                                 << tablet->tablet_id() << ", err: " << status;
664
215
                }
665
84.9k
            }
666
15.9k
            interval = config::generate_compaction_tasks_interval_ms;
667
15.9k
        } else {
668
0
            interval = config::check_auto_compaction_interval_seconds * 1000;
669
0
        }
670
15.9k
        int64_t end_time = UnixMillis();
671
15.9k
        DorisMetrics::instance()->compaction_producer_callback_a_round_time->set_value(end_time -
672
15.9k
                                                                                       cur_time);
673
15.9k
    } while (!_stop_background_threads_latch.wait_for(std::chrono::milliseconds(interval)));
674
1
}
675
676
1
void CloudStorageEngine::_binlog_compaction_tasks_producer_callback() {
677
1
    LOG(INFO) << "try to start binlog compaction producer process!";
678
679
1
    int64_t last_binlog_score_update_time = 0;
680
1
    static const int64_t check_score_interval_ms = 5000;
681
682
1
    int64_t interval = config::generate_compaction_tasks_interval_ms;
683
628
    do {
684
628
        int64_t cur_time = UnixMillis();
685
628
        if (config::enable_feature_binlog && !config::disable_auto_compaction) {
686
0
            Status st = _adjust_compaction_thread_num();
687
0
            if (!st.ok()) {
688
0
                break;
689
0
            }
690
691
0
            bool check_score = false;
692
0
            if (cur_time - last_binlog_score_update_time >= check_score_interval_ms) {
693
0
                check_score = true;
694
0
                last_binlog_score_update_time = cur_time;
695
0
            }
696
697
0
            std::vector<CloudTabletSPtr> tablets_compaction = _generate_cloud_compaction_tasks(
698
0
                    CompactionType::CUMU_BINLOG_COMPACTION, check_score);
699
0
            for (const auto& tablet : tablets_compaction) {
700
0
                Status status =
701
0
                        submit_compaction_task(tablet, CompactionType::CUMU_BINLOG_COMPACTION);
702
0
                if (status.ok()) continue;
703
0
                if ((!status.is<ErrorCode::BE_NO_SUITABLE_VERSION>() &&
704
0
                     !status.is<ErrorCode::CUMULATIVE_NO_SUITABLE_VERSION>()) ||
705
0
                    VLOG_DEBUG_IS_ON) {
706
0
                    LOG(WARNING) << "failed to submit binlog compaction task for tablet: "
707
0
                                 << tablet->tablet_id() << ", err: " << status;
708
0
                }
709
0
            }
710
0
            interval = config::generate_compaction_tasks_interval_ms;
711
628
        } else {
712
628
            interval = config::check_auto_compaction_interval_seconds * 1000;
713
628
        }
714
628
    } while (!_stop_background_threads_latch.wait_for(std::chrono::milliseconds(interval)));
715
1
}
716
717
void CloudStorageEngine::unregister_index_change_compaction(int64_t tablet_id,
718
684
                                                            bool is_base_compact) {
719
684
    std::lock_guard lock(_compaction_mtx);
720
684
    if (is_base_compact) {
721
0
        _submitted_index_change_base_compaction.erase(tablet_id);
722
684
    } else {
723
684
        _submitted_index_change_cumu_compaction.erase(tablet_id);
724
684
    }
725
684
}
726
727
bool CloudStorageEngine::register_index_change_compaction(
728
        std::shared_ptr<CloudIndexChangeCompaction> compact, int64_t tablet_id,
729
686
        bool is_base_compact, std::string& err_reason) {
730
686
    std::lock_guard lock(_compaction_mtx);
731
686
    if (is_base_compact) {
732
2
        if (_submitted_base_compactions.contains(tablet_id) ||
733
2
            _submitted_full_compactions.contains(tablet_id) ||
734
2
            _submitted_index_change_base_compaction.contains(tablet_id)) {
735
1
            std::stringstream ss;
736
1
            ss << "reason:" << ((int)_submitted_base_compactions.contains(tablet_id)) << ", "
737
1
               << ((int)_submitted_full_compactions.contains(tablet_id)) << ", "
738
1
               << ((int)_submitted_index_change_base_compaction.contains(tablet_id));
739
1
            err_reason = ss.str();
740
1
            return false;
741
1
        } else {
742
1
            _submitted_index_change_base_compaction[tablet_id] = compact;
743
1
            return true;
744
1
        }
745
684
    } else {
746
684
        if (_tablet_preparing_cumu_compaction.contains(tablet_id) ||
747
688
            _submitted_cumu_compactions.contains(tablet_id) ||
748
687
            _submitted_index_change_cumu_compaction.contains(tablet_id)) {
749
1
            std::stringstream ss;
750
1
            ss << "reason:" << ((int)_tablet_preparing_cumu_compaction.contains(tablet_id)) << ", "
751
1
               << ((int)_submitted_cumu_compactions.contains(tablet_id)) << ", "
752
1
               << ((int)_submitted_index_change_cumu_compaction.contains(tablet_id));
753
1
            err_reason = ss.str();
754
1
            return false;
755
683
        } else {
756
683
            _submitted_index_change_cumu_compaction[tablet_id] = compact;
757
683
        }
758
683
        return true;
759
684
    }
760
686
}
761
762
std::vector<CloudTabletSPtr> CloudStorageEngine::_generate_cloud_compaction_tasks(
763
15.9k
        CompactionType compaction_type, bool check_score) {
764
15.9k
    DCHECK(compaction_type == CompactionType::BASE_COMPACTION ||
765
15.9k
           compaction_type == CompactionType::CUMULATIVE_COMPACTION ||
766
15.9k
           compaction_type == CompactionType::CUMU_BINLOG_COMPACTION);
767
15.9k
    std::vector<std::shared_ptr<CloudTablet>> tablets_compaction;
768
769
15.9k
    CompactionScoreStats score_stats;
770
15.9k
    bool got_score_stats = false;
771
15.9k
    std::unordered_set<int64_t> tablet_preparing_cumu_compaction;
772
15.9k
    std::unordered_map<int64_t, std::vector<std::shared_ptr<CloudCumulativeCompaction>>>
773
15.9k
            submitted_cumu_compactions;
774
15.9k
    int submitted_cumu_binlog_compaction_count = 0;
775
15.9k
    std::unordered_map<int64_t, std::shared_ptr<CloudBaseCompaction>> submitted_base_compactions;
776
15.9k
    std::unordered_map<int64_t, std::shared_ptr<CloudFullCompaction>> submitted_full_compactions;
777
15.9k
    std::unordered_map<int64_t, std::shared_ptr<CloudIndexChangeCompaction>>
778
15.9k
            submitted_index_change_cumu_compactions;
779
15.9k
    std::unordered_map<int64_t, std::shared_ptr<CloudIndexChangeCompaction>>
780
15.9k
            submitted_index_change_base_compactions;
781
15.9k
    {
782
15.9k
        std::lock_guard lock(_compaction_mtx);
783
15.9k
        tablet_preparing_cumu_compaction = _tablet_preparing_cumu_compaction;
784
15.9k
        submitted_cumu_compactions = _submitted_cumu_compactions;
785
15.9k
        submitted_cumu_binlog_compaction_count = _submitted_cumu_binlog_compaction_count;
786
15.9k
        submitted_base_compactions = _submitted_base_compactions;
787
15.9k
        submitted_full_compactions = _submitted_full_compactions;
788
15.9k
        submitted_index_change_cumu_compactions = _submitted_index_change_cumu_compaction;
789
15.9k
        submitted_index_change_base_compactions = _submitted_index_change_base_compaction;
790
15.9k
    }
791
792
15.9k
    bool need_pick_tablet = true;
793
15.9k
    int thread_per_disk = compaction_type == CompactionType::CUMU_BINLOG_COMPACTION
794
15.9k
                                  ? config::binlog_compaction_task_num_per_disk
795
15.9k
                                  : config::compaction_task_num_per_fast_disk;
796
15.9k
    int num_cumu =
797
15.9k
            std::accumulate(submitted_cumu_compactions.begin(), submitted_cumu_compactions.end(), 0,
798
15.9k
                            [](int a, auto& b) { return a + b.second.size(); });
799
15.9k
    int num_cumu_binlog = submitted_cumu_binlog_compaction_count;
800
15.9k
    int num_cumu_data = num_cumu - num_cumu_binlog;
801
15.9k
    int num_base =
802
15.9k
            cast_set<int>(submitted_base_compactions.size() + submitted_full_compactions.size());
803
15.9k
    int n = compaction_type == CompactionType::CUMU_BINLOG_COMPACTION
804
15.9k
                    ? thread_per_disk - num_cumu_binlog
805
15.9k
                    : thread_per_disk - num_cumu_data - num_base;
806
15.9k
    if (compaction_type == CompactionType::BASE_COMPACTION) {
807
        // We need to reserve at least one thread for cumulative compaction,
808
        // because base compactions may take too long to complete, which may
809
        // leads to "too many rowsets" error.
810
1.59k
        int base_n = std::min(config::max_base_compaction_task_num_per_disk, thread_per_disk - 1) -
811
1.59k
                     num_base;
812
1.59k
        n = std::min(base_n, n);
813
1.59k
    }
814
15.9k
    if (n <= 0) { // No threads available
815
279
        if (!check_score) return tablets_compaction;
816
20
        need_pick_tablet = false;
817
20
        n = 0;
818
20
    }
819
820
    // Return true for skipping compaction
821
15.6k
    std::function<bool(CloudTablet*)> filter_out;
822
15.6k
    if (compaction_type == CompactionType::BASE_COMPACTION) {
823
1.57k
        filter_out = [&submitted_base_compactions, &submitted_full_compactions,
824
51.6M
                      &submitted_index_change_base_compactions](CloudTablet* t) {
825
51.6M
            return t->is_row_binlog_tablet() ||
826
51.6M
                   submitted_base_compactions.contains(t->tablet_id()) ||
827
51.6M
                   submitted_full_compactions.contains(t->tablet_id()) ||
828
51.6M
                   submitted_index_change_base_compactions.contains(t->tablet_id()) ||
829
51.6M
                   t->tablet_state() != TABLET_RUNNING;
830
51.6M
        };
831
14.1k
    } else if (compaction_type == CompactionType::CUMU_BINLOG_COMPACTION) {
832
1
        filter_out = [&tablet_preparing_cumu_compaction, &submitted_cumu_compactions,
833
1
                      &submitted_index_change_cumu_compactions](CloudTablet* t) {
834
1
            return !t->is_row_binlog_tablet() ||
835
1
                   tablet_preparing_cumu_compaction.contains(t->tablet_id()) ||
836
1
                   submitted_index_change_cumu_compactions.contains(t->tablet_id()) ||
837
1
                   submitted_cumu_compactions.contains(t->tablet_id()) ||
838
1
                   (t->tablet_state() != TABLET_RUNNING &&
839
1
                    (!config::enable_new_tablet_do_compaction || t->alter_version() == -1));
840
1
        };
841
14.1k
    } else if (config::enable_parallel_cumu_compaction) {
842
0
        filter_out = [&tablet_preparing_cumu_compaction,
843
0
                      &submitted_index_change_cumu_compactions](CloudTablet* t) {
844
0
            return t->is_row_binlog_tablet() ||
845
0
                   tablet_preparing_cumu_compaction.contains(t->tablet_id()) ||
846
0
                   submitted_index_change_cumu_compactions.contains(t->tablet_id()) ||
847
0
                   (t->tablet_state() != TABLET_RUNNING &&
848
0
                    (!config::enable_new_tablet_do_compaction || t->alter_version() == -1));
849
0
        };
850
14.1k
    } else {
851
14.1k
        filter_out = [&tablet_preparing_cumu_compaction, &submitted_cumu_compactions,
852
385M
                      &submitted_index_change_cumu_compactions](CloudTablet* t) {
853
385M
            return t->is_row_binlog_tablet() ||
854
385M
                   tablet_preparing_cumu_compaction.contains(t->tablet_id()) ||
855
385M
                   submitted_index_change_cumu_compactions.contains(t->tablet_id()) ||
856
385M
                   submitted_cumu_compactions.contains(t->tablet_id()) ||
857
385M
                   (t->tablet_state() != TABLET_RUNNING &&
858
385M
                    (!config::enable_new_tablet_do_compaction || t->alter_version() == -1));
859
385M
        };
860
14.1k
    }
861
862
    // Even if need_pick_tablet is false, we still need to call find_best_tablet_to_compaction(),
863
    // So that we can update the max_compaction_score metric.
864
15.6k
    do {
865
15.6k
        std::vector<CloudTabletSPtr> tablets;
866
15.6k
        auto st = tablet_mgr().get_topn_tablets_to_compact(n, compaction_type, filter_out, &tablets,
867
15.6k
                                                           &score_stats);
868
15.6k
        if (!st.ok()) {
869
0
            LOG(WARNING) << "failed to get tablets to compact, err=" << st;
870
0
            break;
871
0
        }
872
15.6k
        got_score_stats = true;
873
15.6k
        if (!need_pick_tablet) break;
874
15.6k
        tablets_compaction = std::move(tablets);
875
15.6k
    } while (false);
876
877
15.6k
    if (got_score_stats && score_stats.scanned) {
878
15.6k
        if (compaction_type == CompactionType::BASE_COMPACTION && score_stats.max_score > 0) {
879
1.52k
            DorisMetrics::instance()->tablet_base_max_compaction_score->set_value(
880
1.52k
                    score_stats.max_score);
881
14.1k
        } else if (compaction_type == CompactionType::CUMULATIVE_COMPACTION) {
882
14.1k
            if (check_score || score_stats.max_score > 0) {
883
13.6k
                DorisMetrics::instance()->tablet_cumulative_max_compaction_score->set_value(
884
13.6k
                        score_stats.max_score);
885
13.6k
            }
886
14.1k
            if (check_score || score_stats.size_based_max_score > 0) {
887
13.6k
                DorisMetrics::instance()->tablet_size_based_max_compaction_score->set_value(
888
13.6k
                        score_stats.size_based_max_score);
889
13.6k
            }
890
14.1k
            if (check_score || score_stats.time_series_max_score > 0) {
891
7.88k
                DorisMetrics::instance()->tablet_time_series_max_compaction_score->set_value(
892
7.88k
                        score_stats.time_series_max_score);
893
7.88k
            }
894
14.1k
        } else if (compaction_type == CompactionType::CUMU_BINLOG_COMPACTION &&
895
50
                   score_stats.max_score > 0) {
896
1
            DorisMetrics::instance()->tablet_binlog_max_compaction_score->set_value(
897
1
                    score_stats.max_score);
898
1
        }
899
15.6k
    }
900
901
15.6k
    return tablets_compaction;
902
15.9k
}
903
904
Status CloudStorageEngine::_request_tablet_global_compaction_lock(
905
        ReaderType compaction_type, const CloudTabletSPtr& tablet,
906
9.24k
        std::shared_ptr<CloudCompactionMixin> compaction) {
907
9.24k
    long now = duration_cast<std::chrono::milliseconds>(
908
9.24k
                       std::chrono::system_clock::now().time_since_epoch())
909
9.24k
                       .count();
910
9.24k
    if (compaction_type == ReaderType::READER_CUMULATIVE_COMPACTION) {
911
9.11k
        auto cumu_compaction = static_pointer_cast<CloudCumulativeCompaction>(compaction);
912
9.11k
        if (auto st = cumu_compaction->request_global_lock(); !st.ok()) {
913
163
            LOG_WARNING("failed to request cumu compactoin global lock")
914
163
                    .tag("tablet id", tablet->tablet_id())
915
163
                    .tag("msg", st.to_string());
916
163
            tablet->set_last_cumu_compaction_failure_time(now);
917
163
            return st;
918
163
        }
919
8.95k
        {
920
8.95k
            std::lock_guard lock(_compaction_mtx);
921
8.95k
            _executing_cumu_compactions[tablet->tablet_id()].push_back(cumu_compaction);
922
8.95k
        }
923
8.95k
        return Status::OK();
924
9.11k
    } else if (compaction_type == ReaderType::READER_BASE_COMPACTION) {
925
73
        auto base_compaction = static_pointer_cast<CloudBaseCompaction>(compaction);
926
73
        if (auto st = base_compaction->request_global_lock(); !st.ok()) {
927
2
            LOG_WARNING("failed to request base compactoin global lock")
928
2
                    .tag("tablet id", tablet->tablet_id())
929
2
                    .tag("msg", st.to_string());
930
2
            tablet->set_last_base_compaction_failure_time(now);
931
2
            return st;
932
2
        }
933
71
        {
934
71
            std::lock_guard lock(_compaction_mtx);
935
71
            _executing_base_compactions[tablet->tablet_id()] = base_compaction;
936
71
        }
937
71
        return Status::OK();
938
73
    } else if (compaction_type == ReaderType::READER_FULL_COMPACTION) {
939
58
        auto full_compaction = static_pointer_cast<CloudFullCompaction>(compaction);
940
58
        if (auto st = full_compaction->request_global_lock(); !st.ok()) {
941
0
            LOG_WARNING("failed to request full compactoin global lock")
942
0
                    .tag("tablet id", tablet->tablet_id())
943
0
                    .tag("msg", st.to_string());
944
0
            tablet->set_last_full_compaction_failure_time(now);
945
0
            return st;
946
0
        }
947
58
        {
948
58
            std::lock_guard lock(_compaction_mtx);
949
58
            _executing_full_compactions[tablet->tablet_id()] = full_compaction;
950
58
        }
951
58
        return Status::OK();
952
58
    } else {
953
0
        LOG(WARNING) << "unsupport compaction task for tablet: " << tablet->tablet_id()
954
0
                     << ", compaction name: " << compaction->compaction_name();
955
0
        return Status::NotFound("Unsupport compaction type {}", compaction->compaction_name());
956
0
    }
957
9.24k
}
958
959
Status CloudStorageEngine::_submit_base_compaction_task(const CloudTabletSPtr& tablet,
960
2.85k
                                                        int trigger_method) {
961
2.85k
    using namespace std::chrono;
962
2.85k
    {
963
2.85k
        std::lock_guard lock(_compaction_mtx);
964
        // Take a placeholder for base compaction
965
2.85k
        auto [_, success] = _submitted_base_compactions.emplace(tablet->tablet_id(), nullptr);
966
2.85k
        if (!success) {
967
0
            return Status::AlreadyExist(
968
0
                    "other base compaction or full compaction is submitted, tablet_id={}",
969
0
                    tablet->tablet_id());
970
0
        }
971
2.85k
    }
972
2.85k
    auto compaction = std::make_shared<CloudBaseCompaction>(*this, tablet);
973
2.85k
    auto st = compaction->prepare_compact();
974
2.85k
    if (!st.ok()) {
975
2.77k
        long now = duration_cast<std::chrono::milliseconds>(
976
2.77k
                           std::chrono::system_clock::now().time_since_epoch())
977
2.77k
                           .count();
978
2.77k
        tablet->set_last_base_compaction_failure_time(now);
979
2.77k
        std::lock_guard lock(_compaction_mtx);
980
2.77k
        _submitted_base_compactions.erase(tablet->tablet_id());
981
2.77k
        return st;
982
2.77k
    }
983
    // Register task with CompactionTaskTracker as PENDING
984
73
    auto* tracker = CompactionTaskTracker::instance();
985
73
    int64_t compaction_id = compaction->compaction_id();
986
73
    {
987
73
        CompactionTaskInfo info;
988
73
        info.compaction_id = compaction_id;
989
73
        info.tablet_id = tablet->tablet_id();
990
73
        info.table_id = tablet->table_id();
991
73
        info.partition_id = tablet->partition_id();
992
73
        info.compaction_type = CompactionProfileType::BASE;
993
73
        info.status = CompactionTaskStatus::PENDING;
994
73
        info.trigger_method = static_cast<TriggerMethod>(trigger_method);
995
73
        info.scheduled_time_ms =
996
73
                duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
997
73
        info.backend_id = BackendOptions::get_backend_id();
998
73
        info.compaction_score = tablet->get_real_compaction_score();
999
73
        info.input_rowsets_count = compaction->input_rowsets_count();
1000
73
        info.input_row_num = compaction->input_row_num_value();
1001
73
        info.input_data_size = compaction->input_rowsets_data_size();
1002
73
        info.input_index_size = compaction->input_rowsets_index_size();
1003
73
        info.input_total_size = compaction->input_rowsets_total_size();
1004
73
        info.input_segments_num = compaction->input_segments_num_value();
1005
73
        info.input_version_range = compaction->input_version_range_str();
1006
73
        info.is_vertical = compaction->is_vertical();
1007
73
        tracker->register_task(std::move(info));
1008
73
    }
1009
73
    {
1010
73
        std::lock_guard lock(_compaction_mtx);
1011
73
        _submitted_base_compactions[tablet->tablet_id()] = compaction;
1012
73
    }
1013
73
    st = _base_compaction_thread_pool->submit_func([=, this, compaction = std::move(compaction)]() {
1014
73
        DorisMetrics::instance()->base_compaction_task_running_total->increment(1);
1015
73
        DorisMetrics::instance()->base_compaction_task_pending_total->set_value(
1016
73
                _base_compaction_thread_pool->get_queue_size());
1017
73
        g_base_compaction_running_task_count << 1;
1018
73
        signal::tablet_id = tablet->tablet_id();
1019
73
        Defer defer {[&]() {
1020
            // Idempotent cleanup: remove task from tracker
1021
73
            CompactionTaskTracker::instance()->remove_task(compaction_id);
1022
73
            g_base_compaction_running_task_count << -1;
1023
73
            std::lock_guard lock(_compaction_mtx);
1024
73
            _submitted_base_compactions.erase(tablet->tablet_id());
1025
73
            DorisMetrics::instance()->base_compaction_task_running_total->increment(-1);
1026
73
            DorisMetrics::instance()->base_compaction_task_pending_total->set_value(
1027
73
                    _base_compaction_thread_pool->get_queue_size());
1028
73
        }};
1029
73
        auto st = _request_tablet_global_compaction_lock(ReaderType::READER_BASE_COMPACTION, tablet,
1030
73
                                                         compaction);
1031
73
        if (!st.ok()) return;
1032
        // Update tracker to RUNNING after acquiring global lock
1033
71
        {
1034
71
            RunningStats rs;
1035
71
            rs.start_time_ms =
1036
71
                    duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
1037
71
            CompactionTaskTracker::instance()->update_to_running(compaction_id, rs);
1038
71
        }
1039
71
        st = compaction->execute_compact();
1040
71
        if (!st.ok()) {
1041
            // Error log has been output in `execute_compact`
1042
0
            long now = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
1043
0
            tablet->set_last_base_compaction_failure_time(now);
1044
0
        }
1045
71
        std::lock_guard lock(_compaction_mtx);
1046
71
        _executing_base_compactions.erase(tablet->tablet_id());
1047
71
    });
1048
73
    DorisMetrics::instance()->base_compaction_task_pending_total->set_value(
1049
73
            _base_compaction_thread_pool->get_queue_size());
1050
73
    if (!st.ok()) {
1051
0
        tracker->remove_task(compaction_id);
1052
0
        std::lock_guard lock(_compaction_mtx);
1053
0
        _submitted_base_compactions.erase(tablet->tablet_id());
1054
0
        return Status::InternalError("failed to submit base compaction, tablet_id={}",
1055
0
                                     tablet->tablet_id());
1056
0
    }
1057
73
    return st;
1058
73
}
1059
1060
Status CloudStorageEngine::_submit_cumulative_compaction_task(const CloudTabletSPtr& tablet,
1061
                                                              int trigger_method,
1062
91.3k
                                                              CompactionType compaction_type) {
1063
91.3k
    DCHECK(compaction_type == CompactionType::CUMULATIVE_COMPACTION ||
1064
91.3k
           compaction_type == CompactionType::CUMU_BINLOG_COMPACTION);
1065
91.3k
    using namespace std::chrono;
1066
91.3k
    {
1067
91.3k
        std::lock_guard lock(_compaction_mtx);
1068
91.3k
        if (!config::enable_parallel_cumu_compaction &&
1069
91.3k
            _submitted_cumu_compactions.count(tablet->tablet_id())) {
1070
0
            return Status::AlreadyExist("other cumu compaction is submitted, tablet_id={}",
1071
0
                                        tablet->tablet_id());
1072
0
        }
1073
91.3k
        auto [_, success] = _tablet_preparing_cumu_compaction.insert(tablet->tablet_id());
1074
91.3k
        if (!success) {
1075
0
            return Status::AlreadyExist("other cumu compaction is preparing, tablet_id={}",
1076
0
                                        tablet->tablet_id());
1077
0
        }
1078
91.3k
    }
1079
91.3k
    auto compaction = std::make_shared<CloudCumulativeCompaction>(*this, tablet);
1080
91.3k
    auto st = compaction->prepare_compact();
1081
91.3k
    if (!st.ok()) {
1082
82.2k
        long now = duration_cast<std::chrono::milliseconds>(
1083
82.2k
                           std::chrono::system_clock::now().time_since_epoch())
1084
82.2k
                           .count();
1085
82.2k
        if (!st.is<ErrorCode::CUMULATIVE_MEET_DELETE_VERSION>()) {
1086
81.9k
            if (st.is<ErrorCode::CUMULATIVE_NO_SUITABLE_VERSION>()) {
1087
                // Backoff strategy if no suitable version
1088
81.9k
                tablet->last_cumu_no_suitable_version_ms = now;
1089
81.9k
            } else {
1090
0
                tablet->set_last_cumu_compaction_failure_time(now);
1091
0
            }
1092
81.9k
        }
1093
82.2k
        std::lock_guard lock(_compaction_mtx);
1094
82.2k
        _tablet_preparing_cumu_compaction.erase(tablet->tablet_id());
1095
82.2k
        return st;
1096
82.2k
    }
1097
    // Register task with CompactionTaskTracker as PENDING
1098
    // IMPORTANT: use compaction->compaction_id(), NOT tracker->next_compaction_id(),
1099
    // because the Compaction constructor already allocated an ID via the tracker.
1100
9.11k
    auto* tracker = CompactionTaskTracker::instance();
1101
9.11k
    int64_t compaction_id = compaction->compaction_id();
1102
9.11k
    {
1103
9.11k
        CompactionTaskInfo info;
1104
9.11k
        info.compaction_id = compaction_id;
1105
9.11k
        info.tablet_id = tablet->tablet_id();
1106
9.11k
        info.table_id = tablet->table_id();
1107
9.11k
        info.partition_id = tablet->partition_id();
1108
9.11k
        info.compaction_type = CompactionProfileType::CUMULATIVE;
1109
9.11k
        info.status = CompactionTaskStatus::PENDING;
1110
9.11k
        info.trigger_method = static_cast<TriggerMethod>(trigger_method);
1111
9.11k
        info.scheduled_time_ms =
1112
9.11k
                duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
1113
9.11k
        info.backend_id = BackendOptions::get_backend_id();
1114
9.11k
        info.compaction_score = tablet->get_real_compaction_score();
1115
9.11k
        info.input_rowsets_count = compaction->input_rowsets_count();
1116
9.11k
        info.input_row_num = compaction->input_row_num_value();
1117
9.11k
        info.input_data_size = compaction->input_rowsets_data_size();
1118
9.11k
        info.input_index_size = compaction->input_rowsets_index_size();
1119
9.11k
        info.input_total_size = compaction->input_rowsets_total_size();
1120
9.11k
        info.input_segments_num = compaction->input_segments_num_value();
1121
9.11k
        info.input_version_range = compaction->input_version_range_str();
1122
9.11k
        info.is_vertical = compaction->is_vertical();
1123
9.11k
        tracker->register_task(std::move(info));
1124
9.11k
    }
1125
9.11k
    {
1126
9.11k
        std::lock_guard lock(_compaction_mtx);
1127
9.11k
        _tablet_preparing_cumu_compaction.erase(tablet->tablet_id());
1128
9.11k
        _submitted_cumu_compactions[tablet->tablet_id()].push_back(compaction);
1129
9.11k
        if (compaction_type == CompactionType::CUMU_BINLOG_COMPACTION) {
1130
0
            ++_submitted_cumu_binlog_compaction_count;
1131
0
        }
1132
9.11k
    }
1133
9.11k
    auto erase_submitted_cumu_compaction = [=, this]() {
1134
9.11k
        std::lock_guard lock(_compaction_mtx);
1135
9.11k
        auto it = _submitted_cumu_compactions.find(tablet->tablet_id());
1136
9.11k
        DCHECK(it != _submitted_cumu_compactions.end());
1137
9.11k
        auto& compactions = it->second;
1138
9.11k
        auto it1 = std::find(compactions.begin(), compactions.end(), compaction);
1139
9.11k
        DCHECK(it1 != compactions.end());
1140
9.11k
        compactions.erase(it1);
1141
9.11k
        if (compaction_type == CompactionType::CUMU_BINLOG_COMPACTION) {
1142
0
            DCHECK_GT(_submitted_cumu_binlog_compaction_count, 0);
1143
0
            --_submitted_cumu_binlog_compaction_count;
1144
0
        }
1145
9.11k
        if (compactions.empty()) { // No compactions on this tablet, erase key
1146
9.11k
            _submitted_cumu_compactions.erase(it);
1147
            // No cumu compaction on this tablet, reset `last_cumu_no_suitable_version_ms` to enable this tablet to
1148
            // enter the compaction scheduling candidate set. The purpose of doing this is to have at least one BE perform
1149
            // cumu compaction on tablet which has suitable versions for cumu compaction.
1150
9.11k
            tablet->last_cumu_no_suitable_version_ms = 0;
1151
9.11k
        }
1152
9.11k
    };
1153
9.11k
    auto erase_executing_cumu_compaction = [=, this]() {
1154
8.92k
        std::lock_guard lock(_compaction_mtx);
1155
8.92k
        auto it = _executing_cumu_compactions.find(tablet->tablet_id());
1156
8.92k
        DCHECK(it != _executing_cumu_compactions.end());
1157
8.92k
        auto& compactions = it->second;
1158
8.92k
        auto it1 = std::find(compactions.begin(), compactions.end(), compaction);
1159
8.92k
        DCHECK(it1 != compactions.end());
1160
8.92k
        compactions.erase(it1);
1161
8.95k
        if (compactions.empty()) { // No compactions on this tablet, erase key
1162
8.95k
            _executing_cumu_compactions.erase(it);
1163
            // No cumu compaction on this tablet, reset `last_cumu_no_suitable_version_ms` to enable this tablet to
1164
            // enter the compaction scheduling candidate set. The purpose of doing this is to have at least one BE perform
1165
            // cumu compaction on tablet which has suitable versions for cumu compaction.
1166
8.95k
            tablet->last_cumu_no_suitable_version_ms = 0;
1167
8.95k
        }
1168
8.92k
    };
1169
9.11k
    auto& submit_thread_pool = compaction_type == CompactionType::CUMU_BINLOG_COMPACTION
1170
9.11k
                                       ? _binlog_compaction_thread_pool
1171
9.11k
                                       : _cumu_compaction_thread_pool;
1172
9.11k
    st = submit_thread_pool->submit_func([=, this, compaction = std::move(compaction)]() {
1173
9.11k
        if (compaction_type == CompactionType::CUMU_BINLOG_COMPACTION) {
1174
0
            DorisMetrics::instance()->binlog_compaction_task_running_total->increment(1);
1175
0
            DorisMetrics::instance()->binlog_compaction_task_pending_total->set_value(
1176
0
                    _binlog_compaction_thread_pool->get_queue_size());
1177
9.11k
        } else {
1178
9.11k
            DorisMetrics::instance()->cumulative_compaction_task_running_total->increment(1);
1179
9.11k
            DorisMetrics::instance()->cumulative_compaction_task_pending_total->set_value(
1180
9.11k
                    _cumu_compaction_thread_pool->get_queue_size());
1181
9.11k
        }
1182
9.11k
        DBUG_EXECUTE_IF("CloudStorageEngine._submit_cumulative_compaction_task.wait_in_line",
1183
9.11k
                        { sleep(5); })
1184
9.11k
        signal::tablet_id = tablet->tablet_id();
1185
9.11k
        g_cumu_compaction_running_task_count << 1;
1186
9.11k
        bool is_large_task = true;
1187
9.11k
        Defer defer {[&]() {
1188
9.11k
            DBUG_EXECUTE_IF("CloudStorageEngine._submit_cumulative_compaction_task.sleep",
1189
9.11k
                            { sleep(5); })
1190
            // Idempotent cleanup: remove task from tracker
1191
9.11k
            CompactionTaskTracker::instance()->remove_task(compaction_id);
1192
9.11k
            if (compaction_type == CompactionType::CUMULATIVE_COMPACTION) {
1193
9.11k
                std::lock_guard lock(_cumu_compaction_delay_mtx);
1194
9.11k
                _cumu_compaction_thread_pool_used_threads--;
1195
9.11k
                if (!is_large_task) {
1196
8.95k
                    _cumu_compaction_thread_pool_small_tasks_running--;
1197
8.95k
                }
1198
9.11k
            }
1199
9.11k
            g_cumu_compaction_running_task_count << -1;
1200
9.11k
            erase_submitted_cumu_compaction();
1201
9.11k
            if (compaction_type == CompactionType::CUMU_BINLOG_COMPACTION) {
1202
0
                DorisMetrics::instance()->binlog_compaction_task_running_total->increment(-1);
1203
0
                DorisMetrics::instance()->binlog_compaction_task_pending_total->set_value(
1204
0
                        _binlog_compaction_thread_pool->get_queue_size());
1205
9.11k
            } else {
1206
9.11k
                DorisMetrics::instance()->cumulative_compaction_task_running_total->increment(-1);
1207
9.11k
                DorisMetrics::instance()->cumulative_compaction_task_pending_total->set_value(
1208
9.11k
                        _cumu_compaction_thread_pool->get_queue_size());
1209
9.11k
            }
1210
9.11k
        }};
1211
9.11k
        auto st = _request_tablet_global_compaction_lock(ReaderType::READER_CUMULATIVE_COMPACTION,
1212
9.11k
                                                         tablet, compaction);
1213
9.11k
        if (!st.ok()) return;
1214
        // Update tracker to RUNNING after acquiring global lock
1215
8.95k
        {
1216
8.95k
            RunningStats rs;
1217
8.95k
            rs.start_time_ms =
1218
8.95k
                    duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
1219
8.95k
            CompactionTaskTracker::instance()->update_to_running(compaction_id, rs);
1220
8.95k
        }
1221
8.95k
        do {
1222
8.95k
            if (compaction_type != CompactionType::CUMULATIVE_COMPACTION) {
1223
0
                break;
1224
0
            }
1225
8.95k
            std::lock_guard lock(_cumu_compaction_delay_mtx);
1226
8.95k
            _cumu_compaction_thread_pool_used_threads++;
1227
8.95k
            if (config::large_cumu_compaction_task_min_thread_num > 1 &&
1228
8.95k
                _cumu_compaction_thread_pool->max_threads() >=
1229
8.95k
                        config::large_cumu_compaction_task_min_thread_num) {
1230
                // Determine if this is a small task based on configured thresholds
1231
8.95k
                is_large_task = (compaction->get_input_rowsets_bytes() >
1232
8.95k
                                         config::large_cumu_compaction_task_bytes_threshold ||
1233
8.95k
                                 compaction->get_input_num_rows() >
1234
8.95k
                                         config::large_cumu_compaction_task_row_num_threshold);
1235
                // Small task. No delay needed
1236
8.95k
                if (!is_large_task) {
1237
8.95k
                    _cumu_compaction_thread_pool_small_tasks_running++;
1238
8.95k
                    break;
1239
8.95k
                }
1240
                // Deal with large task
1241
0
                if (_should_delay_large_task()) {
1242
0
                    long now = duration_cast<milliseconds>(system_clock::now().time_since_epoch())
1243
0
                                       .count();
1244
                    // sleep 5s for this tablet
1245
0
                    tablet->set_last_cumu_compaction_failure_time(now);
1246
0
                    erase_executing_cumu_compaction();
1247
0
                    LOG_WARNING(
1248
0
                            "failed to do CloudCumulativeCompaction, cumu thread pool is "
1249
0
                            "intensive, delay large task.")
1250
0
                            .tag("tablet_id", tablet->tablet_id())
1251
0
                            .tag("input_rows", compaction->get_input_num_rows())
1252
0
                            .tag("input_rowsets_total_size", compaction->get_input_rowsets_bytes())
1253
0
                            .tag("config::large_cumu_compaction_task_bytes_threshold",
1254
0
                                 config::large_cumu_compaction_task_bytes_threshold)
1255
0
                            .tag("config::large_cumu_compaction_task_row_num_threshold",
1256
0
                                 config::large_cumu_compaction_task_row_num_threshold)
1257
0
                            .tag("remaining threads", _cumu_compaction_thread_pool_used_threads)
1258
0
                            .tag("small_tasks_running",
1259
0
                                 _cumu_compaction_thread_pool_small_tasks_running);
1260
0
                    return;
1261
0
                }
1262
0
            }
1263
8.95k
        } while (false);
1264
8.95k
        st = compaction->execute_compact();
1265
8.95k
        if (!st.ok()) {
1266
            // Error log has been output in `execute_compact`
1267
41
            long now = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
1268
41
            tablet->set_last_cumu_compaction_failure_time(now);
1269
41
        }
1270
8.95k
        erase_executing_cumu_compaction();
1271
8.95k
    });
1272
9.11k
    if (compaction_type == CompactionType::CUMU_BINLOG_COMPACTION) {
1273
0
        DorisMetrics::instance()->binlog_compaction_task_pending_total->set_value(
1274
0
                _binlog_compaction_thread_pool->get_queue_size());
1275
9.11k
    } else {
1276
9.11k
        DorisMetrics::instance()->cumulative_compaction_task_pending_total->set_value(
1277
9.11k
                _cumu_compaction_thread_pool->get_queue_size());
1278
9.11k
    }
1279
9.11k
    if (!st.ok()) {
1280
0
        tracker->remove_task(compaction_id);
1281
0
        erase_submitted_cumu_compaction();
1282
0
        return Status::InternalError("failed to submit cumu compaction, tablet_id={}",
1283
0
                                     tablet->tablet_id());
1284
0
    }
1285
9.11k
    return st;
1286
9.11k
}
1287
1288
Status CloudStorageEngine::_submit_binlog_compaction_task(const CloudTabletSPtr& tablet,
1289
0
                                                          int trigger_method) {
1290
0
    return _submit_cumulative_compaction_task(tablet, trigger_method,
1291
0
                                              CompactionType::CUMU_BINLOG_COMPACTION);
1292
0
}
1293
1294
Status CloudStorageEngine::_submit_full_compaction_task(const CloudTabletSPtr& tablet,
1295
58
                                                        int trigger_method) {
1296
58
    using namespace std::chrono;
1297
58
    {
1298
58
        std::lock_guard lock(_compaction_mtx);
1299
        // Take a placeholder for full compaction
1300
58
        auto [_, success] = _submitted_full_compactions.emplace(tablet->tablet_id(), nullptr);
1301
58
        if (!success) {
1302
0
            return Status::AlreadyExist(
1303
0
                    "other full compaction or base compaction is submitted, tablet_id={}",
1304
0
                    tablet->tablet_id());
1305
0
        }
1306
58
    }
1307
    //auto compaction = std::make_shared<CloudFullCompaction>(tablet);
1308
58
    auto compaction = std::make_shared<CloudFullCompaction>(*this, tablet);
1309
58
    auto st = compaction->prepare_compact();
1310
58
    if (!st.ok()) {
1311
0
        long now = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
1312
0
        tablet->set_last_full_compaction_failure_time(now);
1313
0
        std::lock_guard lock(_compaction_mtx);
1314
0
        _submitted_full_compactions.erase(tablet->tablet_id());
1315
0
        return st;
1316
0
    }
1317
    // Register task with CompactionTaskTracker as PENDING
1318
58
    auto* tracker = CompactionTaskTracker::instance();
1319
58
    int64_t compaction_id = compaction->compaction_id();
1320
58
    {
1321
58
        CompactionTaskInfo info;
1322
58
        info.compaction_id = compaction_id;
1323
58
        info.tablet_id = tablet->tablet_id();
1324
58
        info.table_id = tablet->table_id();
1325
58
        info.partition_id = tablet->partition_id();
1326
58
        info.compaction_type = CompactionProfileType::FULL;
1327
58
        info.status = CompactionTaskStatus::PENDING;
1328
58
        info.trigger_method = static_cast<TriggerMethod>(trigger_method);
1329
58
        info.scheduled_time_ms =
1330
58
                duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
1331
58
        info.backend_id = BackendOptions::get_backend_id();
1332
58
        info.compaction_score = tablet->get_real_compaction_score();
1333
58
        info.input_rowsets_count = compaction->input_rowsets_count();
1334
58
        info.input_row_num = compaction->input_row_num_value();
1335
58
        info.input_data_size = compaction->input_rowsets_data_size();
1336
58
        info.input_index_size = compaction->input_rowsets_index_size();
1337
58
        info.input_total_size = compaction->input_rowsets_total_size();
1338
58
        info.input_segments_num = compaction->input_segments_num_value();
1339
58
        info.input_version_range = compaction->input_version_range_str();
1340
58
        info.is_vertical = compaction->is_vertical();
1341
58
        tracker->register_task(std::move(info));
1342
58
    }
1343
58
    {
1344
58
        std::lock_guard lock(_compaction_mtx);
1345
58
        _submitted_full_compactions[tablet->tablet_id()] = compaction;
1346
58
    }
1347
58
    st = _base_compaction_thread_pool->submit_func([=, this, compaction = std::move(compaction)]() {
1348
58
        g_full_compaction_running_task_count << 1;
1349
58
        signal::tablet_id = tablet->tablet_id();
1350
58
        Defer defer {[&]() {
1351
            // Idempotent cleanup: remove task from tracker
1352
58
            CompactionTaskTracker::instance()->remove_task(compaction_id);
1353
58
            g_full_compaction_running_task_count << -1;
1354
58
            std::lock_guard lock(_compaction_mtx);
1355
58
            _submitted_full_compactions.erase(tablet->tablet_id());
1356
58
        }};
1357
58
        auto st = _request_tablet_global_compaction_lock(ReaderType::READER_FULL_COMPACTION, tablet,
1358
58
                                                         compaction);
1359
58
        if (!st.ok()) return;
1360
        // Update tracker to RUNNING after acquiring global lock
1361
58
        {
1362
58
            RunningStats rs;
1363
58
            rs.start_time_ms =
1364
58
                    duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
1365
58
            CompactionTaskTracker::instance()->update_to_running(compaction_id, rs);
1366
58
        }
1367
58
        st = compaction->execute_compact();
1368
58
        if (!st.ok()) {
1369
            // Error log has been output in `execute_compact`
1370
0
            long now = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
1371
0
            tablet->set_last_full_compaction_failure_time(now);
1372
0
        }
1373
58
        std::lock_guard lock(_compaction_mtx);
1374
58
        _executing_full_compactions.erase(tablet->tablet_id());
1375
58
    });
1376
58
    if (!st.ok()) {
1377
0
        tracker->remove_task(compaction_id);
1378
0
        std::lock_guard lock(_compaction_mtx);
1379
0
        _submitted_full_compactions.erase(tablet->tablet_id());
1380
0
        return Status::InternalError("failed to submit full compaction, tablet_id={}",
1381
0
                                     tablet->tablet_id());
1382
0
    }
1383
58
    return st;
1384
58
}
1385
1386
Status CloudStorageEngine::submit_compaction_task(const CloudTabletSPtr& tablet,
1387
                                                  CompactionType compaction_type,
1388
94.2k
                                                  int trigger_method) {
1389
94.2k
    DCHECK(compaction_type == CompactionType::CUMULATIVE_COMPACTION ||
1390
94.2k
           compaction_type == CompactionType::BASE_COMPACTION ||
1391
94.2k
           compaction_type == CompactionType::CUMU_BINLOG_COMPACTION ||
1392
94.2k
           compaction_type == CompactionType::FULL_COMPACTION);
1393
94.2k
    switch (compaction_type) {
1394
2.85k
    case CompactionType::BASE_COMPACTION:
1395
2.85k
        RETURN_IF_ERROR(_submit_base_compaction_task(tablet, trigger_method));
1396
73
        return Status::OK();
1397
91.3k
    case CompactionType::CUMULATIVE_COMPACTION:
1398
91.3k
        RETURN_IF_ERROR(_submit_cumulative_compaction_task(tablet, trigger_method));
1399
9.11k
        return Status::OK();
1400
0
    case CompactionType::CUMU_BINLOG_COMPACTION:
1401
0
        RETURN_IF_ERROR(_submit_binlog_compaction_task(tablet, trigger_method));
1402
0
        return Status::OK();
1403
58
    case CompactionType::FULL_COMPACTION:
1404
58
        RETURN_IF_ERROR(_submit_full_compaction_task(tablet, trigger_method));
1405
58
        return Status::OK();
1406
0
    default:
1407
0
        return Status::InternalError("unknown compaction type!");
1408
94.2k
    }
1409
94.2k
}
1410
1411
1
void CloudStorageEngine::_lease_compaction_thread_callback() {
1412
157
    while (!_stop_background_threads_latch.wait_for(
1413
157
            std::chrono::seconds(config::lease_compaction_interval_seconds))) {
1414
156
        std::vector<std::shared_ptr<CloudFullCompaction>> full_compactions;
1415
156
        std::vector<std::shared_ptr<CloudBaseCompaction>> base_compactions;
1416
156
        std::vector<std::shared_ptr<CloudCumulativeCompaction>> cumu_compactions;
1417
156
        std::vector<std::shared_ptr<CloudCompactionStopToken>> compation_stop_tokens;
1418
156
        std::vector<std::shared_ptr<CloudIndexChangeCompaction>> index_change_compations;
1419
156
        {
1420
156
            std::lock_guard lock(_compaction_mtx);
1421
156
            for (auto& [_, base] : _executing_base_compactions) {
1422
5
                if (base) { // `base` might be a nullptr placeholder
1423
5
                    base_compactions.push_back(base);
1424
5
                }
1425
5
            }
1426
156
            for (auto& [_, cumus] : _executing_cumu_compactions) {
1427
135
                for (auto& cumu : cumus) {
1428
135
                    cumu_compactions.push_back(cumu);
1429
135
                }
1430
135
            }
1431
156
            for (auto& [_, full] : _executing_full_compactions) {
1432
2
                if (full) {
1433
2
                    full_compactions.push_back(full);
1434
2
                }
1435
2
            }
1436
156
            for (auto& [_, stop_token] : _active_compaction_stop_tokens) {
1437
5
                if (stop_token) {
1438
4
                    compation_stop_tokens.push_back(stop_token);
1439
4
                }
1440
5
            }
1441
156
            for (auto& [_, index_change] : _submitted_index_change_cumu_compaction) {
1442
4
                if (index_change) {
1443
4
                    index_change_compations.push_back(index_change);
1444
4
                }
1445
4
            }
1446
156
            for (auto& [_, index_change] : _submitted_index_change_base_compaction) {
1447
0
                if (index_change) {
1448
0
                    index_change_compations.push_back(index_change);
1449
0
                }
1450
0
            }
1451
156
        }
1452
        // TODO(plat1ko): Support batch lease rpc
1453
156
        for (auto& stop_token : compation_stop_tokens) {
1454
4
            stop_token->do_lease();
1455
4
        }
1456
156
        for (auto& comp : full_compactions) {
1457
2
            comp->do_lease();
1458
2
        }
1459
156
        for (auto& comp : cumu_compactions) {
1460
135
            comp->do_lease();
1461
135
        }
1462
156
        for (auto& comp : base_compactions) {
1463
5
            comp->do_lease();
1464
5
        }
1465
156
        for (auto& comp : index_change_compations) {
1466
4
            comp->do_lease();
1467
4
        }
1468
156
    }
1469
1
}
1470
1471
1
void CloudStorageEngine::_check_tablet_delete_bitmap_score_callback() {
1472
1
    LOG(INFO) << "try to start check tablet delete bitmap score!";
1473
11
    while (!_stop_background_threads_latch.wait_for(
1474
11
            std::chrono::seconds(config::check_tablet_delete_bitmap_interval_seconds))) {
1475
10
        if (!config::enable_check_tablet_delete_bitmap_score) {
1476
0
            return;
1477
0
        }
1478
10
        uint64_t max_delete_bitmap_score = 0;
1479
10
        uint64_t max_base_rowset_delete_bitmap_score = 0;
1480
10
        tablet_mgr().get_topn_tablet_delete_bitmap_score(&max_delete_bitmap_score,
1481
10
                                                         &max_base_rowset_delete_bitmap_score);
1482
10
        _tablet_max_delete_bitmap_score_metrics->set_value(max_delete_bitmap_score);
1483
10
        _tablet_max_base_rowset_delete_bitmap_score_metrics->set_value(
1484
10
                max_base_rowset_delete_bitmap_score);
1485
10
    }
1486
1
}
1487
1488
0
Status CloudStorageEngine::get_compaction_status_json(std::string* result) {
1489
0
    rapidjson::Document root;
1490
0
    root.SetObject();
1491
1492
0
    std::lock_guard lock(_compaction_mtx);
1493
    // cumu
1494
0
    std::string_view cumu = "CumulativeCompaction";
1495
0
    rapidjson::Value cumu_key;
1496
0
    cumu_key.SetString(cumu.data(), cast_set<uint32_t>(cumu.length()), root.GetAllocator());
1497
0
    rapidjson::Document cumu_arr;
1498
0
    cumu_arr.SetArray();
1499
0
    for (auto& [tablet_id, v] : _submitted_cumu_compactions) {
1500
0
        for (int i = 0; i < v.size(); ++i) {
1501
0
            cumu_arr.PushBack(tablet_id, root.GetAllocator());
1502
0
        }
1503
0
    }
1504
0
    root.AddMember(cumu_key, cumu_arr, root.GetAllocator());
1505
    // base
1506
0
    std::string_view base = "BaseCompaction";
1507
0
    rapidjson::Value base_key;
1508
0
    base_key.SetString(base.data(), cast_set<uint32_t>(base.length()), root.GetAllocator());
1509
0
    rapidjson::Document base_arr;
1510
0
    base_arr.SetArray();
1511
0
    for (auto& [tablet_id, _] : _submitted_base_compactions) {
1512
0
        base_arr.PushBack(tablet_id, root.GetAllocator());
1513
0
    }
1514
0
    root.AddMember(base_key, base_arr, root.GetAllocator());
1515
1516
0
    rapidjson::StringBuffer strbuf;
1517
0
    rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(strbuf);
1518
0
    root.Accept(writer);
1519
0
    *result = std::string(strbuf.GetString());
1520
0
    return Status::OK();
1521
0
}
1522
1523
std::shared_ptr<CloudCumulativeCompactionPolicy> CloudStorageEngine::cumu_compaction_policy(
1524
109k
        std::string_view compaction_policy) {
1525
109k
    if (!_cumulative_compaction_policies.contains(compaction_policy)) {
1526
0
        return _cumulative_compaction_policies.at(CUMULATIVE_SIZE_BASED_POLICY);
1527
0
    }
1528
109k
    return _cumulative_compaction_policies.at(compaction_policy);
1529
109k
}
1530
1531
Status CloudStorageEngine::register_compaction_stop_token(CloudTabletSPtr tablet,
1532
1.15k
                                                          int64_t initiator) {
1533
1.15k
    {
1534
1.15k
        std::lock_guard lock(_compaction_mtx);
1535
1.15k
        auto [_, success] = _active_compaction_stop_tokens.emplace(tablet->tablet_id(), nullptr);
1536
1.15k
        if (!success) {
1537
0
            return Status::AlreadyExist("stop token already exists for tablet_id={}",
1538
0
                                        tablet->tablet_id());
1539
0
        }
1540
1.15k
    }
1541
1542
1.15k
    auto stop_token = std::make_shared<CloudCompactionStopToken>(*this, tablet, initiator);
1543
1.15k
    auto st = stop_token->do_register();
1544
1545
1.15k
    if (!st.ok()) {
1546
0
        std::lock_guard lock(_compaction_mtx);
1547
0
        _active_compaction_stop_tokens.erase(tablet->tablet_id());
1548
0
        return st;
1549
0
    }
1550
1551
1.15k
    {
1552
1.15k
        std::lock_guard lock(_compaction_mtx);
1553
1.15k
        _active_compaction_stop_tokens[tablet->tablet_id()] = stop_token;
1554
1.15k
    }
1555
1.15k
    LOG_INFO(
1556
1.15k
            "successfully register compaction stop token for tablet_id={}, "
1557
1.15k
            "delete_bitmap_lock_initiator={}",
1558
1.15k
            tablet->tablet_id(), initiator);
1559
1.15k
    return st;
1560
1.15k
}
1561
1562
1.15k
Status CloudStorageEngine::unregister_compaction_stop_token(CloudTabletSPtr tablet, bool clear_ms) {
1563
1.15k
    std::shared_ptr<CloudCompactionStopToken> stop_token;
1564
1.15k
    {
1565
1.15k
        std::lock_guard lock(_compaction_mtx);
1566
1.15k
        if (auto it = _active_compaction_stop_tokens.find(tablet->tablet_id());
1567
1.15k
            it != _active_compaction_stop_tokens.end()) {
1568
1.15k
            stop_token = it->second;
1569
1.15k
        } else {
1570
0
            return Status::NotFound("stop token not found for tablet_id={}", tablet->tablet_id());
1571
0
        }
1572
1.15k
        _active_compaction_stop_tokens.erase(tablet->tablet_id());
1573
1.15k
    }
1574
1.15k
    LOG_INFO("successfully unregister compaction stop token for tablet_id={}", tablet->tablet_id());
1575
1.15k
    if (stop_token && clear_ms) {
1576
0
        RETURN_IF_ERROR(stop_token->do_unregister());
1577
0
        LOG_INFO(
1578
0
                "successfully remove compaction stop token from MS for tablet_id={}, "
1579
0
                "delete_bitmap_lock_initiator={}",
1580
0
                tablet->tablet_id(), stop_token->initiator());
1581
0
    }
1582
1.15k
    return Status::OK();
1583
1.15k
}
1584
1585
1
Status CloudStorageEngine::_check_all_root_path_cluster_id() {
1586
    // Check if all root paths have the same cluster id
1587
1
    std::set<int32_t> cluster_ids;
1588
1
    for (const auto& path : _options.store_paths) {
1589
1
        auto cluster_id_path = fmt::format("{}/{}", path.path, CLUSTER_ID_PREFIX);
1590
1
        bool exists = false;
1591
1
        RETURN_IF_ERROR(io::global_local_filesystem()->exists(cluster_id_path, &exists));
1592
1
        if (exists) {
1593
0
            io::FileReaderSPtr reader;
1594
0
            RETURN_IF_ERROR(io::global_local_filesystem()->open_file(cluster_id_path, &reader));
1595
0
            size_t fsize = reader->size();
1596
0
            if (fsize > 0) {
1597
0
                std::string content;
1598
0
                content.resize(fsize, '\0');
1599
0
                size_t bytes_read = 0;
1600
0
                RETURN_IF_ERROR(reader->read_at(0, {content.data(), fsize}, &bytes_read));
1601
0
                DCHECK_EQ(fsize, bytes_read);
1602
0
                int32_t tmp_cluster_id = std::stoi(content);
1603
0
                cluster_ids.insert(tmp_cluster_id);
1604
0
            }
1605
0
        }
1606
1
    }
1607
1
    _effective_cluster_id = config::cluster_id;
1608
    // first init
1609
1
    if (cluster_ids.empty()) {
1610
        // not set configured cluster id
1611
1
        if (_effective_cluster_id == -1) {
1612
1
            return Status::OK();
1613
1
        } else {
1614
            // If no cluster id file exists, use the configured cluster id
1615
0
            return set_cluster_id(_effective_cluster_id);
1616
0
        }
1617
1
    }
1618
0
    if (cluster_ids.size() > 1) {
1619
0
        return Status::InternalError(
1620
0
                "All root paths must have the same cluster id, but you have "
1621
0
                "different cluster ids: {}",
1622
0
                fmt::join(cluster_ids, ", "));
1623
0
    }
1624
0
    if (_effective_cluster_id != -1 && !cluster_ids.empty() &&
1625
0
        *cluster_ids.begin() != _effective_cluster_id) {
1626
0
        return Status::Corruption(
1627
0
                "multiple cluster ids is not equal. config::cluster_id={}, "
1628
0
                "storage path cluster_id={}",
1629
0
                _effective_cluster_id, *cluster_ids.begin());
1630
0
    }
1631
0
    return Status::OK();
1632
0
}
1633
1634
1
Status CloudStorageEngine::set_cluster_id(int32_t cluster_id) {
1635
1
    std::lock_guard<std::mutex> l(_store_lock);
1636
1
    for (auto& path : _options.store_paths) {
1637
1
        auto cluster_id_path = fmt::format("{}/{}", path.path, CLUSTER_ID_PREFIX);
1638
1
        bool exists = false;
1639
1
        RETURN_IF_ERROR(io::global_local_filesystem()->exists(cluster_id_path, &exists));
1640
1
        if (!exists) {
1641
1
            io::FileWriterPtr file_writer;
1642
1
            RETURN_IF_ERROR(
1643
1
                    io::global_local_filesystem()->create_file(cluster_id_path, &file_writer));
1644
1
            RETURN_IF_ERROR(file_writer->append(std::to_string(cluster_id)));
1645
1
            RETURN_IF_ERROR(file_writer->close());
1646
1
        }
1647
1
    }
1648
1
    _effective_cluster_id = cluster_id;
1649
1
    return Status::OK();
1650
1
}
1651
1652
} // namespace doris