Coverage Report

Created: 2026-08-14 18:42

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