#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_);
}
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;
}
scan_range_exhausted_ = false;
if (scan_opts_->size() == 0) {
valid_ = false;
return;
}
if (!prev_seek_key_.empty() && icomp_.Compare(target, prev_seek_key_) <= 0) {
return;
}
prev_seek_key_ = target.ToString();
const auto& scan_ranges = scan_opts_->GetScanRanges();
Slice user_seek_target = ExtractUserKey(target);
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, true,
scan_ranges[next_scan_idx_].range.start.value(),
false);
if (compare_next_scan_start_result < 0) {
if (next_scan_idx_ == 0) {
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, true,
scan_ranges[next_scan_idx_ - 1].range.start.value(),
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;
}
SeekToBlock(&user_seek_target);
} else if (compare_next_scan_start_result > 0) {
SeekToBlock(&user_seek_target);
} else {
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) {
SetExhausted();
return;
}
size_t block_idx = std::max(cur_scan_start_idx, cur_idx_);
if (block_idx >= cur_scan_end_idx) {
SetExhausted();
return;
}
SeekToBlockIdx(block_idx);
}
}
void MultiScanIndexIterator::SeekToBlock(const Slice* user_seek_target) {
const auto& scan_ranges = scan_opts_->GetScanRanges();
while (next_scan_idx_ < block_index_ranges_per_scan_.size() &&
user_comparator_.CompareWithoutTimestamp(
*user_seek_target, true,
scan_ranges[next_scan_idx_].range.start.value(),
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;
}
ReleaseBlocks(cur_idx_, cur_scan_start_idx);
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, true,
data_block_separators_[block_idx],
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()) {
cur_idx_ = block_handles_.size();
SetExhausted();
return;
}
cur_idx_ = block_idx;
SeekToBlockIdx(block_idx);
}
void MultiScanIndexIterator::SeekToBlockIdx(size_t block_idx) {
assert(cur_idx_ <= block_idx);
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()) {
valid_ = true;
auto [start, end] = block_index_ranges_per_scan_[next_scan_idx_];
if (start < end) {
cur_idx_ = start;
return;
}
valid_ = false;
} else {
valid_ = false;
}
}
void MultiScanIndexIterator::Next() {
assert(valid_);
read_set_->ReleaseBlock(cur_idx_);
++cur_idx_;
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) {
SetExhausted();
return;
}
}
if (cur_idx_ >= prefetch_max_idx_) {
valid_ = false;
if (scan_opts_->max_prefetch_size > 0) {
status_ = Status::PrefetchLimitReached();
}
return;
}
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& ) {
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());
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(block_handles_[cur_idx_], Slice());
}
uint64_t MultiScanIndexIterator::GetMaxPrefetchSize() const {
return scan_opts_->max_prefetch_size;
}
}