]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
crimson/os/seastore/journal/circular_bounded_journal: find the real 69301/head
authorXuehan Xu <xuxuehan@qianxin.com>
Fri, 5 Jun 2026 09:19:13 +0000 (17:19 +0800)
committerXuehan Xu <xuxuehan@qianxin.com>
Mon, 29 Jun 2026 02:24:57 +0000 (10:24 +0800)
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 <xuxuehan@qianxin.com>
src/crimson/os/seastore/journal/circular_bounded_journal.cc
src/crimson/os/seastore/journal/circular_journal_space.h

index 62e6c175aed2d18c7720c011382c54ff0f310064..4bb31172fd4681111ee343e191af5be626d40ef7 100644 (file)
@@ -343,6 +343,25 @@ Journal::replay_ret CircularBoundedJournal::replay(
   cjs.set_initialized(true);
   std::map<paddr_t, journal_seq_t> map;
   std::map<paddr_t, std::pair<CachedExtentRef, uint32_t>> 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<bool>(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<bool>(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<bool>(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);   
index 049b276b19abf0a1472b924f21d5a1b46fe0fd8c..3a3ad2f483bd004028966035a3826aceb6b61650 100644 (file)
@@ -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) {