/
GraphTreeHeap
/
museumdb2
Обзор
Документация
Войти
/
GraphTreeHeap
/
museumdb2
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
table/block_based/multi_scan_index_iterator.cc
333 строки
11 KB
Peter Dillinger
Fix check-sources.sh non-ASCII check and remove non-ASCII from sources (#14729)
12 май 2026, 03:02
12 май 2026, 03:02
795f3bd
Код
Авторство
О чём код?
// Copyright (c) Meta Platforms, Inc. and affiliates. // This source code is licensed under both the GPLv2 (found in the // COPYING file in the root directory) and Apache 2.0 License // (found in the LICENSE.Apache file in the root directory). #include "table/block_based/multi_scan_index_iterator.h" #include "monitoring/statistics_impl.h" #include "rocksdb/options.h" namespace ROCKSDB_NAMESPACE { MultiScanIndexIterator::MultiScanIndexIterator( std::vector<BlockHandle>&& block_handles, std::vector<std::string>&& data_block_separators, std::vector<std::tuple<size_t, size_t>>&& block_index_ranges_per_scan, const MultiScanArgs* scan_opts, std::shared_ptr<ReadSet> read_set, size_t prefetch_max_idx, const InternalKeyComparator& icomp, Statistics* statistics) : block_handles_(std::move(block_handles)), data_block_separators_(std::move(data_block_separators)), block_index_ranges_per_scan_(std::move(block_index_ranges_per_scan)), scan_opts_(scan_opts), read_set_(std::move(read_set)), prefetch_max_idx_(prefetch_max_idx), icomp_(icomp), user_comparator_(icomp.user_comparator()), statistics_(statistics) {} MultiScanIndexIterator::~MultiScanIndexIterator() { if (statistics_ && wasted_blocks_count_ > 0) { RecordTick(statistics_, MULTISCAN_PREFETCH_BLOCKS_WASTED, wasted_blocks_count_); } // Release any remaining pinned blocks if (read_set_) { for (size_t i = cur_idx_; i < block_handles_.size(); ++i) { read_set_->ReleaseBlock(i); } } } void MultiScanIndexIterator::ReleaseBlocks(size_t from_idx, size_t to_idx) { for (size_t i = from_idx; i < to_idx; ++i) { if (i < prefetch_max_idx_) { wasted_blocks_count_++; } read_set_->ReleaseBlock(i); } } void MultiScanIndexIterator::Seek(const Slice& target) { if (!status_.ok()) { return; } // Reset scan range exhaustion flag on Seek scan_range_exhausted_ = false; // Check the case where there are no ranges prepared if (scan_opts_->size() == 0) { valid_ = false; return; } // Enforce forward-only seek if (!prev_seek_key_.empty() && icomp_.Compare(target, prev_seek_key_) <= 0) { // Seek key is not moving forward -- keep current position return; } prev_seek_key_ = target.ToString(); const auto& scan_ranges = scan_opts_->GetScanRanges(); Slice user_seek_target = ExtractUserKey(target); // Allow reseek on the start of the last prepared range next_scan_idx_ = std::min(next_scan_idx_, block_index_ranges_per_scan_.size() - 1); auto compare_next_scan_start_result = user_comparator_.CompareWithoutTimestamp( user_seek_target, /*a_has_ts=*/true, scan_ranges[next_scan_idx_].range.start.value(), /*b_has_ts=*/false); // There are 3 different Cases we need to handle: // The following diagram explains different seek targets seeking at various // positions on the table, while the next_scan_idx_ points to PreparedRange 2. // // next_scan_idx_: ------------------+ // | // v // table: : __[PreparedRange 1]__[PreparedRange 2]__[PreparedRange 3]__ // Seek target: <----- Case 1 ------>^<------------- Case 2 --------------> // | // Case 3 // // Case 1: seek before the start of next prepared range. This could happen // due to too many delete tombstones triggering reseek or delete range. // Case 2: seek after the start of next prepared range. // This could happen due to seek key adjustment from delete range file. // Case 3: seek at the beginning of a prepared range (expected case) if (compare_next_scan_start_result < 0) { // Case 1: Seek before the start of the next prepared range if (next_scan_idx_ == 0) { // Should not happen -- seek before first prepared range assert(false && "Seek target before the first prepared range"); valid_ = false; return; } auto seek_target_before_previous_prepared_range = user_comparator_.CompareWithoutTimestamp( user_seek_target, /*a_has_ts=*/true, scan_ranges[next_scan_idx_ - 1].range.start.value(), /*b_has_ts=*/false) < 0; assert(!seek_target_before_previous_prepared_range); if (seek_target_before_previous_prepared_range) { status_ = Status::InvalidArgument( "Seek target is before the previous prepared range at index " + std::to_string(next_scan_idx_)); RecordTick(statistics_, MULTISCAN_SEEK_ERRORS); valid_ = false; return; } // Seek within a gap -- advance to the right scan range and find block SeekToBlock(&user_seek_target); } else if (compare_next_scan_start_result > 0) { // Case 2: Seek after the start of the next prepared range SeekToBlock(&user_seek_target); } else { // Case 3: Seek at the beginning of a prepared range (expected case) assert(next_scan_idx_ < block_index_ranges_per_scan_.size()); auto [cur_scan_start_idx, cur_scan_end_idx] = block_index_ranges_per_scan_[next_scan_idx_]; ++next_scan_idx_; if (cur_scan_start_idx >= cur_scan_end_idx) { // No blocks are prepared for this range at current file SetExhausted(); return; } // max_sequential_skip_in_iterations can trigger a reseek on the start // key of a scan range, even though we're already past cur_scan_start_idx size_t block_idx = std::max(cur_scan_start_idx, cur_idx_); if (block_idx >= cur_scan_end_idx) { // cur_idx_ has advanced past this range's blocks (e.g., due to a // reseek after all ranges were exhausted). Treat as exhausted. SetExhausted(); return; } SeekToBlockIdx(block_idx); } } void MultiScanIndexIterator::SeekToBlock(const Slice* user_seek_target) { const auto& scan_ranges = scan_opts_->GetScanRanges(); // Advance next_scan_idx_ past ranges whose start key <= seek target while (next_scan_idx_ < block_index_ranges_per_scan_.size() && user_comparator_.CompareWithoutTimestamp( *user_seek_target, /*a_has_ts=*/true, scan_ranges[next_scan_idx_].range.start.value(), /*b_has_ts=*/false) >= 0) { next_scan_idx_++; } assert(next_scan_idx_ > 0); auto cur_scan_idx = next_scan_idx_ - 1; auto [cur_scan_start_idx, cur_scan_end_idx] = block_index_ranges_per_scan_[cur_scan_idx]; if (cur_scan_start_idx >= cur_scan_end_idx) { SetExhausted(); return; } // Release blocks from current position to cur_scan_start_idx (wasted) ReleaseBlocks(cur_idx_, cur_scan_start_idx); // Find the correct block within the range using linear search on separators size_t block_idx = std::max(cur_scan_start_idx, cur_idx_); while (block_idx < data_block_separators_.size() && user_comparator_.CompareWithoutTimestamp( *user_seek_target, /*a_has_ts=*/true, data_block_separators_[block_idx], /*b_has_ts=*/false) > 0) { if (block_idx < prefetch_max_idx_) { wasted_blocks_count_++; } read_set_->ReleaseBlock(block_idx); block_idx++; } if (block_idx >= data_block_separators_.size()) { // All remaining blocks were released above. Update cur_idx_ so the // destructor does not double-release them. cur_idx_ = block_handles_.size(); SetExhausted(); return; } // Update cur_idx_ before calling SeekToBlockIdx since we've already // released all blocks up to block_idx above. This prevents SeekToBlockIdx's // ReleaseBlocks(cur_idx_, block_idx) from double-releasing. cur_idx_ = block_idx; SeekToBlockIdx(block_idx); } void MultiScanIndexIterator::SeekToBlockIdx(size_t block_idx) { assert(cur_idx_ <= block_idx); // Release any blocks between cur_idx_ and block_idx (wasted) ReleaseBlocks(cur_idx_, block_idx); cur_idx_ = block_idx; valid_ = true; } void MultiScanIndexIterator::SetExhausted() { scan_range_exhausted_ = true; if (next_scan_idx_ < block_index_ranges_per_scan_.size()) { // More ranges remain -- signal out-of-bound for current range. valid_ = true; // Position at the start of the next range so that the next Seek() // can find it. We need to be "valid" so that FindBlockForward sets // is_out_of_bound_ = true. auto [start, end] = block_index_ranges_per_scan_[next_scan_idx_]; if (start < end) { cur_idx_ = start; return; } valid_ = false; } else { // Last range -- natural EOF. Don't set out-of-bound so LevelIterator // advances to the next file. valid_ = false; } } void MultiScanIndexIterator::Next() { assert(valid_); // Release current block read_set_->ReleaseBlock(cur_idx_); ++cur_idx_; // Check if we've crossed a scan range boundary if (next_scan_idx_ > 0) { auto cur_scan_end_idx = std::get<1>(block_index_ranges_per_scan_[next_scan_idx_ - 1]); if (cur_idx_ >= cur_scan_end_idx) { // Current scan range is exhausted SetExhausted(); return; } } // Check prefetch limit if (cur_idx_ >= prefetch_max_idx_) { valid_ = false; if (scan_opts_->max_prefetch_size > 0) { status_ = Status::PrefetchLimitReached(); } return; } // Still within current range, valid valid_ = true; } void MultiScanIndexIterator::SeekToFirst() { if (block_index_ranges_per_scan_.empty()) { valid_ = false; return; } cur_idx_ = 0; next_scan_idx_ = 0; prev_seek_key_.clear(); wasted_blocks_count_ = 0; status_ = Status::OK(); auto [start, end] = block_index_ranges_per_scan_[0]; if (start >= end) { valid_ = false; return; } cur_idx_ = start; next_scan_idx_ = 1; valid_ = true; } void MultiScanIndexIterator::SeekForPrev(const Slice& /*target*/) { valid_ = false; } void MultiScanIndexIterator::SeekToLast() { valid_ = false; } void MultiScanIndexIterator::Prev() { valid_ = false; } Slice MultiScanIndexIterator::key() const { assert(valid_); assert(cur_idx_ < data_block_separators_.size()); // Build internal key: user_key + pack(kMaxSequenceNumber, kValueTypeForSeek) cur_key_buf_.clear(); AppendInternalKey(&cur_key_buf_, ParsedInternalKey(data_block_separators_[cur_idx_], kMaxSequenceNumber, kValueTypeForSeek)); return Slice(cur_key_buf_); } Slice MultiScanIndexIterator::user_key() const { assert(valid_); assert(cur_idx_ < data_block_separators_.size()); return Slice(data_block_separators_[cur_idx_]); } IndexValue MultiScanIndexIterator::value() const { assert(valid_); assert(cur_idx_ < block_handles_.size()); // Return IndexValue with empty first_internal_key to disable // is_at_first_key_from_index_ optimization return IndexValue(block_handles_[cur_idx_], Slice()); } uint64_t MultiScanIndexIterator::GetMaxPrefetchSize() const { return scan_opts_->max_prefetch_size; } } // namespace ROCKSDB_NAMESPACE