]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mds: add minor LogSegment boundaries
authorPatrick Donnelly <pdonnell@redhat.com>
Fri, 4 Nov 2022 17:50:13 +0000 (13:50 -0400)
committerPatrick Donnelly <pdonnell@redhat.com>
Tue, 1 Aug 2023 15:16:01 +0000 (11:16 -0400)
This commit adds a new ESegment event type which can delineate
LogSegments. This event can be used as an alternative to the heavy
weight ESubtreeMap which can be very expensive to generate when the MDS
has a large subtree map.

Fixes: https://tracker.ceph.com/issues/58154
Signed-off-by: Patrick Donnelly <pdonnell@redhat.com>
src/common/options/mds.yaml.in
src/mds/LogEvent.cc
src/mds/LogEvent.h
src/mds/MDLog.cc
src/mds/MDLog.h
src/mds/MDSRank.cc
src/mds/SegmentBoundary.h [new file with mode: 0644]
src/mds/events/EResetJournal.h
src/mds/events/ESegment.h [new file with mode: 0644]
src/mds/events/ESubtreeMap.h
src/mds/journal.cc

index cfa9cb63540342bbb89bf6f3172f28eb48997ef9..8b3a07302bb5281b1dd5d634bd23810bbee32c20 100644 (file)
@@ -523,6 +523,16 @@ options:
   min: 1
   services:
   - mds
+- name: mds_log_major_segment_event_ratio
+  type: uint
+  level: advanced
+  desc: multiple of mds_log_events_per_segment between major segments
+  default: 12
+  services:
+  - mds
+  min: 1
+  see_also:
+  - mds_log_events_per_segment
 # segment size for mds log, default to default file_layout_t
 - name: mds_log_segment_size
   type: size
index 3df8f327c6c9ca019999e107604b4a880e73ec41..8f65024b2d77500a62a7379a81ec2ae9a971f926 100644 (file)
@@ -39,6 +39,8 @@
 
 #include "events/ENoOp.h"
 
+#include "events/ESegment.h"
+
 #define dout_context g_ceph_context
 
 
@@ -89,6 +91,7 @@ std::string_view LogEvent::get_type_str() const
   case EVENT_TABLECLIENT: return "TABLECLIENT";
   case EVENT_TABLESERVER: return "TABLESERVER";
   case EVENT_NOOP: return "NOOP";
+  case EVENT_SEGMENT: return "SEGMENT";
 
   default:
     generic_dout(0) << "get_type_str: unknown type " << _type << dendl;
@@ -114,7 +117,8 @@ const std::map<std::string, LogEvent::EventType> LogEvent::types = {
   {"PURGED", EVENT_PURGED},
   {"TABLECLIENT", EVENT_TABLECLIENT},
   {"TABLESERVER", EVENT_TABLESERVER},
-  {"NOOP", EVENT_NOOP}
+  {"NOOP", EVENT_NOOP},
+  {"SEGMENT", EVENT_SEGMENT}
 };
 
 /*
@@ -195,6 +199,9 @@ std::unique_ptr<LogEvent> LogEvent::decode_event(bufferlist::const_iterator& p,
   case EVENT_NOOP:
     le = std::make_unique<ENoOp>();
     break;
+  case EVENT_SEGMENT:
+    le = std::make_unique<ESegment>();
+    break;
   default:
     generic_dout(0) << "uh oh, unknown log event type " << type << " length " << length << dendl;
     return nullptr;
index 317b6579fc1dba7c67750a8239c5437543a007df..88c70b47660de5a32d4bebc37e3ab30e9c85e79a 100644 (file)
@@ -42,6 +42,8 @@
 #define EVENT_SUBTREEMAP_TEST   50
 #define EVENT_NOOP        51
 
+#define EVENT_SEGMENT      100
+
 
 #include "include/buffer_fwd.h"
 #include "include/Context.h"
index c131da5e4f0da3b9abd68f854fdb162413f51417..33a91b53f89284fe621f7df4540c2a0a6eb7c681 100644 (file)
@@ -26,6 +26,7 @@
 #include "common/Cond.h"
 
 #include "events/ESubtreeMap.h"
+#include "events/ESegment.h"
 
 #include "common/config.h"
 #include "common/errno.h"
@@ -48,6 +49,7 @@ MDLog::MDLog(MDSRank* m)
   debug_subtrees = g_conf().get_val<bool>("mds_debug_subtrees");
   events_per_segment = g_conf().get_val<uint64_t>("mds_log_events_per_segment");
   pause = g_conf().get_val<bool>("mds_log_pause");
+  major_segment_event_ratio = g_conf().get_val<uint64_t>("mds_log_major_segment_event_ratio");
   max_segments = g_conf().get_val<uint64_t>("mds_log_max_segments");
   max_events = g_conf().get_val<int64_t>("mds_log_max_events");
 }
@@ -79,6 +81,7 @@ void MDLog::create_logger()
   plb.add_u64(l_mdl_evexd, "evexd", "Current expired events");
   plb.add_u64(l_mdl_segexg, "segexg", "Expiring segments");
   plb.add_u64(l_mdl_segexd, "segexd", "Current expired segments");
+  plb.add_u64(l_mdl_segmjr, "segmjr", "Major Segments");
   plb.add_u64_counter(l_mdl_replayed, "replayed", "Events replayed",
                      "repl", PerfCountersBuilder::PRIO_INTERESTING);
   plb.add_time_avg(l_mdl_jlat, "jlat", "Journaler flush latency");
@@ -267,6 +270,21 @@ void MDLog::append()
 
 // -------------------------------------------------
 
+LogSegment* MDLog::_start_new_segment(SegmentBoundary* sb)
+{
+  ceph_assert(ceph_mutex_is_locked_by_me(mds->mds_lock));
+
+  auto ls = new LogSegment(event_seq);
+  segments[event_seq] = ls;
+  logger->inc(l_mdl_segadd);
+  logger->set(l_mdl_seg, segments.size());
+  sb->set_seq(event_seq);
+
+  // Adjust to next stray dir
+  mds->mdcache->advance_stray();
+  return ls;
+}
+
 void MDLog::_submit_entry(LogEvent *le, MDSLogContextBase* c)
 {
   dout(20) << __func__ << " " << *le << dendl;
@@ -276,6 +294,16 @@ void MDLog::_submit_entry(LogEvent *le, MDSLogContextBase* c)
   ceph_assert(!mds_is_shutting_down);
 
   event_seq++;
+  events_since_last_major_segment++;
+
+  if (auto sb = dynamic_cast<SegmentBoundary*>(le); sb) {
+    auto ls = _start_new_segment(sb);
+    if (sb->is_major_segment_boundary()) {
+      major_segments.insert(ls->seq);
+      logger->set(l_mdl_segmjr, major_segments.size());
+      events_since_last_major_segment = 0;
+    }
+  }
 
   EMetaBlob *metablob = le->get_metablob();
   if (metablob) {
@@ -305,29 +333,27 @@ void MDLog::_submit_entry(LogEvent *le, MDSLogContextBase* c)
   unflushed++;
 }
 
-void MDLog::_segment_upkeep(LogEvent* le)
+void MDLog::_segment_upkeep()
 {
   ceph_assert(ceph_mutex_is_locked_by_me(mds->mds_lock));
   ceph_assert(ceph_mutex_is_locked_by_me(submit_mutex));
   uint64_t period = journaler->get_layout_period();
   auto ls = get_current_segment();
   // start a new segment?
-  if (le->get_type() == EVENT_SUBTREEMAP ||
-      (le->get_type() == EVENT_IMPORTFINISH && mds->is_resolve())) {
-    // avoid infinite loop when ESubtreeMap is very large.
-    // do not insert ESubtreeMap among EImportFinish events that finish
-    // disambiguate imports. Because the ESubtreeMap reflects the subtree
-    // state when all EImportFinish events are replayed.
+  if (events_since_last_major_segment > events_per_segment*major_segment_event_ratio) {
+    dout(10) << __func__ << ": starting new major segment, current " << *ls << dendl;
+    auto sle = mds->mdcache->create_subtree_map();
+    _submit_entry(sle, NULL);
   } else if (ls->end/period != ls->offset/period || ls->num_events >= events_per_segment) {
-    dout(10) << "submit_entry also starting new segment: last = "
-            << ls->seq  << "/" << ls->offset << ", event seq = " << event_seq << dendl;
-    _start_new_segment();
-  } else if (debug_subtrees && le->get_type() != EVENT_SUBTREEMAP_TEST) {
+    dout(10) << __func__ << ": starting new segment, current " << *ls << dendl;
+    auto sb = new ESegment();
+    _submit_entry(sb, nullptr);
+  } else if (debug_subtrees && ls->num_events > 1) {
     // debug: journal this every time to catch subtree replay bugs.
     // use a different event id so it doesn't get interpreted as a
     // LogSegment boundary on replay.
     dout(10) << __func__ << ": creating test subtree map" << dendl;
-    LogEvent *sle = mds->mdcache->create_subtree_map();
+    auto sle = mds->mdcache->create_subtree_map();
     sle->set_type(EVENT_SUBTREEMAP_TEST);
     _submit_entry(sle, NULL);
   }
@@ -394,8 +420,9 @@ void MDLog::_submit_thread()
       uint64_t write_pos = journaler->get_write_pos();
 
       le->set_start_off(write_pos);
-      if (le->get_type() == EVENT_SUBTREEMAP)
+      if (dynamic_cast<SegmentBoundary*>(le)) {
        ls->offset = write_pos;
+      }
 
       dout(5) << "_submit_thread " << write_pos << "~" << bl.length()
              << " : " << *le << dendl;
@@ -535,43 +562,6 @@ void MDLog::shutdown()
   }
 }
 
-
-// -----------------------------
-// segments
-
-void MDLog::_start_new_segment()
-{
-  _prepare_new_segment();
-  _journal_segment_subtree_map(NULL);
-}
-
-void MDLog::_prepare_new_segment()
-{
-  ceph_assert(ceph_mutex_is_locked_by_me(submit_mutex));
-
-  uint64_t seq = event_seq + 1;
-  dout(7) << __func__ << " seq " << seq << dendl;
-
-  segments[seq] = new LogSegment(seq);
-
-  logger->inc(l_mdl_segadd);
-  logger->set(l_mdl_seg, segments.size());
-
-  // Adjust to next stray dir
-  mds->mdcache->advance_stray();
-}
-
-void MDLog::_journal_segment_subtree_map(MDSContext *onsync)
-{
-  ceph_assert(ceph_mutex_is_locked_by_me(submit_mutex));
-
-  dout(7) << __func__ << dendl;
-  ESubtreeMap *sle = mds->mdcache->create_subtree_map();
-  sle->event_seq = get_last_segment_seq();
-
-  _submit_entry(sle, new C_MDL_Flushed(this, onsync));
-}
-
 class C_OFT_Committed : public MDSInternalContext {
   MDLog *mdlog;
   uint64_t seq;
@@ -823,46 +813,61 @@ void MDLog::_trim_expired_segments()
 {
   ceph_assert(ceph_mutex_is_locked_by_me(submit_mutex));
 
-  uint64_t oft_committed_seq = mds->mdcache->open_file_table.get_committed_log_seq();
+  uint64_t const oft_committed_seq = mds->mdcache->open_file_table.get_committed_log_seq();
 
   // trim expired segments?
   bool trimmed = false;
-  while (!segments.empty()) {
-    LogSegment *ls = segments.begin()->second;
+  uint64_t end = 0;
+  for (auto it = segments.begin(); it != segments.end(); ++it) {
+    auto& [seq, ls] = *it;
+    dout(20) << __func__ << ": examining seq=" << seq << " ls=" << *ls << dendl;
+
+    if (auto msit = major_segments.find(seq); msit != major_segments.end() && end > 0) {
+      dout(10) << __func__ << ": expiring up to this major segment " << seq << dendl;
+      uint64_t expire_pos = 0;
+      for (auto& [seq2, ls2] : segments) {
+        if (seq <= seq2) {
+          break;
+        }
+        dout(20) << __func__ << ": expiring " << *ls2 << dendl;
+        expired_events -= ls2->num_events;
+        expired_segments.erase(ls2);
+        if (pre_segments_size > 0)
+          pre_segments_size--;
+        num_events -= ls2->num_events;
+        logger->inc(l_mdl_evtrm, ls2->num_events);
+        logger->inc(l_mdl_segtrm);
+        expire_pos = ls2->end;
+        delete ls2;
+      }
+      segments.erase(segments.begin(), it);
+      logger->set(l_mdl_seg, segments.size());
+      major_segments.erase(major_segments.begin(), msit);
+      logger->set(l_mdl_segmjr, major_segments.size());
+
+      auto jexpire_pos = journaler->get_expire_pos();
+      if (jexpire_pos < expire_pos) {
+        journaler->set_expire_pos(expire_pos);
+        logger->set(l_mdl_expos, expire_pos);
+      } else {
+        logger->set(l_mdl_expos, jexpire_pos);
+      }
+      trimmed = true;
+    }
+
     if (!expired_segments.count(ls)) {
-      dout(10) << "_trim_expired_segments waiting for " << ls->seq << "/" << ls->offset
-              << " to expire" << dendl;
+      dout(10) << __func__ << " waiting for expiry " << *ls << dendl;
       break;
     }
 
     if (!mds_is_shutting_down && ls->seq >= oft_committed_seq) {
-      dout(10) << "_trim_expired_segments open file table committedseq " << oft_committed_seq
+      dout(10) << __func__ << " defer expire for open file table committedseq " << oft_committed_seq
               << " <= " << ls->seq << "/" << ls->offset << dendl;
       break;
     }
     
-    dout(10) << "_trim_expired_segments trimming expired "
-            << ls->seq << "/0x" << std::hex << ls->offset << std::dec << dendl;
-    expired_events -= ls->num_events;
-    expired_segments.erase(ls);
-    if (pre_segments_size > 0)
-      pre_segments_size--;
-    num_events -= ls->num_events;
-      
-    // this was the oldest segment, adjust expire pos
-    if (journaler->get_expire_pos() < ls->end) {
-      journaler->set_expire_pos(ls->end);
-      logger->set(l_mdl_expos, ls->end);
-    } else {
-      logger->set(l_mdl_expos, ls->offset);
-    }
-    
-    logger->inc(l_mdl_segtrm);
-    logger->inc(l_mdl_evtrm, ls->num_events);
-    
-    segments.erase(ls->seq);
-    delete ls;
-    trimmed = true;
+    end = seq;
+    dout(10) << __func__ << ": maybe expiring " << *ls << dendl;
   }
 
   submit_mutex.unlock();
@@ -901,7 +906,6 @@ void MDLog::_expired(LogSegment *ls)
 
   logger->set(l_mdl_ev, num_events);
   logger->set(l_mdl_evexd, expired_events);
-  logger->set(l_mdl_seg, segments.size());
   logger->set(l_mdl_segexd, expired_segments.size());
 }
 
@@ -1191,10 +1195,8 @@ void MDLog::_reformat_journal(JournalPointer const &jp_in, Journaler *old_journa
     if (le) {
       bool modified = false;
 
-      if (le->get_type() == EVENT_SUBTREEMAP ||
-          le->get_type() == EVENT_RESETJOURNAL) {
-        auto sle = dynamic_cast<ESubtreeMap*>(le.get());
-        if (sle == NULL || sle->event_seq == 0) {
+      if (auto sb = dynamic_cast<SegmentBoundary*>(le.get()); sb) {
+        if (sb->get_seq() == 0) {
           // A non-explicit event seq: the effective sequence number 
           // of this segment is it's position in the old journal and
           // the new effective sequence number will be its position
@@ -1219,7 +1221,7 @@ void MDLog::_reformat_journal(JournalPointer const &jp_in, Journaler *old_journa
           || le->get_type() == EVENT_SUBTREEMAP_TEST) {
         auto& sle = dynamic_cast<ESubtreeMap&>(*le);
         dout(20) << __func__ << " zeroing expire_pos in subtreemap event at "
-          << le_pos << " seq=" << sle.event_seq << dendl;
+          << le_pos << " seq=" << sle.get_seq() << dendl;
         sle.expire_pos = 0;
         modified = true;
       }
@@ -1398,45 +1400,50 @@ void MDLog::_replay_thread()
         ceph_abort();  // Should be unreachable because damaged() calls
                     // respawn()
       }
-
     }
     le->set_start_off(pos);
 
-    // new segment?
-    if (le->get_type() == EVENT_SUBTREEMAP ||
-       le->get_type() == EVENT_RESETJOURNAL) {
-      auto sle = dynamic_cast<ESubtreeMap*>(le.get());
-      if (sle && sle->event_seq > 0)
-       event_seq = sle->event_seq;
-      else
-       event_seq = pos;
+    // have we seen an import map yet?
+    if (segments.empty() && !dynamic_cast<ESubtreeMap*>(le.get())) {
+      dout(1) << "_replay " << pos << "~" << bl.length() << " / " << journaler->get_write_pos()
+              << " " << le->get_stamp() << " -- waiting for ESubtreeMap.  (skipping " << *le << ")" << dendl;
+      continue;
+    }
+
+    events_since_last_major_segment++;
+    if (auto sb = dynamic_cast<SegmentBoundary*>(le.get()); sb) {
+      auto seq = sb->get_seq();
+      if (seq > 0) {
+        event_seq = seq;
+      } else {
+        event_seq = pos;
+      }
       segments[event_seq] = new LogSegment(event_seq, pos);
       logger->set(l_mdl_seg, segments.size());
+      if (sb->is_major_segment_boundary()) {
+        major_segments.insert(event_seq);
+        logger->set(l_mdl_segmjr, major_segments.size());
+        events_since_last_major_segment = 0;
+      }
     } else {
       event_seq++;
     }
 
-    // have we seen an import map yet?
-    if (segments.empty()) {
-      dout(10) << "_replay " << pos << "~" << bl.length() << " / " << journaler->get_write_pos() 
-              << " " << le->get_stamp() << " -- waiting for subtree_map.  (skipping " << *le << ")" << dendl;
-    } else {
-      dout(10) << "_replay " << pos << "~" << bl.length() << " / " << journaler->get_write_pos() 
-              << " " << le->get_stamp() << ": " << *le << dendl;
-      le->_segment = get_current_segment();    // replay may need this
-      le->_segment->num_events++;
-      le->_segment->end = journaler->get_read_pos();
-      num_events++;
-      logger->set(l_mdl_ev, num_events);
-
-      {
-        std::lock_guard l(mds->mds_lock);
-        if (mds->is_daemon_stopping()) {
-          return;
-        }
-        logger->inc(l_mdl_replayed);
-        le->replay(mds);
+    dout(10) << "_replay " << pos << "~" << bl.length() << " / " << journaler->get_write_pos()
+             << " " << le->get_stamp() << ": " << *le << dendl;
+    le->_segment = get_current_segment();    // replay may need this
+    le->_segment->num_events++;
+    le->_segment->end = journaler->get_read_pos();
+    num_events++;
+    logger->set(l_mdl_ev, num_events);
+
+    {
+      std::lock_guard l(mds->mds_lock);
+      if (mds->is_daemon_stopping()) {
+        return;
       }
+      logger->inc(l_mdl_replayed);
+      le->replay(mds);
     }
 
     logger->set(l_mdl_rdpos, pos);
@@ -1526,6 +1533,9 @@ void MDLog::handle_conf_change(const std::set<std::string>& changed, const MDSMa
   if (changed.count("mds_log_events_per_segment")) {
     events_per_segment = g_conf().get_val<uint64_t>("mds_log_events_per_segment");
   }
+  if (changed.count("mds_log_major_segment_event_ratio")) {
+    major_segment_event_ratio = g_conf().get_val<uint64_t>("mds_log_major_segment_event_ratio");
+  }
   if (changed.count("mds_log_max_events")) {
     max_events = g_conf().get_val<int64_t>("mds_log_max_events");
   }
index 0e3d26687a2acc5bbc7764b7ad3f7c54d65f2643..3ec1492f3a6865bed39a9a2382dfd8008eef5de4 100644 (file)
@@ -29,6 +29,7 @@ enum {
   l_mdl_segex,
   l_mdl_segtrm,
   l_mdl_seg,
+  l_mdl_segmjr,
   l_mdl_segexg,
   l_mdl_segexd,
   l_mdl_expos,
@@ -49,6 +50,7 @@ enum {
 
 #include "LogSegment.h"
 #include "MDSMap.h"
+#include "SegmentBoundary.h"
 
 #include <list>
 #include <map>
@@ -73,23 +75,6 @@ public:
   void create_logger();
   void set_write_iohint(unsigned iohint_flags);
 
-  void start_new_segment() {
-    std::lock_guard l(submit_mutex);
-    _start_new_segment();
-  }
-  void prepare_new_segment() {
-    std::lock_guard l(submit_mutex);
-    _prepare_new_segment();
-  }
-  void journal_segment_subtree_map(MDSContext *onsync=NULL) {
-    {
-      std::lock_guard l{submit_mutex};
-      _journal_segment_subtree_map(onsync);
-    }
-    if (onsync)
-      flush();
-  }
-
   LogSegment *peek_current_segment() {
     return segments.empty() ? NULL : segments.rbegin()->second;
   }
@@ -130,6 +115,10 @@ public:
   Journaler *get_journaler() { return journaler; }
   bool empty() const { return segments.empty(); }
 
+  uint64_t get_last_major_segment_seq() const {
+    ceph_assert(!major_segments.empty());
+    return *major_segments.rbegin();
+  }
   uint64_t get_last_segment_seq() const {
     ceph_assert(!segments.empty());
     return segments.rbegin()->first;
@@ -144,7 +133,7 @@ public:
   void submit_entry(LogEvent *e, MDSLogContextBase* c = 0) {
     std::lock_guard l(submit_mutex);
     _submit_entry(e, c);
-    _segment_upkeep(e);
+    _segment_upkeep();
     submit_cond.notify_all();
   }
 
@@ -271,10 +260,8 @@ protected:
 
   // -- segments --
   std::map<uint64_t,LogSegment*> segments;
-  std::set<LogSegment*> expiring_segments;
-  std::set<LogSegment*> expired_segments;
   std::size_t pre_segments_size = 0;            // the num of segments when the mds finished replay-journal, to calc the num of segments growing
-  uint64_t event_seq = 0;
+  LogSegment::seq_t event_seq = 0;
   uint64_t expiring_events = 0;
   uint64_t expired_events = 0;
 
@@ -288,14 +275,10 @@ private:
   friend class C_MDL_Flushed;
   friend class C_OFT_Committed;
 
-  // -- segments --
-  void _start_new_segment();
-  void _prepare_new_segment();
-  void _segment_upkeep(LogEvent* le);
-  void _journal_segment_subtree_map(MDSContext *onsync);
-  void _submit_entry(LogEvent *e, MDSLogContextBase *c);
-
   void try_to_commit_open_file_table(uint64_t last_seq);
+  LogSegment* _start_new_segment(SegmentBoundary* sb);
+  void _segment_upkeep();
+  void _submit_entry(LogEvent* e, MDSLogContextBase* c);
 
   void try_expire(LogSegment *ls, int op_prio);
   void _maybe_expired(LogSegment *ls, int op_prio);
@@ -305,8 +288,14 @@ private:
 
   bool debug_subtrees;
   uint64_t events_per_segment;
+  uint64_t major_segment_event_ratio;
   int64_t max_events;
   uint64_t max_segments;
   bool pause;
+
+  std::set<uint64_t> major_segments;
+  std::set<LogSegment*> expired_segments;
+  std::set<LogSegment*> expiring_segments;
+  uint64_t events_since_last_major_segment = 0;
 };
 #endif
index 78146913b832d12174422a256b7765ca4efedf30..74615cca2b8b152fe3d6992176cb9fb0a6ccd7ad 100644 (file)
@@ -38,6 +38,8 @@
 #include "mon/MonClient.h"
 #include "common/HeartbeatMap.h"
 #include "ScrubStack.h"
+#include "events/ESubtreeMap.h"
+#include "events/ESegment.h"
 
 
 #include "MDSRank.h"
@@ -89,7 +91,8 @@ private:
 
     // I need to seal off the current segment, and then mark all
     // previous segments for expiry
-    mdlog->start_new_segment();
+    auto sle = mdcache->create_subtree_map();
+    mdlog->submit_entry(sle);
 
     Context *ctx = new LambdaContext([this](int r) {
         handle_flush_mdlog(r);
@@ -1742,7 +1745,8 @@ void MDSRank::starting_done()
   ceph_assert(is_starting());
   request_state(MDSMap::STATE_ACTIVE);
 
-  mdlog->start_new_segment();
+  auto sle = mdcache->create_subtree_map();
+  mdlog->submit_entry(sle);
 
   // sync snaptable cache
   snapclient->sync(new C_MDSInternalNoop);
@@ -2160,7 +2164,9 @@ void MDSRank::boot_create()
   mdlog->create(fin.new_sub());
 
   // open new journal segment, but do not journal subtree map (yet)
-  mdlog->prepare_new_segment();
+  // N.B. this singular event will be skipped during replay
+  auto le = new ESegment();
+  mdlog->submit_entry(le);
 
   if (whoami == mdsmap->get_root()) {
     dout(3) << "boot_create creating fresh hierarchy" << dendl;
@@ -2195,8 +2201,10 @@ void MDSRank::boot_create()
   ceph_assert(g_conf()->mds_kill_create_at != 1);
 
   // ok now journal it
-  mdlog->journal_segment_subtree_map(fin.new_sub());
+  auto sle = mdcache->create_subtree_map();
+  mdlog->submit_entry(sle);
   mdlog->flush();
+  mdlog->wait_for_safe(fin.new_sub());
 
   // Usually we do this during reconnect, but creation skips that.
   objecter->enable_blocklist_events();
@@ -3813,6 +3821,7 @@ const char** MDSRankDispatcher::get_tracked_conf_keys() const
     "mds_inject_migrator_session_race",
     "mds_inject_rename_corrupt_dentry_first",
     "mds_log_events_per_segment",
+    "mds_log_major_segment_event_ratio",
     "mds_log_max_events",
     "mds_log_max_segments",
     "mds_log_pause",
diff --git a/src/mds/SegmentBoundary.h b/src/mds/SegmentBoundary.h
new file mode 100644 (file)
index 0000000..fb25993
--- /dev/null
@@ -0,0 +1,42 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation.  See file COPYING.
+ *
+ */
+
+#ifndef CEPH_MDS_SEGMENTBOUNDARY_H
+#define CEPH_MDS_SEGMENTBOUNDARY_H
+
+#include "LogSegment.h"
+
+class SegmentBoundary {
+public:
+  using seq_t = LogSegment::seq_t;
+
+  virtual ~SegmentBoundary() {}
+  SegmentBoundary() = default;
+  SegmentBoundary(seq_t seq) : seq(seq) {}
+
+  virtual bool is_major_segment_boundary() const {
+    return false;
+  }
+
+  seq_t get_seq() const {
+    return seq;
+  }
+  void set_seq(seq_t _seq) {
+    seq = _seq;
+  }
+protected:
+  seq_t seq = 0;
+};
+
+#endif
index fba634abba0768b393b1564fb501281ba60b6b79..73aa26571ea3a968f1a85e3750aa98fde3ed7155 100644 (file)
 #define CEPH_MDS_ERESETJOURNAL_H
 
 #include "../LogEvent.h"
+#include "../SegmentBoundary.h"
 
 // generic log event
-class EResetJournal : public LogEvent {
+class EResetJournal : public LogEvent, public SegmentBoundary {
  public:
   EResetJournal() : LogEvent(EVENT_RESETJOURNAL) { }
   ~EResetJournal() override {}
diff --git a/src/mds/events/ESegment.h b/src/mds/events/ESegment.h
new file mode 100644 (file)
index 0000000..c3f4910
--- /dev/null
@@ -0,0 +1,40 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation.  See file COPYING.
+ *
+ */
+
+#ifndef CEPH_MDS_ESEGMENT_H
+#define CEPH_MDS_ESEGMENT_H
+
+#include <string_view>
+
+#include "../LogEvent.h"
+#include "../SegmentBoundary.h"
+
+class ESegment : public LogEvent, public SegmentBoundary {
+public:
+  ESegment() : LogEvent(EVENT_SEGMENT) {}
+  ESegment(LogSegment::seq_t _seq) : LogEvent(EVENT_SEGMENT), SegmentBoundary(_seq) {}
+
+  void print(std::ostream& out) const override {
+    out << "ESegment(" << seq << ")";
+  }
+
+  void encode(bufferlist& bl, uint64_t features) const override;
+  void decode(bufferlist::const_iterator& bl) override;
+  void dump(Formatter *f) const override;
+  void replay(MDSRank *mds) override;
+  static void generate_test_instances(std::list<ESegment*>& ls);
+};
+WRITE_CLASS_ENCODER_FEATURES(ESegment)
+
+#endif
index ebddff0439e82d3cc5db2ef78d9332f905d42cf4..4415e0adb33aa23a83887fa190920b220770f744 100644 (file)
 #define CEPH_MDS_ESUBTREEMAP_H
 
 #include "../LogEvent.h"
+#include "../SegmentBoundary.h"
 #include "EMetaBlob.h"
 
-class ESubtreeMap : public LogEvent {
+class ESubtreeMap : public LogEvent, public SegmentBoundary {
 public:
   EMetaBlob metablob;
   std::map<dirfrag_t, std::vector<dirfrag_t> > subtrees;
   std::set<dirfrag_t> ambiguous_subtrees;
-  uint64_t expire_pos;
-  uint64_t event_seq;
+  uint64_t expire_pos = 0;
 
-  ESubtreeMap() : LogEvent(EVENT_SUBTREEMAP), expire_pos(0), event_seq(0) { }
+  ESubtreeMap() : LogEvent(EVENT_SUBTREEMAP) {}
   
   void print(std::ostream& out) const override {
     out << "ESubtreeMap " << subtrees.size() << " subtrees " 
@@ -42,6 +42,9 @@ public:
   static void generate_test_instances(std::list<ESubtreeMap*>& ls);
 
   void replay(MDSRank *mds) override;
+  bool is_major_segment_boundary() const override {
+    return true;
+  }
 };
 WRITE_CLASS_ENCODER_FEATURES(ESubtreeMap)
 
index f4fccad2e5c0b0c5c10e04c33abcd7d54c5985ec..16155f813fc57879ef3bcc09224a146421c8e463 100644 (file)
@@ -35,6 +35,7 @@
 
 #include "events/ETableClient.h"
 #include "events/ETableServer.h"
+#include "events/ESegment.h"
 
 #include "include/stringify.h"
 
@@ -396,10 +397,10 @@ void EMetaBlob::add_dir_context(CDir *dir, int mode)
       }
 
       // have we journaled this inode since the last subtree map?
-      auto last_segment_seq = mds->mdlog->get_last_segment_seq();
-      if (!maybenot && diri->last_journaled >= last_segment_seq) {
+      auto last_major_segment_seq = mds->mdlog->get_last_major_segment_seq();
+      if (!maybenot && diri->last_journaled >= last_major_segment_seq) {
        dout(20) << "EMetaBlob::add_dir_context(" << dir << ") already have diri in this segment (" 
-                << diri->last_journaled << " >= " << last_segment_seq << "), setting maybenot flag "
+                << diri->last_journaled << " >= " << last_major_segment_seq << "), setting maybenot flag "
                 << *diri << dendl;
        maybenot = true;
       }
@@ -2680,7 +2681,7 @@ void ESubtreeMap::encode(bufferlist& bl, uint64_t features) const
   encode(subtrees, bl);
   encode(ambiguous_subtrees, bl);
   encode(expire_pos, bl);
-  encode(event_seq, bl);
+  encode(seq, bl);
   ENCODE_FINISH(bl);
 }
  
@@ -2696,7 +2697,7 @@ void ESubtreeMap::decode(bufferlist::const_iterator &bl)
   if (struct_v >= 3)
     decode(expire_pos, bl);
   if (struct_v >= 6)
-    decode(event_seq, bl);
+    decode(seq, bl);
   DECODE_FINISH(bl);
 }
 
@@ -3266,6 +3267,35 @@ void EResetJournal::replay(MDSRank *mds)
   mds->mdcache->show_subtrees();
 }
 
+void ESegment::encode(bufferlist &bl, uint64_t features) const
+{
+  ENCODE_START(1, 1, bl);
+  encode(seq, bl);
+  ENCODE_FINISH(bl);
+}
+
+void ESegment::decode(bufferlist::const_iterator &bl)
+{
+  DECODE_START(1, bl);
+  decode(seq, bl);
+  DECODE_FINISH(bl);
+}
+
+void ESegment::replay(MDSRank *mds)
+{
+  dout(4) << "ESegment::replay, seq " << seq << dendl;
+}
+
+void ESegment::dump(Formatter *f) const
+{
+  f->dump_int("seq", seq);
+}
+
+void ESegment::generate_test_instances(std::list<ESegment*>& ls)
+{
+  ls.push_back(new ESegment);
+}
+
 
 void ENoOp::encode(bufferlist &bl, uint64_t features) const
 {