@@ -982,7 +982,6 @@ void BlockBasedTableIterator::Prepare(const MultiScanArgs* multiscan_opts) {
982982
983983 // Gather all relevant data block handles
984984 std::vector<BlockHandle> blocks_to_prepare;
985- Status s;
986985 std::vector<std::tuple<size_t , size_t >> block_ranges_per_scan;
987986 for (const auto & scan_opt : *scan_opts) {
988987 size_t num_blocks = 0 ;
@@ -1042,11 +1041,26 @@ void BlockBasedTableIterator::Prepare(const MultiScanArgs* multiscan_opts) {
10421041 // Look up entries in cache and pin if exist.
10431042 // Store indices of blocks to read.
10441043 std::vector<size_t > blocks_to_read;
1045- std::vector<CachableEntry<Block>> pinned_data_blocks_guard;
1046- pinned_data_blocks_guard.resize (blocks_to_prepare.size ());
1044+ std::vector<CachableEntry<Block>> pinned_data_blocks_guard (
1045+ blocks_to_prepare.size ());
1046+ uint64_t total_prefetch_size = 0 ;
1047+
10471048 for (size_t i = 0 ; i < blocks_to_prepare.size (); ++i) {
10481049 const auto & data_block_handle = blocks_to_prepare[i];
1049- s = table_->LookupAndPinBlocksInCache <Block_kData>(
1050+
1051+ // Check if we would exceed the prefetch size limit with this block
1052+ total_prefetch_size +=
1053+ BlockBasedTable::BlockSizeWithTrailer (data_block_handle);
1054+ if (multiscan_opts->max_prefetch_size > 0 &&
1055+ total_prefetch_size > multiscan_opts->max_prefetch_size ) {
1056+ // All remaining blocks are by default empty.
1057+ for (size_t j = i; j < blocks_to_prepare.size (); ++j) {
1058+ assert (pinned_data_blocks_guard[j].IsEmpty ());
1059+ }
1060+ break ;
1061+ }
1062+
1063+ Status s = table_->LookupAndPinBlocksInCache <Block_kData>(
10501064 read_options_, data_block_handle,
10511065 &pinned_data_blocks_guard[i].As <Block_kData>());
10521066
@@ -1088,10 +1102,13 @@ void BlockBasedTableIterator::Prepare(const MultiScanArgs* multiscan_opts) {
10881102
10891103 // do IO
10901104 IOOptions io_opts;
1091- s = table_->get_rep ()->file ->PrepareIOOptions (read_options_, io_opts);
1092- if (!s.ok ()) {
1093- // Abort: PrepareIOOptions failed
1094- return ;
1105+ {
1106+ Status s =
1107+ table_->get_rep ()->file ->PrepareIOOptions (read_options_, io_opts);
1108+ if (!s.ok ()) {
1109+ // Abort: PrepareIOOptions failed
1110+ return ;
1111+ }
10951112 }
10961113
10971114 // Init read requests for Multi-Read
@@ -1163,11 +1180,13 @@ void BlockBasedTableIterator::Prepare(const MultiScanArgs* multiscan_opts) {
11631180 }
11641181
11651182 AlignedBuf aligned_buf;
1166- s = table_->get_rep ()->file .get ()->MultiRead (
1167- io_opts, read_reqs.data (), read_reqs.size (),
1168- direct_io ? &aligned_buf : nullptr );
1169- if (!s.ok ()) {
1170- return ;
1183+ {
1184+ Status s = table_->get_rep ()->file .get ()->MultiRead (
1185+ io_opts, read_reqs.data (), read_reqs.size (),
1186+ direct_io ? &aligned_buf : nullptr );
1187+ if (!s.ok ()) {
1188+ return ;
1189+ }
11711190 }
11721191 for (auto & req : read_reqs) {
11731192 if (!req.status .ok ()) {
@@ -1181,7 +1200,8 @@ void BlockBasedTableIterator::Prepare(const MultiScanArgs* multiscan_opts) {
11811200 table_->get_rep ()->decompressor .get ();
11821201 CachableEntry<DecompressorDict> cached_dict;
11831202 if (table_->get_rep ()->uncompression_dict_reader ) {
1184- s = table_->get_rep ()
1203+ Status s =
1204+ table_->get_rep ()
11851205 ->uncompression_dict_reader ->GetOrReadUncompressionDictionary (
11861206 /* prefetch_buffer= */ nullptr , read_options_,
11871207 /* get_context= */ nullptr , /* lookup_context= */ nullptr ,
@@ -1226,7 +1246,7 @@ void BlockBasedTableIterator::Prepare(const MultiScanArgs* multiscan_opts) {
12261246 table_->get_rep ()->footer .GetBlockTrailerSize () > 0 ;
12271247#endif
12281248 assert (pinned_data_blocks_guard[block_idx].IsEmpty ());
1229- s = table_->CreateAndPinBlockInCache <Block_kData>(
1249+ Status s = table_->CreateAndPinBlockInCache <Block_kData>(
12301250 read_options_, block, decompressor, &tmp_contents,
12311251 &(pinned_data_blocks_guard[block_idx].As <Block_kData>()));
12321252 if (!s.ok ()) {
@@ -1290,6 +1310,16 @@ bool BlockBasedTableIterator::SeekMultiScan(const Slice* target) {
12901310 }
12911311
12921312 ResetDataIter ();
1313+
1314+ // Check if we've hit an empty entry indicating prefetch limit reached
1315+ if (multi_scan_->pinned_data_blocks [cur_scan_start_idx].IsEmpty ()) {
1316+ multi_scan_->cur_data_block_idx = cur_scan_start_idx;
1317+ multi_scan_->prefetch_limit_reached = true ;
1318+ assert (!Valid ());
1319+ assert (status ().IsPrefetchLimitReached ());
1320+ return true ;
1321+ }
1322+
12931323 // Note that the block_iter_ takes ownership of the pinned data block
12941324 // TODO: we can delegate the clean up like with pinned_iters_mgr_ if
12951325 // need to pin blocks longer.
@@ -1346,6 +1376,16 @@ void BlockBasedTableIterator::FindBlockForwardInMultiScan() {
13461376 // Move to the next pinned data block
13471377 ResetDataIter ();
13481378 ++multi_scan_->cur_data_block_idx ;
1379+
1380+ // Check if we've hit an empty entry indicating prefetch limit reached
1381+ if (multi_scan_->pinned_data_blocks [multi_scan_->cur_data_block_idx ]
1382+ .IsEmpty ()) {
1383+ multi_scan_->prefetch_limit_reached = true ;
1384+ assert (!Valid ());
1385+ assert (status ().IsPrefetchLimitReached ());
1386+ return ;
1387+ }
1388+
13491389 table_->NewDataBlockIterator <DataBlockIter>(
13501390 read_options_,
13511391 multi_scan_->pinned_data_blocks [multi_scan_->cur_data_block_idx ],
0 commit comments