]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
crimson/os/common/fixed_kv_node_layout: fix const and add replay support
authorSamuel Just <sjust@redhat.com>
Tue, 9 Jun 2020 19:44:41 +0000 (12:44 -0700)
committerSamuel Just <sjust@redhat.com>
Fri, 19 Jun 2020 19:59:23 +0000 (12:59 -0700)
Distinguishes between const and mutable iterators.  Reworks external
interface to enforce journaled mutations.

Signed-off-by: Samuel Just <sjust@redhat.com>
src/crimson/common/fixed_kv_node_layout.h
src/crimson/os/seastore/lba_manager/btree/btree_lba_manager.cc
src/crimson/os/seastore/lba_manager/btree/lba_btree_node_impl.cc
src/crimson/os/seastore/lba_manager/btree/lba_btree_node_impl.h
src/test/crimson/test_fixed_kv_node_layout.cc

index 874edb5744a03a9f155ca4c34dc629a07dd191c5..422e47553586554be704bbf15b61177dd51701a6 100644 (file)
 
 namespace crimson::common {
 
+template <typename T, bool is_const>
+struct maybe_const_t {
+};
+template<typename T>
+struct maybe_const_t<T, true> {
+  using type = const T*;
+};
+template<typename T>
+struct maybe_const_t<T, false> {
+  using type = T*;
+};
+
+
 /**
  * FixedKVNodeLayout
  *
@@ -19,7 +32,7 @@ namespace crimson::common {
  *
  * Uses absl::container_internal::Layout for the actual memory layout.
  *
- * The primary interface exposed is centered on the fixed_node_iter_t
+ * The primary interface exposed is centered on the iterator
  * and related methods.
  *
  * Also included are helpers for doing splits and merges as for a btree.
@@ -29,7 +42,8 @@ template <
   typename K,
   typename KINT,
   typename V,
-  typename VINT>
+  typename VINT,
+  bool VALIDATE_INVARIANTS=true>
 class FixedKVNodeLayout {
   char *buf = nullptr;
 
@@ -37,57 +51,65 @@ class FixedKVNodeLayout {
   static constexpr L layout{1, CAPACITY, CAPACITY};
 
 public:
-  struct fixed_node_iter_t {
+  template <bool is_const>
+  struct iter_t {
     friend class FixedKVNodeLayout;
-    FixedKVNodeLayout *node;
+    using parent_t = typename maybe_const_t<FixedKVNodeLayout, is_const>::type;
+
+    parent_t node;
     uint16_t offset;
 
-    fixed_node_iter_t(
-      FixedKVNodeLayout *parent,
+    iter_t(
+      parent_t parent,
       uint16_t offset) : node(parent), offset(offset) {}
 
-    fixed_node_iter_t(const fixed_node_iter_t &) = default;
-    fixed_node_iter_t(fixed_node_iter_t &&) = default;
-    fixed_node_iter_t &operator=(const fixed_node_iter_t &) = default;
-    fixed_node_iter_t &operator=(fixed_node_iter_t &&) = default;
+    iter_t(const iter_t &) = default;
+    iter_t(iter_t &&) = default;
+    iter_t &operator=(const iter_t &) = default;
+    iter_t &operator=(iter_t &&) = default;
+
+    operator iter_t<!is_const>() const {
+      static_assert(!is_const);
+      return iter_t<!is_const>(node, offset);
+    }
 
     // Work nicely with for loops without requiring a nested type.
-    fixed_node_iter_t &operator*() { return *this; }
-    fixed_node_iter_t *operator->() { return this; }
+    iter_t &operator*() { return *this; }
+    iter_t *operator->() { return this; }
 
-    fixed_node_iter_t operator++(int) {
+    iter_t operator++(int) {
       auto ret = *this;
       ++offset;
       return ret;
     }
 
-    fixed_node_iter_t &operator++() {
+    iter_t &operator++() {
       ++offset;
       return *this;
     }
 
-    uint16_t operator-(const fixed_node_iter_t &rhs) const {
+    uint16_t operator-(const iter_t &rhs) const {
       assert(rhs.node == node);
       return offset - rhs.offset;
     }
 
-    fixed_node_iter_t operator+(uint16_t off) const {
-      return fixed_node_iter_t(
+    iter_t operator+(uint16_t off) const {
+      return iter_t(
        node,
        offset + off);
     }
-    fixed_node_iter_t operator-(uint16_t off) const {
-      return fixed_node_iter_t(
+    iter_t operator-(uint16_t off) const {
+      return iter_t(
        node,
        offset - off);
     }
 
-    bool operator==(const fixed_node_iter_t &rhs) const {
+    bool operator==(const iter_t &rhs) const {
       assert(node == rhs.node);
       return rhs.offset == offset;
     }
 
-    bool operator!=(const fixed_node_iter_t &rhs) const {
+    bool operator!=(const iter_t &rhs) const {
       return !(*this == rhs);
     }
 
@@ -95,12 +117,6 @@ public:
       return K(node->get_key_ptr()[offset]);
     }
 
-    void set_key(K _lb) {
-      KINT lb;
-      lb = _lb;
-      node->get_key_ptr()[offset] = lb;
-    }
-
     K get_next_key_or_max() const {
       auto next = *this + 1;
       if (next == node->end())
@@ -113,11 +129,7 @@ public:
       return V(node->get_val_ptr()[offset]);
     };
 
-    void set_val(V val) {
-      node->get_val_ptr()[offset] = VINT(val);
-    }
-
-    bool contains(K addr) {
+    bool contains(K addr) const {
       return (get_key() <= addr) && (get_next_key_or_max() > addr);
     }
 
@@ -126,38 +138,223 @@ public:
     }
 
   private:
-    char *get_key_ptr() {
-      return reinterpret_cast<char *>(node->get_key_ptr() + offset);
+    void set_key(K _lb) const {
+      static_assert(!is_const);
+      KINT lb;
+      lb = _lb;
+      node->get_key_ptr()[offset] = lb;
     }
 
-    char *get_val_ptr() {
-      return reinterpret_cast<char *>(node->get_val_ptr() + offset);
+    void set_val(V val) const {
+      static_assert(!is_const);
+      node->get_val_ptr()[offset] = VINT(val);
+    }
+
+    typename maybe_const_t<char, is_const>::type get_key_ptr() const {
+      return reinterpret_cast<
+       typename maybe_const_t<char, is_const>::type>(
+         node->get_key_ptr() + offset);
+    }
+
+    typename maybe_const_t<char, is_const>::type get_val_ptr() const {
+      return reinterpret_cast<
+       typename maybe_const_t<char, is_const>::type>(
+         node->get_val_ptr() + offset);
+    }
+  };
+  using const_iterator = iter_t<true>;
+  using iterator = iter_t<false>;
+
+  struct delta_t {
+    enum class op_t : uint8_t {
+      INSERT,
+      REMOVE,
+      UPDATE,
+    } op;
+    KINT key;
+    VINT val;
+
+    void replay(FixedKVNodeLayout &l) {
+      switch (op) {
+      case op_t::INSERT: {
+       l.insert(l.lower_bound(key), key, val);
+       break;
+      }
+      case op_t::REMOVE: {
+       auto iter = l.find(key);
+       assert(iter != l.end());
+       l.remove(iter);
+       break;
+      }
+      case op_t::UPDATE: {
+       auto iter = l.find(key);
+       assert(iter != l.end());
+       l.update(iter, val);
+       break;
+      }
+      default:
+       assert(0 == "Impossible");
+      }
+    }
+
+    bool operator==(const delta_t &rhs) const {
+      return op == rhs.op &&
+       key == rhs.key &&
+       val == rhs.val;
     }
   };
 
 public:
+  class delta_buffer_t {
+    friend class FixedKVNode;
+    std::vector<delta_t> buffer;
+  public:
+    bool empty() const {
+      return buffer.empty();
+    }
+    void insert(
+      const K &key,
+      const V &val) {
+      KINT k;
+      k = key;
+      buffer.push_back(
+       delta_t{
+         delta_t::op_t::INSERT,
+         k,
+         VINT(val)
+       });
+    }
+    void update(
+      const K &key,
+      const V &val) {
+      KINT k;
+      k = key;
+      buffer.push_back(
+       delta_t{
+         delta_t::op_t::UPDATE,
+         k,
+         VINT(val)
+       });
+    }
+    void remove(const K &key) {
+      KINT k;
+      k = key;
+      buffer.push_back(
+       delta_t{
+         delta_t::op_t::REMOVE,
+         k,
+         VINT()
+       });
+    }
+    void replay(FixedKVNodeLayout &node) {
+      for (auto &i: buffer) {
+       i.replay(node);
+      }
+    }
+    size_t get_bytes() const {
+      return buffer.size() * sizeof(delta_t);
+    }
+    void copy_out(char *out, size_t len) {
+      assert(len == get_bytes());
+      ::memcpy(out, reinterpret_cast<const void *>(buffer.data()), get_bytes());
+      buffer.clear();
+    }
+    void copy_in(const char *out, size_t len) {
+      assert(empty());
+      assert(len % sizeof(delta_t) == 0);
+      buffer = std::vector(
+       reinterpret_cast<const delta_t*>(out),
+       reinterpret_cast<const delta_t*>(out + len));
+    }
+    bool operator==(const delta_buffer_t &rhs) const {
+      return buffer == rhs.buffer;
+    }
+  };
+
+  void journal_insert(
+    const_iterator _iter,
+    const K &key,
+    const V &val,
+    delta_buffer_t *recorder) {
+    auto iter = iterator(this, _iter.offset);
+    if (recorder) {
+      recorder->insert(
+       key,
+       val);
+    }
+    insert(iter, key, val);
+  }
+
+  void journal_update(
+    const_iterator _iter,
+    const V &val,
+    delta_buffer_t *recorder) {
+    auto iter = iterator(this, _iter.offset);
+    if (recorder) {
+      recorder->update(iter->get_key(), val);
+    }
+    update(iter, val);
+  }
+
+  void journal_replace(
+    const_iterator _iter,
+    const K &key,
+    const V &val,
+    delta_buffer_t *recorder) {
+    auto iter = iterator(this, _iter.offset);
+    if (recorder) {
+      recorder->remove(iter->get_key());
+      recorder->insert(key, val);
+    }
+    replace(iter, key, val);
+  }
+
+
+  void journal_remove(
+    const_iterator _iter,
+    delta_buffer_t *recorder) {
+    auto iter = iterator(this, _iter.offset);
+    if (recorder) {
+      recorder->remove(iter->get_key());
+    }
+    remove(iter);
+  }
+
+
   FixedKVNodeLayout(char *buf) :
     buf(buf) {}
 
-  fixed_node_iter_t begin() {
-    return fixed_node_iter_t(
+  const_iterator begin() const {
+    return const_iterator(
+      this,
+      0);
+  }
+
+  const_iterator end() const {
+    return const_iterator(
+      this,
+      get_size());
+  }
+
+  iterator begin() {
+    return iterator(
       this,
       0);
   }
 
-  fixed_node_iter_t end() {
-    return fixed_node_iter_t(
+  iterator end() {
+    return iterator(
       this,
       get_size());
   }
 
-  fixed_node_iter_t iter_idx(uint16_t off) {
-    return fixed_node_iter_t(
+  const_iterator iter_idx(uint16_t off) const {
+    return const_iterator(
       this,
       off);
   }
 
-  fixed_node_iter_t find(K l) {
+  const_iterator find(K l) const {
     auto ret = begin();
     for (; ret != end(); ++ret) {
       if (ret->get_key() == l)
@@ -165,74 +362,65 @@ public:
     }
     return ret;
   }
+  iterator find(K l) {
+    const auto &tref = *this;
+    return iterator(this, tref.find(l).offset);
+  }
 
-  fixed_node_iter_t get_split_pivot() {
-    return iter_idx(get_size() / 2);
+  const_iterator lower_bound(K l) const {
+    auto ret = begin();
+    for (; ret != end(); ++ret) {
+      if (ret->get_key() > l)
+       break;
+    }
+    return ret;
+  }
+  iterator lower_bound(K l) {
+    const auto &tref = *this;
+    return iterator(this, tref.lower_bound(l).offset);
   }
 
-private:
-  KINT *get_key_ptr() {
-    return layout.template Pointer<1>(buf);
+  const_iterator upper_bound(K l) const {
+    auto ret = begin();
+    for (; ret != end(); ++ret) {
+      if (ret->get_key() > l)
+       break;
+    }
+    return ret;
+  }
+  iterator upper_bound(K l) {
+    const auto &tref = *this;
+    return iterator(this, tref.upper_bound(l).offset);
   }
 
-  VINT *get_val_ptr() {
-    return layout.template Pointer<2>(buf);
+  const_iterator get_split_pivot() const {
+    return iter_idx(get_size() / 2);
   }
 
-public:
   uint16_t get_size() const {
     return *layout.template Pointer<0>(buf);
   }
 
-  void set_size(uint16_t size) {
-    *layout.template Pointer<0>(buf) = size;
-  }
-
   constexpr static size_t get_capacity() {
     return CAPACITY;
   }
 
-  /**
-   * copy_from_foreign
-   *
-   * Copies entries from [from_src, to_src) to tgt.
-   *
-   * tgt and from_src must be from different nodes.
-   * from_src and to_src must be from the same node.
-   */
-  static void copy_from_foreign(
-    fixed_node_iter_t tgt,
-    fixed_node_iter_t from_src,
-    fixed_node_iter_t to_src) {
-    assert(tgt->node != from_src->node);
-    assert(to_src->node == from_src->node);
-    memcpy(
-      tgt->get_val_ptr(), from_src->get_val_ptr(),
-      to_src->get_val_ptr() - from_src->get_val_ptr());
-    memcpy(
-      tgt->get_key_ptr(), from_src->get_key_ptr(),
-      to_src->get_key_ptr() - from_src->get_key_ptr());
-  }
+  bool operator==(const FixedKVNodeLayout &rhs) const {
+    if (get_size() != rhs.get_size()) {
+      return false;
+    }
 
-  /**
-   * copy_from_local
-   *
-   * Copies entries from [from_src, to_src) to tgt.
-   *
-   * tgt, from_src, and to_src must be from the same node.
-   */
-  static void copy_from_local(
-    fixed_node_iter_t tgt,
-    fixed_node_iter_t from_src,
-    fixed_node_iter_t to_src) {
-    assert(tgt->node == from_src->node);
-    assert(to_src->node == from_src->node);
-    memmove(
-      tgt->get_val_ptr(), from_src->get_val_ptr(),
-      to_src->get_val_ptr() - from_src->get_val_ptr());
-    memmove(
-      tgt->get_key_ptr(), from_src->get_key_ptr(),
-      to_src->get_key_ptr() - from_src->get_key_ptr());
+    auto iter = begin();
+    auto iter2 = rhs.begin();
+    while (iter != end()) {
+      if (iter->get_key() != iter2->get_key() ||
+         iter->get_val() != iter2->get_val()) {
+       return false;
+      }
+      iter++;
+      iter2++;
+    }
+    return true;
   }
 
   /**
@@ -242,7 +430,7 @@ public:
    */
   K split_into(
     FixedKVNodeLayout &left,
-    FixedKVNodeLayout &right) {
+    FixedKVNodeLayout &right) const {
     auto piviter = get_split_pivot();
 
     left.copy_from_foreign(left.begin(), begin(), piviter);
@@ -262,8 +450,8 @@ public:
    * precondition: left.size() + right.size() < CAPACITY
    */
   void merge_from(
-    FixedKVNodeLayout &left,
-    FixedKVNodeLayout &right)
+    const FixedKVNodeLayout &left,
+    const FixedKVNodeLayout &right)
   {
     copy_from_foreign(
       end(),
@@ -286,8 +474,8 @@ public:
    * the left side iff prefer_left.
    */
   static K balance_into_new_nodes(
-    FixedKVNodeLayout &left,
-    FixedKVNodeLayout &right,
+    const FixedKVNodeLayout &left,
+    const FixedKVNodeLayout &right,
     bool prefer_left,
     FixedKVNodeLayout &replacement_left,
     FixedKVNodeLayout &replacement_right)
@@ -341,6 +529,133 @@ public:
 
     return replacement_pivot;
   }
+
+private:
+  void insert(
+    iterator iter,
+    const K &key,
+    const V &val) {
+    if (VALIDATE_INVARIANTS) {
+      if (iter != begin()) {
+       assert((iter - 1)->get_key() < key);
+      }
+      if (iter != end()) {
+       assert(iter->get_key() > key);
+      }
+      assert(get_size() < CAPACITY);
+    }
+    copy_from_local(iter + 1, iter, end());
+    iter->set_key(key);
+    iter->set_val(val);
+    set_size(get_size() + 1);
+  }
+
+  void update(
+    iterator iter,
+    V val) {
+    assert(iter != end());
+    iter->set_val(val);
+  }
+
+  void replace(
+    iterator iter,
+    const K &key,
+    const V &val) {
+    assert(iter != end());
+    if (VALIDATE_INVARIANTS) {
+      if (iter != begin()) {
+       assert((iter - 1)->get_key() < key);
+      }
+      if ((iter + 1) != end()) {
+       assert((iter + 1)->get_key() > key);
+      }
+    }
+    iter->set_key(key);
+    iter->set_val(val);
+  }
+
+  void remove(iterator iter) {
+    assert(iter != end());
+    copy_from_local(iter, iter + 1, end());
+    set_size(get_size() - 1);
+  }
+
+  /**
+   * get_key_ptr
+   *
+   * Get pointer to start of key array
+   */
+  KINT *get_key_ptr() {
+    return layout.template Pointer<1>(buf);
+  }
+  const KINT *get_key_ptr() const {
+    return layout.template Pointer<1>(buf);
+  }
+
+  /**
+   * get_val_ptr
+   *
+   * Get pointer to start of val array
+   */
+  VINT *get_val_ptr() {
+    return layout.template Pointer<2>(buf);
+  }
+  const VINT *get_val_ptr() const {
+    return layout.template Pointer<2>(buf);
+  }
+
+  /**
+   * set_size
+   *
+   * Set size representation to match size
+   */
+  void set_size(uint16_t size) {
+    *layout.template Pointer<0>(buf) = size;
+  }
+
+
+  /**
+   * copy_from_foreign
+   *
+   * Copies entries from [from_src, to_src) to tgt.
+   *
+   * tgt and from_src must be from different nodes.
+   * from_src and to_src must be from the same node.
+   */
+  static void copy_from_foreign(
+    iterator tgt,
+    const_iterator from_src,
+    const_iterator to_src) {
+    assert(tgt->node != from_src->node);
+    assert(to_src->node == from_src->node);
+    memcpy(
+      tgt->get_val_ptr(), from_src->get_val_ptr(),
+      to_src->get_val_ptr() - from_src->get_val_ptr());
+    memcpy(
+      tgt->get_key_ptr(), from_src->get_key_ptr(),
+      to_src->get_key_ptr() - from_src->get_key_ptr());
+  }
+
+  /**
+   * copy_from_local
+   *
+   * Copies entries from [from_src, to_src) to tgt.
+   *
+   * tgt, from_src, and to_src must be from the same node.
+   */
+  static void copy_from_local(
+    iterator tgt,
+    iterator from_src,
+    iterator to_src) {
+    assert(tgt->node == from_src->node);
+    assert(to_src->node == from_src->node);
+    memmove(
+      tgt->get_val_ptr(), from_src->get_val_ptr(),
+      to_src->get_val_ptr() - from_src->get_val_ptr());
+    memmove(
+      tgt->get_key_ptr(), from_src->get_key_ptr(),
+      to_src->get_key_ptr() - from_src->get_key_ptr());
+  }
 };
 
 }
index beb69d1d52090fe456d310ce31951e9d9b45c43a..0a5c9d1fa159bf9917e4af8ce2c108ca0121dfe0 100644 (file)
@@ -194,9 +194,11 @@ BtreeLBAManager::insert_mapping_ret BtreeLBAManager::insert_mapping(
        }
        auto nroot = cache.alloc_new_extent<LBAInternalNode>(t, LBA_BLOCK_SIZE);
        nroot->set_depth(root->depth + 1);
-       nroot->begin()->set_key(L_ADDR_MIN);
-       nroot->begin()->set_val(root->get_paddr());
-       nroot->set_size(1);
+       nroot->journal_insert(
+         nroot->begin(),
+         L_ADDR_MIN,
+         root->get_paddr(),
+         nullptr);
        croot->get_lba_root().lba_root_addr = nroot->get_paddr();
        croot->get_lba_root().lba_depth = root->depth + 1;
        return nroot->split_entry(cache, t, laddr, nroot->begin(), root);
index f221f68a09a986b2ee61cde238e8eef15225fc59..deb9758fa9b219bd5803e325d53f3019779862ff 100644 (file)
@@ -206,16 +206,8 @@ LBAInternalNode::split_entry(
   ceph_assert(!at_max_capacity());
   auto [left, right, pivot] = entry->make_split_children(c, t);
 
-  journal_remove(iter->get_key());
-  journal_insert(iter->get_key(), left->get_paddr());
-  journal_insert(pivot, right->get_paddr());
-
-  copy_from_local(iter + 1, iter, end());
-  iter->set_val(maybe_generate_relative(left->get_paddr()));
-  iter++;
-  iter->set_key(pivot);
-  iter->set_val(maybe_generate_relative(right->get_paddr()));
-  set_size(get_size() + 1);
+  journal_update(iter, left->get_paddr(), maybe_get_delta_buffer());
+  journal_insert(iter + 1, pivot, right->get_paddr(), maybe_get_delta_buffer());
 
   c.retire_extent(t, entry);
 
@@ -230,19 +222,6 @@ LBAInternalNode::split_entry(
   );
 }
 
-void LBAInternalNode::journal_remove(
-  laddr_t to_remove)
-{
-  // TODO
-}
-
-void LBAInternalNode::journal_insert(
-  laddr_t to_insert,
-  paddr_t val)
-{
-  // TODO
-}
-
 LBAInternalNode::merge_ret
 LBAInternalNode::merge_entry(
   Cache &c, Transaction &t, laddr_t addr,
@@ -272,13 +251,8 @@ LBAInternalNode::merge_entry(
        t,
        r);
 
-      journal_remove(riter->get_key());
-      journal_remove(liter->get_key());
-      journal_insert(liter->get_key(), replacement->get_paddr());
-
-      liter->set_val(maybe_generate_relative(replacement->get_paddr()));
-      copy_from_local(riter, riter + 1, end());
-      set_size(get_size() - 1);
+      journal_update(liter, replacement->get_paddr(), maybe_get_delta_buffer());
+      journal_remove(riter, maybe_get_delta_buffer());
 
       c.retire_extent(t, l);
       c.retire_extent(t, r);
@@ -295,16 +269,15 @@ LBAInternalNode::merge_entry(
          r,
          !donor_is_left);
 
-      journal_remove(liter->get_key());
-      journal_remove(riter->get_key());
-      journal_insert(liter->get_key(), replacement_l->get_paddr());
-      journal_insert(pivot, replacement_r->get_paddr());
-
-      liter->set_val(
-       maybe_generate_relative(replacement_l->get_paddr()));
-      riter->set_key(pivot);
-      riter->set_val(
-       maybe_generate_relative(replacement_r->get_paddr()));
+      journal_update(
+       liter,
+       replacement_l->get_paddr(),
+       maybe_get_delta_buffer());
+      journal_replace(
+       riter,
+       pivot,
+       replacement_r->get_paddr(),
+       maybe_get_delta_buffer());
 
       c.retire_extent(t, l);
       c.retire_extent(t, r);
@@ -365,25 +338,22 @@ LBALeafNode::insert_ret LBALeafNode::insert(
   lba_map_val_t val)
 {
   ceph_assert(!at_max_capacity());
-  auto insert_pt = upper_bound(laddr);
-  if (insert_pt != end()) {
-    copy_from_local(insert_pt + 1, insert_pt, end());
-  }
-  set_size(get_size() + 1);
-  insert_pt.set_key(laddr);
+
   val.paddr = maybe_generate_relative(val.paddr);
   logger().debug(
     "LBALeafNode::insert: inserting {}~{} -> {}",
     laddr,
     val.len,
     val.paddr);
-  insert_pt.set_val(val);
+
+  auto insert_pt = lower_bound(laddr);
+  journal_insert(insert_pt, laddr, val, maybe_get_delta_buffer());
+
   logger().debug(
     "LBALeafNode::insert: inserted {}~{} -> {}",
     insert_pt.get_key(),
     insert_pt.get_val().len,
     insert_pt.get_val().paddr);
-  journal_insertion(laddr, val);
   return insert_ret(
     insert_ertr::ready_future_marker{},
     std::make_unique<BtreeLBAPin>(
@@ -409,41 +379,18 @@ LBALeafNode::mutate_mapping_ret LBALeafNode::mutate_mapping(
 
   auto mutated = f(mutation_pt.get_val());
   if (mutated) {
-    mutation_pt.set_val(*mutated);
-    journal_mutated(laddr, *mutated);
+    journal_update(mutation_pt, *mutated, maybe_get_delta_buffer());
     return mutate_mapping_ret(
       mutate_mapping_ertr::ready_future_marker{},
       mutated);
   } else {
-    journal_removal(laddr);
-    copy_from_local(mutation_pt, mutation_pt + 1, end());
-    set_size(get_size() - 1);
+    journal_remove(mutation_pt, maybe_get_delta_buffer());
     return mutate_mapping_ret(
       mutate_mapping_ertr::ready_future_marker{},
       mutated);
   }
 }
 
-void LBALeafNode::journal_mutated(
-  laddr_t laddr,
-  lba_map_val_t val)
-{
-  // TODO
-}
-
-void LBALeafNode::journal_insertion(
-  laddr_t laddr,
-  lba_map_val_t val)
-{
-  // TODO
-}
-
-void LBALeafNode::journal_removal(
-  laddr_t laddr)
-{
-  // TODO
-}
-
 LBALeafNode::find_hole_ret LBALeafNode::find_hole(
   Cache &cache,
   Transaction &t,
index 49611d7b299e4bf5c0176b35e81232fafd8c9ba9..9d419a4138bee2501dcd87696b5740f7f59635e8 100644 (file)
@@ -44,7 +44,7 @@ struct LBAInternalNode
       INTERNAL_NODE_CAPACITY,
       laddr_t, laddr_le_t,
       paddr_t, paddr_le_t> {
-  using internal_iterator_t = fixed_node_iter_t;
+  using internal_iterator_t = const_iterator;
   template <typename... T>
   LBAInternalNode(T&&... t) :
     LBANode(std::forward<T>(t)...),
@@ -53,9 +53,15 @@ struct LBAInternalNode
   static constexpr extent_types_t type = extent_types_t::LADDR_INTERNAL;
 
   CachedExtentRef duplicate_for_write() final {
+    assert(delta_buffer.empty());
     return CachedExtentRef(new LBAInternalNode(*this));
   };
 
+  delta_buffer_t delta_buffer;
+  delta_buffer_t *maybe_get_delta_buffer() {
+    return is_mutation_pending() ? &delta_buffer : nullptr;
+  }
+
   lookup_range_ret lookup_range(
     Cache &cache,
     Transaction &transaction,
@@ -212,13 +218,6 @@ struct LBAInternalNode
 
   /// returns iterator for subtree containing laddr
   internal_iterator_t get_containing_child(laddr_t laddr);
-
-  // delta operations (TODO)
-  void journal_remove(
-    laddr_t to_remove);
-  void journal_insert(
-    laddr_t to_insert,
-    paddr_t val);
 };
 
 /**
@@ -267,7 +266,7 @@ struct LBALeafNode
       LEAF_NODE_CAPACITY,
       laddr_t, laddr_le_t,
       lba_map_val_t, lba_map_val_le_t> {
-  using internal_iterator_t = fixed_node_iter_t;
+  using internal_iterator_t = const_iterator;
   template <typename... T>
   LBALeafNode(T&&... t) :
     LBANode(std::forward<T>(t)...),
@@ -276,9 +275,15 @@ struct LBALeafNode
   static constexpr extent_types_t type = extent_types_t::LADDR_LEAF;
 
   CachedExtentRef duplicate_for_write() final {
+    assert(delta_buffer.empty());
     return CachedExtentRef(new LBALeafNode(*this));
   };
 
+  delta_buffer_t delta_buffer;
+  delta_buffer_t *maybe_get_delta_buffer() {
+    return is_mutation_pending() ? &delta_buffer : nullptr;
+  }
+
   lookup_range_ret lookup_range(
     Cache &cache,
     Transaction &transaction,
@@ -400,27 +405,9 @@ struct LBALeafNode
     }
     return std::make_pair(retl, retr);
   }
-  internal_iterator_t upper_bound(laddr_t l) {
-    auto ret = begin();
-    for (; ret != end(); ++ret) {
-      if (ret->get_key() > l)
-       break;
-    }
-    return ret;
-  }
 
   std::pair<internal_iterator_t, internal_iterator_t>
   get_leaf_entries(laddr_t addr, extent_len_t len);
-
-  // delta operations (TODO)
-  void journal_mutated(
-    laddr_t laddr,
-    lba_map_val_t val);
-  void journal_insertion(
-    laddr_t laddr,
-    lba_map_val_t val);
-  void journal_removal(
-    laddr_t laddr);
 };
 using LBALeafNodeRef = TCachedExtentRef<LBALeafNode>;
 
index 2ca3d3a49327e53e60870e18ded4696777478fc3..5970f0b4ebcffeda22a72809a5276fc6bc8984c7 100644 (file)
@@ -2,6 +2,8 @@
 // vim: ts=8 sw=2 smarttab
 
 #include <stdio.h>
+#include <iostream>
+
 #include "gtest/gtest.h"
 
 #include "crimson/common/fixed_kv_node_layout.h"
@@ -16,6 +18,9 @@ struct test_val_t {
   bool operator==(const test_val_t &rhs) const {
     return rhs.t1 == t1 && rhs.t2 == t2;
   }
+  bool operator!=(const test_val_t &rhs) const {
+    return !(*this == rhs);
+  }
 };
 
 struct test_val_le_t {
@@ -27,9 +32,16 @@ struct test_val_le_t {
   test_val_le_t(const test_val_t &nv)
     : t1(init_le32(nv.t1)), t2(init_les32(nv.t2)) {}
 
-  operator test_val_t() {
+  operator test_val_t() const {
     return test_val_t{t1, t2};
   }
+
+  bool operator==(const test_val_t &rhs) const {
+    return rhs.t1 == t1 && rhs.t2 == t2;
+  }
+  bool operator!=(const test_val_t &rhs) const {
+    return !(*this == rhs);
+  }
 };
 
 constexpr size_t CAPACITY = 341;
@@ -42,20 +54,27 @@ struct TestNode : FixedKVNodeLayout<
   TestNode() : FixedKVNodeLayout(buf) {
     memset(buf, 0, sizeof(buf));
   }
+  TestNode(const TestNode &rhs)
+    : FixedKVNodeLayout(buf) {
+    ::memcpy(buf, rhs.buf, sizeof(buf));
+  }
+
+  TestNode &operator=(const TestNode &rhs) {
+    memcpy(buf, rhs.buf, sizeof(buf));
+    return *this;
+  }
 };
 
 TEST(FixedKVNodeTest, basic) {
   auto node = TestNode();
   ASSERT_EQ(node.get_size(), 0);
-  node.set_size(1);
+
+  auto val = test_val_t{ 1, 1 };
+  node.journal_insert(node.begin(), 1, val, nullptr);
   ASSERT_EQ(node.get_size(), 1);
 
   auto iter = node.begin();
-  iter.set_key(1);
   ASSERT_EQ(iter.get_key(), 1);
-
-  auto val = test_val_t{ 1, 1 };
-  iter.set_val(val);
   ASSERT_EQ(val, iter.get_val());
 
   ASSERT_EQ(std::numeric_limits<uint32_t>::max(), iter.get_next_key_or_max());
@@ -66,15 +85,15 @@ TEST(FixedKVNodeTest, at_capacity) {
   ASSERT_EQ(CAPACITY, node.get_capacity());
 
   ASSERT_EQ(node.get_size(), 0);
-  node.set_size(node.get_capacity());
-  ASSERT_EQ(node.get_size(), CAPACITY);
 
   unsigned short num = 0;
-  for (auto &i : node) {
-    i.set_key(num);
-    i.set_val({ num, num});
+  auto iter = node.begin();
+  while (num < CAPACITY) {
+    node.journal_insert(iter, num, test_val_t{num, num}, nullptr);
     ++num;
+    ++iter;
   }
+  ASSERT_EQ(node.get_size(), CAPACITY);
 
   num = 0;
   for (auto &i : node) {
@@ -94,14 +113,14 @@ TEST(FixedKVNodeTest, split) {
 
   ASSERT_EQ(node.get_size(), 0);
 
-  node.set_size(CAPACITY);
-
   unsigned short num = 0;
-  for (auto &i : node) {
-    i.set_key(num);
-    i.set_val({ num, num});
+  auto iter = node.begin();
+  while (num < CAPACITY) {
+    node.journal_insert(iter, num, test_val_t{num, num}, nullptr);
     ++num;
+    ++iter;
   }
+  ASSERT_EQ(node.get_size(), CAPACITY);
 
   auto split_left = TestNode();
   auto split_right = TestNode();
@@ -132,7 +151,6 @@ TEST(FixedKVNodeTest, split) {
   ASSERT_EQ(num, CAPACITY);
 }
 
-
 TEST(FixedKVNodeTest, merge) {
   auto node = TestNode();
   auto node2 = TestNode();
@@ -140,23 +158,25 @@ TEST(FixedKVNodeTest, merge) {
   ASSERT_EQ(node.get_size(), 0);
   ASSERT_EQ(node2.get_size(), 0);
 
-  node.set_size(CAPACITY / 2);
-  node2.set_size(CAPACITY / 2);
-
-  auto total = node.get_size() + node2.get_size();
-
   unsigned short num = 0;
-  for (auto &i : node) {
-    i.set_key(num);
-    i.set_val({ num, num});
+  auto iter = node.begin();
+  while (num < CAPACITY/2) {
+    node.journal_insert(iter, num, test_val_t{num, num}, nullptr);
     ++num;
+    ++iter;
   }
-  for (auto &i : node2) {
-    i.set_key(num);
-    i.set_val({ num, num});
+  iter = node2.begin();
+  while (num < (2 * (CAPACITY / 2))) {
+    node2.journal_insert(iter, num, test_val_t{num, num}, nullptr);
     ++num;
+    ++iter;
   }
 
+  ASSERT_EQ(node.get_size(), CAPACITY / 2);
+  ASSERT_EQ(node2.get_size(), CAPACITY / 2);
+
+  auto total = node.get_size() + node2.get_size();
+
   auto node_merged = TestNode();
   node_merged.merge_from(node, node2);
 
@@ -183,23 +203,25 @@ void run_balance_test(unsigned left, unsigned right, bool prefer_left)
   ASSERT_EQ(node.get_size(), 0);
   ASSERT_EQ(node2.get_size(), 0);
 
-  node.set_size(left);
-  node2.set_size(right);
-
-  auto total = left + right;
-
   unsigned short num = 0;
-  for (auto &i : node) {
-    i.set_key(num);
-    i.set_val({ num, num});
+  auto iter = node.begin();
+  while (num < left) {
+    node.journal_insert(iter, num, test_val_t{num, num}, nullptr);
     ++num;
+    ++iter;
   }
-  for (auto &i : node2) {
-    i.set_key(num);
-    i.set_val({ num, num});
+  iter = node2.begin();
+  while (num < (left + right)) {
+    node2.journal_insert(iter, num, test_val_t{num, num}, nullptr);
     ++num;
+    ++iter;
   }
 
+  ASSERT_EQ(node.get_size(), left);
+  ASSERT_EQ(node2.get_size(), right);
+
+  auto total = node.get_size() + node2.get_size();
+
   auto node_balanced = TestNode();
   auto node_balanced2 = TestNode();
   TestNode::balance_into_new_nodes(
@@ -253,3 +275,39 @@ TEST(FixedKVNodeTest, balanced) {
   run_balance_test(CAPACITY / 2, CAPACITY - 1, false);
   run_balance_test(CAPACITY / 2, CAPACITY / 2, false);
 }
+
+void run_replay_test(
+  std::vector<std::function<void(TestNode&, TestNode::delta_buffer_t&)>> &&f
+) {
+  TestNode node;
+  for (unsigned i = 0; i < f.size(); ++i) {
+    TestNode::delta_buffer_t buf;
+    TestNode replayed = node;
+    f[i](node, buf);
+    buf.replay(replayed);
+    ASSERT_EQ(node.get_size(), replayed.get_size());
+    ASSERT_EQ(node, replayed);
+  }
+}
+
+TEST(FixedKVNodeTest, replay) {
+  run_replay_test({
+      [](auto &n, auto &b) {
+       n.journal_insert(n.lower_bound(1), 1, test_val_t{1, 1}, &b);
+       ASSERT_EQ(1, n.get_size());
+      },
+      [](auto &n, auto &b) {
+       n.journal_insert(n.lower_bound(3), 3, test_val_t{1, 2}, &b);
+       ASSERT_EQ(2, n.get_size());
+      },
+      [](auto &n, auto &b) {
+       n.journal_remove(n.find(3), &b);
+       ASSERT_EQ(1, n.get_size());
+      },
+      [](auto &n, auto &b) {
+       n.journal_insert(n.lower_bound(2), 2, test_val_t{5, 1}, &b);
+       ASSERT_EQ(2, n.get_size());
+      }
+  });
+
+}