Coverage Report

Created: 2026-08-14 23:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/cloud/cloud_delta_writer.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_delta_writer.h"
19
20
#include "cloud/cloud_meta_mgr.h"
21
#include "cloud/cloud_rowset_builder.h"
22
#include "cloud/cloud_storage_engine.h"
23
#include "cloud/config.h"
24
#include "load/delta_writer/delta_writer.h"
25
#include "load/memtable/memtable_memory_limiter.h"
26
#include "runtime/exec_env.h"
27
#include "runtime/thread_context.h"
28
29
namespace doris {
30
31
bvar::Adder<int64_t> g_cloud_commit_rowset_count("cloud_commit_rowset_count");
32
bvar::Adder<int64_t> g_cloud_commit_empty_rowset_count("cloud_commit_empty_rowset_count");
33
34
CloudDeltaWriter::CloudDeltaWriter(CloudStorageEngine& engine, const WriteRequest& req,
35
                                   RuntimeProfile* profile, const UniqueId& load_id)
36
0
        : BaseDeltaWriter(req, profile, load_id) {
37
0
    _rowset_builder = std::make_unique<CloudRowsetBuilder>(engine, req, profile);
38
0
    _resource_ctx = thread_context()->resource_ctx();
39
0
}
40
41
CloudDeltaWriter::CloudDeltaWriter(CloudStorageEngine& engine, const WriteRequest& group_build_req,
42
                                   const WriteRequest& sub_data_req,
43
                                   const WriteRequest& sub_row_binlog_req, RuntimeProfile* profile,
44
                                   const UniqueId& load_id)
45
0
        : BaseDeltaWriter(group_build_req, profile, load_id) {
46
0
    DCHECK(group_build_req.write_req_type == WriteRequestType::GROUP &&
47
0
           sub_data_req.write_req_type == WriteRequestType::DATA &&
48
0
           sub_row_binlog_req.write_req_type == WriteRequestType::ROW_BINLOG);
49
0
    _rowset_builder = std::make_unique<CloudGroupRowsetBuilder>(
50
0
            engine, group_build_req, sub_data_req, sub_row_binlog_req, profile);
51
0
    _resource_ctx = thread_context()->resource_ctx();
52
0
}
53
54
0
CloudDeltaWriter::~CloudDeltaWriter() = default;
55
56
0
Status CloudDeltaWriter::batch_init(std::vector<CloudDeltaWriter*> writers) {
57
0
    if (writers.empty()) {
58
0
        return Status::OK();
59
0
    }
60
61
0
    std::vector<std::function<Status()>> tasks;
62
0
    tasks.reserve(writers.size());
63
0
    for (auto* writer : writers) {
64
0
        if (writer->_is_init || writer->_is_cancelled) {
65
0
            continue;
66
0
        }
67
68
0
        tasks.emplace_back([writer] {
69
0
            SCOPED_ATTACH_TASK(writer->resource_context());
70
0
            std::lock_guard<bthread::Mutex> lock(writer->_mtx);
71
0
            if (writer->_is_init || writer->_is_cancelled) {
72
0
                return Status::OK();
73
0
            }
74
0
            Status st = writer->init(); // included in SCOPED_ATTACH_TASK
75
0
            return st;
76
0
        });
77
0
    }
78
79
0
    return cloud::bthread_fork_join(tasks, 10);
80
0
}
81
82
Status CloudDeltaWriter::write(const Block* block, const TabletAddRowsPayload& rows,
83
0
                               bool* memtable_flushed) {
84
0
    if (memtable_flushed != nullptr) {
85
0
        *memtable_flushed = false;
86
0
    }
87
0
    if (rows.row_idxs.empty()) [[unlikely]] {
88
0
        return Status::OK();
89
0
    }
90
0
    if (_req.enable_table_memtable_backpressure && !_req.is_high_priority) {
91
0
        ExecEnv::GetInstance()->memtable_memory_limiter()->handle_table_memtable_backpressure(
92
0
                [this]() {
93
0
                    std::lock_guard lock(_mtx);
94
0
                    return _is_cancelled;
95
0
                },
96
0
                table_id());
97
0
    }
98
0
    std::lock_guard lock(_mtx);
99
0
    CHECK(_is_init || _is_cancelled);
100
0
    {
101
0
        SCOPED_TIMER(_wait_flush_limit_timer);
102
0
        while (_memtable_writer->flush_running_count() >=
103
0
               config::memtable_flush_running_count_limit) {
104
0
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
105
0
        }
106
0
    }
107
0
    return _memtable_writer->write(block, rows, memtable_flushed);
108
0
}
109
110
0
Status CloudDeltaWriter::close() {
111
0
    std::lock_guard lock(_mtx);
112
0
    CHECK(_is_init);
113
0
    return _memtable_writer->close();
114
0
}
115
116
0
Status CloudDeltaWriter::flush_memtable_async() {
117
0
    std::lock_guard lock(_mtx);
118
0
    return BaseDeltaWriter::flush_memtable_async();
119
0
}
120
121
0
Status CloudDeltaWriter::cancel_with_status(const Status& st) {
122
0
    std::lock_guard lock(_mtx);
123
0
    return BaseDeltaWriter::cancel_with_status(st);
124
0
}
125
126
0
Status CloudDeltaWriter::build_rowset() {
127
0
    std::lock_guard lock(_mtx);
128
0
    CHECK(_is_init);
129
0
    return BaseDeltaWriter::build_rowset();
130
0
}
131
132
0
CloudRowsetBuilder* CloudDeltaWriter::rowset_builder() {
133
0
    return static_cast<CloudRowsetBuilder*>(_rowset_builder.get());
134
0
}
135
136
0
void CloudDeltaWriter::update_tablet_stats() {
137
0
    rowset_builder()->update_tablet_stats();
138
0
}
139
140
0
Status CloudDeltaWriter::commit_rowset() {
141
0
    g_cloud_commit_rowset_count << 1;
142
0
    std::lock_guard<bthread::Mutex> lock(_mtx);
143
144
    // Handle empty rowset (no data written)
145
0
    if (!_is_init) {
146
0
        g_cloud_commit_empty_rowset_count << 1;
147
0
        return _commit_empty_rowset();
148
0
    }
149
150
0
    return rowset_builder()->commit_rowset("", _rowset_builder->tablet()->table_id());
151
0
}
152
153
0
Status CloudDeltaWriter::_commit_empty_rowset() {
154
    // If skip writing empty rowset metadata is enabled,
155
    // we do not prepare rowset to meta service.
156
0
    if (config::skip_writing_empty_rowset_metadata) {
157
0
        rowset_builder()->set_skip_writing_rowset_metadata(true);
158
0
    }
159
160
0
    RETURN_IF_ERROR(_rowset_builder->init());
161
0
    RETURN_IF_ERROR(_rowset_builder->build_rowset());
162
163
    // If skip writing empty rowset metadata is enabled, we do not commit rowset to meta service.
164
0
    if (config::skip_writing_empty_rowset_metadata) {
165
0
        return Status::OK();
166
0
    }
167
    // write a empty rowset kv to keep version continuous
168
0
    return rowset_builder()->commit_rowset("", _rowset_builder->tablet()->table_id());
169
0
}
170
171
0
Status CloudDeltaWriter::set_txn_related_info() {
172
0
    return rowset_builder()->set_txn_related_info();
173
0
}
174
175
} // namespace doris