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