mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-07 14:47:40 +08:00
453ebd6f37
Summary: Pull Request resolved: https://github.com/facebook/rocksdb/pull/14510 When a filesystem does not return kAsyncIO in SupportedOps(), MultiScan still sends ReadAsync/Poll/AbortIO requests. Regular iterators check via CheckFSFeatureSupport in ArenaWrappedDBIter::Init and ForwardIterator, but MultiScan blindly trusted the caller-provided MultiScanArgs::use_async_io flag. Fix: In the MultiScan constructor, after scan_opts_ is initialized, check CheckFSFeatureSupport and disable use_async_io if the FS doesn't support it. Also pass the (potentially modified) scan_opts_ to Prepare() instead of the original scan_opts parameter. Reviewed By: mszeszko-meta Differential Revision: D97995735 fbshipit-source-id: 331639d950fd3cb9f491feed996d5820294beb4e
92 lines
3.2 KiB
C++
92 lines
3.2 KiB
C++
// 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 "file/file_util.h"
|
|
#include "rocksdb/db.h"
|
|
#include "rocksdb/file_system.h"
|
|
|
|
namespace ROCKSDB_NAMESPACE {
|
|
|
|
using MultiScanIterator = MultiScan::MultiScanIterator;
|
|
|
|
MultiScan::MultiScan(const ReadOptions& read_options,
|
|
const MultiScanArgs& scan_opts, DB* db,
|
|
ColumnFamilyHandle* cfh)
|
|
: read_options_(read_options),
|
|
scan_opts_([&] {
|
|
// Disable async IO if the filesystem does not support it, consistent
|
|
// with how ArenaWrappedDBIter::Init() handles ReadOptions::async_io.
|
|
auto opts = scan_opts;
|
|
if (opts.use_async_io &&
|
|
!CheckFSFeatureSupport(db->GetFileSystem(),
|
|
FSSupportedOps::kAsyncIO)) {
|
|
opts.use_async_io = false;
|
|
}
|
|
return opts;
|
|
}()),
|
|
db_(db),
|
|
cfh_(cfh) {
|
|
bool slow_path = false;
|
|
// Setup read_options with iterate_uuper_bound based on the first scan.
|
|
// Subsequent scans will update and allocate a new DB iterator as necessary
|
|
if (scan_opts_.GetScanRanges()[0].range.limit) {
|
|
upper_bound_ = *scan_opts_.GetScanRanges()[0].range.limit;
|
|
read_options_.iterate_upper_bound = &upper_bound_;
|
|
} else {
|
|
read_options_.iterate_upper_bound = nullptr;
|
|
}
|
|
for (const auto& opts : scan_opts_.GetScanRanges()) {
|
|
// Check that all the ScanOptions either specify an upper bound or not. If
|
|
// its mixed we take the slow path which avoids calling Prepare: we have to
|
|
// reallocate the Iterator with updated read_options everytime we switch
|
|
// between upper bound or no upper bound, which complicates Prepare.
|
|
if (opts.range.limit.has_value() !=
|
|
scan_opts_.GetScanRanges()[0].range.limit.has_value()) {
|
|
slow_path = true;
|
|
break;
|
|
}
|
|
}
|
|
db_iter_.reset(db->NewIterator(read_options_, cfh));
|
|
if (!slow_path) {
|
|
db_iter_->Prepare(scan_opts_);
|
|
}
|
|
}
|
|
|
|
MultiScanIterator& MultiScanIterator::operator++() {
|
|
status_ = db_iter_->status();
|
|
if (!status_.ok()) {
|
|
throw MultiScanException(status_);
|
|
}
|
|
|
|
if (idx_ >= scan_opts_.size()) {
|
|
throw std::logic_error("Index out of range");
|
|
}
|
|
idx_++;
|
|
if (idx_ < scan_opts_.size()) {
|
|
// Check if we need to update read_options_
|
|
if (scan_opts_[idx_].range.limit.has_value() !=
|
|
(read_options_.iterate_upper_bound != nullptr)) {
|
|
if (scan_opts_[idx_].range.limit) {
|
|
*upper_bound_ = *scan_opts_[idx_].range.limit;
|
|
read_options_.iterate_upper_bound = upper_bound_;
|
|
} else {
|
|
read_options_.iterate_upper_bound = nullptr;
|
|
}
|
|
db_iter_.reset(db_->NewIterator(read_options_, cfh_));
|
|
scan_.Reset(db_iter_.get());
|
|
} else if (scan_opts_[idx_].range.limit) {
|
|
*upper_bound_ = *scan_opts_[idx_].range.limit;
|
|
}
|
|
db_iter_->Seek(*scan_opts_[idx_].range.start);
|
|
status_ = db_iter_->status();
|
|
if (!status_.ok()) {
|
|
throw MultiScanException(status_);
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
} // namespace ROCKSDB_NAMESPACE
|