diff --git a/readers/mutation_readers.cc b/readers/mutation_readers.cc index d109902356..5f6d3c8680 100644 --- a/readers/mutation_readers.cc +++ b/readers/mutation_readers.cc @@ -532,6 +532,7 @@ public: return make_ready_future<>(); } if (auto r = next()) { + mrlog.trace("flat_multi_range_mutation_reader {}: fast forwarding to range {}", fmt::ptr(this), *r); return _reader.fast_forward_to(*r); } else { _end_of_stream = true; diff --git a/sstables/mx/reader.cc b/sstables/mx/reader.cc index 11007fc30c..88595172b2 100644 --- a/sstables/mx/reader.cc +++ b/sstables/mx/reader.cc @@ -1511,6 +1511,9 @@ private: } }); } + bool has_sstable_attached() const noexcept { + return bool(_sst); + } bool is_initialized() const { return bool(_context); } @@ -1519,6 +1522,14 @@ private: if (is_initialized()) { co_return true; } + // If the reader has no SSTable attached, the reader was proactively closed in the + // context of fast-forward calls. The higher level code has no way to know that + // underlying reader is really exhausted, so reader is responsible for releasing + // its resources beforehand. From there on, the reader has the same semantics + // as that of an empty reader. + if (!has_sstable_attached()) { + co_return false; + } if (_single_partition_read) { _sst->get_stats().on_single_partition_read(); const auto& key = dht::ring_position_view(_pr.start()->value()); @@ -1612,6 +1623,15 @@ public: } _index_in_current_partition = false; _read_enabled = false; + if (_index_reader->eof()) { + // Close the SSTable reader proactively, if the index is completely exhausted + // and no partition was found in the current fast-forward call. This allows + // disk space of SSTables to be reclaimed earlier if they take part in a + // long-living read and they're deleted midway. + sstlog.trace("Closing reader {} for {} after fast-forward call found that index reached EOF and there's nothing left to read", + fmt::ptr(this), _sst->get_filename()); + return close(); + } return make_ready_future<>(); }); } @@ -1699,7 +1719,7 @@ public: close_index_reader = _index_reader->close().finally([_ = std::move(_index_reader)] {}); } - return when_all_succeed(std::move(close_context), std::move(close_index_reader)).discard_result().handle_exception([] (std::exception_ptr ep) { + return when_all_succeed(std::move(close_context), std::move(close_index_reader)).discard_result().handle_exception([sst = std::move(_sst)] (std::exception_ptr ep) { // close can not fail as it is called either from the destructor or from flat_mutation_reader::close sstlog.warn("Failed closing of sstable_mutation_reader: {}. Ignored since the reader is already done.", ep); });