From d642083cb35d85656a3bc45ece57b7cfcba044db Mon Sep 17 00:00:00 2001 From: yawzhang Date: Tue, 11 Feb 2025 13:13:44 +0800 Subject: [PATCH] Clean up old logs at the beginning of baseline resync. If the follower restarts during baseline resync, it will replay remaining logs first. However, we have already cleared shard info at the beginning of resync, so we cannot get shard while replaying logs, which will raise errors. This change clean up old logs in log store to avoid this situation. --- conanfile.py | 2 +- .../log_store/home_raft_log_store.cpp | 42 +++++++------------ .../log_store/home_raft_log_store.h | 8 ++-- src/lib/replication/repl_dev/raft_repl_dev.h | 7 ++-- .../repl_dev/raft_state_machine.cpp | 4 ++ 5 files changed, 26 insertions(+), 37 deletions(-) diff --git a/conanfile.py b/conanfile.py index c86cbf553..7d930b3af 100644 --- a/conanfile.py +++ b/conanfile.py @@ -9,7 +9,7 @@ class HomestoreConan(ConanFile): name = "homestore" - version = "6.6.17" + version = "6.6.18" homepage = "https://github.com/eBay/Homestore" description = "HomeStore Storage Engine" diff --git a/src/lib/replication/log_store/home_raft_log_store.cpp b/src/lib/replication/log_store/home_raft_log_store.cpp index 55cd690e4..a5b285d60 100644 --- a/src/lib/replication/log_store/home_raft_log_store.cpp +++ b/src/lib/replication/log_store/home_raft_log_store.cpp @@ -49,40 +49,26 @@ static uint64_t extract_term(const log_buffer& log_bytes) { return (*r_cast< uint64_t const* >(raw_ptr)); } -#if 0 -// Since truncate_lsn can not accross compact_lsn passed down by raft server -// and compact will truncate logs upto compact_lsn, we don't need to re-truncate in this function now. -void HomeRaftLogStore::truncate(uint32_t num_reserved_cnt, repl_lsn_t compact_lsn) { - auto const last_lsn = last_index(); - auto const start_lsn = start_index(); - - // compact_lsn will be zero on first time boot, so we should not truncate in that case. - if (compact_lsn == 0 || (start_lsn + num_reserved_cnt >= last_lsn)) { +void HomeRaftLogStore::truncate(uint32_t num_reserved_cnt, repl_lsn_t compact_lsn, bool force) { + auto const last_lsn = to_store_lsn(last_index()); + auto const start_lsn = to_store_lsn(start_index()); + auto const calculated_truncate_lsn = last_lsn - num_reserved_cnt; + + // if force is set true, we will use calculated truncate lsn directly and trigger the truncate both in memory and device. + // if force is set false, we will use the compact_lsn as a barrier to find the minimum lsn to truncate. + auto const truncate_lsn = force ? calculated_truncate_lsn : std::min(calculated_truncate_lsn, to_store_lsn(compact_lsn)); + if (truncate_lsn <= start_lsn) { REPL_STORE_LOG(DEBUG, - "Store={} LogDev={}: Skipping truncating because of reserved logs entries is not enough or " - "compact_lsn is zero. " + "Store={} LogDev={}: Skipping truncating because of reserved logs entries is not enough." "start_lsn={}, resv_cnt={}, last_lsn={}, compact_lsn={}", m_logstore_id, m_logdev_id, start_lsn, num_reserved_cnt, last_lsn, compact_lsn); return; - } else { - // - // truncate_lsn can not accross compact_lsn passed down by raft server; - // - // When will it happen: - // compact_lsn can be smaller than last_lsn - num_reserved_cnt, when raft is configured with - // snapshot_distance of a large value, and dynamic config "resvered log entries" a smaller value. - // - auto truncate_lsn = std::min(last_lsn - num_reserved_cnt, (ulong)to_store_lsn(compact_lsn)); - - REPL_STORE_LOG(INFO, "LogDev={}: Truncating log entries from {} to {}, compact_lsn={}, last_lsn={}", - m_logdev_id, start_lsn, truncate_lsn, compact_lsn, last_lsn); - // this will only truncate in memory. - // we rely on resrouce mgr timer to trigger real truncate for all log stores in system; - // this will be friendly for multiple logstore on same logdev; - m_log_store->truncate(truncate_lsn); } + + REPL_STORE_LOG(INFO, "Store={} LogDev={}: Truncating log entries from {} to {}, last_lsn={}, compact_lsn={}, force={}", + m_logstore_id, m_logdev_id, start_lsn, truncate_lsn, last_lsn, compact_lsn, force); + m_log_store->truncate(truncate_lsn, !force /* in_memory_truncate_only */); } -#endif HomeRaftLogStore::HomeRaftLogStore(logdev_id_t logdev_id, logstore_id_t logstore_id, log_found_cb_t const& log_found_cb, log_replay_done_cb_t const& log_replay_done_cb) : diff --git a/src/lib/replication/log_store/home_raft_log_store.h b/src/lib/replication/log_store/home_raft_log_store.h index d2c0fd57b..ea51892c9 100644 --- a/src/lib/replication/log_store/home_raft_log_store.h +++ b/src/lib/replication/log_store/home_raft_log_store.h @@ -204,16 +204,16 @@ class HomeRaftLogStore : public nuraft::log_store { */ ulong last_index() const; -#if 0 /** * Truncates the log store * * @param num_reserved_cnt The number of log entries to be reserved. * @param compact_lsn This is the truncation barrier passed down by raft server. Truncation should not across this - * LSN; + * LSN except in baseline resync case; + * @param force If true, the truncation will be processed both in memory and device. It is a dangerous operation + * and should be used only in baseline resync case (cleanup logs and restore by snapshot). */ - void truncate(uint32_t num_reserved_cnt, repl_lsn_t compact_lsn); -#endif + void truncate(uint32_t num_reserved_cnt, repl_lsn_t compact_lsn, bool force); void wait_for_log_store_ready(); void set_last_durable_lsn(repl_lsn_t lsn); diff --git a/src/lib/replication/repl_dev/raft_repl_dev.h b/src/lib/replication/repl_dev/raft_repl_dev.h index 619da7843..42aa74779 100644 --- a/src/lib/replication/repl_dev/raft_repl_dev.h +++ b/src/lib/replication/repl_dev/raft_repl_dev.h @@ -250,16 +250,15 @@ class RaftReplDev : public ReplDev, */ void on_create_snapshot(nuraft::snapshot& s, nuraft::async_result< bool >::handler_type& when_done); -#if 0 /** * Truncates the replication log by providing a specified number of reserved entries. * * @param num_reserved_entries The number of reserved entries of the replication log. + * @param force If true, the truncation will be processed both in memory and device. */ - void truncate(uint32_t num_reserved_entries) { - m_data_journal->truncate(num_reserved_entries, m_compact_lsn.load()); + void truncate(uint32_t num_reserved_entries, bool force = false) { + m_data_journal->truncate(num_reserved_entries, m_compact_lsn.load(), force); } -#endif void wait_for_logstore_ready() { m_data_journal->wait_for_log_store_ready(); } diff --git a/src/lib/replication/repl_dev/raft_state_machine.cpp b/src/lib/replication/repl_dev/raft_state_machine.cpp index 47912898e..fb42fd81a 100644 --- a/src/lib/replication/repl_dev/raft_state_machine.cpp +++ b/src/lib/replication/repl_dev/raft_state_machine.cpp @@ -355,6 +355,10 @@ void RaftStateMachine::save_logical_snp_obj(nuraft::snapshot& s, ulong& obj_id, if (is_hs_snp_obj(obj_id)) { // Homestore preserved msg if (m_rd.save_snp_resync_data(data)) { + // If the follower restarts during baseline resync, it will replay remaining logs first. However, we have + // already cleared shard info at the beginning of resync, so we cannot get shard while replaying logs, + // which will raise errors. Therefore, we need to clean up old logs to avoid this situation. + m_rd.truncate(0, true /* force */); obj_id = snp_obj_id_type_app; LOGDEBUG("save_snp_resync_data success, next obj_id={}", obj_id); }