@@ -142,10 +142,8 @@ class ManifestGroup::FilePlanningIterator final
142142 group->delete_index_builder_ .WithScanMetrics (group->scan_metrics_ );
143143 ICEBERG_ASSIGN_OR_RAISE (auto delete_index, group->delete_index_builder_ .Build ());
144144
145- const bool drop_stats = ManifestReader::ShouldDropStats (group->columns_ );
146- if (delete_index->has_equality_deletes ()) {
147- group->columns_ = ManifestReader::WithStatsColumns (group->columns_ );
148- }
145+ const bool drop_stats =
146+ group->PrepareStatsProjection (delete_index->has_equality_deletes ());
149147
150148 std::unique_ptr<Evaluator> data_file_evaluator;
151149 if (group->file_filter_ &&
@@ -426,6 +424,10 @@ class ManifestGroup::FilePlanningIterator final
426424 int32_t current_spec_id_ = 0 ;
427425 bool drop_stats_;
428426
427+ // Limit the number of manifest readers and iterators retained by executor-backed
428+ // planning. The executor still controls actual task concurrency, while this fixed
429+ // cap prevents resource use from scaling with the total manifest count. Entries
430+ // within each manifest remain streamed, so this does not cap manifest size.
429431 static constexpr size_t kManifestReadBatchSize = 32 ;
430432};
431433
@@ -583,10 +585,7 @@ Result<std::vector<std::shared_ptr<ScanTask>>> ManifestGroup::Plan(
583585 delete_index_builder_.WithScanMetrics (scan_metrics_);
584586 ICEBERG_ASSIGN_OR_RAISE (auto delete_index, delete_index_builder_.Build ());
585587
586- bool drop_stats = ManifestReader::ShouldDropStats (columns_);
587- if (delete_index->has_equality_deletes ()) {
588- columns_ = ManifestReader::WithStatsColumns (columns_);
589- }
588+ const bool drop_stats = PrepareStatsProjection (delete_index->has_equality_deletes ());
590589
591590 std::unordered_map<int32_t , std::unique_ptr<TaskContext>> task_context_cache;
592591 auto get_task_context = [&](int32_t spec_id) -> Result<TaskContext*> {
@@ -680,6 +679,18 @@ Result<std::unique_ptr<ManifestReader>> ManifestGroup::MakeReader(
680679 return reader;
681680}
682681
682+ bool ManifestGroup::PrepareStatsProjection (bool has_equality_deletes) {
683+ // The caller's projection records whether stats were requested. Equality-delete
684+ // matching may add stats temporarily, but they should still be dropped from the
685+ // result when the original projection did not request them. Keeping this decision
686+ // here ensures eager and iterator planning use identical semantics.
687+ const bool drop_stats = ManifestReader::ShouldDropStats (columns_);
688+ if (has_equality_deletes) {
689+ columns_ = ManifestReader::WithStatsColumns (columns_);
690+ }
691+ return drop_stats;
692+ }
693+
683694Result<std::unordered_map<int32_t , std::vector<ManifestEntry>>>
684695ManifestGroup::ReadEntries () {
685696 // TODO(zehua): Replace with a thread-safe LRU cache.
0 commit comments