/src/rocksdb/table/meta_blocks.cc
Line | Count | Source |
1 | | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
2 | | // This source code is licensed under both the GPLv2 (found in the |
3 | | // COPYING file in the root directory) and Apache 2.0 License |
4 | | // (found in the LICENSE.Apache file in the root directory). |
5 | | #include "table/meta_blocks.h" |
6 | | |
7 | | #include <array> |
8 | | #include <cstdio> |
9 | | #include <map> |
10 | | #include <string> |
11 | | |
12 | | #include "block_fetcher.h" |
13 | | #include "db/dbformat.h" |
14 | | #include "db/table_properties_collector.h" |
15 | | #include "file/random_access_file_reader.h" |
16 | | #include "logging/logging.h" |
17 | | #include "rocksdb/options.h" |
18 | | #include "rocksdb/table.h" |
19 | | #include "rocksdb/table_properties.h" |
20 | | #include "table/block_based/block.h" |
21 | | #include "table/block_based/reader_common.h" |
22 | | #include "table/format.h" |
23 | | #include "table/internal_iterator.h" |
24 | | #include "table/persistent_cache_helper.h" |
25 | | #include "table/sst_file_writer_collectors.h" |
26 | | #include "table/table_properties_internal.h" |
27 | | #include "test_util/sync_point.h" |
28 | | #include "util/coding.h" |
29 | | |
30 | | namespace ROCKSDB_NAMESPACE { |
31 | | |
32 | | const std::string kPropertiesBlockName = "rocksdb.properties"; |
33 | | // NB: only used with format_version >= 6 |
34 | | const std::string kIndexBlockName = "rocksdb.index"; |
35 | | const std::string kCompressionDictBlockName = "rocksdb.compression_dict"; |
36 | | const std::string kRangeDelBlockName = "rocksdb.range_del"; |
37 | | |
38 | | Status GetGlobalSequenceNumber(const TableProperties& table_properties, |
39 | | SequenceNumber largest_seqno, |
40 | 123k | SequenceNumber* global_seqno) { |
41 | 123k | const auto& props = table_properties.user_collected_properties; |
42 | 123k | const auto version_pos = props.find(ExternalSstFilePropertyNames::kVersion); |
43 | 123k | const auto seqno_pos = props.find(ExternalSstFilePropertyNames::kGlobalSeqno); |
44 | | |
45 | 123k | *global_seqno = kDisableGlobalSequenceNumber; |
46 | 123k | if (version_pos == props.end()) { |
47 | 123k | if (seqno_pos != props.end()) { |
48 | 0 | std::array<char, 200> msg_buf{}; |
49 | 0 | snprintf( |
50 | 0 | msg_buf.data(), msg_buf.max_size(), |
51 | 0 | "A non-external sst file have global seqno property with value %s", |
52 | 0 | seqno_pos->second.c_str()); |
53 | 0 | return Status::Corruption(msg_buf.data()); |
54 | 0 | } |
55 | 123k | return Status::OK(); |
56 | 123k | } |
57 | | |
58 | 100 | const uint32_t version = DecodeFixed32(version_pos->second.c_str()); |
59 | 100 | if (version != 2) { |
60 | 0 | std::array<char, 200> msg_buf{}; |
61 | 0 | if (version != 1) { |
62 | 0 | snprintf(msg_buf.data(), msg_buf.max_size(), |
63 | 0 | "An external sst file has corrupted version %u.", version); |
64 | 0 | return Status::Corruption(msg_buf.data()); |
65 | 0 | } |
66 | 0 | if (seqno_pos != props.end()) { |
67 | 0 | snprintf(msg_buf.data(), msg_buf.max_size(), |
68 | 0 | "An external sst file with version %u has global seqno " |
69 | 0 | "property with value %s", |
70 | 0 | version, seqno_pos->second.c_str()); |
71 | 0 | return Status::Corruption(msg_buf.data()); |
72 | 0 | } |
73 | 0 | return Status::OK(); |
74 | 0 | } |
75 | | |
76 | | // Since we have a plan to deprecate global_seqno, we do not return failure |
77 | | // if seqno_pos == props.end(). We rely on version_pos to detect whether the |
78 | | // SST is external. |
79 | 100 | SequenceNumber resolved_global_seqno = 0; |
80 | 100 | if (seqno_pos != props.end()) { |
81 | 0 | resolved_global_seqno = DecodeFixed64(seqno_pos->second.c_str()); |
82 | 0 | } |
83 | | // SstTableReader opens a table reader with kMaxSequenceNumber as |
84 | | // largest_seqno to denote it is unknown. |
85 | 100 | if (largest_seqno < kMaxSequenceNumber) { |
86 | 0 | if (resolved_global_seqno == 0) { |
87 | 0 | resolved_global_seqno = largest_seqno; |
88 | 0 | } |
89 | 0 | if (resolved_global_seqno != largest_seqno) { |
90 | 0 | std::array<char, 200> msg_buf{}; |
91 | 0 | snprintf( |
92 | 0 | msg_buf.data(), msg_buf.max_size(), |
93 | 0 | "An external sst file with version %u have global seqno property " |
94 | 0 | "with value %s, while largest seqno in the file is %llu", |
95 | 0 | version, seqno_pos->second.c_str(), |
96 | 0 | static_cast<unsigned long long>(largest_seqno)); |
97 | 0 | return Status::Corruption(msg_buf.data()); |
98 | 0 | } |
99 | 0 | } |
100 | 100 | *global_seqno = resolved_global_seqno; |
101 | | |
102 | 100 | if (resolved_global_seqno > kMaxSequenceNumber) { |
103 | 0 | std::array<char, 200> msg_buf{}; |
104 | 0 | snprintf(msg_buf.data(), msg_buf.max_size(), |
105 | 0 | "An external sst file with version %u have global seqno property " |
106 | 0 | "with value %llu, which is greater than kMaxSequenceNumber", |
107 | 0 | version, static_cast<unsigned long long>(resolved_global_seqno)); |
108 | 0 | return Status::Corruption(msg_buf.data()); |
109 | 0 | } |
110 | | |
111 | 100 | return Status::OK(); |
112 | 100 | } |
113 | | |
114 | | MetaIndexBuilder::MetaIndexBuilder() |
115 | 23.2k | : meta_index_block_(new BlockBuilder(BlockBuilder::ForMetaBlock{}, |
116 | 23.2k | 1 /* restart interval */)) {} |
117 | | |
118 | 49.4k | void MetaIndexBuilder::Add(const std::string& key, const BlockHandle& handle) { |
119 | 49.4k | std::string handle_encoding; |
120 | 49.4k | handle.EncodeTo(&handle_encoding); |
121 | 49.4k | meta_block_handles_.insert({key, handle_encoding}); |
122 | 49.4k | } |
123 | | |
124 | 23.2k | Slice MetaIndexBuilder::Finish() { |
125 | 49.4k | for (const auto& metablock : meta_block_handles_) { |
126 | | // NOTE: meta index keys and block handles are guaranteed < 4GB |
127 | 49.4k | meta_index_block_->Add(metablock.first, metablock.second); |
128 | 49.4k | } |
129 | 23.2k | return meta_index_block_->Finish(); |
130 | 23.2k | } |
131 | | |
132 | | // Property block will be read sequentially and cached in a heap located |
133 | | // object, so there's no need for restart points. Thus we set the restart |
134 | | // interval to infinity to save space. |
135 | | PropertyBlockBuilder::PropertyBlockBuilder() |
136 | 23.2k | : properties_block_(new BlockBuilder( |
137 | 23.2k | BlockBuilder::ForMetaBlock{}, |
138 | 23.2k | std::numeric_limits<int32_t>::max() /* restart interval */)) {} |
139 | | |
140 | | void PropertyBlockBuilder::Add(const std::string& name, |
141 | 1.00M | const std::string& val) { |
142 | 1.00M | assert(props_.find(name) == props_.end()); |
143 | 1.00M | props_.insert({name, val}); |
144 | 1.00M | } |
145 | | |
146 | 678k | void PropertyBlockBuilder::Add(const std::string& name, uint64_t val) { |
147 | 678k | std::string dst; |
148 | 678k | PutVarint64(&dst, val); |
149 | | |
150 | 678k | Add(name, dst); |
151 | 678k | } |
152 | | |
153 | | void PropertyBlockBuilder::Add( |
154 | 23.2k | const UserCollectedProperties& user_collected_properties) { |
155 | 93.0k | for (const auto& prop : user_collected_properties) { |
156 | 93.0k | Add(prop.first, prop.second); |
157 | 93.0k | } |
158 | 23.2k | } |
159 | | |
160 | 23.2k | void PropertyBlockBuilder::AddTableProperty(const TableProperties& props) { |
161 | 23.2k | TEST_SYNC_POINT_CALLBACK("PropertyBlockBuilder::AddTableProperty:Start", |
162 | 23.2k | const_cast<TableProperties*>(&props)); |
163 | | |
164 | 23.2k | Add(TablePropertiesNames::kOriginalFileNumber, props.orig_file_number); |
165 | 23.2k | Add(TablePropertiesNames::kRawKeySize, props.raw_key_size); |
166 | 23.2k | Add(TablePropertiesNames::kRawValueSize, props.raw_value_size); |
167 | 23.2k | Add(TablePropertiesNames::kDataSize, props.data_size); |
168 | 23.2k | Add(TablePropertiesNames::kUncompressedDataSize, |
169 | 23.2k | props.uncompressed_data_size); |
170 | 23.2k | Add(TablePropertiesNames::kIndexSize, props.index_size); |
171 | 23.2k | if (props.index_partitions != 0) { |
172 | 0 | Add(TablePropertiesNames::kIndexPartitions, props.index_partitions); |
173 | 0 | Add(TablePropertiesNames::kTopLevelIndexSize, props.top_level_index_size); |
174 | 0 | } |
175 | 23.2k | Add(TablePropertiesNames::kIndexKeyIsUserKey, props.index_key_is_user_key); |
176 | 23.2k | Add(TablePropertiesNames::kIndexValueIsDeltaEncoded, |
177 | 23.2k | props.index_value_is_delta_encoded); |
178 | 23.2k | if (props.udi_is_primary_index != 0) { |
179 | 0 | Add(TablePropertiesNames::kUDIIsPrimaryIndex, props.udi_is_primary_index); |
180 | 0 | } |
181 | 23.2k | Add(TablePropertiesNames::kNumEntries, props.num_entries); |
182 | 23.2k | Add(TablePropertiesNames::kNumFilterEntries, props.num_filter_entries); |
183 | 23.2k | Add(TablePropertiesNames::kDeletedKeys, props.num_deletions); |
184 | 23.2k | Add(TablePropertiesNames::kMergeOperands, props.num_merge_operands); |
185 | 23.2k | Add(TablePropertiesNames::kNumRangeDeletions, props.num_range_deletions); |
186 | 23.2k | Add(TablePropertiesNames::kNumDataBlocks, props.num_data_blocks); |
187 | 23.2k | if (props.num_data_blocks_compression_rejected > 0) { |
188 | 0 | Add(TablePropertiesNames::kNumDataBlocksCompressionRejected, |
189 | 0 | props.num_data_blocks_compression_rejected); |
190 | 0 | } |
191 | 23.2k | if (props.num_data_blocks_compression_bypassed > 0) { |
192 | 22.4k | Add(TablePropertiesNames::kNumDataBlocksCompressionBypassed, |
193 | 22.4k | props.num_data_blocks_compression_bypassed); |
194 | 22.4k | } |
195 | 23.2k | Add(TablePropertiesNames::kNumUniformBlocks, props.num_uniform_blocks); |
196 | 23.2k | Add(TablePropertiesNames::kFilterSize, props.filter_size); |
197 | 23.2k | Add(TablePropertiesNames::kFormatVersion, props.format_version); |
198 | 23.2k | Add(TablePropertiesNames::kFixedKeyLen, props.fixed_key_len); |
199 | 23.2k | Add(TablePropertiesNames::kColumnFamilyId, props.column_family_id); |
200 | 23.2k | Add(TablePropertiesNames::kCreationTime, props.creation_time); |
201 | 23.2k | Add(TablePropertiesNames::kOldestKeyTime, props.oldest_key_time); |
202 | 23.2k | Add(TablePropertiesNames::kNewestKeyTime, props.newest_key_time); |
203 | 23.2k | if (props.file_creation_time > 0) { |
204 | 4.05k | Add(TablePropertiesNames::kFileCreationTime, props.file_creation_time); |
205 | 4.05k | } |
206 | 23.2k | if (props.slow_compression_estimated_data_size > 0) { |
207 | 0 | Add(TablePropertiesNames::kSlowCompressionEstimatedDataSize, |
208 | 0 | props.slow_compression_estimated_data_size); |
209 | 0 | } |
210 | 23.2k | if (props.fast_compression_estimated_data_size > 0) { |
211 | 0 | Add(TablePropertiesNames::kFastCompressionEstimatedDataSize, |
212 | 0 | props.fast_compression_estimated_data_size); |
213 | 0 | } |
214 | 23.2k | Add(TablePropertiesNames::kTailStartOffset, props.tail_start_offset); |
215 | 23.2k | if (props.user_defined_timestamps_persisted == 0) { |
216 | 0 | Add(TablePropertiesNames::kUserDefinedTimestampsPersisted, |
217 | 0 | props.user_defined_timestamps_persisted); |
218 | 0 | } |
219 | 23.2k | if (!props.db_id.empty()) { |
220 | 23.2k | Add(TablePropertiesNames::kDbId, props.db_id); |
221 | 23.2k | } |
222 | 23.2k | if (!props.db_session_id.empty()) { |
223 | 23.2k | Add(TablePropertiesNames::kDbSessionId, props.db_session_id); |
224 | 23.2k | } |
225 | 23.2k | if (!props.db_host_id.empty()) { |
226 | 23.2k | Add(TablePropertiesNames::kDbHostId, props.db_host_id); |
227 | 23.2k | } |
228 | | |
229 | 23.2k | if (!props.filter_policy_name.empty()) { |
230 | 0 | Add(TablePropertiesNames::kFilterPolicy, props.filter_policy_name); |
231 | 0 | } |
232 | 23.2k | if (!props.comparator_name.empty()) { |
233 | 23.2k | Add(TablePropertiesNames::kComparator, props.comparator_name); |
234 | 23.2k | } |
235 | | |
236 | 23.2k | if (!props.merge_operator_name.empty()) { |
237 | 23.2k | Add(TablePropertiesNames::kMergeOperator, props.merge_operator_name); |
238 | 23.2k | } |
239 | 23.2k | if (!props.prefix_extractor_name.empty()) { |
240 | 23.2k | Add(TablePropertiesNames::kPrefixExtractorName, |
241 | 23.2k | props.prefix_extractor_name); |
242 | 23.2k | } |
243 | 23.2k | if (!props.property_collectors_names.empty()) { |
244 | 23.2k | Add(TablePropertiesNames::kPropertyCollectors, |
245 | 23.2k | props.property_collectors_names); |
246 | 23.2k | } |
247 | 23.2k | if (!props.column_family_name.empty()) { |
248 | 23.2k | Add(TablePropertiesNames::kColumnFamilyName, props.column_family_name); |
249 | 23.2k | } |
250 | | |
251 | 23.2k | if (!props.compression_name.empty()) { |
252 | 23.2k | Add(TablePropertiesNames::kCompression, props.compression_name); |
253 | 23.2k | } |
254 | 23.2k | if (!props.compression_options.empty()) { |
255 | 23.2k | Add(TablePropertiesNames::kCompressionOptions, props.compression_options); |
256 | 23.2k | } |
257 | 23.2k | if (!props.seqno_to_time_mapping.empty()) { |
258 | 0 | Add(TablePropertiesNames::kSequenceNumberTimeMapping, |
259 | 0 | props.seqno_to_time_mapping); |
260 | 0 | } |
261 | 23.2k | if (props.key_largest_seqno != UINT64_MAX) { |
262 | 23.2k | Add(TablePropertiesNames::kKeyLargestSeqno, props.key_largest_seqno); |
263 | 23.2k | } |
264 | 23.2k | if (props.key_smallest_seqno != UINT64_MAX) { |
265 | 23.2k | Add(TablePropertiesNames::kKeySmallestSeqno, props.key_smallest_seqno); |
266 | 23.2k | } |
267 | 23.2k | if (props.data_block_restart_interval > 0) { |
268 | 23.2k | Add(TablePropertiesNames::kDataBlockRestartInterval, |
269 | 23.2k | props.data_block_restart_interval); |
270 | 23.2k | } |
271 | 23.2k | if (props.index_block_restart_interval > 0) { |
272 | 23.2k | Add(TablePropertiesNames::kIndexBlockRestartInterval, |
273 | 23.2k | props.index_block_restart_interval); |
274 | 23.2k | } |
275 | 23.2k | if (props.separate_key_value_in_data_block > 0) { |
276 | 0 | Add(TablePropertiesNames::kSeparateKeyValueInDataBlock, |
277 | 0 | props.separate_key_value_in_data_block); |
278 | 0 | } |
279 | 23.2k | if (props.lsm_info_at_creation != 0) { |
280 | 23.2k | Add(TablePropertiesNames::kLsmInfoAtCreation, props.lsm_info_at_creation); |
281 | 23.2k | } |
282 | 23.2k | } |
283 | | |
284 | 23.2k | Slice PropertyBlockBuilder::Finish() { |
285 | 1.00M | for (const auto& prop : props_) { |
286 | 1.00M | assert(last_prop_added_to_block_.empty() || |
287 | 1.00M | comparator_->Compare(prop.first, last_prop_added_to_block_) > 0); |
288 | | // Use first 4GB of key and value strings to avoid overflow, e.g. from |
289 | | // user property collector (see BlockBuilder::Add API comments) |
290 | 1.00M | Slice key(prop.first.data(), static_cast<uint32_t>(prop.first.size())); |
291 | 1.00M | Slice value(prop.second.data(), static_cast<uint32_t>(prop.second.size())); |
292 | 1.00M | properties_block_->Add(key, value); |
293 | | #ifndef NDEBUG |
294 | | last_prop_added_to_block_ = prop.first; |
295 | | #endif /* !NDEBUG */ |
296 | 1.00M | } |
297 | | |
298 | 23.2k | return properties_block_->Finish(); |
299 | 23.2k | } |
300 | | |
301 | | void LogPropertiesCollectionError(Logger* info_log, const std::string& method, |
302 | 0 | const std::string& name) { |
303 | 0 | assert(method == "Add" || method == "Finish"); |
304 | |
|
305 | 0 | std::string msg = |
306 | 0 | "Encountered error when calling TablePropertiesCollector::" + method + |
307 | 0 | "() with collector name: " + name; |
308 | 0 | ROCKS_LOG_ERROR(info_log, "%s", msg.c_str()); |
309 | 0 | } |
310 | | |
311 | | bool NotifyCollectTableCollectorsOnAdd( |
312 | | const Slice& key, const Slice& value, uint64_t file_size, |
313 | | const std::vector<std::unique_ptr<InternalTblPropColl>>& collectors, |
314 | 154k | Logger* info_log) { |
315 | 154k | bool all_succeeded = true; |
316 | 154k | for (auto& collector : collectors) { |
317 | 154k | Status s = collector->InternalAdd(key, value, file_size); |
318 | 154k | all_succeeded = all_succeeded && s.ok(); |
319 | 154k | if (!s.ok()) { |
320 | 0 | LogPropertiesCollectionError(info_log, "Add" /* method */, |
321 | 0 | collector->Name()); |
322 | 0 | } |
323 | 154k | } |
324 | 154k | return all_succeeded; |
325 | 154k | } |
326 | | |
327 | | void NotifyCollectTableCollectorsOnBlockAdd( |
328 | | const std::vector<std::unique_ptr<InternalTblPropColl>>& collectors, |
329 | | const uint64_t block_uncomp_bytes, |
330 | | const uint64_t block_compressed_bytes_fast, |
331 | 29.5k | const uint64_t block_compressed_bytes_slow) { |
332 | 29.5k | for (auto& collector : collectors) { |
333 | 29.5k | collector->BlockAdd(block_uncomp_bytes, block_compressed_bytes_fast, |
334 | 29.5k | block_compressed_bytes_slow); |
335 | 29.5k | } |
336 | 29.5k | } |
337 | | |
338 | | bool NotifyCollectTableCollectorsOnFinish( |
339 | | const std::vector<std::unique_ptr<InternalTblPropColl>>& collectors, |
340 | | Logger* info_log, PropertyBlockBuilder* builder, |
341 | | UserCollectedProperties& user_collected_properties, |
342 | 23.2k | UserCollectedProperties& readable_properties) { |
343 | 23.2k | bool all_succeeded = true; |
344 | 23.2k | for (auto& collector : collectors) { |
345 | 23.2k | UserCollectedProperties user_properties; |
346 | 23.2k | Status s = collector->Finish(&user_properties); |
347 | 23.2k | if (s.ok()) { |
348 | 23.2k | for (const auto& prop : collector->GetReadableProperties()) { |
349 | 0 | readable_properties.insert(prop); |
350 | 0 | } |
351 | | #ifndef NDEBUG |
352 | | // Check different user properties collectors are not adding properties of |
353 | | // the same name. |
354 | | for (const auto& pair : user_properties) { |
355 | | assert(user_collected_properties.find(pair.first) == |
356 | | user_collected_properties.end()); |
357 | | } |
358 | | #endif /* !NDEBUG */ |
359 | 23.2k | user_collected_properties.merge(user_properties); |
360 | 23.2k | } else { |
361 | 0 | LogPropertiesCollectionError(info_log, "Finish" /* method */, |
362 | 0 | collector->Name()); |
363 | 0 | if (all_succeeded) { |
364 | 0 | all_succeeded = false; |
365 | 0 | } |
366 | 0 | } |
367 | 23.2k | } |
368 | 23.2k | builder->Add(user_collected_properties); |
369 | 23.2k | return all_succeeded; |
370 | 23.2k | } |
371 | | |
372 | | Status ParsePropertiesBlock( |
373 | | const ImmutableOptions& ioptions, uint64_t offset, Block& properties_block, |
374 | 122k | std::unique_ptr<TableProperties>& new_table_properties) { |
375 | 122k | std::unique_ptr<MetaBlockIter> iter(properties_block.NewMetaIterator()); |
376 | | |
377 | | // All pre-defined properties of type uint64_t |
378 | 122k | std::unordered_map<std::string, uint64_t*> predefined_uint64_properties = { |
379 | 122k | {TablePropertiesNames::kOriginalFileNumber, |
380 | 122k | &new_table_properties->orig_file_number}, |
381 | 122k | {TablePropertiesNames::kDataSize, &new_table_properties->data_size}, |
382 | 122k | {TablePropertiesNames::kUncompressedDataSize, |
383 | 122k | &new_table_properties->uncompressed_data_size}, |
384 | 122k | {TablePropertiesNames::kLsmInfoAtCreation, |
385 | 122k | &new_table_properties->lsm_info_at_creation}, |
386 | 122k | {TablePropertiesNames::kIndexSize, &new_table_properties->index_size}, |
387 | 122k | {TablePropertiesNames::kIndexPartitions, |
388 | 122k | &new_table_properties->index_partitions}, |
389 | 122k | {TablePropertiesNames::kTopLevelIndexSize, |
390 | 122k | &new_table_properties->top_level_index_size}, |
391 | 122k | {TablePropertiesNames::kIndexKeyIsUserKey, |
392 | 122k | &new_table_properties->index_key_is_user_key}, |
393 | 122k | {TablePropertiesNames::kIndexValueIsDeltaEncoded, |
394 | 122k | &new_table_properties->index_value_is_delta_encoded}, |
395 | 122k | {TablePropertiesNames::kUDIIsPrimaryIndex, |
396 | 122k | &new_table_properties->udi_is_primary_index}, |
397 | 122k | {TablePropertiesNames::kFilterSize, &new_table_properties->filter_size}, |
398 | 122k | {TablePropertiesNames::kRawKeySize, &new_table_properties->raw_key_size}, |
399 | 122k | {TablePropertiesNames::kRawValueSize, |
400 | 122k | &new_table_properties->raw_value_size}, |
401 | 122k | {TablePropertiesNames::kNumDataBlocks, |
402 | 122k | &new_table_properties->num_data_blocks}, |
403 | 122k | {TablePropertiesNames::kNumDataBlocksCompressionRejected, |
404 | 122k | &new_table_properties->num_data_blocks_compression_rejected}, |
405 | 122k | {TablePropertiesNames::kNumDataBlocksCompressionBypassed, |
406 | 122k | &new_table_properties->num_data_blocks_compression_bypassed}, |
407 | 122k | {TablePropertiesNames::kNumUniformBlocks, |
408 | 122k | &new_table_properties->num_uniform_blocks}, |
409 | 122k | {TablePropertiesNames::kNumEntries, &new_table_properties->num_entries}, |
410 | 122k | {TablePropertiesNames::kNumFilterEntries, |
411 | 122k | &new_table_properties->num_filter_entries}, |
412 | 122k | {TablePropertiesNames::kDeletedKeys, |
413 | 122k | &new_table_properties->num_deletions}, |
414 | 122k | {TablePropertiesNames::kMergeOperands, |
415 | 122k | &new_table_properties->num_merge_operands}, |
416 | 122k | {TablePropertiesNames::kNumRangeDeletions, |
417 | 122k | &new_table_properties->num_range_deletions}, |
418 | 122k | {TablePropertiesNames::kFormatVersion, |
419 | 122k | &new_table_properties->format_version}, |
420 | 122k | {TablePropertiesNames::kFixedKeyLen, |
421 | 122k | &new_table_properties->fixed_key_len}, |
422 | 122k | {TablePropertiesNames::kColumnFamilyId, |
423 | 122k | &new_table_properties->column_family_id}, |
424 | 122k | {TablePropertiesNames::kCreationTime, |
425 | 122k | &new_table_properties->creation_time}, |
426 | 122k | {TablePropertiesNames::kOldestKeyTime, |
427 | 122k | &new_table_properties->oldest_key_time}, |
428 | 122k | {TablePropertiesNames::kNewestKeyTime, |
429 | 122k | &new_table_properties->newest_key_time}, |
430 | 122k | {TablePropertiesNames::kFileCreationTime, |
431 | 122k | &new_table_properties->file_creation_time}, |
432 | 122k | {TablePropertiesNames::kSlowCompressionEstimatedDataSize, |
433 | 122k | &new_table_properties->slow_compression_estimated_data_size}, |
434 | 122k | {TablePropertiesNames::kFastCompressionEstimatedDataSize, |
435 | 122k | &new_table_properties->fast_compression_estimated_data_size}, |
436 | 122k | {TablePropertiesNames::kTailStartOffset, |
437 | 122k | &new_table_properties->tail_start_offset}, |
438 | 122k | {TablePropertiesNames::kUserDefinedTimestampsPersisted, |
439 | 122k | &new_table_properties->user_defined_timestamps_persisted}, |
440 | 122k | {TablePropertiesNames::kKeyLargestSeqno, |
441 | 122k | &new_table_properties->key_largest_seqno}, |
442 | 122k | {TablePropertiesNames::kKeySmallestSeqno, |
443 | 122k | &new_table_properties->key_smallest_seqno}, |
444 | 122k | {TablePropertiesNames::kDataBlockRestartInterval, |
445 | 122k | &new_table_properties->data_block_restart_interval}, |
446 | 122k | {TablePropertiesNames::kIndexBlockRestartInterval, |
447 | 122k | &new_table_properties->index_block_restart_interval}, |
448 | 122k | {TablePropertiesNames::kSeparateKeyValueInDataBlock, |
449 | 122k | &new_table_properties->separate_key_value_in_data_block}, |
450 | 122k | }; |
451 | | |
452 | 122k | Status s; |
453 | 122k | std::string last_key; |
454 | 4.58M | for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { |
455 | 4.46M | s = iter->status(); |
456 | 4.46M | if (!s.ok()) { |
457 | 0 | break; |
458 | 0 | } |
459 | | |
460 | 4.46M | auto key = iter->key().ToString(); |
461 | | // properties block should be strictly sorted with no duplicate key. |
462 | 4.46M | if (!last_key.empty() && |
463 | 4.31M | BytewiseComparator()->Compare(key, last_key) <= 0) { |
464 | 0 | s = Status::Corruption("properties unsorted"); |
465 | 0 | break; |
466 | 0 | } |
467 | 4.46M | last_key = key; |
468 | | |
469 | 4.46M | auto raw_val = iter->value(); |
470 | 4.46M | auto pos = predefined_uint64_properties.find(key); |
471 | | |
472 | 4.46M | if (key == ExternalSstFilePropertyNames::kGlobalSeqno) { |
473 | 0 | new_table_properties->external_sst_file_global_seqno_offset = |
474 | 0 | offset + iter->ValueOffset(); |
475 | 0 | } |
476 | | |
477 | 4.46M | if (pos != predefined_uint64_properties.end()) { |
478 | 3.50M | if (key == TablePropertiesNames::kDeletedKeys || |
479 | 3.39M | key == TablePropertiesNames::kMergeOperands) { |
480 | | // Insert in user-collected properties for API backwards compatibility |
481 | 246k | new_table_properties->user_collected_properties.insert( |
482 | 246k | {key, raw_val.ToString()}); |
483 | 246k | } |
484 | | // handle predefined rocksdb properties |
485 | 3.50M | uint64_t val; |
486 | 3.50M | if (!GetVarint64(&raw_val, &val)) { |
487 | | // skip malformed value |
488 | 0 | auto error_msg = |
489 | 0 | "Detect malformed value in properties meta-block:" |
490 | 0 | "\tkey: " + |
491 | 0 | key + "\tval: " + raw_val.ToString(); |
492 | 0 | ROCKS_LOG_ERROR(ioptions.logger, "%s", error_msg.c_str()); |
493 | 0 | continue; |
494 | 0 | } |
495 | 3.50M | *(pos->second) = val; |
496 | 3.50M | } else if (key == TablePropertiesNames::kDbId) { |
497 | 123k | new_table_properties->db_id = raw_val.ToString(); |
498 | 834k | } else if (key == TablePropertiesNames::kDbSessionId) { |
499 | 123k | new_table_properties->db_session_id = raw_val.ToString(); |
500 | 710k | } else if (key == TablePropertiesNames::kDbHostId) { |
501 | 123k | new_table_properties->db_host_id = raw_val.ToString(); |
502 | 587k | } else if (key == TablePropertiesNames::kFilterPolicy) { |
503 | 0 | new_table_properties->filter_policy_name = raw_val.ToString(); |
504 | 587k | } else if (key == TablePropertiesNames::kColumnFamilyName) { |
505 | 123k | new_table_properties->column_family_name = raw_val.ToString(); |
506 | 464k | } else if (key == TablePropertiesNames::kComparator) { |
507 | 123k | new_table_properties->comparator_name = raw_val.ToString(); |
508 | 341k | } else if (key == TablePropertiesNames::kMergeOperator) { |
509 | 123k | new_table_properties->merge_operator_name = raw_val.ToString(); |
510 | 218k | } else if (key == TablePropertiesNames::kPrefixExtractorName) { |
511 | 123k | new_table_properties->prefix_extractor_name = raw_val.ToString(); |
512 | 123k | } else if (key == TablePropertiesNames::kPropertyCollectors) { |
513 | 123k | new_table_properties->property_collectors_names = raw_val.ToString(); |
514 | 18.4E | } else if (key == TablePropertiesNames::kCompression) { |
515 | 123k | new_table_properties->compression_name = raw_val.ToString(); |
516 | 18.4E | } else if (key == TablePropertiesNames::kCompressionOptions) { |
517 | 123k | new_table_properties->compression_options = raw_val.ToString(); |
518 | 18.4E | } else if (key == TablePropertiesNames::kSequenceNumberTimeMapping) { |
519 | 0 | new_table_properties->seqno_to_time_mapping = raw_val.ToString(); |
520 | 18.4E | } else { |
521 | | // handle user-collected properties |
522 | 18.4E | new_table_properties->user_collected_properties.insert( |
523 | 18.4E | {key, raw_val.ToString()}); |
524 | 18.4E | } |
525 | 4.46M | } |
526 | | |
527 | 122k | return s; |
528 | 122k | } |
529 | | |
530 | | // FIXME: should be a parameter for reading table properties to use persistent |
531 | | // cache? |
532 | | Status ReadTablePropertiesHelper( |
533 | | const ReadOptions& ro, const BlockHandle& handle, |
534 | | RandomAccessFileReader* file, FilePrefetchBuffer* prefetch_buffer, |
535 | | const Footer& footer, const ImmutableOptions& ioptions, |
536 | | std::unique_ptr<TableProperties>* table_properties, |
537 | 122k | MemoryAllocator* memory_allocator) { |
538 | 122k | assert(table_properties); |
539 | | |
540 | 122k | Status s; |
541 | 122k | bool retry = false; |
542 | 122k | while (true) { |
543 | 122k | BlockContents block_contents; |
544 | 122k | size_t len = handle.size() + footer.GetBlockTrailerSize(); |
545 | | // If this is an external SST file ingested with write_global_seqno set to |
546 | | // true, then we expect the checksum mismatch because checksum was written |
547 | | // by SstFileWriter, but its global seqno in the properties block may have |
548 | | // been changed during ingestion. For this reason, we initially read |
549 | | // and process without checksum verification, then later try checksum |
550 | | // verification so that if it fails, we can copy to a temporary buffer with |
551 | | // global seqno set to its original value, i.e. 0, and attempt checksum |
552 | | // verification again. |
553 | 122k | if (!retry) { |
554 | 121k | ReadOptions modified_ro = ro; |
555 | 121k | modified_ro.verify_checksums = false; |
556 | 121k | BlockFetcher block_fetcher( |
557 | 121k | file, prefetch_buffer, footer, modified_ro, handle, &block_contents, |
558 | 121k | ioptions, false /* decompress */, false /*maybe_compressed*/, |
559 | 121k | BlockType::kProperties, nullptr /*decompressor*/, |
560 | 121k | PersistentCacheOptions::kEmpty, memory_allocator); |
561 | 121k | s = block_fetcher.ReadBlockContents(); |
562 | 121k | if (!s.ok()) { |
563 | 0 | return s; |
564 | 0 | } |
565 | 121k | assert(block_fetcher.GetBlockSizeWithTrailer() == len); |
566 | 121k | TEST_SYNC_POINT_CALLBACK("ReadTablePropertiesHelper:0", |
567 | 121k | &block_contents.data); |
568 | 121k | } else { |
569 | 155 | assert(s.IsCorruption()); |
570 | | // If retrying, use a stronger file system read to check and correct |
571 | | // data corruption |
572 | 155 | IOOptions opts; |
573 | 155 | IODebugContext dbg; |
574 | 155 | if (PrepareIOFromReadOptions(ro, ioptions.clock, opts, &dbg) != |
575 | 155 | IOStatus::OK()) { |
576 | 0 | return s; |
577 | 0 | } |
578 | 155 | opts.verify_and_reconstruct_read = true; |
579 | 155 | std::unique_ptr<char[]> data(new char[len]); |
580 | 155 | Slice result; |
581 | 155 | IOStatus io_s = file->Read(opts, handle.offset(), len, &result, |
582 | 155 | data.get(), nullptr, &dbg); |
583 | 155 | RecordTick(ioptions.stats, FILE_READ_CORRUPTION_RETRY_COUNT); |
584 | 155 | if (!io_s.ok()) { |
585 | 0 | ROCKS_LOG_INFO(ioptions.info_log, |
586 | 0 | "Reading properties block failed - %s", |
587 | 0 | io_s.ToString().c_str()); |
588 | | // Return the original corruption error as that's more serious |
589 | 0 | return s; |
590 | 0 | } |
591 | 155 | if (result.size() < len) { |
592 | 0 | return Status::Corruption("Reading properties block failed - " + |
593 | 0 | std::to_string(result.size()) + |
594 | 0 | " bytes read"); |
595 | 0 | } |
596 | 155 | RecordTick(ioptions.stats, FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT); |
597 | 155 | block_contents = BlockContents(std::move(data), handle.size()); |
598 | 155 | } |
599 | | |
600 | 122k | uint64_t block_size = block_contents.data.size(); |
601 | 122k | Block properties_block(std::move(block_contents)); |
602 | 122k | std::unique_ptr<TableProperties> new_table_properties{new TableProperties}; |
603 | 122k | s = ParsePropertiesBlock(ioptions, handle.offset(), properties_block, |
604 | 122k | new_table_properties); |
605 | | |
606 | | // Modified version of BlockFetcher checksum verification |
607 | | // (See write_global_seqno comment above) |
608 | 122k | if (s.ok() && footer.GetBlockTrailerSize() > 0) { |
609 | 122k | s = VerifyBlockChecksum(footer, properties_block.data(), block_size, |
610 | 122k | file->file_name(), handle.offset(), |
611 | 122k | BlockType::kProperties); |
612 | 122k | if (s.IsCorruption()) { |
613 | 0 | if (new_table_properties->external_sst_file_global_seqno_offset != 0) { |
614 | 0 | std::string tmp_buf(properties_block.data(), len); |
615 | 0 | uint64_t global_seqno_offset = |
616 | 0 | new_table_properties->external_sst_file_global_seqno_offset - |
617 | 0 | handle.offset(); |
618 | 0 | EncodeFixed64(&tmp_buf[static_cast<size_t>(global_seqno_offset)], 0); |
619 | 0 | s = VerifyBlockChecksum(footer, tmp_buf.data(), block_size, |
620 | 0 | file->file_name(), handle.offset(), |
621 | 0 | BlockType::kProperties); |
622 | 0 | } |
623 | 0 | } |
624 | 122k | } |
625 | | |
626 | | // If we detected a corruption and the file system supports verification |
627 | | // and reconstruction, retry the read |
628 | 122k | if (s.IsCorruption() && !retry && |
629 | 0 | CheckFSFeatureSupport(ioptions.fs.get(), |
630 | 0 | FSSupportedOps::kVerifyAndReconstructRead)) { |
631 | 0 | retry = true; |
632 | 122k | } else { |
633 | 122k | if (s.ok()) { |
634 | 122k | *table_properties = std::move(new_table_properties); |
635 | 122k | } |
636 | 122k | break; |
637 | 122k | } |
638 | 122k | } |
639 | | |
640 | 122k | return s; |
641 | 122k | } |
642 | | |
643 | | Status ReadTableProperties(RandomAccessFileReader* file, uint64_t file_size, |
644 | | uint64_t table_magic_number, |
645 | | const ImmutableOptions& ioptions, |
646 | | const ReadOptions& read_options, |
647 | | std::unique_ptr<TableProperties>* properties, |
648 | | MemoryAllocator* memory_allocator, |
649 | 0 | FilePrefetchBuffer* prefetch_buffer) { |
650 | 0 | BlockHandle block_handle; |
651 | 0 | Footer footer; |
652 | 0 | Status s = |
653 | 0 | FindMetaBlockInFile(file, file_size, table_magic_number, ioptions, |
654 | 0 | read_options, kPropertiesBlockName, &block_handle, |
655 | 0 | memory_allocator, prefetch_buffer, &footer); |
656 | 0 | if (!s.ok()) { |
657 | 0 | return s; |
658 | 0 | } |
659 | | |
660 | 0 | if (!block_handle.IsNull()) { |
661 | 0 | s = ReadTablePropertiesHelper(read_options, block_handle, file, |
662 | 0 | prefetch_buffer, footer, ioptions, properties, |
663 | 0 | memory_allocator); |
664 | 0 | } else { |
665 | 0 | s = Status::NotFound(); |
666 | 0 | } |
667 | 0 | return s; |
668 | 0 | } |
669 | | |
670 | | Status FindOptionalMetaBlock(InternalIterator* meta_index_iter, |
671 | | const std::string& meta_block_name, |
672 | 483k | BlockHandle* block_handle) { |
673 | 483k | assert(block_handle != nullptr); |
674 | 483k | meta_index_iter->Seek(meta_block_name); |
675 | 483k | if (meta_index_iter->status().ok()) { |
676 | 472k | if (meta_index_iter->Valid() && meta_index_iter->key() == meta_block_name) { |
677 | 242k | Slice v = meta_index_iter->value(); |
678 | 242k | return block_handle->DecodeFrom(&v); |
679 | 242k | } |
680 | 472k | } |
681 | | // else |
682 | 241k | *block_handle = BlockHandle::NullBlockHandle(); |
683 | 241k | return meta_index_iter->status(); |
684 | 483k | } |
685 | | |
686 | | Status FindMetaBlock(InternalIterator* meta_index_iter, |
687 | | const std::string& meta_block_name, |
688 | 121k | BlockHandle* block_handle) { |
689 | 121k | Status s = |
690 | 121k | FindOptionalMetaBlock(meta_index_iter, meta_block_name, block_handle); |
691 | 121k | if (s.ok() && block_handle->IsNull()) { |
692 | 0 | return Status::Corruption("Cannot find the meta block", meta_block_name); |
693 | 121k | } else { |
694 | 121k | return s; |
695 | 121k | } |
696 | 121k | } |
697 | | |
698 | | Status ReadMetaIndexBlockInFile(RandomAccessFileReader* file, |
699 | | uint64_t file_size, uint64_t table_magic_number, |
700 | | const ImmutableOptions& ioptions, |
701 | | const ReadOptions& read_options, |
702 | | BlockContents* metaindex_contents, |
703 | | MemoryAllocator* memory_allocator, |
704 | | FilePrefetchBuffer* prefetch_buffer, |
705 | 0 | Footer* footer_out) { |
706 | 0 | Footer footer; |
707 | 0 | IOOptions opts; |
708 | 0 | IODebugContext dbg; |
709 | 0 | Status s; |
710 | 0 | s = file->PrepareIOOptions(read_options, opts, &dbg); |
711 | 0 | if (!s.ok()) { |
712 | 0 | return s; |
713 | 0 | } |
714 | 0 | s = ReadFooterFromFile(opts, file, *ioptions.fs, prefetch_buffer, file_size, |
715 | 0 | &footer, table_magic_number, ioptions.stats); |
716 | 0 | if (!s.ok()) { |
717 | 0 | return s; |
718 | 0 | } |
719 | 0 | if (footer_out) { |
720 | 0 | *footer_out = footer; |
721 | 0 | } |
722 | |
|
723 | 0 | auto metaindex_handle = footer.metaindex_handle(); |
724 | 0 | return BlockFetcher(file, prefetch_buffer, footer, read_options, |
725 | 0 | metaindex_handle, metaindex_contents, ioptions, |
726 | 0 | false /* do decompression */, false /*maybe_compressed*/, |
727 | 0 | BlockType::kMetaIndex, nullptr /*decompressor*/, |
728 | 0 | PersistentCacheOptions::kEmpty, memory_allocator) |
729 | 0 | .ReadBlockContents(); |
730 | 0 | } |
731 | | |
732 | | Status FindMetaBlockInFile( |
733 | | RandomAccessFileReader* file, uint64_t file_size, |
734 | | uint64_t table_magic_number, const ImmutableOptions& ioptions, |
735 | | const ReadOptions& read_options, const std::string& meta_block_name, |
736 | | BlockHandle* block_handle, MemoryAllocator* memory_allocator, |
737 | 0 | FilePrefetchBuffer* prefetch_buffer, Footer* footer_out) { |
738 | 0 | BlockContents metaindex_contents; |
739 | 0 | auto s = ReadMetaIndexBlockInFile( |
740 | 0 | file, file_size, table_magic_number, ioptions, read_options, |
741 | 0 | &metaindex_contents, memory_allocator, prefetch_buffer, footer_out); |
742 | 0 | if (!s.ok()) { |
743 | 0 | return s; |
744 | 0 | } |
745 | | // meta blocks are never compressed. Need to add uncompress logic if we are to |
746 | | // compress it. |
747 | 0 | Block metaindex_block(std::move(metaindex_contents)); |
748 | |
|
749 | 0 | std::unique_ptr<InternalIterator> meta_iter; |
750 | 0 | meta_iter.reset(metaindex_block.NewMetaIterator()); |
751 | |
|
752 | 0 | return FindMetaBlock(meta_iter.get(), meta_block_name, block_handle); |
753 | 0 | } |
754 | | |
755 | | Status ReadMetaBlock(RandomAccessFileReader* file, |
756 | | FilePrefetchBuffer* prefetch_buffer, uint64_t file_size, |
757 | | uint64_t table_magic_number, |
758 | | const ImmutableOptions& ioptions, |
759 | | const ReadOptions& read_options, |
760 | | const std::string& meta_block_name, BlockType block_type, |
761 | | BlockContents* contents, |
762 | 0 | MemoryAllocator* memory_allocator) { |
763 | | // TableProperties requires special handling because of checksum issues. |
764 | | // Call ReadTableProperties instead for that case. |
765 | 0 | assert(block_type != BlockType::kProperties); |
766 | |
|
767 | 0 | BlockHandle block_handle; |
768 | 0 | Footer footer; |
769 | 0 | Status status = |
770 | 0 | FindMetaBlockInFile(file, file_size, table_magic_number, ioptions, |
771 | 0 | read_options, meta_block_name, &block_handle, |
772 | 0 | memory_allocator, prefetch_buffer, &footer); |
773 | 0 | if (!status.ok()) { |
774 | 0 | return status; |
775 | 0 | } |
776 | | |
777 | 0 | return BlockFetcher(file, prefetch_buffer, footer, read_options, block_handle, |
778 | 0 | contents, ioptions, false /* decompress */, |
779 | 0 | false /*maybe_compressed*/, block_type, |
780 | 0 | nullptr /*decompressor*/, PersistentCacheOptions::kEmpty, |
781 | 0 | memory_allocator) |
782 | 0 | .ReadBlockContents(); |
783 | 0 | } |
784 | | |
785 | | } // namespace ROCKSDB_NAMESPACE |