From ee113eca5279aff55dafeef7d98bcbc53dd159dc Mon Sep 17 00:00:00 2001 From: Avi Kivity Date: Tue, 7 Jul 2020 21:36:14 +0300 Subject: [PATCH] Merge 'hinted handoff: fix commitlog memory leak' from Piotr D " When commitlog is recreated in hints manager, only shutdown() method is called, but not release(). Because of that, some internal commitlog objects (`segment_manager` and `segment`s) may be left pointing to each other through shared_ptr reference cycles, which may result in memory leak when the parent commitlog object is destroyed. This PR prevents memory leaks that may happen this way by calling release() after shutdown() from the hints manager. Fixes: #6409, Fixes #6776 " * piodul-fix-commitlog-memory-leak-in-hinted-handoff: hinted handoff: disable warnings about segments left on disk hinted handoff: release memory on commitlog termination (cherry picked from commit 4c221855a14dd3107efa14bd17b53433595dd1f7) --- db/commitlog/commitlog.cc | 2 +- db/commitlog/commitlog.hh | 1 + db/hints/manager.cc | 12 ++++++++++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/db/commitlog/commitlog.cc b/db/commitlog/commitlog.cc index ebb0268698..89a1aab924 100644 --- a/db/commitlog/commitlog.cc +++ b/db/commitlog/commitlog.cc @@ -521,7 +521,7 @@ public: _segment_manager->totals.total_size_on_disk -= size_on_disk(); _segment_manager->totals.total_size -= (size_on_disk() + _buffer.size_bytes()); _segment_manager->add_file_to_delete(_file_name, _desc); - } else { + } else if (_segment_manager->cfg.warn_about_segments_left_on_disk_after_shutdown) { clogger.warn("Segment {} is dirty and is left on disk.", *this); } } diff --git a/db/commitlog/commitlog.hh b/db/commitlog/commitlog.hh index 726410003f..b07b1c401e 100644 --- a/db/commitlog/commitlog.hh +++ b/db/commitlog/commitlog.hh @@ -137,6 +137,7 @@ public: bool reuse_segments = true; bool use_o_dsync = false; + bool warn_about_segments_left_on_disk_after_shutdown = true; const db::extensions * extensions = nullptr; }; diff --git a/db/hints/manager.cc b/db/hints/manager.cc index 5166b58f25..434dad94b6 100644 --- a/db/hints/manager.cc +++ b/db/hints/manager.cc @@ -224,7 +224,9 @@ future<> manager::end_point_hints_manager::stop(drain should_drain) noexcept { with_lock(file_update_mutex(), [this] { if (_hints_store_anchor) { hints_store_ptr tmp = std::exchange(_hints_store_anchor, nullptr); - return tmp->shutdown().finally([tmp] {}); + return tmp->shutdown().finally([tmp] { + return tmp->release(); + }).finally([tmp] {}); } return make_ready_future<>(); }).handle_exception([&eptr] (auto e) { eptr = std::move(e); }).get(); @@ -326,6 +328,10 @@ future manager::end_point_hints_manager::add_store() noexcept { // HH doesn't utilize the flow that benefits from reusing segments. // Therefore let's simply disable it to avoid any possible confusion. cfg.reuse_segments = false; + // HH leaves segments on disk after commitlog shutdown, and later reads + // them when commitlog is re-created. This is expected to happen regularly + // during standard HH workload, so no need to print a warning about it. + cfg.warn_about_segments_left_on_disk_after_shutdown = false; return commitlog::create_commitlog(std::move(cfg)).then([this] (commitlog l) { // add_store() is triggered every time hint files are forcefully flushed to I/O (every hints_flush_period). @@ -352,7 +358,9 @@ future<> manager::end_point_hints_manager::flush_current_hints() noexcept { return futurize_invoke([this] { return with_lock(file_update_mutex(), [this]() -> future<> { return get_or_load().then([] (hints_store_ptr cptr) { - return cptr->shutdown(); + return cptr->shutdown().finally([cptr] { + return cptr->release(); + }).finally([cptr] {}); }).then([this] { // Un-hold the commitlog object. Since we are under the exclusive _file_update_mutex lock there are no // other hints_store_ptr copies and this would destroy the commitlog shared value.