From: Xuehan Xu Date: Fri, 5 Jun 2026 09:19:13 +0000 (+0800) Subject: crimson/os/seastore/journal/circular_bounded_journal: find the real X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=69cd81f14718e62d8c8c71900813086beed415f4;p=ceph.git crimson/os/seastore/journal/circular_bounded_journal: find the real journal tail on startup Recover the correct journal tail during replay by scanning JOURNAL_TAIL records instead of relying solely on the on-disk header, which may be stale if a crash occurs between record write and header update. The journal header is stored *separately* from the (append-only) journal and is not updated atomically with journal writes, so it may lag behind after a crash. Treat the journal as the source of truth and rebuild the tail from persisted records. Replay is reorganized into three passes: 1) discover latest journal tail 2) rebuild allocation map 3) replay deltas Signed-off-by: Xuehan Xu --- diff --git a/src/crimson/os/seastore/journal/circular_bounded_journal.cc b/src/crimson/os/seastore/journal/circular_bounded_journal.cc index 62e6c175aed..4bb31172fd4 100644 --- a/src/crimson/os/seastore/journal/circular_bounded_journal.cc +++ b/src/crimson/os/seastore/journal/circular_bounded_journal.cc @@ -343,6 +343,25 @@ Journal::replay_ret CircularBoundedJournal::replay( cjs.set_initialized(true); std::map map; std::map> crc_info; + auto tail = get_dirty_tail() <= get_alloc_tail() ? + get_dirty_tail() : get_alloc_tail(); + set_written_to(tail); + // the first pass to find the real journal tail. + auto find_tail = [this]( + const auto&, + const auto &e, + sea_time_point) { + if (e.type == extent_types_t::JOURNAL_TAIL) { + journal_tail_delta_t tails; + decode(tails, e.bl); + cjs.update_journal_tail_on_startup( + tails.dirty_tail, tails.alloc_tail); + } + return replay_ertr::make_ready_future(true); + }; + co_await scan_valid_record_delta(std::move(find_tail), tail); + tail = get_dirty_tail() <= get_alloc_tail() ? + get_dirty_tail() : get_alloc_tail(); auto build_paddr_seq_map = [&map]( const auto &offsets, const auto &e, @@ -359,10 +378,7 @@ Journal::replay_ret CircularBoundedJournal::replay( } return replay_ertr::make_ready_future(true); }; - auto tail = get_dirty_tail() <= get_alloc_tail() ? - get_dirty_tail() : get_alloc_tail(); - set_written_to(tail); - // The first pass to build the paddr->journal_seq_t map + // The second pass to build the paddr->journal_seq_t map // from extent allocations co_await scan_valid_record_delta(std::move(build_paddr_seq_map), tail); auto call_d_handler_if_valid = [this, &map, &d_handler, &crc_info]( @@ -390,7 +406,7 @@ Journal::replay_ret CircularBoundedJournal::replay( } return replay_ertr::make_ready_future(true); }; - // The second pass to replay deltas + // The third pass to replay deltas co_await scan_valid_record_delta(std::move(call_d_handler_if_valid), tail); for (auto p : crc_info) { ceph_assert_always(p.second.first->get_last_committed_crc() == p.second.second); diff --git a/src/crimson/os/seastore/journal/circular_journal_space.h b/src/crimson/os/seastore/journal/circular_journal_space.h index 049b276b19a..3a3ad2f483b 100644 --- a/src/crimson/os/seastore/journal/circular_journal_space.h +++ b/src/crimson/os/seastore/journal/circular_journal_space.h @@ -220,6 +220,23 @@ class CircularJournalSpace : public JournalAllocator { return device->read(offset, bptr); } + void update_journal_tail_on_startup( + journal_seq_t dirty, + journal_seq_t alloc) { + LOG_PREFIX(CircularJournalSpace); + SUBDEBUG(seastore_journal, + "update tail during replay: dirty={} alloc={}", + dirty, alloc); + if (dirty > header.dirty_tail || + header.dirty_tail == JOURNAL_SEQ_NULL) { + header.dirty_tail = dirty; + } + if (alloc >= header.alloc_tail || + header.alloc_tail == JOURNAL_SEQ_NULL) { + header.alloc_tail = alloc; + } + } + seastar::future<> update_journal_tail( journal_seq_t dirty, journal_seq_t alloc) {