Coverage Report

Created: 2026-07-07 22:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/load/delta_writer/delta_writer_v2.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 "load/delta_writer/delta_writer_v2.h"
19
20
#include <brpc/controller.h>
21
#include <butil/errno.h>
22
#include <fmt/format.h>
23
#include <gen_cpp/internal_service.pb.h>
24
#include <gen_cpp/olap_file.pb.h>
25
26
#include <filesystem>
27
#include <ostream>
28
#include <string>
29
#include <utility>
30
31
#include "common/compiler_util.h" // IWYU pragma: keep
32
#include "common/config.h"
33
#include "common/logging.h"
34
#include "common/status.h"
35
#include "core/block/block.h"
36
#include "exec/sink/load_stream_stub.h"
37
#include "io/fs/file_writer.h" // IWYU pragma: keep
38
#include "load/memtable/memtable_memory_limiter.h"
39
#include "runtime/exec_env.h"
40
#include "runtime/query_context.h"
41
#include "service/backend_options.h"
42
#include "storage/data_dir.h"
43
#include "storage/index/inverted/inverted_index_desc.h"
44
#include "storage/olap_define.h"
45
#include "storage/rowset/beta_rowset.h"
46
#include "storage/rowset/beta_rowset_writer_v2.h"
47
#include "storage/rowset/rowset_meta.h"
48
#include "storage/rowset/rowset_writer.h"
49
#include "storage/rowset/rowset_writer_context.h"
50
#include "storage/schema.h"
51
#include "storage/schema_change/schema_change.h"
52
#include "storage/segment/segment.h"
53
#include "storage/storage_engine.h"
54
#include "storage/tablet/tablet_manager.h"
55
#include "storage/tablet/tablet_schema.h"
56
#include "storage/tablet/tablet_schema_cache.h"
57
#include "storage/tablet_info.h"
58
#include "util/brpc_client_cache.h"
59
#include "util/brpc_closure.h"
60
#include "util/debug_points.h"
61
#include "util/mem_info.h"
62
#include "util/stopwatch.hpp"
63
#include "util/time.h"
64
65
namespace doris {
66
using namespace ErrorCode;
67
68
DeltaWriterV2::DeltaWriterV2(WriteRequest* req,
69
                             const std::vector<std::shared_ptr<LoadStreamStub>>& streams,
70
                             std::shared_ptr<WorkloadGroup> workload_group)
71
6
        : _req(*req),
72
6
          _workload_group(std::move(workload_group)),
73
6
          _tablet_schema(new TabletSchema),
74
6
          _memtable_writer(new MemTableWriter(*req)),
75
6
          _streams(streams) {}
76
77
0
void DeltaWriterV2::_update_profile(RuntimeProfile* profile) {
78
0
    auto child = profile->create_child(fmt::format("DeltaWriterV2 {}", _req.tablet_id), true, true);
79
0
    auto write_memtable_timer = ADD_TIMER(child, "WriteMemTableTime");
80
0
    auto wait_flush_limit_timer = ADD_TIMER(child, "WaitFlushLimitTime");
81
0
    auto close_wait_timer = ADD_TIMER(child, "CloseWaitTime");
82
0
    COUNTER_SET(write_memtable_timer, _write_memtable_time);
83
0
    COUNTER_SET(wait_flush_limit_timer, _wait_flush_limit_time);
84
0
    COUNTER_SET(close_wait_timer, _close_wait_time);
85
0
}
86
87
6
DeltaWriterV2::~DeltaWriterV2() {
88
6
    if (!_is_init) {
89
4
        return;
90
4
    }
91
92
    // cancel and wait all memtables in flush queue to be finished
93
2
    static_cast<void>(_memtable_writer->cancel());
94
2
}
95
96
0
int64_t DeltaWriterV2::_table_id() const {
97
0
    DORIS_CHECK(_req.table_schema_param != nullptr);
98
0
    return _req.table_schema_param->table_id();
99
0
}
100
101
4
Status DeltaWriterV2::init() {
102
4
    if (_is_init) {
103
0
        return Status::OK();
104
0
    }
105
    // build tablet schema in request level
106
4
    DBUG_EXECUTE_IF("DeltaWriterV2.init.stream_size", { _streams.clear(); });
107
4
    if (_streams.size() == 0 || _streams[0]->tablet_schema(_req.index_id) == nullptr) {
108
2
        return Status::InternalError("failed to find tablet schema for {}", _req.index_id);
109
2
    }
110
2
    RETURN_IF_ERROR(_build_current_tablet_schema(_req.index_id, _req.table_schema_param.get(),
111
2
                                                 *_streams[0]->tablet_schema(_req.index_id)));
112
2
    RowsetWriterContext context;
113
2
    context.txn_id = _req.txn_id;
114
2
    context.load_id = _req.load_id;
115
2
    context.index_id = _req.index_id;
116
2
    context.partition_id = _req.partition_id;
117
2
    context.rowset_state = PREPARED;
118
2
    context.segments_overlap = OVERLAPPING;
119
2
    context.tablet_schema = _tablet_schema;
120
2
    context.db_id = _tablet_schema->db_id();
121
2
    context.table_id = _tablet_schema->table_id();
122
2
    context.newest_write_timestamp = UnixSeconds();
123
2
    context.tablet = nullptr;
124
2
    context.write_type = DataWriteType::TYPE_DIRECT;
125
2
    context.tablet_id = _req.tablet_id;
126
2
    context.partition_id = _req.partition_id;
127
2
    context.tablet_schema_hash = _req.schema_hash;
128
2
    context.enable_unique_key_merge_on_write = _streams[0]->enable_unique_mow(_req.index_id);
129
2
    context.rowset_type = RowsetTypePB::BETA_ROWSET;
130
2
    context.rowset_id = ExecEnv::GetInstance()->storage_engine().next_rowset_id();
131
2
    context.data_dir = nullptr;
132
2
    context.partial_update_info = _partial_update_info;
133
2
    context.memtable_on_sink_support_index_v2 = true;
134
2
    context.encrypt_algorithm = EncryptionAlgorithmPB::PLAINTEXT;
135
136
2
    _rowset_writer = std::make_shared<BetaRowsetWriterV2>(_streams);
137
2
    RETURN_IF_ERROR(_rowset_writer->init(context));
138
2
    RETURN_IF_ERROR(_memtable_writer->init(_rowset_writer, _tablet_schema, _partial_update_info,
139
2
                                           _workload_group,
140
2
                                           _streams[0]->enable_unique_mow(_req.index_id)));
141
2
    ExecEnv::GetInstance()->memtable_memory_limiter()->register_writer(_memtable_writer);
142
2
    _is_init = true;
143
2
    _streams.clear();
144
2
    return Status::OK();
145
2
}
146
147
Status DeltaWriterV2::write(const Block* block, const DorisVector<uint32_t>& row_idxs,
148
2
                            const std::function<Status()>& cancel_check, bool* memtable_flushed) {
149
2
    if (memtable_flushed != nullptr) {
150
2
        *memtable_flushed = false;
151
2
    }
152
2
    if (UNLIKELY(row_idxs.empty())) {
153
0
        return Status::OK();
154
0
    }
155
2
    if (_req.enable_table_memtable_backpressure && !_req.is_high_priority) {
156
0
        Status cancel_status = Status::OK();
157
0
        ExecEnv::GetInstance()->memtable_memory_limiter()->handle_table_memtable_backpressure(
158
0
                [&]() {
159
0
                    cancel_status = cancel_check();
160
0
                    return !cancel_status.ok();
161
0
                },
162
0
                _table_id());
163
0
        RETURN_IF_ERROR(cancel_status);
164
0
    }
165
2
    _lock_watch.start();
166
2
    std::lock_guard<std::mutex> l(_lock);
167
2
    _lock_watch.stop();
168
2
    if (!_is_init && !_is_cancelled) {
169
2
        RETURN_IF_ERROR(init());
170
2
    }
171
2
    {
172
2
        SCOPED_RAW_TIMER(&_wait_flush_limit_time);
173
2
        auto memtable_flush_running_count_limit = config::memtable_flush_running_count_limit;
174
2
        DBUG_EXECUTE_IF("DeltaWriterV2.write.back_pressure",
175
2
                        { std::this_thread::sleep_for(std::chrono::milliseconds(10 * 1000)); });
176
2
        while (_memtable_writer->flush_running_count() >= memtable_flush_running_count_limit) {
177
0
            DBUG_EXECUTE_IF("DeltaWriterV2.write.flush_limit_wait", DBUG_RUN_CALLBACK());
178
0
            RETURN_IF_ERROR(cancel_check());
179
0
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
180
0
        }
181
2
    }
182
2
    SCOPED_RAW_TIMER(&_write_memtable_time);
183
2
    return _memtable_writer->write(block, row_idxs, memtable_flushed);
184
2
}
185
186
2
Status DeltaWriterV2::close() {
187
2
    _lock_watch.start();
188
2
    std::lock_guard<std::mutex> l(_lock);
189
2
    _lock_watch.stop();
190
2
    if (!_is_init && !_is_cancelled) {
191
        // if this delta writer is not initialized, but close() is called.
192
        // which means this tablet has no data loaded, but at least one tablet
193
        // in same partition has data loaded.
194
        // so we have to also init this DeltaWriterV2, so that it can create an empty rowset
195
        // for this tablet when being closed.
196
2
        RETURN_IF_ERROR(init());
197
2
    }
198
0
    return _memtable_writer->close();
199
2
}
200
201
0
Status DeltaWriterV2::close_wait(int32_t& num_segments, RuntimeProfile* profile) {
202
0
    SCOPED_RAW_TIMER(&_close_wait_time);
203
0
    std::lock_guard<std::mutex> l(_lock);
204
0
    DCHECK(_is_init)
205
0
            << "delta writer is supposed be to initialized before close_wait() being called";
206
207
0
    if (profile != nullptr) {
208
0
        _update_profile(profile);
209
0
    }
210
0
    RETURN_IF_ERROR(_memtable_writer->close_wait(profile));
211
0
    num_segments = _rowset_writer->next_segment_id();
212
213
0
    _delta_written_success = true;
214
0
    return Status::OK();
215
0
}
216
217
0
Status DeltaWriterV2::cancel() {
218
0
    return cancel_with_status(Status::Cancelled("already cancelled"));
219
0
}
220
221
2
Status DeltaWriterV2::cancel_with_status(const Status& st) {
222
2
    std::lock_guard<std::mutex> l(_lock);
223
2
    if (_is_cancelled) {
224
0
        return Status::OK();
225
0
    }
226
2
    RETURN_IF_ERROR(_memtable_writer->cancel_with_status(st));
227
2
    _is_cancelled = true;
228
2
    return Status::OK();
229
2
}
230
231
Status DeltaWriterV2::_build_current_tablet_schema(int64_t index_id,
232
                                                   const OlapTableSchemaParam* table_schema_param,
233
2
                                                   const TabletSchema& ori_tablet_schema) {
234
    // find the right index id
235
2
    const OlapTableIndexSchema* index_schema = nullptr;
236
2
    for (const auto* schema : table_schema_param->indexes()) {
237
2
        if (schema->index_id == index_id) {
238
2
            index_schema = schema;
239
2
            break;
240
2
        }
241
2
    }
242
243
2
    auto cache_key = TabletSchemaCache::build_load_schema_cache_key(
244
2
            index_id, table_schema_param, ori_tablet_schema, index_schema);
245
2
    auto cached_schema = TabletSchemaCache::instance()->lookup_schema(cache_key);
246
2
    if (cached_schema.first != nullptr) {
247
0
        _tablet_schema = cached_schema.second;
248
0
        TabletSchemaCache::instance()->release(cached_schema.first);
249
2
    } else {
250
2
        _tablet_schema->copy_from(ori_tablet_schema);
251
2
        if (index_schema != nullptr && !index_schema->columns.empty() &&
252
2
            index_schema->columns[0]->unique_id() >= 0) {
253
2
            _tablet_schema->build_current_tablet_schema(
254
2
                    index_id, static_cast<int32_t>(table_schema_param->version()), index_schema,
255
2
                    ori_tablet_schema);
256
2
        }
257
2
        _tablet_schema->set_table_id(table_schema_param->table_id());
258
2
        _tablet_schema->set_db_id(table_schema_param->db_id());
259
2
        if (table_schema_param->is_partial_update()) {
260
0
            _tablet_schema->set_auto_increment_column(table_schema_param->auto_increment_coulumn());
261
0
        }
262
2
        auto inserted_schema = TabletSchemaCache::instance()->insert(cache_key, _tablet_schema);
263
2
        _tablet_schema = inserted_schema.second;
264
2
        TabletSchemaCache::instance()->release(inserted_schema.first);
265
2
    }
266
267
    // set partial update columns info
268
2
    _partial_update_info = std::make_shared<PartialUpdateInfo>();
269
2
    RETURN_IF_ERROR(_partial_update_info->init(
270
2
            _req.tablet_id, _req.txn_id, *_tablet_schema,
271
2
            table_schema_param->unique_key_update_mode(),
272
2
            table_schema_param->partial_update_new_key_policy(),
273
2
            table_schema_param->partial_update_input_columns(),
274
2
            table_schema_param->is_strict_mode(), table_schema_param->timestamp_ms(),
275
2
            table_schema_param->nano_seconds(), table_schema_param->timezone(),
276
2
            table_schema_param->auto_increment_coulumn()));
277
2
    return Status::OK();
278
2
}
279
280
} // namespace doris