Coverage Report

Created: 2026-07-13 13:25

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