Coverage Report

Created: 2026-08-17 23:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/rowset/rowset_writer_context.h
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
#pragma once
19
20
#include <gen_cpp/olap_file.pb.h>
21
#include <glog/logging.h>
22
23
#include <functional>
24
#include <optional>
25
#include <string_view>
26
#include <unordered_map>
27
28
#include "cloud/config.h"
29
#include "common/status.h"
30
#include "io/fs/encrypted_fs_factory.h"
31
#include "io/fs/file_system.h"
32
#include "io/fs/file_writer.h"
33
#include "io/fs/local_file_system.h"
34
#include "io/fs/packed_file_system.h"
35
#include "runtime/exec_env.h"
36
#include "storage/binlog.h"
37
#include "storage/olap_define.h"
38
#include "storage/partial_update_info.h"
39
#include "storage/segment/historical_row_retriever.h"
40
#include "storage/storage_policy.h"
41
#include "storage/tablet/tablet.h"
42
#include "storage/tablet/tablet_schema.h"
43
44
namespace doris {
45
46
class RowsetWriterContextBuilder;
47
using RowsetWriterContextBuilderSharedPtr = std::shared_ptr<RowsetWriterContextBuilder>;
48
class DataDir;
49
class Tablet;
50
class FileWriterCreator;
51
class SegmentCollector;
52
53
namespace segment_v2 {
54
struct HistoricalRowRetrieverContext;
55
}
56
57
struct RowsetWriterContext {
58
2.74k
    RowsetWriterContext() : schema_lock(new std::mutex) {
59
2.74k
        load_id.set_hi(0);
60
2.74k
        load_id.set_lo(0);
61
2.74k
    }
62
63
    RowsetId rowset_id;
64
    int64_t db_id {0};
65
    int64_t table_id {0};
66
    int64_t tablet_id {0};
67
    int32_t tablet_schema_hash {0};
68
    int64_t index_id {0};
69
    int64_t partition_id {0};
70
    RowsetTypePB rowset_type {BETA_ROWSET};
71
72
    TabletSchemaSPtr tablet_schema;
73
74
    // PREPARED/COMMITTED for pending rowset
75
    // VISIBLE for non-pending rowset
76
    RowsetStatePB rowset_state {PREPARED};
77
    // properties for non-pending rowset
78
    Version version {0, 0};
79
80
    // properties for pending rowset
81
    int64_t txn_id {0};
82
    int64_t txn_expiration {0}; // For cloud mode
83
    PUniqueId load_id;
84
    TabletUid tablet_uid {0, 0};
85
    // indicate whether the data among segments is overlapping.
86
    // default is OVERLAP_UNKNOWN.
87
    SegmentsOverlapPB segments_overlap {OVERLAP_UNKNOWN};
88
    // segment file use uint32 to represent row number, therefore the maximum is UINT32_MAX.
89
    // the default is set to INT32_MAX to avoid overflow issue when casting from uint32_t to int.
90
    // test cases can change this value to control flush timing
91
    uint32_t max_rows_per_segment = INT32_MAX;
92
    // not owned, point to the data dir of this rowset
93
    // for checking disk capacity when write data to disk.
94
    // ATTN: not support for RowsetConvertor.
95
    // (because it hard to refactor, and RowsetConvertor will be deprecated in future)
96
    DataDir* data_dir = nullptr;
97
98
    int64_t newest_write_timestamp = -1;
99
    bool enable_unique_key_merge_on_write = false;
100
    // store column_unique_id to do index compaction
101
    std::set<int32_t> columns_to_do_index_compaction;
102
    // SNII only: (column_unique_id, index_id) pairs whose postings are produced
103
    // by index compaction. The segment writer raw-builds every OTHER SNII index
104
    // of the column, so one eligible and one new index on the same column can
105
    // coexist in a single pass. V2/V3 keep columns_to_do_index_compaction:
106
    // their per-column CLucene directories cannot split an index off a column.
107
    std::set<std::pair<int32_t, int64_t>> snii_indexes_to_do_compaction;
108
    DataWriteType write_type = DataWriteType::TYPE_DEFAULT;
109
    // need to figure out the sub type of compaction
110
    ReaderType compaction_type = ReaderType::UNKNOWN;
111
    BaseTabletSPtr tablet = nullptr;
112
113
    std::shared_ptr<MowContext> mow_context;
114
    std::shared_ptr<FileWriterCreator> file_writer_creator;
115
    std::shared_ptr<SegmentCollector> segment_collector;
116
117
    // memtable_on_sink_support_index_v2 = true, we will create SinkFileWriter to send inverted index file
118
    bool memtable_on_sink_support_index_v2 = false;
119
120
    /// begin file cache opts
121
    bool write_file_cache = false;
122
    bool is_hot_data = false;
123
    uint64_t file_cache_ttl_sec = 0;
124
    uint64_t approximate_bytes_to_write = 0;
125
    // If true, compaction output only writes index files to file cache, not data files
126
    bool compaction_output_write_index_only = false;
127
    /// end file cache opts
128
129
    // segcompaction for this RowsetWriter, only enabled when importing data
130
    bool enable_segcompaction = false;
131
132
    std::shared_ptr<PartialUpdateInfo> partial_update_info;
133
134
    bool is_transient_rowset_writer = false;
135
136
    segment_v2::HistoricalRowRetrieverContext make_historical_row_retriever_context();
137
138
    // Intent flag: caller can actively turn merge-file feature on/off for this rowset.
139
    // This describes whether we *want* to try small-file merging.
140
    bool allow_packed_file = true;
141
142
    // Effective flag: whether this context actually ends up using MergeFileSystem for writes.
143
    // This is decided inside fs() based on enable_merge_file plus other conditions
144
    // (cloud mode, S3 filesystem, V1 inverted index, global config, etc.), and once
145
    // set to true it remains stable even if config::enable_merge_file changes later.
146
    mutable bool packed_file_active = false;
147
148
    // Cached FileSystem instance to ensure consistency across multiple fs() calls.
149
    // This prevents creating multiple MergeFileSystem instances and ensures
150
    // packed_file_active flag remains consistent.
151
    mutable io::FileSystemSPtr _cached_fs = nullptr;
152
153
    // For collect segment statistics for compaction
154
    std::vector<RowsetReaderSharedPtr> input_rs_readers;
155
156
    // TODO(lihangyu) remove this lock
157
    // In semi-structure senario tablet_schema will be updated concurrently,
158
    // this lock need to be held when update.Use shared_ptr to avoid delete copy contructor
159
    std::shared_ptr<std::mutex> schema_lock;
160
161
    int64_t compaction_level = 0;
162
163
    // For local rowset
164
    std::string tablet_path;
165
166
    // For remote rowset
167
    std::optional<StorageResource> storage_resource;
168
169
    std::optional<EncryptionAlgorithmPB> encrypt_algorithm;
170
171
    std::string job_id;
172
173
7.87k
    bool is_local_rowset() const { return !storage_resource; }
174
175
6.45k
    std::string segment_path(int seg_id) const {
176
6.45k
        if (is_local_rowset()) {
177
6.42k
            return local_segment_path(tablet_path, rowset_id.to_string(), seg_id);
178
6.42k
        } else {
179
32
            return storage_resource->remote_segment_path(tablet_id, rowset_id.to_string(), seg_id);
180
32
        }
181
6.45k
    }
182
183
4.01k
    io::FileSystemSPtr fs() const {
184
        // Return cached instance if available to ensure consistency across multiple calls
185
4.01k
        if (_cached_fs != nullptr) {
186
3.10k
            return _cached_fs;
187
3.10k
        }
188
189
912
        auto fs = [this]() -> io::FileSystemSPtr {
190
912
            if (is_local_rowset()) {
191
907
                return io::global_local_filesystem();
192
907
            } else {
193
5
                return storage_resource->fs;
194
5
            }
195
912
        }();
196
197
912
        bool is_s3_fs = fs->type() == io::FileSystemType::S3;
198
199
912
        auto algorithm = encrypt_algorithm;
200
201
912
        if (!algorithm.has_value()) {
202
#ifndef BE_TEST
203
            constexpr std::string_view msg =
204
                    "RowsetWriterContext::determine_encryption is not called when creating this "
205
                    "RowsetWriterContext, it will result in encrypted rowsets left unencrypted";
206
            auto st = Status::InternalError(msg);
207
208
            LOG(WARNING) << st;
209
            DCHECK(false) << st;
210
#else
211
792
            algorithm = EncryptionAlgorithmPB::PLAINTEXT;
212
792
#endif
213
792
        }
214
215
        // Apply packed file system first for write path if enabled
216
        // Create empty index_map for write path
217
        // Index information will be populated after write completes
218
912
        bool has_v1_inverted_index = tablet_schema != nullptr &&
219
912
                                     tablet_schema->has_inverted_index() &&
220
912
                                     tablet_schema->get_inverted_index_storage_format() ==
221
203
                                             InvertedIndexStorageFormatPB::V1;
222
223
912
        if (has_v1_inverted_index && allow_packed_file && config::enable_packed_file) {
224
8
            static constexpr std::string_view kMsg =
225
8
                    "Disable packed file for V1 inverted index tablet to avoid missing index "
226
8
                    "metadata (temporary workaround)";
227
8
            LOG(INFO) << kMsg << ", tablet_id=" << tablet_id << ", rowset_id=" << rowset_id;
228
8
        }
229
230
        // Only enable merge file for S3 file system, not for HDFS or other remote file systems
231
912
        packed_file_active = allow_packed_file && config::is_cloud_mode() &&
232
912
                             config::enable_packed_file && !has_v1_inverted_index && is_s3_fs;
233
234
912
        if (packed_file_active) {
235
0
            io::PackedAppendContext append_info;
236
0
            append_info.tablet_id = tablet_id;
237
0
            append_info.rowset_id = rowset_id.to_string();
238
0
            append_info.txn_id = txn_id;
239
0
            append_info.expiration_time = file_cache_ttl_sec > 0 && newest_write_timestamp > 0
240
0
                                                  ? newest_write_timestamp + file_cache_ttl_sec
241
0
                                                  : 0;
242
0
            fs = std::make_shared<io::PackedFileSystem>(fs, append_info);
243
0
        }
244
245
        // Then apply encryption on top
246
912
        if (algorithm.has_value()) {
247
912
            fs = io::make_file_system(fs, algorithm.value());
248
912
        }
249
250
        // Cache the result to ensure consistency across multiple calls
251
912
        _cached_fs = fs;
252
912
        return fs;
253
4.01k
    }
254
255
0
    io::FileSystem& fs_ref() const { return *fs(); }
256
257
4.00k
    io::FileWriterOptions get_file_writer_options(FileType file_type = FileType::SEGMENT_FILE) {
258
4.00k
        io::FileWriterOptions opts {.write_file_cache = write_file_cache,
259
4.00k
                                    .is_cold_data = is_hot_data,
260
4.00k
                                    .file_cache_expiration_time = file_cache_ttl_sec,
261
4.00k
                                    .approximate_bytes_to_write = approximate_bytes_to_write};
262
263
4.00k
        if (config::enable_file_cache_write_index_file_only) {
264
20
            opts.allow_adaptive_file_cache_write = false;
265
20
            opts.approximate_bytes_to_write = 0;
266
20
            opts.write_file_cache = file_type == FileType::INVERTED_INDEX_FILE;
267
20
            return opts;
268
20
        }
269
270
3.98k
        if (compaction_output_write_index_only && file_type == FileType::SEGMENT_FILE) {
271
4
            opts.write_file_cache = false;
272
4
            opts.allow_adaptive_file_cache_write = false;
273
4
            opts.approximate_bytes_to_write = 0;
274
4
        }
275
276
3.98k
        return opts;
277
4.00k
    }
278
279
    struct BinlogOptions {
280
    public:
281
        bool enable = false;
282
283
26
        void set_need_before(bool need_before) {
284
26
            this->_need_before = need_before;
285
26
            _segment_write_binlog_opt.write_before = need_before;
286
26
        }
287
288
76
        segment_v2::SegmentWriteBinlogOptions& write_binlog_config() {
289
76
            return _segment_write_binlog_opt;
290
76
        }
291
292
1
        const segment_v2::SegmentWriteBinlogOptions& write_binlog_config() const {
293
1
            return _segment_write_binlog_opt;
294
1
        }
295
296
    private:
297
        bool _need_before = false;
298
        segment_v2::SegmentWriteBinlogOptions _segment_write_binlog_opt;
299
    } _write_binlog_opt;
300
301
4.03k
    BinlogOptions& write_binlog_opt() { return _write_binlog_opt; }
302
303
4.22k
    const BinlogOptions& write_binlog_opt() const { return _write_binlog_opt; }
304
};
305
306
inline segment_v2::HistoricalRowRetrieverContext
307
56
RowsetWriterContext::make_historical_row_retriever_context() {
308
56
    return segment_v2::HistoricalRowRetrieverContext {
309
56
            .tablet = tablet,
310
56
            .tablet_schema = tablet_schema,
311
56
            .rowset_writer_ctx = this,
312
56
            .partial_update_info = partial_update_info,
313
56
            .is_transient_rowset_writer = is_transient_rowset_writer,
314
56
            .write_type = write_type};
315
56
}
316
317
} // namespace doris