From 32aa6ddd7e9e21e3b758bf5e08044b707b97f9c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20=C3=81vila=20de=20Esp=C3=ADndola?= Date: Thu, 14 Nov 2019 09:48:10 -0800 Subject: [PATCH] commitlog: make sure a file is closed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If allocate or truncate throws, we have to close the file. Fixes #4877 Signed-off-by: Rafael Ávila de Espíndola Message-Id: <20191114174810.49004-1-espindola@scylladb.com> (cherry picked from commit 6160b9017db9d8bf96538cc8346288bc7953aaae) --- db/commitlog/commitlog.cc | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/db/commitlog/commitlog.cc b/db/commitlog/commitlog.cc index 8c0d49a8d4..71e80eea21 100644 --- a/db/commitlog/commitlog.cc +++ b/db/commitlog/commitlog.cc @@ -1236,6 +1236,34 @@ void db::commitlog::segment_manager::flush_segments(bool force) { } } +/// \brief Helper for ensuring a file is closed if an exception is thrown. +/// +/// The file provided by the file_fut future is passed to func. +/// * If func throws an exception E, the file is closed and we return +/// a failed future with E. +/// * If func returns a value V, the file is not closed and we return +/// a future with V. +/// Note that when an exception is not thrown, it is the +/// responsibility of func to make sure the file will be closed. It +/// can close the file itself, return it, or store it somewhere. +/// +/// \tparam Func The type of function this wraps +/// \param file_fut A future that produces a file +/// \param func A function that uses a file +/// \return A future that passes the file produced by file_fut to func +/// and closes it if func fails +template +static auto close_on_failure(future file_fut, Func func) { + return file_fut.then([func = std::move(func)](file f) { + return futurize_apply(func, f).handle_exception([f] (std::exception_ptr e) mutable { + return f.close().then_wrapped([f, e = std::move(e)] (future<> x) { + using futurator = futurize>; + return futurator::make_exception_future(e); + }); + }); + }); +} + future db::commitlog::segment_manager::allocate_segment_ex(const descriptor& d, sstring filename, open_flags flags, bool active) { file_open_options opt; opt.extent_allocation_size_hint = max_size; @@ -1262,7 +1290,7 @@ future db::commitlog::segment_manager: return fut; }); - return fut.then([this, d, active, filename](file f) { + return close_on_failure(std::move(fut), [this, d, active, filename] (file f) { f = make_checked_file(commit_error_handler, f); // xfs doesn't like files extended betond eof, so enlarge the file return f.truncate(max_size).then([this, d, active, f, filename] () mutable {