]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
crimson/os/seastore: add space accounting to segment_cleaner and wire in
authorSamuel Just <sjust@redhat.com>
Thu, 27 Aug 2020 07:14:30 +0000 (00:14 -0700)
committerSamuel Just <sjust@redhat.com>
Tue, 20 Oct 2020 19:27:04 +0000 (12:27 -0700)
Adds support for space accounting to SegmentCleaner and wires into
Journal, Cache, and tests.

SegmentCleaner has two tracking implementations, SpaceTrackerSimple
and SpaceTrackerDetailed.  SpaceTrackerSimple simply keeps a count
of live bytes and is intended to be the normal implementation.
SpaceTrackerDetailed maintains a bitmap and is simply useful
for debugging unit tests.  It may be removed in the future.

Signed-off-by: Samuel Just <sjust@redhat.com>
src/crimson/os/seastore/cache.cc
src/crimson/os/seastore/cache.h
src/crimson/os/seastore/journal.cc
src/crimson/os/seastore/journal.h
src/crimson/os/seastore/segment_cleaner.cc
src/crimson/os/seastore/segment_cleaner.h
src/crimson/os/seastore/transaction_manager.cc
src/test/crimson/seastore/test_btree_lba_manager.cc
src/test/crimson/seastore/test_seastore_journal.cc
src/test/crimson/seastore/test_transaction_manager.cc

index f54235fedd1f717ea74272776d62602c5d99a480..2fb51bd018314bc96e9fa1449d3f178add4c389e 100644 (file)
@@ -257,7 +257,8 @@ std::optional<record_t> Cache::try_construct_record(Transaction &t)
 void Cache::complete_commit(
   Transaction &t,
   paddr_t final_block_start,
-  journal_seq_t seq)
+  journal_seq_t seq,
+  SegmentCleaner *cleaner)
 {
   if (t.root) {
     remove_extent(root);
@@ -284,6 +285,11 @@ void Cache::complete_commit(
     i->state = CachedExtent::extent_state_t::CLEAN;
     logger().debug("complete_commit: fresh {}", *i);
     add_extent(i);
+    if (cleaner) {
+      cleaner->mark_space_used(
+       i->get_paddr(),
+       i->get_length());
+    }
   }
 
   // Add new copy of mutated blocks, set_io_wait to block until written
@@ -302,6 +308,14 @@ void Cache::complete_commit(
     }
   }
 
+  if (cleaner) {
+    for (auto &i: t.retired_set) {
+      cleaner->mark_space_free(
+       i->get_paddr(),
+       i->get_length());
+    }
+  }
+
   for (auto &i: t.mutated_block_list) {
     i->complete_io();
   }
index c0e9d53c208ee8930d1bb6171851d46268945f3a..807dfb9d38e44c0f479fe68363c449c21bee5e25 100644 (file)
@@ -295,7 +295,8 @@ public:
   void complete_commit(
     Transaction &t,            ///< [in, out] current transaction
     paddr_t final_block_start, ///< [in] offset of initial block
-    journal_seq_t seq          ///< [in] journal commit seq
+    journal_seq_t seq,         ///< [in] journal commit seq
+    SegmentCleaner *cleaner=nullptr ///< [out] optional segment stat listener
   );
 
   /**
index 8b234c6731cd5d4a44507939c9496278fc5cc9da..8bdadaa21f52503196df8bd28bc3c2b68e22bec1 100644 (file)
@@ -141,13 +141,6 @@ Journal::roll_journal_segment()
          current_journal_segment->close() :
          Segment::close_ertr::now()).safe_then(
     [this, old_segment_id] {
-      // TODO: pretty sure this needs to be atomic in some sense with
-      // making use of the new segment, maybe this bit needs to take
-      // the first transaction of the new segment?  Or the segment
-      // header should include deltas?
-      if (old_segment_id != NULL_SEG_ID) {
-       segment_provider->put_segment(old_segment_id);
-      }
       return segment_provider->get_segment();
     }).safe_then([this](auto segment) {
       return segment_manager.open(segment);
@@ -155,6 +148,14 @@ Journal::roll_journal_segment()
       current_journal_segment = sref;
       written_to = 0;
       return initialize_segment(*current_journal_segment);
+    }).safe_then([=](auto seq) {
+      if (old_segment_id != NULL_SEG_ID) {
+       segment_provider->close_segment(old_segment_id);
+      }
+      segment_provider->set_journal_segment(
+       current_journal_segment->get_segment_id(),
+       seq);
+      return seq;
     }).handle_error(
       roll_journal_segment_ertr::pass_further{},
       crimson::ct_error::all_same_way([] { ceph_assert(0 == "TODO"); })
@@ -226,8 +227,17 @@ Journal::find_replay_segments_fut Journal::find_replay_segments()
              return lt.second.journal_segment_seq <
                rt.second.journal_segment_seq;
            });
+
          current_journal_segment_seq =
            segments.rbegin()->second.journal_segment_seq + 1;
+         std::for_each(
+           segments.begin(),
+           segments.end(),
+           [this](auto &seg) {
+             segment_provider->init_mark_segment_closed(
+               seg.first,
+               seg.second.journal_segment_seq);
+           });
 
          auto journal_tail = segments.rbegin()->second.journal_tail;
          segment_provider->update_journal_tail_committed(journal_tail);
index 81885af3a7b1e2de89e5708c81d347c250cebb9b..ce42bbba035786ce2eb30b3e278bc0ff8c7767ec 100644 (file)
@@ -71,12 +71,18 @@ public:
   using get_segment_ret = get_segment_ertr::future<segment_id_t>;
   virtual get_segment_ret get_segment() = 0;
 
-  /* TODO: we'll want to use this to propogate information about segment contents */
-  virtual void put_segment(segment_id_t segment) = 0;
+  virtual void close_segment(segment_id_t) {}
+
+  virtual void set_journal_segment(
+    segment_id_t segment,
+    segment_seq_t seq) {}
 
   virtual journal_seq_t get_journal_tail_target() const = 0;
   virtual void update_journal_tail_committed(journal_seq_t tail_committed) = 0;
 
+  virtual void init_mark_segment_closed(
+    segment_id_t segment, segment_seq_t seq) {}
+
   virtual ~JournalSegmentProvider() {}
 };
 
index 72da5a4202b54f8ef874ea55d3f88608cbd210e2..fd39c229138755f7c070adb501b00aa8fbe827cc 100644 (file)
@@ -13,12 +13,150 @@ namespace {
 
 namespace crimson::os::seastore {
 
+bool SpaceTrackerSimple::equals(const SpaceTrackerI &_other) const
+{
+  const auto &other = static_cast<const SpaceTrackerSimple&>(_other);
+
+  if (other.live_bytes_by_segment.size() != live_bytes_by_segment.size()) {
+    logger().error("{}: different segment counts, bug in test");
+    assert(0 == "segment counts should match");
+    return false;
+  }
+
+  bool all_match = true;
+  for (segment_id_t i = 0; i < live_bytes_by_segment.size(); ++i) {
+    if (other.live_bytes_by_segment[i] != live_bytes_by_segment[i]) {
+      all_match = false;
+      logger().debug(
+       "{}: segment_id {} live bytes mismatch *this: {}, other: {}",
+       __func__,
+       i,
+       live_bytes_by_segment[i],
+       other.live_bytes_by_segment[i]);
+    }
+  }
+  return all_match;
+}
+
+int64_t SpaceTrackerDetailed::SegmentMap::allocate(
+  segment_id_t segment,
+  segment_off_t offset,
+  extent_len_t len,
+  const extent_len_t block_size)
+{
+  assert(offset % block_size == 0);
+  assert(len % block_size == 0);
+
+  const auto b = (offset / block_size);
+  const auto e = (offset + len) / block_size;
+
+  bool error = false;
+  for (auto i = b; i < e; ++i) {
+    if (bitmap[i]) {
+      if (!error) {
+       logger().error(
+         "SegmentMap::allocate found allocated in {}, {} ~ {}",
+         segment,
+         offset,
+         len);
+       error = true;
+      }
+      logger().debug(
+       "SegmentMap::allocate block {} allocated",
+       i * block_size);
+    }
+    bitmap[i] = true;
+  }
+  return update_usage(block_size);
+}
+
+int64_t SpaceTrackerDetailed::SegmentMap::release(
+  segment_id_t segment,
+  segment_off_t offset,
+  extent_len_t len,
+  const extent_len_t block_size)
+{
+  assert(offset % block_size == 0);
+  assert(len % block_size == 0);
+
+  const auto b = (offset / block_size);
+  const auto e = (offset + len) / block_size;
+
+  bool error = false;
+  for (auto i = b; i < e; ++i) {
+    if (!bitmap[i]) {
+      if (!error) {
+       logger().error(
+         "SegmentMap::release found unallocated in {}, {} ~ {}",
+         segment,
+         offset,
+         len);
+       error = true;
+      }
+      logger().debug(
+       "SegmentMap::release block {} unallocated",
+       i * block_size);
+    }
+    bitmap[i] = false;
+  }
+  return update_usage(-(int64_t)block_size);
+}
+
+bool SpaceTrackerDetailed::equals(const SpaceTrackerI &_other) const
+{
+  const auto &other = static_cast<const SpaceTrackerDetailed&>(_other);
+
+  if (other.segment_usage.size() != segment_usage.size()) {
+    logger().error("{}: different segment counts, bug in test");
+    assert(0 == "segment counts should match");
+    return false;
+  }
+
+  bool all_match = true;
+  for (segment_id_t i = 0; i < segment_usage.size(); ++i) {
+    if (other.segment_usage[i].get_usage() != segment_usage[i].get_usage()) {
+      all_match = false;
+      logger().error(
+       "{}: segment_id {} live bytes mismatch *this: {}, other: {}",
+       __func__,
+       i,
+       segment_usage[i].get_usage(),
+       other.segment_usage[i].get_usage());
+    }
+  }
+  return all_match;
+}
+
+void SpaceTrackerDetailed::SegmentMap::dump_usage(extent_len_t block_size) const
+{
+  for (unsigned i = 0; i < bitmap.size(); ++i) {
+    if (bitmap[i]) {
+      logger().debug("    {} still live", i * block_size);
+    }
+  }
+}
+
+void SpaceTrackerDetailed::dump_usage(segment_id_t id) const
+{
+  logger().debug("SpaceTrackerDetailed::dump_usage {}", id);
+  segment_usage[id].dump_usage(block_size);
+}
+
 SegmentCleaner::get_segment_ret SegmentCleaner::get_segment()
 {
-  // TODO
+  for (size_t i = 0; i < segments.size(); ++i) {
+    if (segments[i].is_empty()) {
+      mark_open(i);
+      logger().debug("{}: returning segment {}", __func__, i);
+      return get_segment_ret(
+       get_segment_ertr::ready_future_marker{},
+       i);
+    }
+  }
+  assert(0 == "out of space handling todo");
   return get_segment_ret(
     get_segment_ertr::ready_future_marker{},
-    next++);
+    0);
 }
 
 void SegmentCleaner::update_journal_tail_target(journal_seq_t target)
@@ -53,9 +191,9 @@ void SegmentCleaner::update_journal_tail_committed(journal_seq_t committed)
   }
 }
 
-void SegmentCleaner::put_segment(segment_id_t segment)
+void SegmentCleaner::close_segment(segment_id_t segment)
 {
-  return;
+  mark_closed(segment);
 }
 
 SegmentCleaner::do_immediate_work_ret SegmentCleaner::do_immediate_work(
@@ -94,4 +232,12 @@ SegmentCleaner::do_immediate_work_ret SegmentCleaner::do_immediate_work(
   });
 }
 
+SegmentCleaner::do_deferred_work_ret SegmentCleaner::do_deferred_work(
+  Transaction &t)
+{
+  return do_deferred_work_ret(
+    do_deferred_work_ertr::ready_future_marker{},
+    ceph::timespan());
+}
+
 }
index 43a2897ca050517ae767c7e8bcf651e06e51e99e..6bb698acb90919209f0b0ce08b785955fbd7db3a 100644 (file)
 namespace crimson::os::seastore {
 class Transaction;
 
+struct segment_info_t {
+  Segment::segment_state_t state = Segment::segment_state_t::EMPTY;
+
+  // Will be non-null for any segments in the current journal
+  segment_seq_t journal_segment_seq = NULL_SEG_SEQ;
+
+  bool is_empty() const {
+    return state == Segment::segment_state_t::EMPTY;
+  }
+
+  bool is_closed() const {
+    return state == Segment::segment_state_t::CLOSED;
+  }
+
+  bool is_open() const {
+    return state == Segment::segment_state_t::OPEN;
+  }
+};
+
+class SpaceTrackerI {
+public:
+  virtual int64_t allocate(
+    segment_id_t segment,
+    segment_off_t offset,
+    extent_len_t len) = 0;
+
+  virtual int64_t release(
+    segment_id_t segment,
+    segment_off_t offset,
+    extent_len_t len) = 0;
+
+  virtual int64_t get_usage(
+    segment_id_t segment) const = 0;
+
+  virtual bool equals(const SpaceTrackerI &other) const = 0;
+
+  virtual std::unique_ptr<SpaceTrackerI> make_empty() const = 0;
+
+  virtual void dump_usage(segment_id_t) const = 0;
+
+  virtual void reset() = 0;
+
+  virtual ~SpaceTrackerI() = default;
+};
+using SpaceTrackerIRef = std::unique_ptr<SpaceTrackerI>;
+
+class SpaceTrackerSimple : public SpaceTrackerI {
+  // Tracks live space for each segment
+  std::vector<int64_t> live_bytes_by_segment;
+
+  int64_t update_usage(segment_id_t segment, int64_t delta) {
+    assert(segment < live_bytes_by_segment.size());
+    live_bytes_by_segment[segment] += delta;
+    assert(live_bytes_by_segment[segment] >= 0);
+    return live_bytes_by_segment[segment];
+  }
+public:
+  SpaceTrackerSimple(size_t num_segments)
+    : live_bytes_by_segment(num_segments, 0) {}
+
+  int64_t allocate(
+    segment_id_t segment,
+    segment_off_t offset,
+    extent_len_t len) final {
+    return update_usage(segment, len);
+  }
+
+  int64_t release(
+    segment_id_t segment,
+    segment_off_t offset,
+    extent_len_t len) final {
+    return update_usage(segment, -len);
+  }
+
+  int64_t get_usage(segment_id_t segment) const final {
+    assert(segment < live_bytes_by_segment.size());
+    return live_bytes_by_segment[segment];
+  }
+
+  void dump_usage(segment_id_t) const final {}
+
+  void reset() final {
+    for (auto &i: live_bytes_by_segment)
+      i = 0;
+  }
+
+  SpaceTrackerIRef make_empty() const final {
+    return SpaceTrackerIRef(
+      new SpaceTrackerSimple(live_bytes_by_segment.size()));
+  }
+
+  bool equals(const SpaceTrackerI &other) const;
+};
+
+class SpaceTrackerDetailed : public SpaceTrackerI {
+  class SegmentMap {
+    int64_t used = 0;
+    std::vector<bool> bitmap;
+
+  public:
+    SegmentMap(size_t blocks) : bitmap(blocks, false) {}
+
+    int64_t update_usage(int64_t delta) {
+      used += delta;
+      return used;
+    }
+
+    int64_t allocate(
+      segment_id_t segment,
+      segment_off_t offset,
+      extent_len_t len,
+      const extent_len_t block_size);
+
+    int64_t release(
+      segment_id_t segment,
+      segment_off_t offset,
+      extent_len_t len,
+      const extent_len_t block_size);
+
+    int64_t get_usage() const {
+      return used;
+    }
+
+    void dump_usage(extent_len_t block_size) const;
+
+    void reset() {
+      used = 0;
+      for (auto &&i: bitmap) {
+       i = false;
+      }
+    }
+  };
+  const size_t block_size;
+  const size_t segment_size;
+
+  // Tracks live space for each segment
+  std::vector<SegmentMap> segment_usage;
+
+public:
+  SpaceTrackerDetailed(size_t num_segments, size_t segment_size, size_t block_size)
+    : block_size(block_size),
+      segment_size(segment_size),
+      segment_usage(num_segments, segment_size / block_size) {}
+
+  int64_t allocate(
+    segment_id_t segment,
+    segment_off_t offset,
+    extent_len_t len) final {
+    assert(segment < segment_usage.size());
+    return segment_usage[segment].allocate(segment, offset, len, block_size);
+  }
+
+  int64_t release(
+    segment_id_t segment,
+    segment_off_t offset,
+    extent_len_t len) final {
+    assert(segment < segment_usage.size());
+    return segment_usage[segment].release(segment, offset, len, block_size);
+  }
+
+  int64_t get_usage(segment_id_t segment) const final {
+    assert(segment < segment_usage.size());
+    return segment_usage[segment].get_usage();
+  }
+
+  void dump_usage(segment_id_t seg) const final;
+
+  void reset() final {
+    for (auto &i: segment_usage)
+      i.reset();
+  }
+
+  SpaceTrackerIRef make_empty() const final {
+    return SpaceTrackerIRef(
+      new SpaceTrackerDetailed(
+       segment_usage.size(),
+       segment_size,
+       block_size));
+  }
+
+  bool equals(const SpaceTrackerI &other) const;
+};
+
+
 class SegmentCleaner : public JournalSegmentProvider {
 public:
   /// Config
   struct config_t {
     size_t num_segments = 0;
     size_t segment_size = 0;
+    size_t block_size = 0;
     size_t target_journal_segments = 0;
     size_t max_journal_segments = 0;
 
@@ -29,6 +214,7 @@ public:
       return config_t{
        manager.get_num_segments(),
        static_cast<size_t>(manager.get_segment_size()),
+       (size_t)manager.get_block_size(),
        2,
        4};
     }
@@ -67,9 +253,14 @@ public:
   };
 
 private:
-  segment_id_t next = 0;
   const config_t config;
 
+  SpaceTrackerIRef space_tracker;
+  std::vector<segment_info_t> segments;
+  size_t empty_segments;
+  int64_t used_bytes = 0;
+  bool init_complete = false;
+
   journal_seq_t journal_tail_target;
   journal_seq_t journal_tail_committed;
   journal_seq_t journal_head;
@@ -77,21 +268,29 @@ private:
   ExtentCallbackInterface *ecb = nullptr;
 
 public:
-  SegmentCleaner(config_t config)
-    : config(config) {}
+  SegmentCleaner(config_t config, bool detailed = false)
+    : config(config),
+      space_tracker(
+       detailed ?
+       (SpaceTrackerI*)new SpaceTrackerDetailed(
+         config.num_segments,
+         config.segment_size,
+         config.block_size) :
+       (SpaceTrackerI*)new SpaceTrackerSimple(
+         config.num_segments)),
+      segments(config.num_segments),
+      empty_segments(config.num_segments) {}
 
   get_segment_ret get_segment() final;
 
-  // hack for testing until we get real space handling
-  void set_next(segment_id_t _next) {
-    next = _next;
-  }
-  segment_id_t get_next() const {
-    return next;
-  }
-
+  void close_segment(segment_id_t segment) final;
 
-  void put_segment(segment_id_t segment) final;
+  void set_journal_segment(
+    segment_id_t segment, segment_seq_t seq) final {
+    assert(segment < segments.size());
+    segments[segment].journal_segment_seq = seq;
+    assert(segments[segment].is_open());
+  }
 
   journal_seq_t get_journal_tail_target() const final {
     return journal_tail_target;
@@ -110,10 +309,66 @@ public:
     journal_head = head;
   }
 
+  void init_mark_segment_closed(segment_id_t segment, segment_seq_t seq) final {
+    crimson::get_logger(ceph_subsys_filestore).debug(
+      "SegmentCleaner::init_mark_segment_closed: segment {}, seq {}",
+      segment,
+      seq);
+    mark_closed(segment);
+    segments[segment].journal_segment_seq = seq;
+  }
+
+  void mark_space_used(
+    paddr_t addr,
+    extent_len_t len,
+    bool init_scan = false) {
+    assert(addr.segment < segments.size());
+
+    if (!init_scan && !init_complete)
+      return;
+
+    if (!init_scan) {
+      assert(segments[addr.segment].state == Segment::segment_state_t::OPEN);
+    }
+
+    used_bytes += len;
+    auto ret = space_tracker->allocate(
+      addr.segment,
+      addr.offset,
+      len);
+    assert(ret > 0);
+  }
+
+  void mark_space_free(
+    paddr_t addr,
+    extent_len_t len) {
+    if (!init_complete)
+      return;
+
+    used_bytes -= len;
+    assert(addr.segment < segments.size());
+
+    auto ret = space_tracker->release(
+      addr.segment,
+      addr.offset,
+      len);
+    assert(ret >= 0);
+  }
+
+  SpaceTrackerIRef get_empty_space_tracker() const {
+    return space_tracker->make_empty();
+  }
+
+  void complete_init() { init_complete = true; }
+
   void set_extent_callback(ExtentCallbackInterface *cb) {
     ecb = cb;
   }
 
+  bool debug_check_space(const SpaceTrackerI &tracker) {
+    return space_tracker->equals(tracker);
+  }
+
   /**
    * do_immediate_work
    *
@@ -147,6 +402,9 @@ public:
     Transaction &t);
 
 private:
+
+  // journal status helpers
+
   journal_seq_t get_dirty_tail() const {
     auto ret = journal_head;
     ret.segment_seq -= std::min(
@@ -162,6 +420,40 @@ private:
       config.max_journal_segments);
     return ret;
   }
+  void mark_closed(segment_id_t segment) {
+    assert(segments.size() > segment);
+    if (init_complete) {
+      assert(segments[segment].is_open());
+    } else {
+      assert(segments[segment].is_empty());
+      assert(empty_segments > 0);
+      --empty_segments;
+    }
+    crimson::get_logger(ceph_subsys_filestore).debug(
+      "mark_closed: empty_segments: {}",
+      empty_segments);
+    segments[segment].state = Segment::segment_state_t::CLOSED;
+  }
+
+  void mark_empty(segment_id_t segment) {
+    assert(segments.size() > segment);
+    assert(segments[segment].is_closed());
+    assert(segments.size() > empty_segments);
+    ++empty_segments;
+    if (space_tracker->get_usage(segment) != 0) {
+      space_tracker->dump_usage(segment);
+      assert(space_tracker->get_usage(segment) == 0);
+    }
+    segments[segment].state = Segment::segment_state_t::EMPTY;
+  }
+
+  void mark_open(segment_id_t segment) {
+    assert(segments.size() > segment);
+    assert(segments[segment].is_empty());
+    assert(empty_segments > 0);
+    --empty_segments;
+    segments[segment].state = Segment::segment_state_t::OPEN;
+  }
 };
 
 }
index e269cd1fe6f87653962cd6e46abb9e792452b7a7..36d86b0b89900722790f73565dff9980532d5d9e 100644 (file)
@@ -78,6 +78,26 @@ TransactionManager::mount_ertr::future<> TransactionManager::mount()
          return submit_transaction(std::move(t));
        });
       });
+  }).safe_then([this] {
+    return seastar::do_with(
+      make_weak_transaction(),
+      [this](auto &t) {
+       assert(segment_cleaner.debug_check_space(
+                *segment_cleaner.get_empty_space_tracker()));
+       return lba_manager.scan_mapped_space(
+         *t,
+         [this](paddr_t addr, extent_len_t len) {
+           logger().debug("TransactionManager::mount: marking {}~{} used",
+                        addr,
+                        len);
+           segment_cleaner.mark_space_used(
+             addr,
+             len ,
+             /* init_scan = */ true);
+         });
+      });
+  }).safe_then([this] {
+    segment_cleaner.complete_init();
   }).handle_error(
     mount_ertr::pass_further{},
     crimson::ct_error::all_same_way([] {
@@ -170,7 +190,7 @@ TransactionManager::submit_transaction(
       [this, t=std::move(t)](auto p) mutable {
        auto [addr, journal_seq] = p;
        segment_cleaner.set_journal_head(journal_seq);
-       cache.complete_commit(*t, addr, journal_seq);
+       cache.complete_commit(*t, addr, journal_seq, &segment_cleaner);
        lba_manager.complete_transaction(*t);
       },
       submit_transaction_ertr::pass_further{},
index 91531b39f884dedc77d021badb997f1520293544..b02d34427b180ce1933c62b4528524174bc74b9c 100644 (file)
@@ -50,10 +50,6 @@ struct btree_lba_manager_test :
       next++);
   }
 
-  void put_segment(segment_id_t segment) final {
-    return;
-  }
-
   journal_seq_t get_journal_tail_target() const final { return journal_seq_t{}; }
   void update_journal_tail_committed(journal_seq_t committed) final {}
 
index 2f4c87476b8c07474cd35627916c44d643357672..3be26fd5c04313f6e8f3fbf19b87950a4758aee5 100644 (file)
@@ -85,10 +85,6 @@ struct journal_test_t : seastar_test_suite_t, JournalSegmentProvider {
       next++);
   }
 
-  void put_segment(segment_id_t segment) final {
-    return;
-  }
-
   journal_seq_t get_journal_tail_target() const final { return journal_seq_t{}; }
   void update_journal_tail_committed(journal_seq_t paddr) final {}
 
index 96e35c793d6c727caefa8394d2bbfd918f3bc2d7..0c6067ca8012c6242f4b11d28c522d5af97d8499 100644 (file)
@@ -68,7 +68,8 @@ struct transaction_manager_test_t : public seastar_test_suite_t {
   void init() {
     segment_cleaner = std::make_unique<SegmentCleaner>(
       SegmentCleaner::config_t::default_from_segment_manager(
-       *segment_manager));
+       *segment_manager),
+      true);
     journal = std::make_unique<Journal>(*segment_manager);
     cache = std::make_unique<Cache>(*segment_manager);
     lba_manager = lba_manager::create_lba_manager(*segment_manager, *cache);
@@ -198,12 +199,34 @@ struct transaction_manager_test_t : public seastar_test_suite_t {
       get_random_contents());
   }
 
+  bool check_usage() {
+    auto t = create_weak_transaction();
+    SpaceTrackerIRef tracker(segment_cleaner->get_empty_space_tracker());
+    lba_manager->scan_mapped_space(
+      *t.t,
+      [&tracker](auto offset, auto len) {
+       tracker->allocate(
+         offset.segment,
+         offset.offset,
+         len);
+      }).unsafe_get0();
+    return segment_cleaner->debug_check_space(*tracker);
+  }
+
   void replay() {
+    logger().debug("{}: begin", __func__);
+    EXPECT_TRUE(check_usage());
     tm->close().unsafe_get();
     destroy();
     static_cast<segment_manager::EphemeralSegmentManager*>(&*segment_manager)->remount();
     init();
     tm->mount().unsafe_get();
+    logger().debug("{}: end", __func__);
+  }
+
+  void check() {
+    check_mappings();
+    check_usage();
   }
 
   void check_mappings() {
@@ -300,9 +323,9 @@ TEST_F(transaction_manager_test_t, basic)
        'a');
       ASSERT_EQ(ADDR, extent->get_laddr());
       check_mappings(t);
-      check_mappings();
+      check();
       submit_transaction(std::move(t));
-      check_mappings();
+      check();
     }
   });
 }
@@ -321,10 +344,11 @@ TEST_F(transaction_manager_test_t, mutate)
        'a');
       ASSERT_EQ(ADDR, extent->get_laddr());
       check_mappings(t);
-      check_mappings();
+      check();
       submit_transaction(std::move(t));
-      check_mappings();
+      check();
     }
+    ASSERT_TRUE(check_usage());
     replay();
     {
       auto t = create_transaction();
@@ -334,12 +358,13 @@ TEST_F(transaction_manager_test_t, mutate)
        SIZE);
       auto mut = mutate_extent(t, ext);
       check_mappings(t);
-      check_mappings();
+      check();
       submit_transaction(std::move(t));
-      check_mappings();
+      check();
     }
+    ASSERT_TRUE(check_usage());
     replay();
-    check_mappings();
+    check();
   });
 }
 
@@ -367,14 +392,13 @@ TEST_F(transaction_manager_test_t, create_remove_same_transaction)
        'a');
 
       submit_transaction(std::move(t));
-      check_mappings();
+      check();
     }
     replay();
-    check_mappings();
+    check();
   });
 }
 
-
 TEST_F(transaction_manager_test_t, inc_dec_ref)
 {
   constexpr laddr_t SIZE = 4096;
@@ -389,35 +413,35 @@ TEST_F(transaction_manager_test_t, inc_dec_ref)
        'a');
       ASSERT_EQ(ADDR, extent->get_laddr());
       check_mappings(t);
-      check_mappings();
+      check();
       submit_transaction(std::move(t));
-      check_mappings();
+      check();
     }
     replay();
     {
       auto t = create_transaction();
       inc_ref(t, ADDR);
       check_mappings(t);
-      check_mappings();
+      check();
       submit_transaction(std::move(t));
-      check_mappings();
+      check();
     }
     {
       auto t = create_transaction();
       dec_ref(t, ADDR);
       check_mappings(t);
-      check_mappings();
+      check();
       submit_transaction(std::move(t));
-      check_mappings();
+      check();
     }
     replay();
     {
       auto t = create_transaction();
       dec_ref(t, ADDR);
       check_mappings(t);
-      check_mappings();
+      check();
       submit_transaction(std::move(t));
-      check_mappings();
+      check();
     }
   });
 }
@@ -436,7 +460,7 @@ TEST_F(transaction_manager_test_t, cause_lba_split)
       ASSERT_EQ(i * SIZE, extent->get_laddr());
       submit_transaction(std::move(t));
     }
-    check_mappings();
+    check();
   });
 }
 
@@ -457,8 +481,8 @@ TEST_F(transaction_manager_test_t, random_writes)
       submit_transaction(std::move(t));
     }
 
-    for (unsigned i = 0; i < 5; ++i) {
-      for (unsigned j = 0; j < 50; ++j) {
+    for (unsigned i = 0; i < 4; ++i) {
+      for (unsigned j = 0; j < 65; ++j) {
        auto t = create_transaction();
        for (unsigned k = 0; k < 2; ++k) {
          auto ext = get_extent(
@@ -477,7 +501,7 @@ TEST_F(transaction_manager_test_t, random_writes)
       }
       replay();
       logger().debug("random_writes: checking");
-      check_mappings();
+      check();
       logger().debug("random_writes: done replaying/checking");
     }
   });