Coverage Report

Created: 2026-04-14 10:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/cloud/cloud_tablet.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_tablet.h"
19
20
#include <bvar/bvar.h>
21
#include <bvar/latency_recorder.h>
22
#include <gen_cpp/Types_types.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/rapidjson.h>
28
#include <rapidjson/stringbuffer.h>
29
30
#include <algorithm>
31
#include <atomic>
32
#include <chrono>
33
#include <cstdint>
34
#include <memory>
35
#include <ranges>
36
#include <ratio>
37
#include <shared_mutex>
38
#include <unordered_map>
39
#include <vector>
40
41
#include "cloud/cloud_meta_mgr.h"
42
#include "cloud/cloud_storage_engine.h"
43
#include "cloud/cloud_tablet_mgr.h"
44
#include "cloud/cloud_warm_up_manager.h"
45
#include "cloud/config.h"
46
#include "common/cast_set.h"
47
#include "common/config.h"
48
#include "common/logging.h"
49
#include "cpp/sync_point.h"
50
#include "io/cache/block_file_cache_downloader.h"
51
#include "io/cache/block_file_cache_factory.h"
52
#include "storage/compaction/compaction.h"
53
#include "storage/compaction/cumulative_compaction_time_series_policy.h"
54
#include "storage/index/inverted/inverted_index_desc.h"
55
#include "storage/olap_define.h"
56
#include "storage/rowset/beta_rowset.h"
57
#include "storage/rowset/rowset.h"
58
#include "storage/rowset/rowset_factory.h"
59
#include "storage/rowset/rowset_fwd.h"
60
#include "storage/rowset/rowset_writer.h"
61
#include "storage/storage_policy.h"
62
#include "storage/tablet/base_tablet.h"
63
#include "storage/tablet/tablet_schema.h"
64
#include "storage/txn/txn_manager.h"
65
#include "util/debug_points.h"
66
#include "util/stack_util.h"
67
68
namespace doris {
69
using namespace ErrorCode;
70
71
bvar::LatencyRecorder g_cu_compaction_get_delete_bitmap_lock_time_ms(
72
        "cu_compaction_get_delete_bitmap_lock_time_ms");
73
bvar::LatencyRecorder g_base_compaction_get_delete_bitmap_lock_time_ms(
74
        "base_compaction_get_delete_bitmap_lock_time_ms");
75
76
bvar::Adder<int64_t> g_unused_rowsets_count("unused_rowsets_count");
77
bvar::Adder<int64_t> g_unused_rowsets_bytes("unused_rowsets_bytes");
78
79
bvar::Adder<int64_t> g_capture_prefer_cache_count("capture_prefer_cache_count");
80
bvar::Adder<int64_t> g_capture_with_freshness_tolerance_count(
81
        "capture_with_freshness_tolerance_count");
82
bvar::Adder<int64_t> g_capture_with_freshness_tolerance_fallback_count(
83
        "capture_with_freshness_tolerance_fallback_count");
84
bvar::Adder<int64_t> g_rowset_warmup_state_missing_count("rowset_warmup_state_missing_count");
85
bvar::Window<bvar::Adder<int64_t>> g_capture_prefer_cache_count_window(
86
        "capture_prefer_cache_count_window", &g_capture_prefer_cache_count, 30);
87
bvar::Window<bvar::Adder<int64_t>> g_capture_with_freshness_tolerance_count_window(
88
        "capture_with_freshness_tolerance_count_window", &g_capture_with_freshness_tolerance_count,
89
        30);
90
bvar::Window<bvar::Adder<int64_t>> g_capture_with_freshness_tolerance_fallback_count_window(
91
        "capture_with_freshness_tolerance_fallback_count_window",
92
        &g_capture_with_freshness_tolerance_fallback_count, 30);
93
94
static constexpr int LOAD_INITIATOR_ID = -1;
95
96
bvar::Adder<uint64_t> g_file_cache_cloud_tablet_submitted_segment_size(
97
        "file_cache_cloud_tablet_submitted_segment_size");
98
bvar::Adder<uint64_t> g_file_cache_cloud_tablet_submitted_segment_num(
99
        "file_cache_cloud_tablet_submitted_segment_num");
100
bvar::Adder<uint64_t> g_file_cache_cloud_tablet_submitted_index_size(
101
        "file_cache_cloud_tablet_submitted_index_size");
102
bvar::Adder<uint64_t> g_file_cache_cloud_tablet_submitted_index_num(
103
        "file_cache_cloud_tablet_submitted_index_num");
104
bvar::Adder<uint64_t> g_file_cache_cloud_tablet_finished_segment_size(
105
        "file_cache_cloud_tablet_finished_segment_size");
106
bvar::Adder<uint64_t> g_file_cache_cloud_tablet_finished_segment_num(
107
        "file_cache_cloud_tablet_finished_segment_num");
108
bvar::Adder<uint64_t> g_file_cache_cloud_tablet_finished_index_size(
109
        "file_cache_cloud_tablet_finished_index_size");
110
bvar::Adder<uint64_t> g_file_cache_cloud_tablet_finished_index_num(
111
        "file_cache_cloud_tablet_finished_index_num");
112
113
bvar::Adder<uint64_t> g_file_cache_recycle_cached_data_segment_num(
114
        "file_cache_recycle_cached_data_segment_num");
115
bvar::Adder<uint64_t> g_file_cache_recycle_cached_data_segment_size(
116
        "file_cache_recycle_cached_data_segment_size");
117
bvar::Adder<uint64_t> g_file_cache_recycle_cached_data_index_num(
118
        "file_cache_recycle_cached_data_index_num");
119
120
bvar::Adder<uint64_t> g_file_cache_warm_up_segment_complete_num(
121
        "file_cache_warm_up_segment_complete_num");
122
bvar::Adder<uint64_t> g_file_cache_warm_up_segment_failed_num(
123
        "file_cache_warm_up_segment_failed_num");
124
bvar::Adder<uint64_t> g_file_cache_warm_up_inverted_idx_complete_num(
125
        "file_cache_warm_up_inverted_idx_complete_num");
126
bvar::Adder<uint64_t> g_file_cache_warm_up_inverted_idx_failed_num(
127
        "file_cache_warm_up_inverted_idx_failed_num");
128
bvar::Adder<uint64_t> g_file_cache_warm_up_rowset_complete_num(
129
        "file_cache_warm_up_rowset_complete_num");
130
bvar::Adder<uint64_t> g_file_cache_warm_up_rowset_triggered_by_job_num(
131
        "file_cache_warm_up_rowset_triggered_by_job_num");
132
bvar::Adder<uint64_t> g_file_cache_warm_up_rowset_triggered_by_sync_rowset_num(
133
        "file_cache_warm_up_rowset_triggered_by_sync_rowset_num");
134
bvar::Adder<uint64_t> g_file_cache_warm_up_rowset_triggered_by_event_driven_num(
135
        "file_cache_warm_up_rowset_triggered_by_event_driven_num");
136
bvar::LatencyRecorder g_file_cache_warm_up_rowset_all_segments_latency(
137
        "file_cache_warm_up_rowset_all_segments_latency");
138
139
CloudTablet::CloudTablet(CloudStorageEngine& engine, TabletMetaSharedPtr tablet_meta)
140
118k
        : BaseTablet(std::move(tablet_meta)), _engine(engine) {}
141
142
19.4k
CloudTablet::~CloudTablet() = default;
143
144
0
bool CloudTablet::exceed_version_limit(int32_t limit) {
145
0
    return _approximate_num_rowsets.load(std::memory_order_relaxed) > limit;
146
0
}
147
148
12.3k
std::string CloudTablet::tablet_path() const {
149
12.3k
    return "";
150
12.3k
}
151
152
Status CloudTablet::capture_rs_readers(const Version& spec_version,
153
                                       std::vector<RowSetSplits>* rs_splits,
154
5.35k
                                       const CaptureRowsetOps& opts) {
155
5.35k
    DBUG_EXECUTE_IF("CloudTablet.capture_rs_readers.return.e-230", {
156
5.35k
        LOG_WARNING("CloudTablet.capture_rs_readers.return e-230").tag("tablet_id", tablet_id());
157
5.35k
        return Status::Error<false>(-230, "injected error");
158
5.35k
    });
159
5.35k
    std::shared_lock rlock(_meta_lock);
160
5.35k
    *rs_splits = DORIS_TRY(capture_rs_readers_unlocked(
161
5.35k
            spec_version, CaptureRowsetOps {.skip_missing_versions = opts.skip_missing_versions}));
162
5.35k
    return Status::OK();
163
5.35k
}
164
165
[[nodiscard]] Result<std::vector<Version>> CloudTablet::capture_consistent_versions_unlocked(
166
1.01M
        const Version& version_range, const CaptureRowsetOps& options) const {
167
1.01M
    if (options.query_freshness_tolerance_ms > 0) {
168
28
        return capture_versions_with_freshness_tolerance(version_range, options);
169
1.01M
    } else if (options.enable_prefer_cached_rowset && !enable_unique_key_merge_on_write()) {
170
15
        return capture_versions_prefer_cache(version_range);
171
15
    }
172
1.01M
    return BaseTablet::capture_consistent_versions_unlocked(version_range, options);
173
1.01M
}
174
175
Result<std::vector<Version>> CloudTablet::capture_versions_prefer_cache(
176
15
        const Version& spec_version) const {
177
15
    g_capture_prefer_cache_count << 1;
178
15
    Versions version_path;
179
15
    std::shared_lock rlock(_meta_lock);
180
15
    auto st = _timestamped_version_tracker.capture_consistent_versions_prefer_cache(
181
15
            spec_version, version_path,
182
134
            [&](int64_t start, int64_t end) { return rowset_is_warmed_up_unlocked(start, end); });
183
15
    if (!st.ok()) {
184
0
        return ResultError(st);
185
0
    }
186
15
    int64_t path_max_version = version_path.back().second;
187
15
    VLOG_DEBUG << fmt::format(
188
0
            "[verbose] CloudTablet::capture_versions_prefer_cache, capture path: {}, "
189
0
            "tablet_id={}, spec_version={}, path_max_version={}",
190
0
            fmt::join(version_path | std::views::transform([](const auto& version) {
191
0
                          return fmt::format("{}", version.to_string());
192
0
                      }),
193
0
                      ", "),
194
0
            tablet_id(), spec_version.to_string(), path_max_version);
195
15
    return version_path;
196
15
}
197
198
356
bool CloudTablet::rowset_is_warmed_up_unlocked(int64_t start_version, int64_t end_version) const {
199
356
    if (start_version > end_version) {
200
0
        return false;
201
0
    }
202
356
    Version version {start_version, end_version};
203
356
    auto it = _rs_version_map.find(version);
204
356
    if (it == _rs_version_map.end()) {
205
78
        it = _stale_rs_version_map.find(version);
206
78
        if (it == _stale_rs_version_map.end()) {
207
0
            LOG_WARNING(
208
0
                    "fail to find Rowset in rs_version or stale_rs_version for version. "
209
0
                    "tablet={}, version={}",
210
0
                    tablet_id(), version.to_string());
211
0
            return false;
212
0
        }
213
78
    }
214
356
    const auto& rs = it->second;
215
356
    if (rs->visible_timestamp() < _engine.startup_timepoint()) {
216
        // We only care about rowsets that are created after startup time point. For other rowsets,
217
        // we assume they are warmed up.
218
19
        return true;
219
19
    }
220
337
    return is_rowset_warmed_up(rs->rowset_id());
221
356
};
222
223
Result<std::vector<Version>> CloudTablet::capture_versions_with_freshness_tolerance(
224
28
        const Version& spec_version, const CaptureRowsetOps& options) const {
225
28
    g_capture_with_freshness_tolerance_count << 1;
226
28
    using namespace std::chrono;
227
28
    auto query_freshness_tolerance_ms = options.query_freshness_tolerance_ms;
228
28
    auto freshness_limit_tp = system_clock::now() - milliseconds(query_freshness_tolerance_ms);
229
    // find a version path where every edge(rowset) has been warmuped
230
28
    Versions version_path;
231
28
    std::shared_lock rlock(_meta_lock);
232
28
    if (enable_unique_key_merge_on_write()) {
233
        // For merge-on-write table, newly generated delete bitmap marks will be on the rowsets which are in newest layout.
234
        // So we can ony capture rowsets which are in newest data layout. Otherwise there may be data correctness issue.
235
14
        RETURN_IF_ERROR_RESULT(
236
14
                _timestamped_version_tracker.capture_consistent_versions_with_validator_mow(
237
14
                        spec_version, version_path, [&](int64_t start, int64_t end) {
238
14
                            return rowset_is_warmed_up_unlocked(start, end);
239
14
                        }));
240
14
    } else {
241
14
        RETURN_IF_ERROR_RESULT(
242
14
                _timestamped_version_tracker.capture_consistent_versions_with_validator(
243
14
                        spec_version, version_path, [&](int64_t start, int64_t end) {
244
14
                            return rowset_is_warmed_up_unlocked(start, end);
245
14
                        }));
246
14
    }
247
28
    int64_t path_max_version = version_path.back().second;
248
    // use std::views::concat after C++26
249
501
    auto check_fn = [this, path_max_version, freshness_limit_tp](const auto& rs_meta) {
250
501
        return _check_rowset_should_be_visible_but_not_warmed_up(rs_meta, path_max_version,
251
501
                                                                 freshness_limit_tp);
252
501
    };
253
28
    bool should_fallback =
254
28
            std::ranges::any_of(std::views::values(_tablet_meta->all_rs_metas()), check_fn) ||
255
28
            std::ranges::any_of(std::views::values(_tablet_meta->all_stale_rs_metas()), check_fn);
256
28
    if (should_fallback) {
257
5
        rlock.unlock();
258
5
        g_capture_with_freshness_tolerance_fallback_count << 1;
259
        // if there exists a rowset which satisfies freshness tolerance and its start version is larger than the path max version
260
        // but has not been warmuped up yet, fallback to capture rowsets as usual
261
5
        return BaseTablet::capture_consistent_versions_unlocked(spec_version, options);
262
5
    }
263
23
    VLOG_DEBUG << fmt::format(
264
0
            "[verbose] CloudTablet::capture_versions_with_freshness_tolerance, capture path: {}, "
265
0
            "tablet_id={}, spec_version={}, path_max_version={}",
266
0
            fmt::join(version_path | std::views::transform([](const auto& version) {
267
0
                          return fmt::format("{}", version.to_string());
268
0
                      }),
269
0
                      ", "),
270
0
            tablet_id(), spec_version.to_string(), path_max_version);
271
23
    return version_path;
272
28
}
273
274
// There are only two tablet_states RUNNING and NOT_READY in cloud mode
275
// This function will erase the tablet from `CloudTabletMgr` when it can't find this tablet in MS.
276
1.08M
Status CloudTablet::sync_rowsets(const SyncOptions& options, SyncRowsetStats* stats) {
277
1.08M
    RETURN_IF_ERROR(sync_if_not_running(stats));
278
279
1.08M
    if (options.query_version > 0) {
280
1.00M
        auto lock_start = std::chrono::steady_clock::now();
281
1.00M
        std::shared_lock rlock(_meta_lock);
282
1.00M
        if (stats) {
283
1.00M
            stats->meta_lock_wait_ns += std::chrono::duration_cast<std::chrono::nanoseconds>(
284
1.00M
                                                std::chrono::steady_clock::now() - lock_start)
285
1.00M
                                                .count();
286
1.00M
        }
287
1.01M
        if (_max_version >= options.query_version) {
288
1.01M
            return Status::OK();
289
1.01M
        }
290
1.00M
    }
291
292
    // serially execute sync to reduce unnecessary network overhead
293
70.3k
    auto sync_lock_start = std::chrono::steady_clock::now();
294
70.3k
    std::unique_lock lock(_sync_meta_lock);
295
70.3k
    if (stats) {
296
1.75k
        stats->sync_meta_lock_wait_ns += std::chrono::duration_cast<std::chrono::nanoseconds>(
297
1.75k
                                                 std::chrono::steady_clock::now() - sync_lock_start)
298
1.75k
                                                 .count();
299
1.75k
    }
300
70.3k
    if (options.query_version > 0) {
301
1.76k
        auto lock_start = std::chrono::steady_clock::now();
302
1.76k
        std::shared_lock rlock(_meta_lock);
303
1.76k
        if (stats) {
304
1.75k
            stats->meta_lock_wait_ns += std::chrono::duration_cast<std::chrono::nanoseconds>(
305
1.75k
                                                std::chrono::steady_clock::now() - lock_start)
306
1.75k
                                                .count();
307
1.75k
        }
308
1.76k
        if (_max_version >= options.query_version) {
309
111
            return Status::OK();
310
111
        }
311
1.76k
    }
312
313
70.2k
    auto st = _engine.meta_mgr().sync_tablet_rowsets_unlocked(this, lock, options, stats);
314
70.2k
    if (st.is<ErrorCode::NOT_FOUND>()) {
315
0
        clear_cache();
316
0
    }
317
318
70.2k
    return st;
319
70.3k
}
320
321
// Sync tablet meta and all rowset meta if not running.
322
// This could happen when BE didn't finish schema change job and another BE committed this schema change job.
323
// It should be a quite rare situation.
324
1.08M
Status CloudTablet::sync_if_not_running(SyncRowsetStats* stats) {
325
1.08M
    if (tablet_state() == TABLET_RUNNING) {
326
1.08M
        return Status::OK();
327
1.08M
    }
328
329
    // Serially execute sync to reduce unnecessary network overhead
330
2.34k
    auto sync_lock_start = std::chrono::steady_clock::now();
331
2.34k
    std::unique_lock lock(_sync_meta_lock);
332
2.34k
    if (stats) {
333
0
        stats->sync_meta_lock_wait_ns += std::chrono::duration_cast<std::chrono::nanoseconds>(
334
0
                                                 std::chrono::steady_clock::now() - sync_lock_start)
335
0
                                                 .count();
336
0
    }
337
338
2.34k
    {
339
2.34k
        auto lock_start = std::chrono::steady_clock::now();
340
2.34k
        std::shared_lock rlock(_meta_lock);
341
2.34k
        if (stats) {
342
0
            stats->meta_lock_wait_ns += std::chrono::duration_cast<std::chrono::nanoseconds>(
343
0
                                                std::chrono::steady_clock::now() - lock_start)
344
0
                                                .count();
345
0
        }
346
2.34k
        if (tablet_state() == TABLET_RUNNING) {
347
0
            return Status::OK();
348
0
        }
349
2.34k
    }
350
351
2.34k
    TabletMetaSharedPtr tablet_meta;
352
2.34k
    auto st = _engine.meta_mgr().get_tablet_meta(tablet_id(), &tablet_meta);
353
2.34k
    if (!st.ok()) {
354
0
        if (st.is<ErrorCode::NOT_FOUND>()) {
355
0
            clear_cache();
356
0
        }
357
0
        return st;
358
0
    }
359
360
10.8k
    if (tablet_meta->tablet_state() != TABLET_RUNNING) [[unlikely]] {
361
        // MoW may go to here when load while schema change
362
10.8k
        return Status::OK();
363
10.8k
    }
364
365
18.4E
    TimestampedVersionTracker empty_tracker;
366
18.4E
    {
367
18.4E
        auto lock_start = std::chrono::steady_clock::now();
368
18.4E
        std::lock_guard wlock(_meta_lock);
369
18.4E
        if (stats) {
370
0
            stats->meta_lock_wait_ns += std::chrono::duration_cast<std::chrono::nanoseconds>(
371
0
                                                std::chrono::steady_clock::now() - lock_start)
372
0
                                                .count();
373
0
        }
374
18.4E
        RETURN_IF_ERROR(set_tablet_state(TABLET_RUNNING));
375
18.4E
        _rs_version_map.clear();
376
18.4E
        _stale_rs_version_map.clear();
377
18.4E
        std::swap(_timestamped_version_tracker, empty_tracker);
378
18.4E
        _tablet_meta->clear_rowsets();
379
18.4E
        _tablet_meta->clear_stale_rowset();
380
18.4E
        _max_version = -1;
381
18.4E
    }
382
383
0
    st = _engine.meta_mgr().sync_tablet_rowsets_unlocked(this, lock, {}, stats);
384
18.4E
    if (st.is<ErrorCode::NOT_FOUND>()) {
385
0
        clear_cache();
386
0
    }
387
18.4E
    return st;
388
18.4E
}
389
390
void CloudTablet::add_rowsets(std::vector<RowsetSharedPtr> to_add, bool version_overlap,
391
                              std::unique_lock<std::shared_mutex>& meta_lock,
392
310k
                              bool warmup_delta_data) {
393
310k
    if (to_add.empty()) {
394
5.67k
        return;
395
5.67k
    }
396
397
18.4E
    VLOG_DEBUG << "add_rowsets tablet_id=" << tablet_id() << " stack: " << get_stack_trace();
398
399
305k
    if (!version_overlap) {
400
298k
        _add_rowsets_directly(to_add, warmup_delta_data);
401
298k
        return;
402
298k
    }
403
404
    // Filter out existed rowsets
405
6.90k
    auto remove_it =
406
12.4k
            std::remove_if(to_add.begin(), to_add.end(), [this](const RowsetSharedPtr& rs) {
407
12.4k
                if (auto find_it = _rs_version_map.find(rs->version());
408
12.4k
                    find_it == _rs_version_map.end()) {
409
11.5k
                    return false;
410
11.5k
                } else if (find_it->second->rowset_id() == rs->rowset_id()) {
411
40
                    return true; // Same rowset
412
40
                }
413
414
                // If version of rowset in `to_add` is equal to rowset in tablet but rowset_id is not equal,
415
                // replace existed rowset with `to_add` rowset. This may occur when:
416
                //  1. schema change converts rowsets which have been double written to new tablet
417
                //  2. cumu compaction picks single overlapping input rowset to perform compaction
418
419
                // add existed rowset to unused_rowsets to remove delete bitmap and recycle cached data
420
421
821
                std::vector<RowsetSharedPtr> unused_rowsets;
422
821
                if (auto find_it = _rs_version_map.find(rs->version());
423
821
                    find_it != _rs_version_map.end()) {
424
821
                    if (find_it->second->rowset_id() == rs->rowset_id()) {
425
0
                        LOG(WARNING) << "tablet_id=" << tablet_id()
426
0
                                     << ", rowset_id=" << rs->rowset_id().to_string()
427
0
                                     << ", existed rowset_id="
428
0
                                     << find_it->second->rowset_id().to_string();
429
0
                        DCHECK(find_it->second->rowset_id() != rs->rowset_id())
430
0
                                << "tablet_id=" << tablet_id()
431
0
                                << ", rowset_id=" << rs->rowset_id().to_string()
432
0
                                << ", existed rowset_id="
433
0
                                << find_it->second->rowset_id().to_string();
434
0
                    }
435
821
                    unused_rowsets.push_back(find_it->second);
436
821
                }
437
821
                add_unused_rowsets(unused_rowsets);
438
439
821
                _tablet_meta->delete_rs_meta_by_version(rs->version(), nullptr);
440
821
                _rs_version_map[rs->version()] = rs;
441
821
                _tablet_meta->add_rowsets_unchecked({rs});
442
821
                update_base_size(*rs);
443
821
                return true;
444
12.4k
            });
445
446
6.90k
    to_add.erase(remove_it, to_add.end());
447
448
    // delete rowsets with overlapped version
449
6.90k
    std::vector<RowsetSharedPtr> to_add_directly;
450
11.5k
    for (auto& to_add_rs : to_add) {
451
        // delete rowsets with overlapped version
452
11.5k
        std::vector<RowsetSharedPtr> to_delete;
453
11.5k
        Version to_add_v = to_add_rs->version();
454
        // if start_version  > max_version, we can skip checking overlap here.
455
11.5k
        if (to_add_v.first > _max_version) {
456
            // if start_version  > max_version, we can skip checking overlap here.
457
11.5k
            to_add_directly.push_back(to_add_rs);
458
11.5k
        } else {
459
56
            to_add_directly.push_back(to_add_rs);
460
273
            for (auto& [v, rs] : _rs_version_map) {
461
273
                if (to_add_v.contains(v)) {
462
0
                    to_delete.push_back(rs);
463
0
                }
464
273
            }
465
56
            delete_rowsets(to_delete, meta_lock);
466
56
        }
467
11.5k
    }
468
469
6.90k
    _add_rowsets_directly(to_add_directly, warmup_delta_data);
470
6.90k
}
471
472
void CloudTablet::delete_rowsets(const std::vector<RowsetSharedPtr>& to_delete,
473
6.83k
                                 std::unique_lock<std::shared_mutex>&) {
474
6.83k
    if (to_delete.empty()) {
475
58
        return;
476
58
    }
477
6.77k
    std::vector<RowsetMetaSharedPtr> rs_metas;
478
6.77k
    rs_metas.reserve(to_delete.size());
479
6.77k
    int64_t now = ::time(nullptr);
480
58.2k
    for (auto&& rs : to_delete) {
481
58.2k
        rs->rowset_meta()->set_stale_at(now);
482
58.2k
        rs_metas.push_back(rs->rowset_meta());
483
58.2k
        _stale_rs_version_map[rs->version()] = rs;
484
58.2k
    }
485
6.77k
    _timestamped_version_tracker.add_stale_path_version(rs_metas);
486
58.1k
    for (auto&& rs : to_delete) {
487
58.1k
        _rs_version_map.erase(rs->version());
488
58.1k
    }
489
490
6.77k
    _tablet_meta->modify_rs_metas({}, rs_metas, false);
491
6.77k
}
492
493
void CloudTablet::delete_rowsets_for_schema_change(const std::vector<RowsetSharedPtr>& to_delete,
494
11
                                                   std::unique_lock<std::shared_mutex>&) {
495
11
    if (to_delete.empty()) {
496
1
        return;
497
1
    }
498
10
    std::vector<RowsetMetaSharedPtr> rs_metas;
499
10
    rs_metas.reserve(to_delete.size());
500
14
    for (auto&& rs : to_delete) {
501
14
        rs_metas.push_back(rs->rowset_meta());
502
14
        _rs_version_map.erase(rs->version());
503
        // Remove edge from version graph so that the greedy capture algorithm
504
        // won't prefer the wider stale compaction rowset over individual SC
505
        // output rowsets (e.g. [818-822] vs [818],[819],...,[822]).
506
14
        _timestamped_version_tracker.delete_version(rs->version());
507
14
    }
508
509
    // Use same_version=true to skip adding to _stale_rs_metas. Do NOT use the
510
    // stale tracking mechanism (_stale_rs_version_map / _stale_version_path_map)
511
    // because SC output will create new rowsets with identical version ranges;
512
    // a later compaction could put those into stale as well, causing two stale
513
    // paths to reference the same version key -- when one path is cleaned first,
514
    // the other hits a DCHECK(false) in delete_expired_stale_rowsets().
515
10
    _tablet_meta->modify_rs_metas({}, rs_metas, true);
516
517
    // Schedule for direct cache cleanup. MS has already recycled these rowsets.
518
10
    add_unused_rowsets(to_delete);
519
10
}
520
521
4.68k
uint64_t CloudTablet::delete_expired_stale_rowsets() {
522
4.68k
    if (config::enable_mow_verbose_log) {
523
0
        LOG_INFO("begin delete_expired_stale_rowset for tablet={}", tablet_id());
524
0
    }
525
4.68k
    std::vector<RowsetSharedPtr> expired_rowsets;
526
    // ATTN: trick, Use stale_rowsets to temporarily increase the reference count of the rowset shared pointer in _stale_rs_version_map so that in the recycle_cached_data function, it checks if the reference count is 2.
527
4.68k
    std::vector<std::pair<Version, std::vector<RowsetSharedPtr>>> deleted_stale_rowsets;
528
4.68k
    int64_t expired_stale_sweep_endtime =
529
4.68k
            ::time(nullptr) - config::tablet_rowset_stale_sweep_time_sec;
530
4.68k
    {
531
4.68k
        std::unique_lock wlock(_meta_lock);
532
533
4.68k
        std::vector<int64_t> path_ids;
534
        // capture the path version to delete
535
4.68k
        _timestamped_version_tracker.capture_expired_paths(expired_stale_sweep_endtime, &path_ids);
536
537
4.68k
        if (path_ids.empty()) {
538
2.36k
            return 0;
539
2.36k
        }
540
541
6.06k
        for (int64_t path_id : path_ids) {
542
6.06k
            int64_t start_version = -1;
543
6.06k
            int64_t end_version = -1;
544
6.06k
            std::vector<RowsetSharedPtr> stale_rowsets;
545
            // delete stale versions in version graph
546
6.06k
            auto version_path = _timestamped_version_tracker.fetch_and_delete_path_by_id(path_id);
547
53.2k
            for (auto& v_ts : version_path->timestamped_versions()) {
548
53.2k
                auto rs_it = _stale_rs_version_map.find(v_ts->version());
549
53.2k
                if (rs_it != _stale_rs_version_map.end()) {
550
53.2k
                    expired_rowsets.push_back(rs_it->second);
551
53.2k
                    stale_rowsets.push_back(rs_it->second);
552
53.2k
                    VLOG_DEBUG << "erase stale rowset, tablet_id=" << tablet_id()
553
0
                               << " rowset_id=" << rs_it->second->rowset_id().to_string()
554
0
                               << " version=" << rs_it->first.to_string();
555
53.2k
                    _stale_rs_version_map.erase(rs_it);
556
53.2k
                } else {
557
0
                    LOG(WARNING) << "cannot find stale rowset " << v_ts->version() << " in tablet "
558
0
                                 << tablet_id();
559
                    // clang-format off
560
0
                    DCHECK(false) << [this, &wlock]() { wlock.unlock(); std::string json; get_compaction_status(&json); return json; }();
561
                    // clang-format on
562
0
                }
563
53.2k
                if (start_version < 0) {
564
6.06k
                    start_version = v_ts->version().first;
565
6.06k
                }
566
53.2k
                end_version = v_ts->version().second;
567
53.2k
                _tablet_meta->delete_stale_rs_meta_by_version(v_ts->version());
568
53.2k
            }
569
6.06k
            Version version(start_version, end_version);
570
6.06k
            if (!stale_rowsets.empty()) {
571
6.06k
                deleted_stale_rowsets.emplace_back(version, std::move(stale_rowsets));
572
6.06k
            }
573
6.06k
        }
574
2.32k
        _reconstruct_version_tracker_if_necessary();
575
2.32k
    }
576
577
    // if the rowset is not used by any query, we can recycle its cached data early.
578
0
    auto recycled_rowsets = recycle_cached_data(expired_rowsets);
579
2.32k
    if (!recycled_rowsets.empty()) {
580
2.31k
        auto& manager = ExecEnv::GetInstance()->storage_engine().to_cloud().cloud_warm_up_manager();
581
2.31k
        manager.recycle_cache(tablet_id(), recycled_rowsets);
582
2.31k
    }
583
2.32k
    if (config::enable_mow_verbose_log) {
584
0
        LOG_INFO("finish delete_expired_stale_rowset for tablet={}", tablet_id());
585
0
    }
586
587
2.32k
    add_unused_rowsets(expired_rowsets);
588
2.32k
    if (config::enable_agg_and_remove_pre_rowsets_delete_bitmap && keys_type() == UNIQUE_KEYS &&
589
2.32k
        enable_unique_key_merge_on_write() && !deleted_stale_rowsets.empty()) {
590
        // agg delete bitmap for pre rowsets; record unused delete bitmap key ranges
591
582
        OlapStopWatch watch;
592
3.14k
        for (const auto& [version, unused_rowsets] : deleted_stale_rowsets) {
593
            // agg delete bitmap for pre rowset
594
3.14k
            DeleteBitmapKeyRanges remove_delete_bitmap_key_ranges;
595
3.14k
            agg_delete_bitmap_for_stale_rowsets(version, remove_delete_bitmap_key_ranges);
596
            // add remove delete bitmap
597
3.14k
            if (!remove_delete_bitmap_key_ranges.empty()) {
598
757
                std::vector<RowsetId> rowset_ids;
599
9.30k
                for (const auto& rs : unused_rowsets) {
600
9.30k
                    rowset_ids.push_back(rs->rowset_id());
601
9.30k
                }
602
757
                std::lock_guard<std::mutex> lock(_gc_mutex);
603
757
                _unused_delete_bitmap.push_back(
604
757
                        std::make_pair(rowset_ids, remove_delete_bitmap_key_ranges));
605
757
            }
606
3.14k
        }
607
582
        LOG(INFO) << "agg pre rowsets delete bitmap. tablet_id=" << tablet_id()
608
582
                  << ", size=" << deleted_stale_rowsets.size()
609
582
                  << ", cost(us)=" << watch.get_elapse_time_us();
610
582
    }
611
2.32k
    return expired_rowsets.size();
612
4.68k
}
613
614
749k
bool CloudTablet::need_remove_unused_rowsets() {
615
749k
    std::lock_guard<std::mutex> lock(_gc_mutex);
616
749k
    return !_unused_rowsets.empty() || !_unused_delete_bitmap.empty();
617
749k
}
618
619
3.15k
void CloudTablet::add_unused_rowsets(const std::vector<RowsetSharedPtr>& rowsets) {
620
3.15k
    std::lock_guard<std::mutex> lock(_gc_mutex);
621
54.0k
    for (const auto& rowset : rowsets) {
622
54.0k
        _unused_rowsets[rowset->rowset_id()] = rowset;
623
54.0k
        g_unused_rowsets_bytes << rowset->total_disk_size();
624
54.0k
    }
625
3.15k
    g_unused_rowsets_count << rowsets.size();
626
3.15k
}
627
628
2.50k
void CloudTablet::remove_unused_rowsets() {
629
2.50k
    std::vector<std::shared_ptr<Rowset>> removed_rowsets;
630
2.50k
    int64_t removed_delete_bitmap_num = 0;
631
2.50k
    OlapStopWatch watch;
632
2.50k
    {
633
2.50k
        std::lock_guard<std::mutex> lock(_gc_mutex);
634
        // 1. remove unused rowsets's cache data and delete bitmap
635
56.6k
        for (auto it = _unused_rowsets.begin(); it != _unused_rowsets.end();) {
636
54.1k
            auto& rs = it->second;
637
54.1k
            if (rs.use_count() > 1) {
638
78
                LOG(WARNING) << "tablet_id:" << tablet_id() << " rowset: " << rs->rowset_id()
639
78
                             << " has " << rs.use_count() << " references, it cannot be removed";
640
78
                ++it;
641
78
                continue;
642
78
            }
643
54.0k
            tablet_meta()->remove_rowset_delete_bitmap(rs->rowset_id(), rs->version());
644
54.0k
            _rowset_warm_up_states.erase(rs->rowset_id());
645
54.0k
            rs->clear_cache();
646
54.0k
            g_unused_rowsets_count << -1;
647
54.0k
            g_unused_rowsets_bytes << -rs->total_disk_size();
648
54.0k
            removed_rowsets.push_back(std::move(rs));
649
54.0k
            it = _unused_rowsets.erase(it);
650
54.0k
        }
651
2.50k
    }
652
653
2.50k
    {
654
2.50k
        std::vector<RecycledRowsets> recycled_rowsets;
655
656
54.0k
        for (auto& rs : removed_rowsets) {
657
54.0k
            auto index_names = rs->get_index_file_names();
658
54.0k
            recycled_rowsets.emplace_back(rs->rowset_id(), rs->num_segments(), index_names);
659
54.0k
            int64_t segment_size_sum = 0;
660
76.4k
            for (int32_t i = 0; i < rs->num_segments(); i++) {
661
22.4k
                segment_size_sum += rs->rowset_meta()->segment_file_size(i);
662
22.4k
            }
663
54.0k
            g_file_cache_recycle_cached_data_segment_num << rs->num_segments();
664
54.0k
            g_file_cache_recycle_cached_data_segment_size << segment_size_sum;
665
54.0k
            g_file_cache_recycle_cached_data_index_num << index_names.size();
666
54.0k
        }
667
668
2.50k
        if (recycled_rowsets.size() > 0) {
669
2.49k
            auto& manager =
670
2.49k
                    ExecEnv::GetInstance()->storage_engine().to_cloud().cloud_warm_up_manager();
671
2.49k
            manager.recycle_cache(tablet_id(), recycled_rowsets);
672
2.49k
        }
673
2.50k
    }
674
675
2.50k
    {
676
2.50k
        std::lock_guard<std::mutex> lock(_gc_mutex);
677
        // 2. remove delete bitmap of pre rowsets
678
3.26k
        for (auto it = _unused_delete_bitmap.begin(); it != _unused_delete_bitmap.end();) {
679
757
            auto& rowset_ids = std::get<0>(*it);
680
757
            bool find_unused_rowset = false;
681
9.30k
            for (const auto& rowset_id : rowset_ids) {
682
9.30k
                if (_unused_rowsets.find(rowset_id) != _unused_rowsets.end()) {
683
0
                    LOG(INFO) << "can not remove pre rowset delete bitmap because rowset is in use"
684
0
                              << ", tablet_id=" << tablet_id() << ", rowset_id=" << rowset_id;
685
0
                    find_unused_rowset = true;
686
0
                    break;
687
0
                }
688
9.30k
            }
689
757
            if (find_unused_rowset) {
690
0
                ++it;
691
0
                continue;
692
0
            }
693
757
            auto& key_ranges = std::get<1>(*it);
694
757
            tablet_meta()->delete_bitmap().remove(key_ranges);
695
757
            it = _unused_delete_bitmap.erase(it);
696
757
            removed_delete_bitmap_num++;
697
            // TODO(kaijie): recycle cache for unused delete bitmap
698
757
        }
699
2.50k
    }
700
701
2.50k
    LOG(INFO) << "tablet_id=" << tablet_id() << ", unused_rowset size=" << _unused_rowsets.size()
702
2.50k
              << ", unused_delete_bitmap size=" << _unused_delete_bitmap.size()
703
2.50k
              << ", removed_rowsets_num=" << removed_rowsets.size()
704
2.50k
              << ", removed_delete_bitmap_num=" << removed_delete_bitmap_num
705
2.50k
              << ", cost(us)=" << watch.get_elapse_time_us();
706
2.50k
}
707
708
322k
void CloudTablet::update_base_size(const Rowset& rs) {
709
    // Define base rowset as the rowset of version [2-x]
710
322k
    if (rs.start_version() == 2) {
711
106k
        _base_size = rs.total_disk_size();
712
106k
    }
713
322k
}
714
715
583
void CloudTablet::clear_cache() {
716
583
    auto recycled_rowsets = CloudTablet::recycle_cached_data(get_snapshot_rowset(true));
717
583
    if (!recycled_rowsets.empty()) {
718
567
        auto& manager = ExecEnv::GetInstance()->storage_engine().to_cloud().cloud_warm_up_manager();
719
567
        manager.recycle_cache(tablet_id(), recycled_rowsets);
720
567
    }
721
583
    _engine.tablet_mgr().erase_tablet(tablet_id());
722
583
}
723
724
std::vector<RecycledRowsets> CloudTablet::recycle_cached_data(
725
2.90k
        const std::vector<RowsetSharedPtr>& rowsets) {
726
2.90k
    std::vector<RecycledRowsets> recycled_rowsets;
727
59.4k
    for (const auto& rs : rowsets) {
728
        // rowsets and tablet._rs_version_map each hold a rowset shared_ptr, so at this point, the reference count of the shared_ptr is at least 2.
729
59.4k
        if (rs.use_count() > 2) {
730
4.77k
            LOG(WARNING) << "Rowset " << rs->rowset_id().to_string() << " has " << rs.use_count()
731
4.77k
                         << " references. File Cache won't be recycled when query is using it.";
732
4.77k
            continue;
733
4.77k
        }
734
54.6k
        rs->clear_cache();
735
54.6k
        auto index_names = rs->get_index_file_names();
736
54.6k
        recycled_rowsets.emplace_back(rs->rowset_id(), rs->num_segments(), index_names);
737
738
54.6k
        int64_t segment_size_sum = 0;
739
77.2k
        for (int32_t i = 0; i < rs->num_segments(); i++) {
740
22.6k
            segment_size_sum += rs->rowset_meta()->segment_file_size(i);
741
22.6k
        }
742
54.6k
        g_file_cache_recycle_cached_data_segment_num << rs->num_segments();
743
54.6k
        g_file_cache_recycle_cached_data_segment_size << segment_size_sum;
744
54.6k
        g_file_cache_recycle_cached_data_index_num << index_names.size();
745
54.6k
    }
746
2.90k
    return recycled_rowsets;
747
2.90k
}
748
749
void CloudTablet::reset_approximate_stats(int64_t num_rowsets, int64_t num_segments,
750
224k
                                          int64_t num_rows, int64_t data_size) {
751
224k
    _approximate_num_segments.store(num_segments, std::memory_order_relaxed);
752
224k
    _approximate_num_rows.store(num_rows, std::memory_order_relaxed);
753
224k
    _approximate_data_size.store(data_size, std::memory_order_relaxed);
754
224k
    int64_t cumu_num_deltas = 0;
755
224k
    int64_t cumu_num_rowsets = 0;
756
224k
    auto cp = _cumulative_point.load(std::memory_order_relaxed);
757
466k
    for (auto& [v, r] : _rs_version_map) {
758
466k
        if (v.second < cp) {
759
233k
            continue;
760
233k
        }
761
232k
        cumu_num_deltas += r->is_segments_overlapping() ? r->num_segments() : 1;
762
232k
        ++cumu_num_rowsets;
763
232k
    }
764
    // num_rowsets may be less than the size of _rs_version_map when there are some hole rowsets
765
    // in the version map, so we use the max value to ensure that the approximate number
766
    // of rowsets is at least the size of _rs_version_map.
767
    // Note that this is not the exact number of rowsets, but an approximate number.
768
224k
    int64_t approximate_num_rowsets =
769
224k
            std::max(num_rowsets, static_cast<int64_t>(_rs_version_map.size()));
770
224k
    _approximate_num_rowsets.store(approximate_num_rowsets, std::memory_order_relaxed);
771
224k
    _approximate_cumu_num_rowsets.store(cumu_num_rowsets, std::memory_order_relaxed);
772
224k
    _approximate_cumu_num_deltas.store(cumu_num_deltas, std::memory_order_relaxed);
773
224k
}
774
775
Result<std::unique_ptr<RowsetWriter>> CloudTablet::create_rowset_writer(
776
192k
        RowsetWriterContext& context, bool vertical) {
777
192k
    context.rowset_id = _engine.next_rowset_id();
778
    // FIXME(plat1ko): Seems `tablet_id` and `index_id` has been set repeatedly
779
192k
    context.tablet_id = tablet_id();
780
192k
    context.index_id = index_id();
781
192k
    context.partition_id = partition_id();
782
192k
    context.enable_unique_key_merge_on_write = enable_unique_key_merge_on_write();
783
192k
    context.encrypt_algorithm = tablet_meta()->encryption_algorithm();
784
192k
    return RowsetFactory::create_rowset_writer(_engine, context, vertical);
785
192k
}
786
787
// create a rowset writer with rowset_id and seg_id
788
// after writer, merge this transient rowset with original rowset
789
Result<std::unique_ptr<RowsetWriter>> CloudTablet::create_transient_rowset_writer(
790
        const Rowset& rowset, std::shared_ptr<PartialUpdateInfo> partial_update_info,
791
3.28k
        int64_t txn_expiration) {
792
3.28k
    if (rowset.rowset_meta_state() != RowsetStatePB::BEGIN_PARTIAL_UPDATE &&
793
3.28k
        rowset.rowset_meta_state() != RowsetStatePB::COMMITTED) [[unlikely]] {
794
0
        auto msg = fmt::format(
795
0
                "wrong rowset state when create_transient_rowset_writer, rowset state should be "
796
0
                "BEGIN_PARTIAL_UPDATE or COMMITTED, but found {}, rowset_id={}, tablet_id={}",
797
0
                RowsetStatePB_Name(rowset.rowset_meta_state()), rowset.rowset_id().to_string(),
798
0
                tablet_id());
799
        // see `CloudRowsetWriter::build` for detail.
800
        // if this is in a retry task, the rowset state may have been changed to RowsetStatePB::COMMITTED
801
        // in `RowsetMeta::merge_rowset_meta()` in previous trials.
802
0
        LOG(WARNING) << msg;
803
0
        DCHECK(false) << msg;
804
0
    }
805
3.28k
    RowsetWriterContext context;
806
3.28k
    context.rowset_state = PREPARED;
807
3.28k
    context.segments_overlap = OVERLAPPING;
808
    // During a partial update, the extracted columns of a variant should not be included in the tablet schema.
809
    // This is because the partial update for a variant needs to ignore the extracted columns.
810
    // Otherwise, the schema types in different rowsets might be inconsistent. When performing a partial update,
811
    // the complete variant is constructed by reading all the sub-columns of the variant.
812
3.28k
    context.tablet_schema = rowset.tablet_schema()->copy_without_variant_extracted_columns();
813
3.28k
    context.newest_write_timestamp = UnixSeconds();
814
3.28k
    context.tablet_id = table_id();
815
3.28k
    context.enable_segcompaction = false;
816
3.28k
    context.write_type = DataWriteType::TYPE_DIRECT;
817
3.28k
    context.partial_update_info = std::move(partial_update_info);
818
3.28k
    context.is_transient_rowset_writer = true;
819
3.28k
    context.rowset_id = rowset.rowset_id();
820
3.28k
    context.tablet_id = tablet_id();
821
3.28k
    context.index_id = index_id();
822
3.28k
    context.partition_id = partition_id();
823
3.28k
    context.enable_unique_key_merge_on_write = enable_unique_key_merge_on_write();
824
3.28k
    context.txn_expiration = txn_expiration;
825
3.28k
    context.encrypt_algorithm = tablet_meta()->encryption_algorithm();
826
    // TODO(liaoxin) enable packed file for transient rowset
827
3.28k
    context.allow_packed_file = false;
828
829
3.28k
    auto storage_resource = rowset.rowset_meta()->remote_storage_resource();
830
3.28k
    if (!storage_resource) {
831
0
        return ResultError(std::move(storage_resource.error()));
832
0
    }
833
834
3.28k
    context.storage_resource = *storage_resource.value();
835
836
3.28k
    return RowsetFactory::create_rowset_writer(_engine, context, false)
837
3.28k
            .transform([&](auto&& writer) {
838
3.23k
                writer->set_segment_start_id(cast_set<int32_t>(rowset.num_segments()));
839
3.23k
                return writer;
840
3.23k
            });
841
3.28k
}
842
843
77.2M
int64_t CloudTablet::get_cloud_base_compaction_score() const {
844
77.2M
    if (_tablet_meta->compaction_policy() == CUMULATIVE_TIME_SERIES_POLICY) {
845
33.5k
        bool has_delete = false;
846
33.5k
        int64_t point = cumulative_layer_point();
847
33.5k
        std::shared_lock<std::shared_mutex> rlock(_meta_lock);
848
402k
        for (const auto& [_, rs_meta] : _tablet_meta->all_rs_metas()) {
849
402k
            if (rs_meta->start_version() >= point) {
850
367k
                continue;
851
367k
            }
852
34.6k
            if (rs_meta->has_delete_predicate()) {
853
0
                has_delete = true;
854
0
                break;
855
0
            }
856
34.6k
        }
857
33.5k
        if (!has_delete) {
858
33.5k
            return 0;
859
33.5k
        }
860
33.5k
    }
861
862
77.2M
    return _approximate_num_rowsets.load(std::memory_order_relaxed) -
863
77.2M
           _approximate_cumu_num_rowsets.load(std::memory_order_relaxed);
864
77.2M
}
865
866
695M
int64_t CloudTablet::get_cloud_cumu_compaction_score() const {
867
    // TODO(plat1ko): Propose an algorithm that considers tablet's key type, number of delete rowsets,
868
    //  number of tablet versions simultaneously.
869
695M
    return _approximate_cumu_num_deltas.load(std::memory_order_relaxed);
870
695M
}
871
872
// return a json string to show the compaction status of this tablet
873
8.91k
void CloudTablet::get_compaction_status(std::string* json_result) {
874
8.91k
    rapidjson::Document root;
875
8.91k
    root.SetObject();
876
877
8.91k
    rapidjson::Document path_arr;
878
8.91k
    path_arr.SetArray();
879
880
8.91k
    std::vector<RowsetSharedPtr> rowsets;
881
8.91k
    std::vector<RowsetSharedPtr> stale_rowsets;
882
8.91k
    {
883
8.91k
        std::shared_lock rdlock(_meta_lock);
884
8.91k
        rowsets.reserve(_rs_version_map.size());
885
26.7k
        for (auto& it : _rs_version_map) {
886
26.7k
            rowsets.push_back(it.second);
887
26.7k
        }
888
8.91k
        stale_rowsets.reserve(_stale_rs_version_map.size());
889
104k
        for (auto& it : _stale_rs_version_map) {
890
104k
            stale_rowsets.push_back(it.second);
891
104k
        }
892
8.91k
    }
893
8.91k
    std::sort(rowsets.begin(), rowsets.end(), Rowset::comparator);
894
8.91k
    std::sort(stale_rowsets.begin(), stale_rowsets.end(), Rowset::comparator);
895
896
    // get snapshot version path json_doc
897
8.91k
    _timestamped_version_tracker.get_stale_version_path_json_doc(path_arr);
898
8.91k
    root.AddMember("cumulative point", _cumulative_point.load(), root.GetAllocator());
899
8.91k
    rapidjson::Value cumu_value;
900
8.91k
    std::string format_str = ToStringFromUnixMillis(_last_cumu_compaction_failure_millis.load());
901
8.91k
    cumu_value.SetString(format_str.c_str(), cast_set<uint>(format_str.length()),
902
8.91k
                         root.GetAllocator());
903
8.91k
    root.AddMember("last cumulative failure time", cumu_value, root.GetAllocator());
904
8.91k
    rapidjson::Value base_value;
905
8.91k
    format_str = ToStringFromUnixMillis(_last_base_compaction_failure_millis.load());
906
8.91k
    base_value.SetString(format_str.c_str(), cast_set<uint>(format_str.length()),
907
8.91k
                         root.GetAllocator());
908
8.91k
    root.AddMember("last base failure time", base_value, root.GetAllocator());
909
8.91k
    rapidjson::Value full_value;
910
8.91k
    format_str = ToStringFromUnixMillis(_last_full_compaction_failure_millis.load());
911
8.91k
    full_value.SetString(format_str.c_str(), cast_set<uint>(format_str.length()),
912
8.91k
                         root.GetAllocator());
913
8.91k
    root.AddMember("last full failure time", full_value, root.GetAllocator());
914
8.91k
    rapidjson::Value cumu_success_value;
915
8.91k
    format_str = ToStringFromUnixMillis(_last_cumu_compaction_success_millis.load());
916
8.91k
    cumu_success_value.SetString(format_str.c_str(), cast_set<uint>(format_str.length()),
917
8.91k
                                 root.GetAllocator());
918
8.91k
    root.AddMember("last cumulative success time", cumu_success_value, root.GetAllocator());
919
8.91k
    rapidjson::Value base_success_value;
920
8.91k
    format_str = ToStringFromUnixMillis(_last_base_compaction_success_millis.load());
921
8.91k
    base_success_value.SetString(format_str.c_str(), cast_set<uint>(format_str.length()),
922
8.91k
                                 root.GetAllocator());
923
8.91k
    root.AddMember("last base success time", base_success_value, root.GetAllocator());
924
8.91k
    rapidjson::Value full_success_value;
925
8.91k
    format_str = ToStringFromUnixMillis(_last_full_compaction_success_millis.load());
926
8.91k
    full_success_value.SetString(format_str.c_str(), cast_set<uint>(format_str.length()),
927
8.91k
                                 root.GetAllocator());
928
8.91k
    root.AddMember("last full success time", full_success_value, root.GetAllocator());
929
8.91k
    rapidjson::Value cumu_schedule_value;
930
8.91k
    format_str = ToStringFromUnixMillis(_last_cumu_compaction_schedule_millis.load());
931
8.91k
    cumu_schedule_value.SetString(format_str.c_str(), cast_set<uint>(format_str.length()),
932
8.91k
                                  root.GetAllocator());
933
8.91k
    root.AddMember("last cumulative schedule time", cumu_schedule_value, root.GetAllocator());
934
8.91k
    rapidjson::Value base_schedule_value;
935
8.91k
    format_str = ToStringFromUnixMillis(_last_base_compaction_schedule_millis.load());
936
8.91k
    base_schedule_value.SetString(format_str.c_str(), cast_set<uint>(format_str.length()),
937
8.91k
                                  root.GetAllocator());
938
8.91k
    root.AddMember("last base schedule time", base_schedule_value, root.GetAllocator());
939
8.91k
    rapidjson::Value full_schedule_value;
940
8.91k
    format_str = ToStringFromUnixMillis(_last_full_compaction_schedule_millis.load());
941
8.91k
    full_schedule_value.SetString(format_str.c_str(), cast_set<uint>(format_str.length()),
942
8.91k
                                  root.GetAllocator());
943
8.91k
    root.AddMember("last full schedule time", full_schedule_value, root.GetAllocator());
944
8.91k
    rapidjson::Value cumu_compaction_status_value;
945
8.91k
    cumu_compaction_status_value.SetString(_last_cumu_compaction_status.c_str(),
946
8.91k
                                           cast_set<uint>(_last_cumu_compaction_status.length()),
947
8.91k
                                           root.GetAllocator());
948
8.91k
    root.AddMember("last cumulative status", cumu_compaction_status_value, root.GetAllocator());
949
8.91k
    rapidjson::Value base_compaction_status_value;
950
8.91k
    base_compaction_status_value.SetString(_last_base_compaction_status.c_str(),
951
8.91k
                                           cast_set<uint>(_last_base_compaction_status.length()),
952
8.91k
                                           root.GetAllocator());
953
8.91k
    root.AddMember("last base status", base_compaction_status_value, root.GetAllocator());
954
8.91k
    rapidjson::Value full_compaction_status_value;
955
8.91k
    full_compaction_status_value.SetString(_last_full_compaction_status.c_str(),
956
8.91k
                                           cast_set<uint>(_last_full_compaction_status.length()),
957
8.91k
                                           root.GetAllocator());
958
8.91k
    root.AddMember("last full status", full_compaction_status_value, root.GetAllocator());
959
8.91k
    rapidjson::Value exec_compaction_time;
960
8.91k
    std::string num_str {std::to_string(exec_compaction_time_us.load())};
961
8.91k
    exec_compaction_time.SetString(num_str.c_str(), cast_set<uint>(num_str.length()),
962
8.91k
                                   root.GetAllocator());
963
8.91k
    root.AddMember("exec compaction time us", exec_compaction_time, root.GetAllocator());
964
8.91k
    rapidjson::Value local_read_time;
965
8.91k
    num_str = std::to_string(local_read_time_us.load());
966
8.91k
    local_read_time.SetString(num_str.c_str(), cast_set<uint>(num_str.length()),
967
8.91k
                              root.GetAllocator());
968
8.91k
    root.AddMember("compaction local read time us", local_read_time, root.GetAllocator());
969
8.91k
    rapidjson::Value remote_read_time;
970
8.91k
    num_str = std::to_string(remote_read_time_us.load());
971
8.91k
    remote_read_time.SetString(num_str.c_str(), cast_set<uint>(num_str.length()),
972
8.91k
                               root.GetAllocator());
973
8.91k
    root.AddMember("compaction remote read time us", remote_read_time, root.GetAllocator());
974
975
    // print all rowsets' version as an array
976
8.91k
    rapidjson::Document versions_arr;
977
8.91k
    rapidjson::Document missing_versions_arr;
978
8.91k
    versions_arr.SetArray();
979
8.91k
    missing_versions_arr.SetArray();
980
8.91k
    int64_t last_version = -1;
981
26.7k
    for (auto& rowset : rowsets) {
982
26.7k
        const Version& ver = rowset->version();
983
26.7k
        if (ver.first != last_version + 1) {
984
0
            rapidjson::Value miss_value;
985
0
            miss_value.SetString(fmt::format("[{}-{}]", last_version + 1, ver.first - 1).c_str(),
986
0
                                 missing_versions_arr.GetAllocator());
987
0
            missing_versions_arr.PushBack(miss_value, missing_versions_arr.GetAllocator());
988
0
        }
989
26.7k
        rapidjson::Value value;
990
26.7k
        std::string version_str = rowset->get_rowset_info_str();
991
26.7k
        value.SetString(version_str.c_str(), cast_set<uint32_t>(version_str.length()),
992
26.7k
                        versions_arr.GetAllocator());
993
26.7k
        versions_arr.PushBack(value, versions_arr.GetAllocator());
994
26.7k
        last_version = ver.second;
995
26.7k
    }
996
8.91k
    root.AddMember("rowsets", versions_arr, root.GetAllocator());
997
8.91k
    root.AddMember("missing_rowsets", missing_versions_arr, root.GetAllocator());
998
999
    // print all stale rowsets' version as an array
1000
8.91k
    rapidjson::Document stale_versions_arr;
1001
8.91k
    stale_versions_arr.SetArray();
1002
104k
    for (auto& rowset : stale_rowsets) {
1003
104k
        rapidjson::Value value;
1004
104k
        std::string version_str = rowset->get_rowset_info_str();
1005
104k
        value.SetString(version_str.c_str(), cast_set<uint32_t>(version_str.length()),
1006
104k
                        stale_versions_arr.GetAllocator());
1007
104k
        stale_versions_arr.PushBack(value, stale_versions_arr.GetAllocator());
1008
104k
    }
1009
8.91k
    root.AddMember("stale_rowsets", stale_versions_arr, root.GetAllocator());
1010
1011
    // add stale version rowsets
1012
8.91k
    root.AddMember("stale version path", path_arr, root.GetAllocator());
1013
1014
    // to json string
1015
8.91k
    rapidjson::StringBuffer strbuf;
1016
8.91k
    rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(strbuf);
1017
8.91k
    root.Accept(writer);
1018
8.91k
    *json_result = std::string(strbuf.GetString());
1019
8.91k
}
1020
1021
223k
void CloudTablet::set_cumulative_layer_point(int64_t new_point) {
1022
224k
    if (new_point == Tablet::K_INVALID_CUMULATIVE_POINT || new_point >= _cumulative_point) {
1023
224k
        _cumulative_point = new_point;
1024
224k
        return;
1025
224k
    }
1026
    // cumulative point should only be reset to -1, or be increased
1027
    // FIXME: could happen in currently unresolved race conditions
1028
18.4E
    LOG(WARNING) << "Unexpected cumulative point: " << new_point
1029
18.4E
                 << ", origin: " << _cumulative_point.load();
1030
18.4E
}
1031
1032
Status CloudTablet::check_rowset_schema_for_build_index(std::vector<TColumn>& columns,
1033
596
                                                        int schema_version) {
1034
596
    std::map<std::string, TabletColumn> fe_col_map;
1035
4.00k
    for (int i = 0; i < columns.size(); i++) {
1036
3.40k
        fe_col_map[columns[i].column_name] = TabletColumn(columns[i]);
1037
3.40k
    }
1038
1039
596
    std::shared_lock rlock(_meta_lock);
1040
1.59k
    for (const auto& [version, rs] : _rs_version_map) {
1041
1.59k
        if (version.first == 0) {
1042
575
            continue;
1043
575
        }
1044
1045
1.01k
        if (rs->tablet_schema()->schema_version() >= schema_version) {
1046
177
            continue;
1047
177
        }
1048
1049
5.87k
        for (auto rs_col : rs->tablet_schema()->columns()) {
1050
5.87k
            auto find_ret = fe_col_map.find(rs_col->name());
1051
5.87k
            if (find_ret == fe_col_map.end()) {
1052
7
                return Status::InternalError(
1053
7
                        "check rowset meta failed:rowset's col is dropped in FE.");
1054
7
            }
1055
1056
5.86k
            if (rs_col->unique_id() != find_ret->second.unique_id()) {
1057
4
                return Status::InternalError("check rowset meta failed:col id not match.");
1058
4
            }
1059
1060
5.86k
            if (rs_col->type() != find_ret->second.type()) {
1061
1
                return Status::InternalError("check rowset meta failed:col type not match.");
1062
1
            }
1063
5.86k
        }
1064
841
    }
1065
1066
584
    return Status::OK();
1067
596
}
1068
1069
Result<RowsetSharedPtr> CloudTablet::pick_a_rowset_for_index_change(int schema_version,
1070
2.21k
                                                                    bool& is_base_rowset) {
1071
2.21k
    TEST_SYNC_POINT_RETURN_WITH_VALUE("CloudTablet::pick_a_rowset_for_index_change",
1072
2.21k
                                      Result<RowsetSharedPtr>(nullptr));
1073
2.21k
    RowsetSharedPtr ret_rowset = nullptr;
1074
2.21k
    std::shared_lock rlock(_meta_lock);
1075
11.8k
    for (const auto& [version, rs] : _rs_version_map) {
1076
11.8k
        if (version.first == 0) {
1077
2.21k
            continue;
1078
2.21k
        }
1079
9.59k
        if (rs->num_rows() == 0) {
1080
6.22k
            VLOG_DEBUG << "[index_change]find empty rs, index change may "
1081
0
                          "failed, id="
1082
0
                       << rs->rowset_id().to_string();
1083
6.22k
        }
1084
1085
9.59k
        if (rs->tablet_schema()->schema_version() >= schema_version) {
1086
4.45k
            VLOG_DEBUG << "[index_change] skip rowset " << rs->tablet_schema()->schema_version()
1087
0
                       << "," << schema_version;
1088
4.45k
            continue;
1089
4.45k
        }
1090
1091
5.13k
        if (ret_rowset == nullptr) {
1092
1.64k
            ret_rowset = rs;
1093
1.64k
            continue;
1094
1.64k
        }
1095
1096
3.49k
        if (rs->start_version() > ret_rowset->start_version()) {
1097
299
            ret_rowset = rs;
1098
299
        }
1099
3.49k
    }
1100
1101
2.21k
    if (ret_rowset != nullptr) {
1102
1.64k
        is_base_rowset = ret_rowset->version().first < _cumulative_point;
1103
1.64k
    }
1104
1105
2.21k
    return ret_rowset;
1106
2.21k
}
1107
1108
3.62k
std::vector<RowsetSharedPtr> CloudTablet::pick_candidate_rowsets_to_base_compaction() {
1109
3.62k
    std::vector<RowsetSharedPtr> candidate_rowsets;
1110
3.62k
    {
1111
3.62k
        std::shared_lock rlock(_meta_lock);
1112
19.0k
        for (const auto& [version, rs] : _rs_version_map) {
1113
19.0k
            if (version.first != 0 && version.first < _cumulative_point &&
1114
19.0k
                (_alter_version == -1 || version.second <= _alter_version)) {
1115
11.7k
                candidate_rowsets.push_back(rs);
1116
11.7k
            }
1117
19.0k
        }
1118
3.62k
    }
1119
3.62k
    std::sort(candidate_rowsets.begin(), candidate_rowsets.end(), Rowset::comparator);
1120
3.62k
    return candidate_rowsets;
1121
3.62k
}
1122
1123
105
std::vector<RowsetSharedPtr> CloudTablet::pick_candidate_rowsets_to_full_compaction() {
1124
105
    std::vector<RowsetSharedPtr> candidate_rowsets;
1125
105
    {
1126
105
        std::shared_lock rlock(_meta_lock);
1127
709
        for (auto& [v, rs] : _rs_version_map) {
1128
            // MUST NOT compact rowset [0-1] for some historical reasons (see cloud_schema_change)
1129
709
            if (v.first != 0) {
1130
604
                candidate_rowsets.push_back(rs);
1131
604
            }
1132
709
        }
1133
105
    }
1134
105
    std::sort(candidate_rowsets.begin(), candidate_rowsets.end(), Rowset::comparator);
1135
105
    return candidate_rowsets;
1136
105
}
1137
1138
50
CalcDeleteBitmapExecutor* CloudTablet::calc_delete_bitmap_executor() {
1139
50
    return _engine.calc_delete_bitmap_executor();
1140
50
}
1141
1142
Status CloudTablet::save_delete_bitmap(const TabletTxnInfo* txn_info, int64_t txn_id,
1143
                                       DeleteBitmapPtr delete_bitmap, RowsetWriter* rowset_writer,
1144
                                       const RowsetIdUnorderedSet& cur_rowset_ids, int64_t lock_id,
1145
50.0k
                                       int64_t next_visible_version) {
1146
50.0k
    RowsetSharedPtr rowset = txn_info->rowset;
1147
50.0k
    int64_t cur_version = rowset->start_version();
1148
    // update delete bitmap info, in order to avoid recalculation when trying again
1149
50.0k
    RETURN_IF_ERROR(_engine.txn_delete_bitmap_cache().update_tablet_txn_info(
1150
50.0k
            txn_id, tablet_id(), delete_bitmap, cur_rowset_ids, PublishStatus::PREPARE));
1151
1152
50.4k
    if (txn_info->partial_update_info && txn_info->partial_update_info->is_partial_update() &&
1153
50.0k
        rowset_writer->num_rows() > 0) {
1154
19
        DBUG_EXECUTE_IF("CloudTablet::save_delete_bitmap.update_tmp_rowset.error", {
1155
19
            return Status::InternalError<false>("injected update_tmp_rowset error.");
1156
19
        });
1157
19
        const auto& rowset_meta = rowset->rowset_meta();
1158
19
        RETURN_IF_ERROR(_engine.meta_mgr().update_tmp_rowset(*rowset_meta));
1159
19
    }
1160
1161
50.0k
    RETURN_IF_ERROR(save_delete_bitmap_to_ms(cur_version, txn_id, delete_bitmap, lock_id,
1162
50.0k
                                             next_visible_version, rowset));
1163
1164
    // store the delete bitmap with sentinel marks in txn_delete_bitmap_cache because if the txn is retried for some reason,
1165
    // it will use the delete bitmap from txn_delete_bitmap_cache when re-calculating the delete bitmap, during which it will do
1166
    // delete bitmap correctness check. If we store the new_delete_bitmap, the delete bitmap correctness check will fail
1167
50.0k
    RETURN_IF_ERROR(_engine.txn_delete_bitmap_cache().update_tablet_txn_info(
1168
50.0k
            txn_id, tablet_id(), delete_bitmap, cur_rowset_ids, PublishStatus::SUCCEED,
1169
50.0k
            txn_info->publish_info));
1170
1171
50.0k
    DBUG_EXECUTE_IF("CloudTablet::save_delete_bitmap.enable_sleep", {
1172
50.0k
        auto sleep_sec = dp->param<int>("sleep", 5);
1173
50.0k
        std::this_thread::sleep_for(std::chrono::seconds(sleep_sec));
1174
50.0k
    });
1175
1176
50.0k
    DBUG_EXECUTE_IF("CloudTablet::save_delete_bitmap.injected_error", {
1177
50.0k
        auto retry = dp->param<bool>("retry", false);
1178
50.0k
        auto sleep_sec = dp->param<int>("sleep", 0);
1179
50.0k
        std::this_thread::sleep_for(std::chrono::seconds(sleep_sec));
1180
50.0k
        if (retry) { // return DELETE_BITMAP_LOCK_ERROR to let it retry
1181
50.0k
            return Status::Error<ErrorCode::DELETE_BITMAP_LOCK_ERROR>(
1182
50.0k
                    "injected DELETE_BITMAP_LOCK_ERROR");
1183
50.0k
        } else {
1184
50.0k
            return Status::InternalError<false>("injected non-retryable error");
1185
50.0k
        }
1186
50.0k
    });
1187
1188
50.0k
    return Status::OK();
1189
50.0k
}
1190
1191
Status CloudTablet::save_delete_bitmap_to_ms(int64_t cur_version, int64_t txn_id,
1192
                                             DeleteBitmapPtr delete_bitmap, int64_t lock_id,
1193
50.3k
                                             int64_t next_visible_version, RowsetSharedPtr rowset) {
1194
50.3k
    DeleteBitmapPtr new_delete_bitmap = std::make_shared<DeleteBitmap>(tablet_id());
1195
50.3k
    for (auto iter = delete_bitmap->delete_bitmap.begin();
1196
260k
         iter != delete_bitmap->delete_bitmap.end(); ++iter) {
1197
        // skip sentinel mark, which is used for delete bitmap correctness check
1198
210k
        if (std::get<1>(iter->first) != DeleteBitmap::INVALID_SEGMENT_ID) {
1199
8.20k
            new_delete_bitmap->merge(
1200
8.20k
                    {std::get<0>(iter->first), std::get<1>(iter->first), cur_version},
1201
8.20k
                    iter->second);
1202
8.20k
        }
1203
210k
    }
1204
    // lock_id != -1 means this is in an explict txn
1205
50.3k
    bool is_explicit_txn = (lock_id != -1);
1206
50.3k
    auto ms_lock_id = !is_explicit_txn ? txn_id : lock_id;
1207
50.3k
    std::optional<StorageResource> storage_resource;
1208
50.3k
    auto storage_resource_result = rowset->rowset_meta()->remote_storage_resource();
1209
50.3k
    if (storage_resource_result) {
1210
49.9k
        storage_resource = *storage_resource_result.value();
1211
49.9k
    }
1212
50.3k
    RETURN_IF_ERROR(_engine.meta_mgr().update_delete_bitmap(
1213
50.3k
            *this, ms_lock_id, LOAD_INITIATOR_ID, new_delete_bitmap.get(), new_delete_bitmap.get(),
1214
50.3k
            rowset->rowset_id().to_string(), storage_resource,
1215
50.3k
            config::delete_bitmap_store_write_version, txn_id, is_explicit_txn,
1216
50.3k
            next_visible_version));
1217
50.3k
    return Status::OK();
1218
50.3k
}
1219
1220
0
Versions CloudTablet::calc_missed_versions(int64_t spec_version, Versions existing_versions) const {
1221
0
    DCHECK(spec_version > 0) << "invalid spec_version: " << spec_version;
1222
1223
    // sort the existing versions in ascending order
1224
0
    std::sort(existing_versions.begin(), existing_versions.end(),
1225
0
              [](const Version& a, const Version& b) {
1226
                  // simple because 2 versions are certainly not overlapping
1227
0
                  return a.first < b.first;
1228
0
              });
1229
1230
    // From the first version(=0), find the missing version until spec_version
1231
0
    int64_t last_version = -1;
1232
0
    Versions missed_versions;
1233
0
    for (const Version& version : existing_versions) {
1234
0
        if (version.first > last_version + 1) {
1235
            // there is a hole between versions
1236
0
            missed_versions.emplace_back(last_version + 1, std::min(version.first, spec_version));
1237
0
        }
1238
0
        last_version = version.second;
1239
0
        if (last_version >= spec_version) {
1240
0
            break;
1241
0
        }
1242
0
    }
1243
0
    if (last_version < spec_version) {
1244
        // there is a hole between the last version and the specificed version.
1245
0
        missed_versions.emplace_back(last_version + 1, spec_version);
1246
0
    }
1247
0
    return missed_versions;
1248
0
}
1249
1250
Status CloudTablet::calc_delete_bitmap_for_compaction(
1251
        const std::vector<RowsetSharedPtr>& input_rowsets, const RowsetSharedPtr& output_rowset,
1252
        const RowIdConversion& rowid_conversion, ReaderType compaction_type, int64_t merged_rows,
1253
        int64_t filtered_rows, int64_t initiator, DeleteBitmapPtr& output_rowset_delete_bitmap,
1254
3.28k
        bool allow_delete_in_cumu_compaction, int64_t& get_delete_bitmap_lock_start_time) {
1255
3.28k
    output_rowset_delete_bitmap = std::make_shared<DeleteBitmap>(tablet_id());
1256
3.28k
    std::unique_ptr<RowLocationSet> missed_rows;
1257
3.28k
    if ((config::enable_missing_rows_correctness_check ||
1258
3.28k
         config::enable_mow_compaction_correctness_check_core ||
1259
3.28k
         config::enable_mow_compaction_correctness_check_fail) &&
1260
3.28k
        !allow_delete_in_cumu_compaction &&
1261
3.28k
        (compaction_type == ReaderType::READER_CUMULATIVE_COMPACTION ||
1262
0
         !config::enable_prune_delete_sign_when_base_compaction)) {
1263
        // also check duplicate key for base compaction when config::enable_prune_delete_sign_when_base_compaction==false
1264
0
        missed_rows = std::make_unique<RowLocationSet>();
1265
0
        LOG(INFO) << "RowLocation Set inited succ for tablet:" << tablet_id();
1266
0
    }
1267
1268
3.28k
    std::unique_ptr<std::map<RowsetSharedPtr, RowLocationPairList>> location_map;
1269
3.28k
    if (config::enable_rowid_conversion_correctness_check &&
1270
3.28k
        tablet_schema()->cluster_key_uids().empty()) {
1271
0
        location_map = std::make_unique<std::map<RowsetSharedPtr, RowLocationPairList>>();
1272
0
        LOG(INFO) << "Location Map inited succ for tablet:" << tablet_id();
1273
0
    }
1274
1275
    // 1. calc delete bitmap for historical data
1276
3.28k
    RETURN_IF_ERROR(_engine.meta_mgr().sync_tablet_rowsets(this));
1277
3.28k
    Version version = max_version();
1278
3.28k
    std::size_t missed_rows_size = 0;
1279
3.28k
    calc_compaction_output_rowset_delete_bitmap(
1280
3.28k
            input_rowsets, rowid_conversion, 0, version.second + 1, missed_rows.get(),
1281
3.28k
            location_map.get(), tablet_meta()->delete_bitmap(), output_rowset_delete_bitmap.get());
1282
3.28k
    if (missed_rows) {
1283
0
        missed_rows_size = missed_rows->size();
1284
0
        if (!allow_delete_in_cumu_compaction) {
1285
0
            if ((compaction_type == ReaderType::READER_CUMULATIVE_COMPACTION ||
1286
0
                 !config::enable_prune_delete_sign_when_base_compaction) &&
1287
0
                tablet_state() == TABLET_RUNNING) {
1288
0
                if (merged_rows + filtered_rows >= 0 &&
1289
0
                    merged_rows + filtered_rows != missed_rows_size) {
1290
0
                    std::string err_msg = fmt::format(
1291
0
                            "cumulative compaction: the merged rows({}), the filtered rows({}) is "
1292
0
                            "not equal to missed rows({}) in rowid conversion, tablet_id: {}, "
1293
0
                            "table_id:{}",
1294
0
                            merged_rows, filtered_rows, missed_rows_size, tablet_id(), table_id());
1295
0
                    LOG(WARNING) << err_msg;
1296
0
                    if (config::enable_mow_compaction_correctness_check_core) {
1297
0
                        CHECK(false) << err_msg;
1298
0
                    } else if (config::enable_mow_compaction_correctness_check_fail) {
1299
0
                        return Status::InternalError<false>(err_msg);
1300
0
                    } else {
1301
0
                        DCHECK(false) << err_msg;
1302
0
                    }
1303
0
                }
1304
0
            }
1305
0
        }
1306
0
    }
1307
3.28k
    if (location_map) {
1308
0
        RETURN_IF_ERROR(check_rowid_conversion(output_rowset, *location_map));
1309
0
        location_map->clear();
1310
0
    }
1311
1312
    // 2. calc delete bitmap for incremental data
1313
3.28k
    int64_t t1 = MonotonicMicros();
1314
3.28k
    RETURN_IF_ERROR(_engine.meta_mgr().get_delete_bitmap_update_lock(
1315
3.28k
            *this, COMPACTION_DELETE_BITMAP_LOCK_ID, initiator));
1316
3.28k
    int64_t t2 = MonotonicMicros();
1317
3.28k
    if (compaction_type == ReaderType::READER_CUMULATIVE_COMPACTION) {
1318
3.27k
        g_cu_compaction_get_delete_bitmap_lock_time_ms << (t2 - t1) / 1000;
1319
3.27k
    } else if (compaction_type == ReaderType::READER_BASE_COMPACTION) {
1320
15
        g_base_compaction_get_delete_bitmap_lock_time_ms << (t2 - t1) / 1000;
1321
15
    }
1322
3.28k
    get_delete_bitmap_lock_start_time = t2;
1323
3.28k
    RETURN_IF_ERROR(_engine.meta_mgr().sync_tablet_rowsets(this));
1324
3.28k
    int64_t t3 = MonotonicMicros();
1325
1326
3.28k
    calc_compaction_output_rowset_delete_bitmap(
1327
3.28k
            input_rowsets, rowid_conversion, version.second, UINT64_MAX, missed_rows.get(),
1328
3.28k
            location_map.get(), tablet_meta()->delete_bitmap(), output_rowset_delete_bitmap.get());
1329
3.28k
    int64_t t4 = MonotonicMicros();
1330
3.28k
    if (location_map) {
1331
0
        RETURN_IF_ERROR(check_rowid_conversion(output_rowset, *location_map));
1332
0
    }
1333
3.28k
    int64_t t5 = MonotonicMicros();
1334
1335
    // 3. store delete bitmap
1336
3.28k
    DeleteBitmapPtr delete_bitmap_v2 = nullptr;
1337
3.28k
    auto delete_bitmap_size = output_rowset_delete_bitmap->delete_bitmap.size();
1338
3.28k
    auto store_version = config::delete_bitmap_store_write_version;
1339
3.29k
    if (store_version == 2 || store_version == 3) {
1340
0
        delete_bitmap_v2 = std::make_shared<DeleteBitmap>(*output_rowset_delete_bitmap);
1341
0
        std::vector<std::pair<RowsetId, int64_t>> retained_rowsets_to_seg_num;
1342
0
        {
1343
0
            std::shared_lock rlock(get_header_lock());
1344
0
            for (const auto& [rowset_version, rowset_ptr] : rowset_map()) {
1345
0
                if (rowset_version.second < output_rowset->start_version()) {
1346
0
                    retained_rowsets_to_seg_num.emplace_back(
1347
0
                            std::make_pair(rowset_ptr->rowset_id(), rowset_ptr->num_segments()));
1348
0
                }
1349
0
            }
1350
0
        }
1351
0
        if (config::enable_agg_delta_delete_bitmap_for_store_v2) {
1352
0
            tablet_meta()->delete_bitmap().subset_and_agg(
1353
0
                    retained_rowsets_to_seg_num, output_rowset->start_version(),
1354
0
                    output_rowset->end_version(), delete_bitmap_v2.get());
1355
0
        } else {
1356
0
            tablet_meta()->delete_bitmap().subset(
1357
0
                    retained_rowsets_to_seg_num, output_rowset->start_version(),
1358
0
                    output_rowset->end_version(), delete_bitmap_v2.get());
1359
0
        }
1360
0
    }
1361
3.28k
    std::optional<StorageResource> storage_resource;
1362
3.28k
    auto storage_resource_result = output_rowset->rowset_meta()->remote_storage_resource();
1363
3.29k
    if (storage_resource_result) {
1364
3.29k
        storage_resource = *storage_resource_result.value();
1365
3.29k
    }
1366
3.28k
    auto st = _engine.meta_mgr().update_delete_bitmap(
1367
3.28k
            *this, -1, initiator, output_rowset_delete_bitmap.get(), delete_bitmap_v2.get(),
1368
3.28k
            output_rowset->rowset_id().to_string(), storage_resource, store_version);
1369
3.28k
    int64_t t6 = MonotonicMicros();
1370
3.28k
    LOG(INFO) << "calc_delete_bitmap_for_compaction, tablet_id=" << tablet_id()
1371
3.28k
              << ", get lock cost " << (t2 - t1) << " us, sync rowsets cost " << (t3 - t2)
1372
3.28k
              << " us, calc delete bitmap cost " << (t4 - t3) << " us, check rowid conversion cost "
1373
3.28k
              << (t5 - t4) << " us, store delete bitmap cost " << (t6 - t5)
1374
3.28k
              << " us, st=" << st.to_string() << ". store_version=" << store_version
1375
3.28k
              << ", calculated delete bitmap size=" << delete_bitmap_size
1376
3.28k
              << ", update delete bitmap size="
1377
3.28k
              << output_rowset_delete_bitmap->delete_bitmap.size();
1378
3.28k
    return st;
1379
3.28k
}
1380
1381
void CloudTablet::agg_delete_bitmap_for_compaction(
1382
        int64_t start_version, int64_t end_version, const std::vector<RowsetSharedPtr>& pre_rowsets,
1383
        DeleteBitmapPtr& new_delete_bitmap,
1384
3.18k
        std::map<std::string, int64_t>& pre_rowset_to_versions) {
1385
8.25k
    for (auto& rowset : pre_rowsets) {
1386
12.2k
        for (uint32_t seg_id = 0; seg_id < rowset->num_segments(); ++seg_id) {
1387
4.02k
            auto d = tablet_meta()->delete_bitmap().get_agg_without_cache(
1388
4.02k
                    {rowset->rowset_id(), seg_id, end_version}, start_version);
1389
4.02k
            if (d->isEmpty()) {
1390
3.19k
                continue;
1391
3.19k
            }
1392
837
            VLOG_DEBUG << "agg delete bitmap for tablet_id=" << tablet_id()
1393
0
                       << ", rowset_id=" << rowset->rowset_id() << ", seg_id=" << seg_id
1394
0
                       << ", rowset_version=" << rowset->version().to_string()
1395
0
                       << ". compaction start_version=" << start_version
1396
0
                       << ", end_version=" << end_version
1397
0
                       << ". delete_bitmap cardinality=" << d->cardinality();
1398
837
            DeleteBitmap::BitmapKey end_key {rowset->rowset_id(), seg_id, end_version};
1399
837
            new_delete_bitmap->set(end_key, *d);
1400
837
            pre_rowset_to_versions[rowset->rowset_id().to_string()] = rowset->version().second;
1401
837
        }
1402
8.25k
    }
1403
3.18k
}
1404
1405
62.9k
Status CloudTablet::sync_meta() {
1406
62.9k
    if (!config::enable_file_cache) {
1407
1
        return Status::OK();
1408
1
    }
1409
1410
62.9k
    TabletMetaSharedPtr tablet_meta;
1411
62.9k
    auto st = _engine.meta_mgr().get_tablet_meta(tablet_id(), &tablet_meta);
1412
62.9k
    if (!st.ok()) {
1413
0
        if (st.is<ErrorCode::NOT_FOUND>()) {
1414
0
            clear_cache();
1415
0
        }
1416
0
        return st;
1417
0
    }
1418
1419
62.9k
    auto new_compaction_policy = tablet_meta->compaction_policy();
1420
62.9k
    if (_tablet_meta->compaction_policy() != new_compaction_policy) {
1421
1
        _tablet_meta->set_compaction_policy(new_compaction_policy);
1422
1
    }
1423
62.9k
    auto new_time_series_compaction_goal_size_mbytes =
1424
62.9k
            tablet_meta->time_series_compaction_goal_size_mbytes();
1425
62.9k
    if (_tablet_meta->time_series_compaction_goal_size_mbytes() !=
1426
62.9k
        new_time_series_compaction_goal_size_mbytes) {
1427
0
        _tablet_meta->set_time_series_compaction_goal_size_mbytes(
1428
0
                new_time_series_compaction_goal_size_mbytes);
1429
0
    }
1430
62.9k
    auto new_time_series_compaction_file_count_threshold =
1431
62.9k
            tablet_meta->time_series_compaction_file_count_threshold();
1432
62.9k
    if (_tablet_meta->time_series_compaction_file_count_threshold() !=
1433
62.9k
        new_time_series_compaction_file_count_threshold) {
1434
0
        _tablet_meta->set_time_series_compaction_file_count_threshold(
1435
0
                new_time_series_compaction_file_count_threshold);
1436
0
    }
1437
62.9k
    auto new_time_series_compaction_time_threshold_seconds =
1438
62.9k
            tablet_meta->time_series_compaction_time_threshold_seconds();
1439
62.9k
    if (_tablet_meta->time_series_compaction_time_threshold_seconds() !=
1440
62.9k
        new_time_series_compaction_time_threshold_seconds) {
1441
0
        _tablet_meta->set_time_series_compaction_time_threshold_seconds(
1442
0
                new_time_series_compaction_time_threshold_seconds);
1443
0
    }
1444
62.9k
    auto new_time_series_compaction_empty_rowsets_threshold =
1445
62.9k
            tablet_meta->time_series_compaction_empty_rowsets_threshold();
1446
62.9k
    if (_tablet_meta->time_series_compaction_empty_rowsets_threshold() !=
1447
62.9k
        new_time_series_compaction_empty_rowsets_threshold) {
1448
0
        _tablet_meta->set_time_series_compaction_empty_rowsets_threshold(
1449
0
                new_time_series_compaction_empty_rowsets_threshold);
1450
0
    }
1451
62.9k
    auto new_time_series_compaction_level_threshold =
1452
62.9k
            tablet_meta->time_series_compaction_level_threshold();
1453
62.9k
    if (_tablet_meta->time_series_compaction_level_threshold() !=
1454
62.9k
        new_time_series_compaction_level_threshold) {
1455
0
        _tablet_meta->set_time_series_compaction_level_threshold(
1456
0
                new_time_series_compaction_level_threshold);
1457
0
    }
1458
    // Sync disable_auto_compaction (stored in tablet_schema)
1459
62.9k
    auto new_disable_auto_compaction = tablet_meta->tablet_schema()->disable_auto_compaction();
1460
62.9k
    if (_tablet_meta->tablet_schema()->disable_auto_compaction() != new_disable_auto_compaction) {
1461
6
        _tablet_meta->mutable_tablet_schema()->set_disable_auto_compaction(
1462
6
                new_disable_auto_compaction);
1463
6
    }
1464
    // Sync vertical_compaction_num_columns_per_group
1465
62.9k
    auto new_vertical_compaction_num_columns_per_group =
1466
62.9k
            tablet_meta->vertical_compaction_num_columns_per_group();
1467
62.9k
    if (_tablet_meta->vertical_compaction_num_columns_per_group() !=
1468
62.9k
        new_vertical_compaction_num_columns_per_group) {
1469
0
        _tablet_meta->set_vertical_compaction_num_columns_per_group(
1470
0
                new_vertical_compaction_num_columns_per_group);
1471
0
    }
1472
1473
62.9k
    return Status::OK();
1474
62.9k
}
1475
1476
3.74M
void CloudTablet::build_tablet_report_info(TTabletInfo* tablet_info) {
1477
3.74M
    std::shared_lock rdlock(_meta_lock);
1478
3.74M
    tablet_info->__set_total_version_count(_tablet_meta->version_count());
1479
3.74M
    tablet_info->__set_tablet_id(_tablet_meta->tablet_id());
1480
    // Currently, this information will not be used by the cloud report,
1481
    // but it may be used in the future.
1482
3.74M
}
1483
1484
Status CloudTablet::check_delete_bitmap_cache(int64_t txn_id,
1485
50.9k
                                              DeleteBitmap* expected_delete_bitmap) {
1486
50.9k
    DeleteBitmapPtr cached_delete_bitmap;
1487
50.9k
    CloudStorageEngine& engine = ExecEnv::GetInstance()->storage_engine().to_cloud();
1488
50.9k
    Status st = engine.txn_delete_bitmap_cache().get_delete_bitmap(
1489
50.9k
            txn_id, tablet_id(), &cached_delete_bitmap, nullptr, nullptr);
1490
50.9k
    if (st.ok()) {
1491
50.7k
        bool res = (expected_delete_bitmap->cardinality() == cached_delete_bitmap->cardinality());
1492
50.7k
        auto msg = fmt::format(
1493
50.7k
                "delete bitmap cache check failed, cur_cardinality={}, cached_cardinality={}"
1494
50.7k
                "txn_id={}, tablet_id={}",
1495
50.7k
                expected_delete_bitmap->cardinality(), cached_delete_bitmap->cardinality(), txn_id,
1496
50.7k
                tablet_id());
1497
50.7k
        if (!res) {
1498
0
            DCHECK(res) << msg;
1499
0
            return Status::InternalError<false>(msg);
1500
0
        }
1501
50.7k
    }
1502
50.9k
    return Status::OK();
1503
50.9k
}
1504
1505
35
WarmUpState CloudTablet::get_rowset_warmup_state(RowsetId rowset_id) {
1506
35
    std::shared_lock rlock(_meta_lock);
1507
35
    if (!_rowset_warm_up_states.contains(rowset_id)) {
1508
1
        return {.trigger_source = WarmUpTriggerSource::NONE, .progress = WarmUpProgress::NONE};
1509
1
    }
1510
34
    auto& warmup_info = _rowset_warm_up_states[rowset_id];
1511
34
    warmup_info.update_state();
1512
34
    return warmup_info.state;
1513
35
}
1514
1515
bool CloudTablet::add_rowset_warmup_state(const RowsetMeta& rowset, WarmUpTriggerSource source,
1516
34
                                          std::chrono::steady_clock::time_point start_tp) {
1517
34
    std::lock_guard wlock(_meta_lock);
1518
34
    return add_rowset_warmup_state_unlocked(rowset, source, start_tp);
1519
34
}
1520
1521
bool CloudTablet::update_rowset_warmup_state_inverted_idx_num(WarmUpTriggerSource source,
1522
5
                                                              RowsetId rowset_id, int64_t delta) {
1523
5
    std::lock_guard wlock(_meta_lock);
1524
5
    return update_rowset_warmup_state_inverted_idx_num_unlocked(source, rowset_id, delta);
1525
5
}
1526
1527
bool CloudTablet::update_rowset_warmup_state_inverted_idx_num_unlocked(WarmUpTriggerSource source,
1528
                                                                       RowsetId rowset_id,
1529
5.18k
                                                                       int64_t delta) {
1530
5.18k
    auto it = _rowset_warm_up_states.find(rowset_id);
1531
5.18k
    if (it == _rowset_warm_up_states.end()) {
1532
0
        return false;
1533
0
    }
1534
5.18k
    if (it->second.state.trigger_source != source) {
1535
        // Only the same trigger source can update the state
1536
2
        return false;
1537
2
    }
1538
5.18k
    it->second.num_inverted_idx += delta;
1539
5.18k
    return true;
1540
5.18k
}
1541
1542
bool CloudTablet::add_rowset_warmup_state_unlocked(const RowsetMeta& rowset,
1543
                                                   WarmUpTriggerSource source,
1544
51.9k
                                                   std::chrono::steady_clock::time_point start_tp) {
1545
51.9k
    auto rowset_id = rowset.rowset_id();
1546
1547
    // Check if rowset already has warmup state
1548
51.9k
    if (_rowset_warm_up_states.contains(rowset_id)) {
1549
10
        auto existing_state = _rowset_warm_up_states[rowset_id].state;
1550
1551
        // For job-triggered warmup (one-time and periodic warmup), allow it to proceed
1552
        // except when there's already another job-triggered warmup in progress
1553
10
        if (source == WarmUpTriggerSource::JOB) {
1554
5
            if (existing_state.trigger_source == WarmUpTriggerSource::JOB &&
1555
5
                existing_state.progress == WarmUpProgress::DOING) {
1556
                // Same job type already in progress, skip to avoid duplicate warmup
1557
1
                return false;
1558
1
            }
1559
5
        } else {
1560
            // For non-job warmup (EVENT_DRIVEN, SYNC_ROWSET), skip if any warmup exists
1561
5
            return false;
1562
5
        }
1563
10
    }
1564
1565
51.9k
    if (source == WarmUpTriggerSource::JOB) {
1566
9
        g_file_cache_warm_up_rowset_triggered_by_job_num << 1;
1567
51.9k
    } else if (source == WarmUpTriggerSource::SYNC_ROWSET) {
1568
51.9k
        g_file_cache_warm_up_rowset_triggered_by_sync_rowset_num << 1;
1569
51.9k
    } else if (source == WarmUpTriggerSource::EVENT_DRIVEN) {
1570
13
        g_file_cache_warm_up_rowset_triggered_by_event_driven_num << 1;
1571
13
    }
1572
51.9k
    _rowset_warm_up_states[rowset_id] = {
1573
51.9k
            .state = {.trigger_source = source,
1574
51.9k
                      .progress = (rowset.num_segments() == 0 ? WarmUpProgress::DONE
1575
51.9k
                                                              : WarmUpProgress::DOING)},
1576
51.9k
            .num_segments = rowset.num_segments(),
1577
51.9k
            .start_tp = start_tp};
1578
51.9k
    return true;
1579
51.9k
}
1580
1581
57.1k
void CloudTablet::RowsetWarmUpInfo::update_state() {
1582
57.1k
    if (has_finished()) {
1583
51.8k
        g_file_cache_warm_up_rowset_complete_num << 1;
1584
51.8k
        auto cost = std::chrono::duration_cast<std::chrono::milliseconds>(
1585
51.8k
                            std::chrono::steady_clock::now() - start_tp)
1586
51.8k
                            .count();
1587
51.8k
        g_file_cache_warm_up_rowset_all_segments_latency << cost;
1588
51.8k
        state.progress = WarmUpProgress::DONE;
1589
51.8k
    }
1590
57.1k
}
1591
1592
WarmUpState CloudTablet::complete_rowset_segment_warmup(WarmUpTriggerSource trigger_source,
1593
                                                        RowsetId rowset_id, Status status,
1594
                                                        int64_t segment_num,
1595
57.1k
                                                        int64_t inverted_idx_num) {
1596
57.1k
    std::lock_guard wlock(_meta_lock);
1597
57.1k
    auto it = _rowset_warm_up_states.find(rowset_id);
1598
57.1k
    if (it == _rowset_warm_up_states.end()) {
1599
1
        return {.trigger_source = WarmUpTriggerSource::NONE, .progress = WarmUpProgress::NONE};
1600
1
    }
1601
57.1k
    auto& warmup_info = it->second;
1602
57.1k
    if (warmup_info.state.trigger_source != trigger_source) {
1603
        // Only the same trigger source can update the state
1604
2
        return warmup_info.state;
1605
2
    }
1606
18.4E
    VLOG_DEBUG << "complete rowset segment warmup for rowset " << rowset_id << ", " << status;
1607
57.0k
    if (segment_num > 0) {
1608
51.9k
        g_file_cache_warm_up_segment_complete_num << segment_num;
1609
51.9k
        if (!status.ok()) {
1610
1
            g_file_cache_warm_up_segment_failed_num << segment_num;
1611
1
        }
1612
51.9k
    }
1613
57.0k
    if (inverted_idx_num > 0) {
1614
5.17k
        g_file_cache_warm_up_inverted_idx_complete_num << inverted_idx_num;
1615
5.17k
        if (!status.ok()) {
1616
0
            g_file_cache_warm_up_inverted_idx_failed_num << inverted_idx_num;
1617
0
        }
1618
5.17k
    }
1619
57.0k
    warmup_info.done(segment_num, inverted_idx_num);
1620
57.0k
    return warmup_info.state;
1621
57.1k
}
1622
1623
342
bool CloudTablet::is_rowset_warmed_up(const RowsetId& rowset_id) const {
1624
342
    auto it = _rowset_warm_up_states.find(rowset_id);
1625
342
    if (it == _rowset_warm_up_states.end()) {
1626
        // The rowset is not in warmup state, which means the rowset has never been warmed up.
1627
        // This may happen when the upstream BE tried to warm up rowsets on this BE but this BE
1628
        // was restarting so the warmup failed, and _rowset_warm_up_states has no entry for it.
1629
        //
1630
        // Normally the startup_timepoint check in rowset_is_warmed_up_unlocked() would filter out
1631
        // such rowsets (visible_timestamp < startup_timepoint → assumed warmed up). However,
1632
        // compaction-produced rowsets have their visible_timestamp set at rowset builder
1633
        // initialization time rather than the final transaction commit time on meta-service,
1634
        // so their visible_timestamp can be earlier than startup_timepoint, causing the
1635
        // startup_timepoint check to NOT filter them out and reaching here with no warmup entry.
1636
        //
1637
        // If such a rowset is before the cumulative compaction point and base compaction never
1638
        // happens, returning false here would cause the version path algorithm to exclude it,
1639
        // leading to a persistently low path_max_version. With continuous upstream ingestion,
1640
        // the freshness tolerance fallback check would keep triggering, making every query on
1641
        // this tablet fall back to reading all data from remote storage.
1642
        //
1643
        // Returning true (optimistically treating it as warmed up) allows the version path to
1644
        // include it. On cache miss the data is transparently read from remote storage per-segment
1645
        // and cached locally in 1MB blocks, so the problem self-heals through subsequent queries.
1646
7
        g_rowset_warmup_state_missing_count << 1;
1647
7
        LOG_EVERY_N(WARNING, 100) << fmt::format(
1648
1
                "rowset warmup state missing, considering it as warmed up. tablet_id={}, "
1649
1
                "rowset_id={}",
1650
1
                tablet_id(), rowset_id.to_string());
1651
7
        return true;
1652
7
    }
1653
335
    return it->second.state.progress == WarmUpProgress::DONE;
1654
342
}
1655
1656
675
void CloudTablet::add_warmed_up_rowset(const RowsetId& rowset_id) {
1657
675
    _rowset_warm_up_states[rowset_id] = {
1658
675
            .state = {.trigger_source = WarmUpTriggerSource::SYNC_ROWSET,
1659
675
                      .progress = WarmUpProgress::DONE},
1660
675
            .num_segments = 1,
1661
675
            .start_tp = std::chrono::steady_clock::now()};
1662
675
}
1663
1664
93
void CloudTablet::add_not_warmed_up_rowset(const RowsetId& rowset_id) {
1665
93
    _rowset_warm_up_states[rowset_id] = {
1666
93
            .state = {.trigger_source = WarmUpTriggerSource::SYNC_ROWSET,
1667
93
                      .progress = WarmUpProgress::DOING},
1668
93
            .num_segments = 1,
1669
93
            .start_tp = std::chrono::steady_clock::now()};
1670
93
}
1671
1672
bool CloudTablet::_check_rowset_should_be_visible_but_not_warmed_up(
1673
        const RowsetMetaSharedPtr& rs_meta, int64_t path_max_version,
1674
501
        std::chrono::system_clock::time_point freshness_limit_tp) const {
1675
501
    if (rs_meta->version() == Version {0, 1}) {
1676
        // skip rowset[0-1]
1677
26
        return false;
1678
26
    }
1679
475
    bool ret = rs_meta->start_version() > path_max_version &&
1680
475
               rs_meta->visible_timestamp() < freshness_limit_tp;
1681
475
    if (ret && config::read_cluster_cache_opt_verbose_log) {
1682
5
        using namespace std::chrono;
1683
5
        std::time_t t1 = system_clock::to_time_t(rs_meta->visible_timestamp());
1684
5
        std::tm tm1 = *std::localtime(&t1);
1685
5
        std::ostringstream oss1;
1686
5
        oss1 << std::put_time(&tm1, "%Y-%m-%d %H:%M:%S");
1687
1688
5
        std::time_t t2 = system_clock::to_time_t(freshness_limit_tp);
1689
5
        std::tm tm2 = *std::localtime(&t2);
1690
5
        std::ostringstream oss2;
1691
5
        oss2 << std::put_time(&tm2, "%Y-%m-%d %H:%M:%S");
1692
5
        LOG_INFO(
1693
5
                "[verbose] CloudTablet::capture_rs_readers_with_freshness_tolerance, "
1694
5
                "find a rowset which should be visible but not warmed up, tablet_id={}, "
1695
5
                "path_max_version={}, rowset_id={}, version={}, visible_time={}, "
1696
5
                "freshness_limit={}, version_graph={}, rowset_warmup_digest={}",
1697
5
                tablet_id(), path_max_version, rs_meta->rowset_id().to_string(),
1698
5
                rs_meta->version().to_string(), oss1.str(), oss2.str(),
1699
5
                _timestamped_version_tracker.debug_string(), rowset_warmup_digest());
1700
5
    }
1701
475
    return ret;
1702
501
}
1703
1704
void CloudTablet::_submit_segment_download_task(const RowsetSharedPtr& rs,
1705
                                                const StorageResource* storage_resource, int seg_id,
1706
1707
51.9k
                                                int64_t expiration_time) {
1708
    // clang-format off
1709
51.9k
    const auto& rowset_meta = rs->rowset_meta();
1710
51.9k
    auto self = std::dynamic_pointer_cast<CloudTablet>(shared_from_this());
1711
    // Use rowset_meta->fs() instead of storage_resource->fs to support packed file.
1712
    // RowsetMeta::fs() wraps the underlying FileSystem with PackedFileSystem when
1713
    // packed_slice_locations is not empty, which correctly maps segment file paths
1714
    // to their actual locations within packed files.
1715
51.9k
    auto file_system = rowset_meta->fs();
1716
51.9k
    if (!file_system) {
1717
0
        LOG(WARNING) << "failed to get file system for tablet_id=" << _tablet_meta->tablet_id()
1718
0
                     << ", rowset_id=" << rowset_meta->rowset_id();
1719
0
        return;
1720
0
    }
1721
51.9k
    _engine.file_cache_block_downloader().submit_download_task(io::DownloadFileMeta {
1722
51.9k
            .path = storage_resource->remote_segment_path(*rowset_meta, seg_id),
1723
51.9k
            .file_size = rs->rowset_meta()->segment_file_size(seg_id),
1724
51.9k
            .file_system = file_system,
1725
51.9k
            .ctx = {
1726
51.9k
                    .expiration_time = expiration_time,
1727
51.9k
                    .is_dryrun = config::enable_reader_dryrun_when_download_file_cache,
1728
51.9k
                    .is_warmup = true
1729
51.9k
            },
1730
51.9k
            .download_done {[=](Status st) {
1731
51.9k
                DBUG_EXECUTE_IF("CloudTablet::add_rowsets.download_data.callback.block_compaction_rowset", {
1732
51.9k
                            if (rs->version().second > rs->version().first) {
1733
51.9k
                                auto sleep_time = dp->param<int>("sleep", 3);
1734
51.9k
                                LOG_INFO(
1735
51.9k
                                        "[verbose] block download for rowset={}, "
1736
51.9k
                                        "version={}, sleep={}",
1737
51.9k
                                        rs->rowset_id().to_string(),
1738
51.9k
                                        rs->version().to_string(), sleep_time);
1739
51.9k
                                std::this_thread::sleep_for(
1740
51.9k
                                        std::chrono::seconds(sleep_time));
1741
51.9k
                            }
1742
51.9k
                });
1743
51.9k
                self->complete_rowset_segment_warmup(WarmUpTriggerSource::SYNC_ROWSET, rowset_meta->rowset_id(), st, 1, 0);
1744
51.9k
                if (!st) {
1745
0
                    LOG_WARNING("add rowset warm up error ").error(st);
1746
0
                }
1747
51.9k
            }},
1748
51.9k
            .tablet_id = _tablet_meta->tablet_id(),
1749
51.9k
    });
1750
    // clang-format on
1751
51.9k
}
1752
1753
void CloudTablet::_submit_inverted_index_download_task(const RowsetSharedPtr& rs,
1754
                                                       const StorageResource* storage_resource,
1755
                                                       const io::Path& idx_path, int64_t idx_size,
1756
5.17k
                                                       int64_t expiration_time) {
1757
    // clang-format off
1758
5.17k
    const auto& rowset_meta = rs->rowset_meta();
1759
5.17k
    auto self = std::dynamic_pointer_cast<CloudTablet>(shared_from_this());
1760
    // Use rowset_meta->fs() instead of storage_resource->fs to support packed file for idx files.
1761
5.17k
    auto file_system = rowset_meta->fs();
1762
5.17k
    if (!file_system) {
1763
0
        LOG(WARNING) << "failed to get file system for tablet_id=" << _tablet_meta->tablet_id()
1764
0
                     << ", rowset_id=" << rowset_meta->rowset_id();
1765
0
        return;
1766
0
    }
1767
5.17k
    io::DownloadFileMeta meta {
1768
5.17k
            .path = idx_path,
1769
5.17k
            .file_size = idx_size,
1770
5.17k
            .file_system = file_system,
1771
5.17k
            .ctx = {
1772
5.17k
                    .expiration_time = expiration_time,
1773
5.17k
                    .is_dryrun = config::enable_reader_dryrun_when_download_file_cache,
1774
5.17k
                    .is_warmup = true
1775
5.17k
            },
1776
5.17k
            .download_done {[=](Status st) {
1777
5.17k
                DBUG_EXECUTE_IF("CloudTablet::add_rowsets.download_idx.callback.block", {
1778
5.17k
                    auto sleep_time = dp->param<int>("sleep", 3);
1779
5.17k
                    LOG_INFO(
1780
5.17k
                            "[verbose] block download for "
1781
5.17k
                            "rowset={}, inverted_idx_file={}, "
1782
5.17k
                            "sleep={}",
1783
5.17k
                            rs->rowset_id().to_string(), idx_path.string(), sleep_time);
1784
5.17k
                    std::this_thread::sleep_for(std::chrono::seconds(sleep_time));
1785
5.17k
                });
1786
5.17k
                self->complete_rowset_segment_warmup(WarmUpTriggerSource::SYNC_ROWSET, rowset_meta->rowset_id(), st, 0, 1);
1787
5.17k
                if (!st) {
1788
0
                    LOG_WARNING("add rowset warm up error ").error(st);
1789
0
                }
1790
5.17k
            }},
1791
5.17k
            .tablet_id = _tablet_meta->tablet_id(),
1792
5.17k
    };
1793
5.17k
    self->update_rowset_warmup_state_inverted_idx_num_unlocked(WarmUpTriggerSource::SYNC_ROWSET, rowset_meta->rowset_id(), 1);
1794
5.17k
    _engine.file_cache_block_downloader().submit_download_task(std::move(meta));
1795
5.17k
    g_file_cache_cloud_tablet_submitted_index_num << 1;
1796
5.17k
    g_file_cache_cloud_tablet_submitted_index_size << idx_size;
1797
    // clang-format on
1798
5.17k
}
1799
1800
void CloudTablet::_add_rowsets_directly(std::vector<RowsetSharedPtr>& rowsets,
1801
305k
                                        bool warmup_delta_data) {
1802
#ifdef BE_TEST
1803
    warmup_delta_data = false;
1804
#endif
1805
321k
    for (auto& rs : rowsets) {
1806
321k
        if (warmup_delta_data) {
1807
            // Pre-set encryption algorithm to avoid re-entrant get_tablet() call
1808
            // inside RowsetMeta::fs() which causes SingleFlight deadlock when the
1809
            // tablet is not yet cached (during initial load_tablet).
1810
170k
            rs->rowset_meta()->set_encryption_algorithm(_tablet_meta->encryption_algorithm());
1811
170k
            bool warm_up_state_updated = false;
1812
            // Warmup rowset data in background
1813
222k
            for (int seg_id = 0; seg_id < rs->num_segments(); ++seg_id) {
1814
51.9k
                const auto& rowset_meta = rs->rowset_meta();
1815
51.9k
                constexpr int64_t interval = 600; // 10 mins
1816
                // When BE restart and receive the `load_sync` rpc, it will sync all historical rowsets first time.
1817
                // So we need to filter out the old rowsets avoid to download the whole table.
1818
51.9k
                if (warmup_delta_data &&
1819
51.9k
                    ::time(nullptr) - rowset_meta->newest_write_timestamp() >= interval) {
1820
0
                    continue;
1821
0
                }
1822
1823
51.9k
                auto storage_resource = rowset_meta->remote_storage_resource();
1824
51.9k
                if (!storage_resource) {
1825
0
                    LOG(WARNING) << storage_resource.error();
1826
0
                    continue;
1827
0
                }
1828
1829
51.9k
                int64_t expiration_time = _tablet_meta->ttl_seconds();
1830
51.9k
                g_file_cache_cloud_tablet_submitted_segment_num << 1;
1831
51.9k
                if (rs->rowset_meta()->segment_file_size(seg_id) > 0) {
1832
51.9k
                    g_file_cache_cloud_tablet_submitted_segment_size
1833
51.9k
                            << rs->rowset_meta()->segment_file_size(seg_id);
1834
51.9k
                }
1835
51.9k
                if (!warm_up_state_updated) {
1836
51.9k
                    VLOG_DEBUG << "warm up rowset " << rs->version() << "(" << rs->rowset_id()
1837
1
                               << ") triggerd by sync rowset";
1838
51.9k
                    if (!add_rowset_warmup_state_unlocked(*(rs->rowset_meta()),
1839
51.9k
                                                          WarmUpTriggerSource::SYNC_ROWSET)) {
1840
0
                        LOG(INFO) << "found duplicate warmup task for rowset " << rs->rowset_id()
1841
0
                                  << ", skip it";
1842
0
                        break;
1843
0
                    }
1844
51.9k
                    warm_up_state_updated = true;
1845
51.9k
                }
1846
1847
51.9k
                if (!config::file_cache_enable_only_warm_up_idx) {
1848
51.9k
                    _submit_segment_download_task(rs, storage_resource.value(), seg_id,
1849
51.9k
                                                  expiration_time);
1850
51.9k
                }
1851
1852
51.9k
                auto schema_ptr = rowset_meta->tablet_schema();
1853
51.9k
                auto idx_version = schema_ptr->get_inverted_index_storage_format();
1854
51.9k
                if (idx_version == InvertedIndexStorageFormatPB::V1) {
1855
1.14k
                    std::unordered_map<int64_t, int64_t> index_size_map;
1856
1.14k
                    auto&& inverted_index_info = rowset_meta->inverted_index_file_info(seg_id);
1857
1.14k
                    for (const auto& info : inverted_index_info.index_info()) {
1858
434
                        if (info.index_file_size() != -1) {
1859
434
                            index_size_map[info.index_id()] = info.index_file_size();
1860
434
                        } else {
1861
0
                            VLOG_DEBUG << "Invalid index_file_size for segment_id " << seg_id
1862
0
                                       << ", index_id " << info.index_id();
1863
0
                        }
1864
434
                    }
1865
1.14k
                    for (const auto& index : schema_ptr->inverted_indexes()) {
1866
434
                        auto idx_path = storage_resource.value()->remote_idx_v1_path(
1867
434
                                *rowset_meta, seg_id, index->index_id(), index->get_index_suffix());
1868
434
                        _submit_inverted_index_download_task(rs, storage_resource.value(), idx_path,
1869
434
                                                             index_size_map[index->index_id()],
1870
434
                                                             expiration_time);
1871
434
                    }
1872
50.7k
                } else {
1873
50.7k
                    if (schema_ptr->has_inverted_index() || schema_ptr->has_ann_index()) {
1874
4.74k
                        auto&& inverted_index_info = rowset_meta->inverted_index_file_info(seg_id);
1875
4.74k
                        int64_t idx_size = 0;
1876
4.74k
                        if (inverted_index_info.has_index_size()) {
1877
4.72k
                            idx_size = inverted_index_info.index_size();
1878
4.72k
                        } else {
1879
18
                            VLOG_DEBUG << "index_size is not set for segment " << seg_id;
1880
18
                        }
1881
4.74k
                        auto idx_path =
1882
4.74k
                                storage_resource.value()->remote_idx_v2_path(*rowset_meta, seg_id);
1883
4.74k
                        _submit_inverted_index_download_task(rs, storage_resource.value(), idx_path,
1884
4.74k
                                                             idx_size, expiration_time);
1885
4.74k
                    }
1886
50.7k
                }
1887
51.9k
            }
1888
170k
        }
1889
321k
        _rs_version_map.emplace(rs->version(), rs);
1890
321k
        _timestamped_version_tracker.add_version(rs->version());
1891
321k
        _max_version = std::max(rs->end_version(), _max_version);
1892
321k
        update_base_size(*rs);
1893
321k
    }
1894
305k
    _tablet_meta->add_rowsets_unchecked(rowsets);
1895
305k
}
1896
1897
870k
void CloudTablet::clear_unused_visible_pending_rowsets() {
1898
870k
    int64_t cur_max_version = max_version().second;
1899
870k
    int32_t max_version_count = max_version_config();
1900
870k
    int64_t current_time = std::chrono::duration_cast<std::chrono::seconds>(
1901
870k
                                   std::chrono::system_clock::now().time_since_epoch())
1902
870k
                                   .count();
1903
1904
870k
    std::unique_lock<std::mutex> wlock(_visible_pending_rs_lock);
1905
990k
    for (auto it = _visible_pending_rs_map.begin(); it != _visible_pending_rs_map.end();) {
1906
120k
        if (int64_t version = it->first, expiration_time = it->second.expiration_time;
1907
120k
            version <= cur_max_version || expiration_time < current_time) {
1908
120k
            it = _visible_pending_rs_map.erase(it);
1909
120k
        } else {
1910
12
            ++it;
1911
12
        }
1912
120k
    }
1913
1914
870k
    while (!_visible_pending_rs_map.empty() && _visible_pending_rs_map.size() > max_version_count) {
1915
0
        _visible_pending_rs_map.erase(--_visible_pending_rs_map.end());
1916
0
    }
1917
870k
}
1918
1919
void CloudTablet::try_make_committed_rs_visible(int64_t txn_id, int64_t visible_version,
1920
171k
                                                int64_t version_update_time_ms) {
1921
171k
    if (enable_unique_key_merge_on_write()) {
1922
        // for mow tablet, we get committed rowset from `CloudTxnDeleteBitmapCache` rather than `CommittedRowsetManager`
1923
51.5k
        try_make_committed_rs_visible_for_mow(txn_id, visible_version, version_update_time_ms);
1924
51.5k
        return;
1925
51.5k
    }
1926
1927
120k
    auto& committed_rs_mgr = _engine.committed_rs_mgr();
1928
120k
    auto res = committed_rs_mgr.get_committed_rowset(txn_id, tablet_id());
1929
120k
    if (!res.has_value()) {
1930
0
        return;
1931
0
    }
1932
120k
    auto [rowset_meta, expiration_time] = res.value();
1933
120k
    bool is_empty_rowset = (rowset_meta == nullptr);
1934
120k
    if (!is_empty_rowset) {
1935
120k
        rowset_meta->set_cloud_fields_after_visible(visible_version, version_update_time_ms);
1936
120k
    }
1937
120k
    {
1938
120k
        std::lock_guard<std::mutex> lock(_visible_pending_rs_lock);
1939
120k
        _visible_pending_rs_map.emplace(
1940
120k
                visible_version,
1941
120k
                VisiblePendingRowset {rowset_meta, expiration_time, is_empty_rowset});
1942
120k
    }
1943
120k
    apply_visible_pending_rowsets();
1944
120k
    committed_rs_mgr.remove_committed_rowset(txn_id, tablet_id());
1945
120k
}
1946
1947
void CloudTablet::try_make_committed_rs_visible_for_mow(int64_t txn_id, int64_t visible_version,
1948
51.5k
                                                        int64_t version_update_time_ms) {
1949
51.5k
    Defer defer {[&] {
1950
51.5k
        _engine.txn_delete_bitmap_cache().remove_unused_tablet_txn_info(txn_id, tablet_id());
1951
51.5k
    }};
1952
51.5k
    auto res = _engine.txn_delete_bitmap_cache().get_rowset_and_delete_bitmap(txn_id, tablet_id());
1953
51.5k
    if (!res.has_value()) {
1954
219
        return;
1955
219
    }
1956
51.3k
    auto [rowset, delete_bitmap] = res.value();
1957
51.3k
    bool is_empty_rowset = (rowset == nullptr);
1958
51.3k
    {
1959
51.3k
        std::unique_lock lock {_sync_meta_lock};
1960
51.3k
        std::unique_lock meta_wlock {_meta_lock};
1961
51.3k
        if (_max_version + 1 != visible_version) {
1962
364
            return;
1963
364
        }
1964
51.0k
        if (is_empty_rowset) {
1965
0
            Versions existing_versions;
1966
0
            for (const auto& [_, rs] : tablet_meta()->all_rs_metas()) {
1967
0
                existing_versions.emplace_back(rs->version());
1968
0
            }
1969
0
            if (existing_versions.empty()) {
1970
0
                return;
1971
0
            }
1972
0
            auto max_version = std::ranges::max(existing_versions, {}, &Version::first);
1973
0
            auto prev_rowset = get_rowset_by_version(max_version);
1974
0
            auto st = _engine.meta_mgr().create_empty_rowset_for_hole(
1975
0
                    this, visible_version, prev_rowset->rowset_meta(), &rowset);
1976
0
            if (!st.ok()) {
1977
0
                return;
1978
0
            }
1979
51.0k
        } else {
1980
209k
            for (const auto& [delete_bitmap_key, bitmap_value] : delete_bitmap->delete_bitmap) {
1981
                // skip sentinel mark, which is used for delete bitmap correctness check
1982
209k
                if (std::get<1>(delete_bitmap_key) != DeleteBitmap::INVALID_SEGMENT_ID) {
1983
7.96k
                    tablet_meta()->delete_bitmap().merge(
1984
7.96k
                            {std::get<0>(delete_bitmap_key), std::get<1>(delete_bitmap_key),
1985
7.96k
                             visible_version},
1986
7.96k
                            bitmap_value);
1987
7.96k
                }
1988
209k
            }
1989
51.0k
        }
1990
51.0k
        rowset->rowset_meta()->set_cloud_fields_after_visible(visible_version,
1991
51.0k
                                                              version_update_time_ms);
1992
51.0k
        add_rowsets({rowset}, false, meta_wlock, true);
1993
51.0k
    }
1994
51.0k
    LOG(INFO) << "mow added visible pending rowset, txn_id=" << txn_id
1995
51.0k
              << ", tablet_id=" << tablet_id() << ", version=" << visible_version
1996
51.0k
              << ", rowset_id=" << rowset->rowset_id().to_string();
1997
51.0k
}
1998
1999
120k
void CloudTablet::apply_visible_pending_rowsets() {
2000
120k
    Defer defer {[&] { clear_unused_visible_pending_rowsets(); }};
2001
2002
120k
    std::unique_lock lock(_sync_meta_lock);
2003
120k
    std::unique_lock<std::shared_mutex> meta_wlock(_meta_lock);
2004
120k
    int64_t next_version = _max_version + 1;
2005
120k
    std::vector<RowsetSharedPtr> to_add;
2006
120k
    std::lock_guard<std::mutex> pending_lock(_visible_pending_rs_lock);
2007
120k
    for (auto it = _visible_pending_rs_map.upper_bound(_max_version);
2008
239k
         it != _visible_pending_rs_map.end(); ++it) {
2009
119k
        int64_t version = it->first;
2010
119k
        if (version != next_version) break;
2011
2012
119k
        auto& pending_rs = it->second;
2013
119k
        if (pending_rs.is_empty_rowset) {
2014
3
            RowsetSharedPtr prev_rowset {nullptr};
2015
3
            if (!to_add.empty()) {
2016
1
                prev_rowset = to_add.back();
2017
2
            } else {
2018
2
                Versions existing_versions;
2019
2
                for (const auto& [_, rs] : tablet_meta()->all_rs_metas()) {
2020
1
                    existing_versions.emplace_back(rs->version());
2021
1
                }
2022
2
                if (existing_versions.empty()) {
2023
1
                    break;
2024
1
                }
2025
1
                auto max_version = std::ranges::max(existing_versions, {}, &Version::first);
2026
1
                prev_rowset = get_rowset_by_version(max_version);
2027
1
            }
2028
2
            RowsetSharedPtr rowset;
2029
2
            auto st = _engine.meta_mgr().create_empty_rowset_for_hole(
2030
2
                    this, version, prev_rowset->rowset_meta(), &rowset);
2031
2
            if (!st.ok()) {
2032
0
                return;
2033
0
            }
2034
2
            to_add.push_back(std::move(rowset));
2035
119k
        } else {
2036
119k
            RowsetSharedPtr rowset;
2037
119k
            auto st = RowsetFactory::create_rowset(nullptr, "", pending_rs.rowset_meta, &rowset);
2038
119k
            if (!st.ok()) {
2039
0
                LOG(WARNING) << "failed to create rowset from pending rowset meta, tablet_id="
2040
0
                             << tablet_id() << ", version=" << version
2041
0
                             << ", rowset_id=" << pending_rs.rowset_meta->rowset_id().to_string()
2042
0
                             << ", error=" << st;
2043
0
                break;
2044
0
            }
2045
119k
            to_add.push_back(std::move(rowset));
2046
119k
        }
2047
119k
        next_version++;
2048
119k
    }
2049
120k
    if (!to_add.empty()) {
2050
119k
        add_rowsets(to_add, false, meta_wlock, true);
2051
119k
        LOG_INFO(
2052
119k
                "applied_visible_pending_rowsets, tablet_id={}, new_max_version={}, "
2053
119k
                "count={}, new_rowsets={}",
2054
119k
                tablet_id(), _max_version, to_add.size(),
2055
119k
                fmt::join(to_add | std::views::transform([](const RowsetSharedPtr& rs) {
2056
119k
                              return fmt::format("{}{}", rs->rowset_id().to_string(),
2057
119k
                                                 rs->version().to_string());
2058
119k
                          }),
2059
119k
                          ","));
2060
119k
    }
2061
120k
}
2062
2063
} // namespace doris