onode_manager/staged-fltree/node.cc
onode_manager/staged-fltree/node_extent_manager.cc
onode_manager/staged-fltree/node_extent_mutable.cc
+ onode_manager/staged-fltree/node_impl.cc
onode_manager/staged-fltree/stages/item_iterator_stage.cc
onode_manager/staged-fltree/stages/key_layout.cc
- onode_manager/staged-fltree/stages/node_layout.cc
+ onode_manager/staged-fltree/stages/node_stage_layout.cc
onode_manager/staged-fltree/stages/node_stage.cc
onode_manager/staged-fltree/stages/sub_items_stage.cc
onode_manager/staged-fltree/super.cc
#include <string>
#include "crimson/common/errorator.h"
+#include "crimson/os/seastore/cached_extent.h"
#include "crimson/os/seastore/seastore_types.h"
#include "crimson/os/seastore/transaction.h"
using crimson::os::seastore::L_ADDR_NULL;
using crimson::os::seastore::extent_len_t;
+class NodeExtent;
class NodeExtentManager;
class RootNodeTracker;
+using NodeExtentRef = crimson::os::seastore::TCachedExtentRef<NodeExtent>;
+using NodeExtentManagerURef = std::unique_ptr<NodeExtentManager>;
+using RootNodeTrackerURef = std::unique_ptr<RootNodeTracker>;
struct context_t {
NodeExtentManager& nm;
Transaction& t;
};
-using NodeExtentManagerURef = std::unique_ptr<NodeExtentManager>;
-using RootNodeTrackerURef = std::unique_ptr<RootNodeTracker>;
+class LeafNodeImpl;
+class InternalNodeImpl;
+class NodeImpl;
+using LeafNodeImplURef = std::unique_ptr<LeafNodeImpl>;
+using InternalNodeImplURef = std::unique_ptr<InternalNodeImpl>;
+using NodeImplURef = std::unique_ptr<NodeImpl>;
+
+using level_t = uint8_t;
constexpr auto INDEX_END = std::numeric_limits<size_t>::max();
// TODO: decide by NODE_BLOCK_SIZE
#include <cassert>
#include <exception>
+#include <iostream>
+#include "common/likely.h"
+#include "node_extent_manager.h"
#include "node_impl.h"
+#include "stages/node_stage_layout.h"
namespace crimson::os::seastore::onode {
template <class... ValuesT>
using node_future = Node::node_future<ValuesT...>;
+/*
+ * tree_cursor_t
+ */
+
tree_cursor_t::tree_cursor_t(
Ref<LeafNode> node, const search_position_t& pos, const onode_t* p_value)
: leaf_node{node}, position{pos}, p_value{p_value} {
}
}
+const onode_t* tree_cursor_t::get_p_value() const {
+ assert(!is_end());
+ if (!p_value) {
+ // NOTE: the leaf node is always present when we hold its reference.
+ p_value = leaf_node->get_p_value(position);
+ }
+ assert(p_value);
+ return p_value;
+}
+
void tree_cursor_t::update_track(
Ref<LeafNode> node, const search_position_t& pos) {
// already untracked
leaf_node->do_track_cursor(*this);
}
-const onode_t* tree_cursor_t::get_p_value() const {
- assert(!is_end());
+void tree_cursor_t::set_p_value(const onode_t* _p_value) {
if (!p_value) {
- // NOTE: the leaf node is always present when we hold its reference.
- p_value = leaf_node->get_p_value(position);
+ p_value = _p_value;
+ } else {
+ assert(p_value == _p_value);
}
- assert(p_value);
- return p_value;
}
+/*
+ * Node
+ */
+
+Node::Node(NodeImplURef&& impl) : impl{std::move(impl)} {}
+
Node::~Node() {
// XXX: tolerate failure between allocate() and as_child()
if (is_root()) {
}
}
-node_future<> Node::upgrade_root(context_t c) {
- assert(is_root());
- assert(is_level_tail());
- assert(field_type() == field_type_t::N0);
- super->do_untrack_root(*this);
- return InternalNode0::allocate_root(c, level(), laddr(), std::move(super)
- ).safe_then([this](auto new_root) {
- as_child(search_position_t::end(), new_root);
- });
+level_t Node::level() const {
+ return impl->level();
}
-template <bool VALIDATE>
-void Node::as_child(const search_position_t& pos, Ref<InternalNode> parent_node) {
- assert(!super);
- _parent_info = parent_info_t{pos, parent_node};
- parent_info().ptr->do_track_child<VALIDATE>(*this);
-}
-template void Node::as_child<true>(const search_position_t&, Ref<InternalNode>);
-template void Node::as_child<false>(const search_position_t&, Ref<InternalNode>);
-
-
-node_future<Node::search_result_t>
-Node::lower_bound(context_t c, const key_hobj_t& key) {
+node_future<Node::search_result_t> Node::lower_bound(
+ context_t c, const key_hobj_t& key) {
return seastar::do_with(
MatchHistory(), [this, c, &key](auto& history) {
- return do_lower_bound(c, key, history);
+ return lower_bound_tracked(c, key, history);
}
);
}
-node_future<std::pair<Ref<tree_cursor_t>, bool>>
-Node::insert(context_t c, const key_hobj_t& key, const onode_t& value) {
+node_future<std::pair<Ref<tree_cursor_t>, bool>> Node::insert(
+ context_t c, const key_hobj_t& key, const onode_t& value) {
return seastar::do_with(
MatchHistory(), [this, c, &key, &value](auto& history) {
- return do_lower_bound(c, key, history
+ return lower_bound_tracked(c, key, history
).safe_then([c, &key, &value, &history](auto result) {
if (result.match == MatchKindBS::EQ) {
return node_ertr::make_ready_future<std::pair<Ref<tree_cursor_t>, bool>>(
);
}
+std::ostream& Node::dump(std::ostream& os) const {
+ return impl->dump(os);
+}
+
+std::ostream& Node::dump_brief(std::ostream& os) const {
+ return impl->dump_brief(os);
+}
+
+void Node::test_make_destructable(
+ context_t c, NodeExtentMutable& mut, Super::URef&& _super) {
+ impl->test_set_tail(mut);
+ make_root(c, std::move(_super));
+}
+
node_future<> Node::mkfs(context_t c, RootNodeTracker& root_tracker) {
- return LeafNode0::mkfs(c, root_tracker);
+ return LeafNode::allocate_root(c, root_tracker
+ ).safe_then([](auto ret) { /* FIXME: discard_result(); */ });
}
-node_future<Ref<Node>>
-Node::load_root(context_t c, RootNodeTracker& root_tracker) {
+node_future<Ref<Node>> Node::load_root(context_t c, RootNodeTracker& root_tracker) {
return c.nm.get_super(c.t, root_tracker
).safe_then([c, &root_tracker](auto&& _super) {
auto root_addr = _super->get_root_laddr();
assert(root_addr != L_ADDR_NULL);
- return load_node(c, root_addr, true
+ return Node::load(c, root_addr, true
).safe_then([c, _super = std::move(_super),
&root_tracker](auto root) mutable {
- assert(root->field_type() == field_type_t::N0);
+ assert(root->impl->field_type() == field_type_t::N0);
root->as_root(std::move(_super));
assert(root == root_tracker.get_root(c.t));
return node_ertr::make_ready_future<Ref<Node>>(root);
});
}
+void Node::make_root(context_t c, Super::URef&& _super) {
+ _super->write_root_laddr(c, impl->laddr());
+ as_root(std::move(_super));
+}
+
+void Node::as_root(Super::URef&& _super) {
+ assert(!super && !_parent_info);
+ assert(_super->get_root_laddr() == impl->laddr());
+ assert(impl->is_level_tail());
+ super = std::move(_super);
+ super->do_track_root(*this);
+}
+
+node_future<> Node::upgrade_root(context_t c) {
+ assert(is_root());
+ assert(impl->is_level_tail());
+ assert(impl->field_type() == field_type_t::N0);
+ super->do_untrack_root(*this);
+ return InternalNode::allocate_root(c, impl->level(), impl->laddr(), std::move(super)
+ ).safe_then([this](auto new_root) {
+ as_child(search_position_t::end(), new_root);
+ });
+}
+
+template <bool VALIDATE>
+void Node::as_child(const search_position_t& pos, Ref<InternalNode> parent_node) {
+ assert(!super);
+ _parent_info = parent_info_t{pos, parent_node};
+ parent_info().ptr->do_track_child<VALIDATE>(*this);
+}
+template void Node::as_child<true>(const search_position_t&, Ref<InternalNode>);
+template void Node::as_child<false>(const search_position_t&, Ref<InternalNode>);
+
node_future<> Node::insert_parent(context_t c, Ref<Node> right_node) {
assert(!is_root());
// TODO(cross-node string dedup)
- auto my_key = get_largest_key_view();
return parent_info().ptr->apply_child_split(
- c, parent_info().position, my_key, this, right_node);
+ c, parent_info().position, this, right_node);
+}
+
+node_future<Ref<Node>> Node::load(
+ context_t c, laddr_t addr, bool expect_is_level_tail) {
+ // NOTE:
+ // *option1: all types of node have the same length;
+ // option2: length is defined by node/field types;
+ // option3: length is totally flexible;
+ return c.nm.read_extent(c.t, addr, NODE_BLOCK_SIZE
+ ).safe_then([expect_is_level_tail](auto extent) {
+ const auto header = reinterpret_cast<const node_header_t*>(extent->get_read());
+ auto node_type = header->get_node_type();
+ auto field_type = header->get_field_type();
+ if (!field_type.has_value()) {
+ throw std::runtime_error("load failed: bad field type");
+ }
+ if (node_type == node_type_t::LEAF) {
+ auto impl = LeafNodeImpl::load(extent, *field_type, expect_is_level_tail);
+ return Ref<Node>(new LeafNode(impl.get(), std::move(impl)));
+ } else if (node_type == node_type_t::INTERNAL) {
+ auto impl = InternalNodeImpl::load(extent, *field_type, expect_is_level_tail);
+ return Ref<Node>(new InternalNode(impl.get(), std::move(impl)));
+ } else {
+ assert(false && "impossible path");
+ }
+ });
+}
+
+/*
+ * InternalNode
+ */
+
+InternalNode::InternalNode(InternalNodeImpl* impl, NodeImplURef&& impl_ref)
+ : Node(std::move(impl_ref)), impl{impl} {}
+
+node_future<> InternalNode::apply_child_split(
+ context_t c, const search_position_t& pos,
+ Ref<Node> left_child, Ref<Node> right_child) {
+#ifndef NDEBUG
+ if (pos.is_end()) {
+ assert(impl->is_level_tail());
+ }
+#endif
+ impl->prepare_mutate(c);
+
+ // update pos => left_child to pos => right_child
+ auto left_child_addr = left_child->impl->laddr();
+ auto right_child_addr = right_child->impl->laddr();
+ impl->replace_child_addr(pos, right_child_addr, left_child_addr);
+ replace_track(pos, right_child, left_child);
+
+ auto left_key = left_child->impl->get_largest_key_view();
+ search_position_t insert_pos = pos;
+ auto [insert_stage, insert_size] = impl->evaluate_insert(
+ left_key, left_child_addr, insert_pos);
+ auto free_size = impl->free_size();
+ if (free_size >= insert_size) {
+ // insert
+ auto p_value = impl->insert(left_key, left_child_addr,
+ insert_pos, insert_stage, insert_size);
+ assert(impl->free_size() == free_size - insert_size);
+ assert(insert_pos <= pos);
+ assert(*p_value == left_child_addr);
+ track_insert(insert_pos, insert_stage, left_child, right_child);
+ validate_tracked_children();
+ return node_ertr::now();
+ }
+ // split and insert
+ std::cout << " try insert at: " << insert_pos
+ << ", insert_stage=" << (int)insert_stage
+ << ", insert_size=" << insert_size
+ << ", values=0x" << std::hex << left_child_addr
+ << ",0x" << right_child_addr << std::dec << std::endl;
+ Ref<InternalNode> this_ref = this;
+ return (is_root() ? upgrade_root(c) : node_ertr::now()
+ ).safe_then([this, c] {
+ return InternalNode::allocate(
+ c, impl->field_type(), impl->is_level_tail(), impl->level());
+ }).safe_then([this_ref, this, c, left_key, left_child, right_child,
+ insert_pos, insert_stage, insert_size](auto fresh_right) mutable {
+ auto right_node = fresh_right.node;
+ auto left_child_addr = left_child->impl->laddr();
+ auto [split_pos, is_insert_left, p_value] = impl->split_insert(
+ fresh_right.mut, *right_node->impl, left_key, left_child_addr,
+ insert_pos, insert_stage, insert_size);
+ assert(*p_value == left_child_addr);
+ track_split(split_pos, right_node);
+ if (is_insert_left) {
+ track_insert(insert_pos, insert_stage, left_child);
+ } else {
+ right_node->track_insert(insert_pos, insert_stage, left_child);
+ }
+ validate_tracked_children();
+ right_node->validate_tracked_children();
+
+ // propagate index to parent
+ return insert_parent(c, right_node);
+ // TODO (optimize)
+ // try to acquire space from siblings before split... see btrfs
+ });
+}
+
+node_future<Ref<InternalNode>> InternalNode::allocate_root(
+ context_t c, level_t old_root_level,
+ laddr_t old_root_addr, Super::URef&& super) {
+ return InternalNode::allocate(c, field_type_t::N0, true, old_root_level + 1
+ ).safe_then([c, old_root_addr,
+ super = std::move(super)](auto fresh_node) mutable {
+ auto root = fresh_node.node;
+ const laddr_t* p_value = root->impl->get_p_value(search_position_t::end());
+ fresh_node.mut.copy_in_absolute(
+ const_cast<laddr_t*>(p_value), old_root_addr);
+ root->make_root_from(c, std::move(super), old_root_addr);
+ return root;
+ });
+}
+
+node_future<Ref<tree_cursor_t>>
+InternalNode::lookup_smallest(context_t c) {
+ auto position = search_position_t::begin();
+ laddr_t child_addr = *impl->get_p_value(position);
+ return get_or_track_child(c, position, child_addr
+ ).safe_then([c](auto child) {
+ return child->lookup_smallest(c);
+ });
+}
+
+node_future<Ref<tree_cursor_t>>
+InternalNode::lookup_largest(context_t c) {
+ // NOTE: unlike LeafNode::lookup_largest(), this only works for the tail
+ // internal node to return the tail child address.
+ auto position = search_position_t::end();
+ laddr_t child_addr = *impl->get_p_value(position);
+ return get_or_track_child(c, position, child_addr).safe_then([c](auto child) {
+ return child->lookup_largest(c);
+ });
+}
+
+node_future<Node::search_result_t>
+InternalNode::lower_bound_tracked(
+ context_t c, const key_hobj_t& key, MatchHistory& history) {
+ auto result = impl->lower_bound(key, history);
+ return get_or_track_child(c, result.position, *result.p_value
+ ).safe_then([c, &key, &history](auto child) {
+ // XXX(multi-type): pass result.mstat to child
+ return child->lower_bound_tracked(c, key, history);
+ });
+}
+
+node_future<> InternalNode::test_clone_root(
+ context_t c_other, RootNodeTracker& tracker_other) const {
+ assert(is_root());
+ assert(impl->is_level_tail());
+ assert(impl->field_type() == field_type_t::N0);
+ Ref<const InternalNode> this_ref = this;
+ return InternalNode::allocate(c_other, field_type_t::N0, true, impl->level()
+ ).safe_then([this, c_other, &tracker_other](auto fresh_other) {
+ impl->test_copy_to(fresh_other.mut);
+ auto cloned_root = fresh_other.node;
+ return c_other.nm.get_super(c_other.t, tracker_other
+ ).safe_then([c_other, cloned_root](auto&& super_other) {
+ cloned_root->make_root_new(c_other, std::move(super_other));
+ return cloned_root;
+ });
+ }).safe_then([this_ref, this, c_other](auto cloned_root) {
+ // clone tracked children
+ // In some unit tests, the children are stubbed out that they
+ // don't exist in NodeExtentManager, and are only tracked in memory.
+ return crimson::do_for_each(
+ tracked_child_nodes.begin(),
+ tracked_child_nodes.end(),
+ [this_ref, c_other, cloned_root](auto& kv) {
+ assert(kv.first == kv.second->parent_info().position);
+ return kv.second->test_clone_non_root(c_other, cloned_root);
+ }
+ );
+ });
}
node_future<Ref<Node>> InternalNode::get_or_track_child(
bool level_tail = position.is_end();
Ref<Node> child;
auto found = tracked_child_nodes.find(position);
+ Ref<InternalNode> this_ref = this;
return (found == tracked_child_nodes.end()
- ? load_node(c, child_addr, level_tail
+ ? Node::load(c, child_addr, level_tail
).safe_then([this, position] (auto child) {
child->as_child(position, this);
return child;
})
: node_ertr::make_ready_future<Ref<Node>>(found->second)
- ).safe_then([this, position, child_addr] (auto child) {
- assert(child_addr == child->laddr());
+ ).safe_then([this_ref, this, position, child_addr] (auto child) {
+ assert(child_addr == child->impl->laddr());
assert(position == child->parent_info().position);
validate_child(*child);
return child;
});
}
+void InternalNode::track_insert(
+ const search_position_t& insert_pos, match_stage_t insert_stage,
+ Ref<Node> insert_child, Ref<Node> nxt_child) {
+ // update tracks
+ auto pos_upper_bound = insert_pos;
+ pos_upper_bound.index_by_stage(insert_stage) = INDEX_END;
+ auto first = tracked_child_nodes.lower_bound(insert_pos);
+ auto last = tracked_child_nodes.lower_bound(pos_upper_bound);
+ std::vector<Node*> nodes;
+ std::for_each(first, last, [&nodes](auto& kv) {
+ nodes.push_back(kv.second);
+ });
+ tracked_child_nodes.erase(first, last);
+ for (auto& node : nodes) {
+ auto _pos = node->parent_info().position;
+ assert(!_pos.is_end());
+ ++_pos.index_by_stage(insert_stage);
+ node->as_child(_pos, this);
+ }
+ // track insert
+ insert_child->as_child(insert_pos, this);
+
+#ifndef NDEBUG
+ // validate left_child is before right_child
+ if (nxt_child) {
+ auto iter = tracked_child_nodes.find(insert_pos);
+ ++iter;
+ assert(iter->second == nxt_child);
+ }
+#endif
+}
+
+void InternalNode::replace_track(
+ const search_position_t& position, Ref<Node> new_child, Ref<Node> old_child) {
+ assert(tracked_child_nodes[position] == old_child);
+ tracked_child_nodes.erase(position);
+ new_child->as_child(position, this);
+ assert(tracked_child_nodes[position] == new_child);
+}
+
+void InternalNode::track_split(
+ const search_position_t& split_pos, Ref<InternalNode> right_node) {
+ auto first = tracked_child_nodes.lower_bound(split_pos);
+ auto iter = first;
+ while (iter != tracked_child_nodes.end()) {
+ search_position_t new_pos = iter->first;
+ new_pos -= split_pos;
+ iter->second->as_child<false>(new_pos, right_node);
+ ++iter;
+ }
+ tracked_child_nodes.erase(first, tracked_child_nodes.end());
+}
+
void InternalNode::validate_child(const Node& child) const {
#ifndef NDEBUG
- assert(this->level() - 1 == child.level());
+ assert(impl->level() - 1 == child.impl->level());
assert(this == child.parent_info().ptr);
auto& child_pos = child.parent_info().position;
- assert(*get_p_value(child_pos) == child.laddr());
+ assert(*impl->get_p_value(child_pos) == child.impl->laddr());
if (child_pos.is_end()) {
- assert(this->is_level_tail());
- assert(child.is_level_tail());
+ assert(impl->is_level_tail());
+ assert(child.impl->is_level_tail());
} else {
- assert(!child.is_level_tail());
- assert(get_key_view(child_pos) == child.get_largest_key_view());
+ assert(!child.impl->is_level_tail());
+ assert(impl->get_key_view(child_pos) == child.impl->get_largest_key_view());
}
// XXX(multi-type)
- assert(this->field_type() <= child.field_type());
+ assert(impl->field_type() <= child.impl->field_type());
#endif
}
+node_future<InternalNode::fresh_node_t> InternalNode::allocate(
+ context_t c, field_type_t field_type, bool is_level_tail, level_t level) {
+ return InternalNodeImpl::allocate(c, field_type, is_level_tail, level
+ ).safe_then([](auto&& fresh_impl) {
+ auto node = Ref<InternalNode>(new InternalNode(
+ fresh_impl.impl.get(), std::move(fresh_impl.impl)));
+ return fresh_node_t{node, fresh_impl.mut};
+ });
+}
+
+/*
+ * LeafNode
+ */
+
+LeafNode::LeafNode(LeafNodeImpl* impl, NodeImplURef&& impl_ref)
+ : Node(std::move(impl_ref)), impl{impl} {}
+
+const onode_t* LeafNode::get_p_value(const search_position_t& pos) const {
+ return impl->get_p_value(pos);
+}
+
+node_future<Ref<tree_cursor_t>>
+LeafNode::lookup_smallest(context_t) {
+ search_position_t pos;
+ const onode_t* p_value;
+ if (unlikely(impl->is_empty())) {
+ assert(is_root());
+ pos = search_position_t::end();
+ p_value = nullptr;
+ } else {
+ pos = search_position_t::begin();
+ p_value = impl->get_p_value(pos);
+ }
+ return node_ertr::make_ready_future<Ref<tree_cursor_t>>(
+ get_or_track_cursor(pos, p_value));
+}
+
+node_future<Ref<tree_cursor_t>>
+LeafNode::lookup_largest(context_t) {
+ search_position_t pos;
+ const onode_t* p_value = nullptr;
+ if (unlikely(impl->is_empty())) {
+ assert(is_root());
+ pos = search_position_t::end();
+ } else {
+ impl->get_largest_value(pos, p_value);
+ assert(p_value != nullptr);
+ }
+ return node_ertr::make_ready_future<Ref<tree_cursor_t>>(
+ get_or_track_cursor(pos, p_value));
+}
+
+node_future<Node::search_result_t>
+LeafNode::lower_bound_tracked(
+ context_t c, const key_hobj_t& key, MatchHistory& history) {
+ auto result = impl->lower_bound(key, history);
+ auto cursor_ref = get_or_track_cursor(result.position, result.p_value);
+ return node_ertr::make_ready_future<search_result_t>(
+ search_result_t{cursor_ref, result.match()});
+}
+
+node_future<> LeafNode::test_clone_root(
+ context_t c_other, RootNodeTracker& tracker_other) const {
+ assert(is_root());
+ assert(impl->is_level_tail());
+ assert(impl->field_type() == field_type_t::N0);
+ Ref<const LeafNode> this_ref = this;
+ return LeafNode::allocate(c_other, field_type_t::N0, true
+ ).safe_then([this, c_other, &tracker_other](auto fresh_other) {
+ impl->test_copy_to(fresh_other.mut);
+ auto cloned_root = fresh_other.node;
+ return c_other.nm.get_super(c_other.t, tracker_other
+ ).safe_then([c_other, cloned_root](auto&& super_other) {
+ cloned_root->make_root_new(c_other, std::move(super_other));
+ });
+ }).safe_then([this_ref]{});
+}
+
+node_future<Ref<tree_cursor_t>> LeafNode::insert_value(
+ context_t c, const key_hobj_t& key, const onode_t& value,
+ const search_position_t& pos, const MatchHistory& history) {
+#ifndef NDEBUG
+ if (pos.is_end()) {
+ assert(impl->is_level_tail());
+ }
+#endif
+ impl->prepare_mutate(c);
+
+ search_position_t insert_pos = pos;
+ auto [insert_stage, insert_size] = impl->evaluate_insert(
+ key, value, history, insert_pos);
+ auto free_size = impl->free_size();
+ if (free_size >= insert_size) {
+ // insert
+ auto p_value = impl->insert(key, value, insert_pos, insert_stage, insert_size);
+ assert(impl->free_size() == free_size - insert_size);
+ assert(insert_pos <= pos);
+ assert(p_value->size == value.size);
+ auto ret = track_insert(insert_pos, insert_stage, p_value);
+ validate_tracked_cursors();
+ return node_ertr::make_ready_future<Ref<tree_cursor_t>>(ret);
+ }
+ // split and insert
+ std::cout << " try insert at: " << insert_pos
+ << ", insert_stage=" << (int)insert_stage
+ << ", insert_size=" << insert_size
+ << std::endl;
+ Ref<LeafNode> this_ref = this;
+ return (is_root() ? upgrade_root(c) : node_ertr::now()
+ ).safe_then([this, c] {
+ return LeafNode::allocate(c, impl->field_type(), impl->is_level_tail());
+ }).safe_then([this_ref, this, c, &key, &value, &history,
+ insert_pos, insert_stage, insert_size](auto fresh_right) mutable {
+ auto right_node = fresh_right.node;
+ auto [split_pos, is_insert_left, p_value] = impl->split_insert(
+ fresh_right.mut, *right_node->impl, key, value,
+ insert_pos, insert_stage, insert_size);
+ assert(p_value->size == value.size);
+ track_split(split_pos, right_node);
+ Ref<tree_cursor_t> ret;
+ if (is_insert_left) {
+ ret = track_insert(insert_pos, insert_stage, p_value);
+ } else {
+ ret = right_node->track_insert(insert_pos, insert_stage, p_value);
+ }
+ validate_tracked_cursors();
+ right_node->validate_tracked_cursors();
+
+ // propagate insert to parent
+ return insert_parent(c, right_node).safe_then([ret] {
+ return ret;
+ });
+ // TODO (optimize)
+ // try to acquire space from siblings before split... see btrfs
+ });
+}
+
+node_future<Ref<LeafNode>> LeafNode::allocate_root(
+ context_t c, RootNodeTracker& root_tracker) {
+ return LeafNode::allocate(c, field_type_t::N0, true
+ ).safe_then([c, &root_tracker](auto fresh_node) {
+ auto root = fresh_node.node;
+ return c.nm.get_super(c.t, root_tracker
+ ).safe_then([c, root](auto&& super) {
+ root->make_root_new(c, std::move(super));
+ return root;
+ });
+ });
+}
+
+Ref<tree_cursor_t> LeafNode::get_or_track_cursor(
+ const search_position_t& position, const onode_t* p_value) {
+ if (position.is_end()) {
+ assert(impl->is_level_tail());
+ assert(!p_value);
+ // we need to return the leaf node to insert
+ return new tree_cursor_t(this, position, p_value);
+ }
+
+ Ref<tree_cursor_t> p_cursor;
+ auto found = tracked_cursors.find(position);
+ if (found == tracked_cursors.end()) {
+ p_cursor = new tree_cursor_t(this, position, p_value);
+ } else {
+ p_cursor = found->second;
+ assert(p_cursor->get_leaf_node() == this);
+ assert(p_cursor->get_position() == position);
+ p_cursor->set_p_value(p_value);
+ }
+ return p_cursor;
+}
+
+Ref<tree_cursor_t> LeafNode::track_insert(
+ const search_position_t& insert_pos, match_stage_t insert_stage,
+ const onode_t* p_onode) {
+ // invalidate cursor value
+ // TODO: version based invalidation
+ auto pos_invalidate_begin = insert_pos;
+ pos_invalidate_begin.index_by_stage(STAGE_RIGHT) = 0;
+ auto begin_invalidate = tracked_cursors.lower_bound(pos_invalidate_begin);
+ std::for_each(begin_invalidate, tracked_cursors.end(), [](auto& kv) {
+ kv.second->invalidate_p_value();
+ });
+
+ // update cursor position
+ auto pos_upper_bound = insert_pos;
+ pos_upper_bound.index_by_stage(insert_stage) = INDEX_END;
+ auto first = tracked_cursors.lower_bound(insert_pos);
+ auto last = tracked_cursors.lower_bound(pos_upper_bound);
+ std::vector<tree_cursor_t*> p_cursors;
+ std::for_each(first, last, [&p_cursors](auto& kv) {
+ p_cursors.push_back(kv.second);
+ });
+ tracked_cursors.erase(first, last);
+ for (auto& p_cursor : p_cursors) {
+ search_position_t new_pos = p_cursor->get_position();
+ ++new_pos.index_by_stage(insert_stage);
+ p_cursor->update_track(this, new_pos);
+ }
+
+ // track insert
+ return new tree_cursor_t(this, insert_pos, p_onode);
+}
+
+void LeafNode::track_split(
+ const search_position_t& split_pos, Ref<LeafNode> right_node) {
+ // invalidate cursor value
+ // TODO: version based invalidation
+ auto pos_invalidate_begin = split_pos;
+ pos_invalidate_begin.index_by_stage(STAGE_RIGHT) = 0;
+ auto begin_invalidate = tracked_cursors.lower_bound(pos_invalidate_begin);
+ std::for_each(begin_invalidate, tracked_cursors.end(), [](auto& kv) {
+ kv.second->invalidate_p_value();
+ });
+
+ // update cursor ownership and position
+ auto first = tracked_cursors.lower_bound(split_pos);
+ auto iter = first;
+ while (iter != tracked_cursors.end()) {
+ search_position_t new_pos = iter->first;
+ new_pos -= split_pos;
+ iter->second->update_track(right_node, new_pos);
+ ++iter;
+ }
+ tracked_cursors.erase(first, tracked_cursors.end());
+}
+
+node_future<LeafNode::fresh_node_t> LeafNode::allocate(
+ context_t c, field_type_t field_type, bool is_level_tail) {
+ return LeafNodeImpl::allocate(c, field_type, is_level_tail
+ ).safe_then([](auto&& fresh_impl) {
+ auto node = Ref<LeafNode>(new LeafNode(
+ fresh_impl.impl.get(), std::move(fresh_impl.impl)));
+ return fresh_node_t{node, fresh_impl.mut};
+ });
+}
+
}
#pragma once
#include <map>
+#include <memory>
#include <ostream>
#include <boost/smart_ptr/intrusive_ref_counter.hpp>
#include "crimson/common/type_helpers.h"
-#include "node_types.h"
+#include "node_extent_mutable.h"
#include "stages/stage_types.h"
#include "super.h"
#include "tree_types.h"
* LeafNode --> tree_cursor_t*
*/
+struct key_hobj_t;
class LeafNode;
class InternalNode;
-class NodeExtentMutable;
class tree_cursor_t final
: public boost::intrusive_ref_counter<
- tree_cursor_t, boost::thread_unsafe_counter> {
+ tree_cursor_t, boost::thread_unsafe_counter> {
public:
+ // public to Btree
~tree_cursor_t();
+ tree_cursor_t(const tree_cursor_t&) = delete;
+ tree_cursor_t(tree_cursor_t&&) = delete;
+ tree_cursor_t& operator=(const tree_cursor_t&) = delete;
+ tree_cursor_t& operator=(tree_cursor_t&&) = delete;
+
bool is_end() const { return position.is_end(); }
const onode_t* get_p_value() const;
// TODO: version based invalidation
void invalidate_p_value() { p_value = nullptr; }
void update_track(Ref<LeafNode>, const search_position_t&);
- void set_p_value(const onode_t* _p_value) {
- if (!p_value) {
- p_value = _p_value;
- } else {
- assert(p_value == _p_value);
- }
- }
+ void set_p_value(const onode_t* _p_value);
+ private:
Ref<LeafNode> leaf_node;
search_position_t position;
mutable const onode_t* p_value;
friend class Node; // get_position(), get_leaf_node()
};
-struct key_view_t;
-struct key_hobj_t;
-
class Node
- : public boost::intrusive_ref_counter<Node, boost::thread_unsafe_counter> {
+ : public boost::intrusive_ref_counter<
+ Node, boost::thread_unsafe_counter> {
public:
+ // public to Btree
using node_ertr = crimson::errorator<
crimson::ct_error::input_output_error,
crimson::ct_error::invarg,
crimson::ct_error::erange>;
template <class... ValuesT>
using node_future = node_ertr::future<ValuesT...>;
-
struct search_result_t {
bool is_end() const { return p_cursor->is_end(); }
Ref<tree_cursor_t> p_cursor;
};
virtual ~Node();
- virtual level_t level() const = 0;
+ Node(const Node&) = delete;
+ Node(Node&&) = delete;
+ Node& operator=(const Node&) = delete;
+ Node& operator=(Node&&) = delete;
+
+ level_t level() const;
virtual node_future<Ref<tree_cursor_t>> lookup_smallest(context_t) = 0;
virtual node_future<Ref<tree_cursor_t>> lookup_largest(context_t) = 0;
- node_future<search_result_t> lower_bound(context_t, const key_hobj_t& key);
-
- node_future<std::pair<Ref<tree_cursor_t>, bool>>
- insert(context_t, const key_hobj_t&, const onode_t&);
+ node_future<search_result_t> lower_bound(context_t c, const key_hobj_t& key);
+ node_future<std::pair<Ref<tree_cursor_t>, bool>> insert(
+ context_t, const key_hobj_t&, const onode_t&);
+ std::ostream& dump(std::ostream&) const;
+ std::ostream& dump_brief(std::ostream&) const;
- virtual std::ostream& dump(std::ostream&) const = 0;
- virtual std::ostream& dump_brief(std::ostream&) const = 0;
+ void test_make_destructable(context_t, NodeExtentMutable&, Super::URef&&);
+ virtual node_future<> test_clone_root(context_t, RootNodeTracker&) const = 0;
static node_future<> mkfs(context_t, RootNodeTracker&);
-
static node_future<Ref<Node>> load_root(context_t, RootNodeTracker&);
- virtual void test_make_destructable(
- context_t, NodeExtentMutable&, Super::URef&&) = 0;
- virtual node_future<> test_clone_root(context_t, RootNodeTracker&) const {
- assert(false && "impossible path");
- }
+ protected:
virtual node_future<> test_clone_non_root(context_t, Ref<InternalNode>) const {
assert(false && "impossible path");
}
-
- public: // used by node_impl.h, XXX: protected?
- virtual bool is_level_tail() const = 0;
- virtual field_type_t field_type() const = 0;
- virtual laddr_t laddr() const = 0;
- virtual key_view_t get_key_view(const search_position_t&) const = 0;
- virtual key_view_t get_largest_key_view() const = 0;
- virtual node_future<search_result_t>
- do_lower_bound(context_t, const key_hobj_t&, MatchHistory&) = 0;
+ virtual node_future<search_result_t> lower_bound_tracked(
+ context_t, const key_hobj_t&, MatchHistory&) = 0;
protected:
- Node() {}
-
- struct parent_info_t {
- search_position_t position;
- Ref<InternalNode> ptr;
- };
+ Node(NodeImplURef&&);
bool is_root() const {
assert((super && !_parent_info.has_value()) ||
(!super && _parent_info.has_value()));
return !_parent_info.has_value();
}
- void make_root(context_t c, Super::URef&& _super) {
- _super->write_root_laddr(c, laddr());
- as_root(std::move(_super));
- }
+
+ // as root
+ void make_root(context_t c, Super::URef&& _super);
void make_root_new(context_t c, Super::URef&& _super) {
assert(_super->get_root_laddr() == L_ADDR_NULL);
make_root(c, std::move(_super));
assert(_super->get_root_laddr() == from_addr);
make_root(c, std::move(_super));
}
- void as_root(Super::URef&& _super) {
- assert(!super && !_parent_info);
- assert(_super->get_root_laddr() == laddr());
- assert(is_level_tail());
- super = std::move(_super);
- super->do_track_root(*this);
- }
+ void as_root(Super::URef&& _super);
node_future<> upgrade_root(context_t);
+
+ // as child/non-root
template <bool VALIDATE = true>
void as_child(const search_position_t&, Ref<InternalNode>);
+ struct parent_info_t {
+ search_position_t position;
+ Ref<InternalNode> ptr;
+ };
const parent_info_t& parent_info() const { return *_parent_info; }
node_future<> insert_parent(context_t, Ref<Node> right_node);
- static node_future<Ref<Node>> load(
- context_t, laddr_t, bool expect_is_level_tail);
-
private:
- // as child/non-root
- std::optional<parent_info_t> _parent_info;
// as root
Super::URef super;
+ // as child/non-root
+ std::optional<parent_info_t> _parent_info;
+ private:
+ static node_future<Ref<Node>> load(context_t, laddr_t, bool expect_is_level_tail);
+
+ NodeImplURef impl;
friend class InternalNode;
};
inline std::ostream& operator<<(std::ostream& os, const Node& node) {
return node.dump_brief(os);
}
-// TODO: remove virtual inheritance once decoupled with layout
-class InternalNode : virtual public Node {
+class InternalNode final : public Node {
public:
- virtual ~InternalNode() { assert(tracked_child_nodes.empty()); }
-
- protected:
- // XXX: extract a common tracker for InternalNode to track Node,
- // and LeafNode to track tree_cursor_t.
- node_future<Ref<Node>> get_or_track_child(
- context_t, const search_position_t&, laddr_t);
-
- void track_insert(
- const search_position_t& insert_pos, match_stage_t insert_stage,
- Ref<Node> insert_child, Ref<Node> nxt_child = nullptr) {
- // update tracks
- auto pos_upper_bound = insert_pos;
- pos_upper_bound.index_by_stage(insert_stage) = INDEX_END;
- auto first = tracked_child_nodes.lower_bound(insert_pos);
- auto last = tracked_child_nodes.lower_bound(pos_upper_bound);
- std::vector<Node*> nodes;
- std::for_each(first, last, [&nodes](auto& kv) {
- nodes.push_back(kv.second);
- });
- tracked_child_nodes.erase(first, last);
- for (auto& node : nodes) {
- auto _pos = node->parent_info().position;
- assert(!_pos.is_end());
- ++_pos.index_by_stage(insert_stage);
- node->as_child(_pos, this);
- }
- // track insert
- insert_child->as_child(insert_pos, this);
-
-#ifndef NDEBUG
- // validate left_child is before right_child
- if (nxt_child) {
- auto iter = tracked_child_nodes.find(insert_pos);
- ++iter;
- assert(iter->second == nxt_child);
- }
-#endif
- }
-
- void replace_track(
- const search_position_t& position,
- Ref<Node> old_child, Ref<Node> new_child) {
- assert(tracked_child_nodes[position] == old_child);
- tracked_child_nodes.erase(position);
- new_child->as_child(position, this);
- assert(tracked_child_nodes[position] == new_child);
- }
-
- void track_split(
- const search_position_t& split_pos, Ref<InternalNode> right_node) {
- auto first = tracked_child_nodes.lower_bound(split_pos);
- auto iter = first;
- while (iter != tracked_child_nodes.end()) {
- search_position_t new_pos = iter->first;
- new_pos -= split_pos;
- iter->second->as_child<false>(new_pos, right_node);
- ++iter;
- }
- tracked_child_nodes.erase(first, tracked_child_nodes.end());
- }
-
- void validate_tracked_children() const {
-#ifndef NDEBUG
- for (auto& kv : tracked_child_nodes) {
- assert(kv.first == kv.second->parent_info().position);
- validate_child(*kv.second);
- }
-#endif
- }
-
- node_future<> test_clone_children(
- context_t c_other, Ref<InternalNode> clone) const {
- Ref<const InternalNode> this_ref = this;
- return crimson::do_for_each(
- tracked_child_nodes.begin(),
- tracked_child_nodes.end(),
- [this_ref, c_other, clone](auto& kv) {
- assert(kv.first == kv.second->parent_info().position);
- return kv.second->test_clone_non_root(c_other, clone);
- }
- );
- }
-
- private:
- virtual node_future<> apply_child_split(
- context_t, const search_position_t&, const key_view_t&, Ref<Node>, Ref<Node>) = 0;
- virtual const laddr_t* get_p_value(const search_position_t&) const = 0;
- void validate_child(const Node& child) const;
+ // public to Node
+ InternalNode(InternalNodeImpl*, NodeImplURef&&);
+ ~InternalNode() override { assert(tracked_child_nodes.empty()); }
+ InternalNode(const InternalNode&) = delete;
+ InternalNode(InternalNode&&) = delete;
+ InternalNode& operator=(const InternalNode&) = delete;
+ InternalNode& operator=(InternalNode&&) = delete;
+
+ node_future<> apply_child_split(
+ context_t, const search_position_t&, Ref<Node> left, Ref<Node> right);
template <bool VALIDATE>
void do_track_child(Node& child) {
if constexpr (VALIDATE) {
assert(removed);
}
- // XXX: leverage intrusive data structure to control memory overhead
- // track the current living child nodes by position
- std::map<search_position_t, Node*> tracked_child_nodes;
+ static node_future<Ref<InternalNode>> allocate_root(
+ context_t, level_t, laddr_t, Super::URef&&);
- friend class Node;
-};
+ protected:
+ node_future<Ref<tree_cursor_t>> lookup_smallest(context_t) override;
+ node_future<Ref<tree_cursor_t>> lookup_largest(context_t) override;
+ node_future<search_result_t> lower_bound_tracked(
+ context_t, const key_hobj_t&, MatchHistory&) override;
-// TODO: remove virtual inheritance once decoupled with layout
-class LeafNode : virtual public Node {
- public:
- virtual ~LeafNode() { assert(tracked_cursors.empty()); }
+ node_future<> test_clone_root(context_t, RootNodeTracker&) const override;
- protected:
+ private:
// XXX: extract a common tracker for InternalNode to track Node,
// and LeafNode to track tree_cursor_t.
- Ref<tree_cursor_t> get_or_track_cursor(
- const search_position_t& position, const onode_t* p_value) {
- if (position.is_end()) {
- assert(this->is_level_tail());
- assert(!p_value);
- // we need to return the leaf node to insert
- return new tree_cursor_t(this, position, p_value);
- }
-
- Ref<tree_cursor_t> p_cursor;
- auto found = tracked_cursors.find(position);
- if (found == tracked_cursors.end()) {
- p_cursor = new tree_cursor_t(this, position, p_value);
- } else {
- p_cursor = found->second;
- assert(p_cursor->get_leaf_node() == this);
- assert(p_cursor->get_position() == position);
- p_cursor->set_p_value(p_value);
+ node_future<Ref<Node>> get_or_track_child(context_t, const search_position_t&, laddr_t);
+ void track_insert(
+ const search_position_t&, match_stage_t, Ref<Node>, Ref<Node> nxt_child = nullptr);
+ void replace_track(const search_position_t&, Ref<Node> new_child, Ref<Node> old_child);
+ void track_split(const search_position_t&, Ref<InternalNode>);
+ void validate_tracked_children() const {
+#ifndef NDEBUG
+ for (auto& kv : tracked_child_nodes) {
+ assert(kv.first == kv.second->parent_info().position);
+ validate_child(*kv.second);
}
- return p_cursor;
+#endif
}
+ void validate_child(const Node& child) const;
- Ref<tree_cursor_t> track_insert(
- const search_position_t& insert_pos, match_stage_t insert_stage,
- const onode_t* p_onode) {
- // invalidate cursor value
- // TODO: version based invalidation
- auto pos_invalidate_begin = insert_pos;
- pos_invalidate_begin.index_by_stage(STAGE_RIGHT) = 0;
- auto begin_invalidate = tracked_cursors.lower_bound(pos_invalidate_begin);
- std::for_each(begin_invalidate, tracked_cursors.end(), [](auto& kv) {
- kv.second->invalidate_p_value();
- });
-
- // update cursor position
- auto pos_upper_bound = insert_pos;
- pos_upper_bound.index_by_stage(insert_stage) = INDEX_END;
- auto first = tracked_cursors.lower_bound(insert_pos);
- auto last = tracked_cursors.lower_bound(pos_upper_bound);
- std::vector<tree_cursor_t*> p_cursors;
- std::for_each(first, last, [&p_cursors](auto& kv) {
- p_cursors.push_back(kv.second);
- });
- tracked_cursors.erase(first, last);
- for (auto& p_cursor : p_cursors) {
- search_position_t new_pos = p_cursor->get_position();
- ++new_pos.index_by_stage(insert_stage);
- p_cursor->update_track(this, new_pos);
+ struct fresh_node_t {
+ Ref<InternalNode> node;
+ NodeExtentMutable mut;
+ std::pair<Ref<Node>, NodeExtentMutable> make_pair() {
+ return std::make_pair(Ref<Node>(node), mut);
}
+ };
+ static node_future<fresh_node_t> allocate(context_t, field_type_t, bool, level_t);
- // track insert
- return new tree_cursor_t(this, insert_pos, p_onode);
- }
+ private:
+ // XXX: leverage intrusive data structure to control memory overhead
+ // track the current living child nodes by position
+ std::map<search_position_t, Node*> tracked_child_nodes;
+ InternalNodeImpl* impl;
+};
- void track_split(
- const search_position_t& split_pos, Ref<LeafNode> right_node) {
- // invalidate cursor value
- // TODO: version based invalidation
- auto pos_invalidate_begin = split_pos;
- pos_invalidate_begin.index_by_stage(STAGE_RIGHT) = 0;
- auto begin_invalidate = tracked_cursors.lower_bound(pos_invalidate_begin);
- std::for_each(begin_invalidate, tracked_cursors.end(), [](auto& kv) {
- kv.second->invalidate_p_value();
- });
-
- // update cursor ownership and position
- auto first = tracked_cursors.lower_bound(split_pos);
- auto iter = first;
- while (iter != tracked_cursors.end()) {
- search_position_t new_pos = iter->first;
- new_pos -= split_pos;
- iter->second->update_track(right_node, new_pos);
- ++iter;
- }
- tracked_cursors.erase(first, tracked_cursors.end());
+class LeafNode final : public Node {
+ public:
+ // public to tree_cursor_t
+ ~LeafNode() override { assert(tracked_cursors.empty()); }
+ LeafNode(const LeafNode&) = delete;
+ LeafNode(LeafNode&&) = delete;
+ LeafNode& operator=(const LeafNode&) = delete;
+ LeafNode& operator=(LeafNode&&) = delete;
+
+ const onode_t* get_p_value(const search_position_t&) const;
+ void do_track_cursor(tree_cursor_t& cursor) {
+ validate_cursor(cursor);
+ auto& cursor_pos = cursor.get_position();
+ assert(tracked_cursors.find(cursor_pos) == tracked_cursors.end());
+ tracked_cursors[cursor_pos] = &cursor;
}
+ void do_untrack_cursor(tree_cursor_t& cursor) {
+ validate_cursor(cursor);
+ auto& cursor_pos = cursor.get_position();
+ assert(tracked_cursors.find(cursor_pos)->second == &cursor);
+ auto removed = tracked_cursors.erase(cursor_pos);
+ assert(removed);
+ }
+
+ protected:
+ node_future<Ref<tree_cursor_t>> lookup_smallest(context_t) override;
+ node_future<Ref<tree_cursor_t>> lookup_largest(context_t) override;
+ node_future<search_result_t> lower_bound_tracked(
+ context_t, const key_hobj_t&, MatchHistory&) override;
+
+ node_future<> test_clone_root(context_t, RootNodeTracker&) const override;
+
+ private:
+ LeafNode(LeafNodeImpl*, NodeImplURef&&);
+ node_future<Ref<tree_cursor_t>> insert_value(
+ context_t, const key_hobj_t&, const onode_t&,
+ const search_position_t&, const MatchHistory&);
+ static node_future<Ref<LeafNode>> allocate_root(context_t, RootNodeTracker&);
+ friend class Node;
+ private:
+ // XXX: extract a common tracker for InternalNode to track Node,
+ // and LeafNode to track tree_cursor_t.
+ Ref<tree_cursor_t> get_or_track_cursor(const search_position_t&, const onode_t*);
+ Ref<tree_cursor_t> track_insert(
+ const search_position_t&, match_stage_t, const onode_t*);
+ void track_split(const search_position_t&, Ref<LeafNode>);
void validate_tracked_cursors() const {
#ifndef NDEBUG
for (auto& kv : tracked_cursors) {
}
#endif
}
-
- private:
- virtual node_future<Ref<tree_cursor_t>> insert_value(
- context_t,
- const key_hobj_t&,
- const onode_t&,
- const search_position_t&,
- const MatchHistory&) = 0;
- friend class Node;
-
- virtual const onode_t* get_p_value(const search_position_t&) const = 0;
void validate_cursor(tree_cursor_t& cursor) const {
assert(this == cursor.get_leaf_node().get());
assert(!cursor.is_end());
assert(get_p_value(cursor.get_position()) == cursor.get_p_value());
}
- void do_track_cursor(tree_cursor_t& cursor) {
- validate_cursor(cursor);
- auto& cursor_pos = cursor.get_position();
- assert(tracked_cursors.find(cursor_pos) == tracked_cursors.end());
- tracked_cursors[cursor_pos] = &cursor;
- }
- void do_untrack_cursor(tree_cursor_t& cursor) {
- validate_cursor(cursor);
- auto& cursor_pos = cursor.get_position();
- assert(tracked_cursors.find(cursor_pos)->second == &cursor);
- auto removed = tracked_cursors.erase(cursor_pos);
- assert(removed);
- }
+
+ struct fresh_node_t {
+ Ref<LeafNode> node;
+ NodeExtentMutable mut;
+ std::pair<Ref<Node>, NodeExtentMutable> make_pair() {
+ return std::make_pair(Ref<Node>(node), mut);
+ }
+ };
+ static node_future<fresh_node_t> allocate(context_t, field_type_t, bool);
+
+ private:
// XXX: leverage intrusive data structure to control memory overhead
// track the current living cursors by position
std::map<search_position_t, tree_cursor_t*> tracked_cursors;
- friend class tree_cursor_t;
+ LeafNodeImpl* impl;
};
}
using crimson::os::seastore::LogicalCachedExtent;
class NodeExtent : public LogicalCachedExtent {
public:
- using Ref = crimson::os::seastore::TCachedExtentRef<NodeExtent>;
virtual ~NodeExtent() = default;
const char* get_read() const {
return get_bptr().c_str();
assert(is_pending());
return NodeExtentMutable(*this);
}
- virtual Ref mutate(context_t/* DeltaBuffer::Ref */) = 0;
+ virtual NodeExtentRef mutate(context_t/* DeltaBuffer::Ref */) = 0;
protected:
template <typename... T>
using tm_future = tm_ertr::future<ValuesT...>;
virtual bool is_read_isolated() const = 0;
- virtual tm_future<NodeExtent::Ref> read_extent(
+ virtual tm_future<NodeExtentRef> read_extent(
Transaction&, laddr_t, extent_len_t) = 0;
- virtual tm_future<NodeExtent::Ref> alloc_extent(Transaction&, extent_len_t) = 0;
+ virtual tm_future<NodeExtentRef> alloc_extent(Transaction&, extent_len_t) = 0;
virtual tm_future<Super::URef> get_super(Transaction&, RootNodeTracker&) = 0;
static NodeExtentManagerURef create_dummy();
}
~DummyNodeExtent() override = default;
protected:
- Ref mutate(context_t) override {
+ NodeExtentRef mutate(context_t) override {
assert(false && "impossible path"); }
CachedExtentRef duplicate_for_write() override {
assert(false && "impossible path"); }
protected:
bool is_read_isolated() const { return false; }
- tm_future<NodeExtent::Ref> read_extent(
+ tm_future<NodeExtentRef> read_extent(
Transaction& t, laddr_t addr, extent_len_t len) {
auto iter = allocate_map.find(addr);
assert(iter != allocate_map.end());
assert(iter->second->get_length() == len);
- return tm_ertr::make_ready_future<NodeExtent::Ref>(iter->second);
+ return tm_ertr::make_ready_future<NodeExtentRef>(iter->second);
}
- tm_future<NodeExtent::Ref> alloc_extent(
+ tm_future<NodeExtentRef> alloc_extent(
Transaction& t, extent_len_t len) {
assert(len % ALIGNMENT == 0);
auto r = ceph::buffer::create_aligned(len, ALIGNMENT);
extent->set_laddr(addr);
assert(allocate_map.find(extent->get_laddr()) == allocate_map.end());
allocate_map.insert({extent->get_laddr(), extent});
- return tm_ertr::make_ready_future<NodeExtent::Ref>(extent);
+ return tm_ertr::make_ready_future<NodeExtentRef>(extent);
}
tm_future<Super::URef> get_super(Transaction& t, RootNodeTracker& tracker) {
: NodeExtent(other) {}
~SeastoreNodeExtent() override = default;
protected:
- Ref mutate(context_t c) override;
+ NodeExtentRef mutate(context_t c) override;
CachedExtentRef duplicate_for_write() override {
return CachedExtentRef(new SeastoreNodeExtent(*this));
}
protected:
bool is_read_isolated() const { return true; }
- tm_future<NodeExtent::Ref> read_extent(
+ tm_future<NodeExtentRef> read_extent(
Transaction& t, laddr_t addr, extent_len_t len) {
return tm.read_extents<SeastoreNodeExtent>(t, addr, len
).safe_then([](auto&& extents) {
assert(extents.size() == 1);
[[maybe_unused]] auto [laddr, e] = extents.front();
- return NodeExtent::Ref(e);
+ return NodeExtentRef(e);
});
}
- tm_future<NodeExtent::Ref> alloc_extent(
+ tm_future<NodeExtentRef> alloc_extent(
Transaction& t, extent_len_t len) {
return tm.alloc_extent<SeastoreNodeExtent>(t, addr_min, len
).safe_then([](auto extent) {
- return NodeExtent::Ref(extent);
+ return NodeExtentRef(extent);
});
}
const laddr_t addr_min;
};
-inline NodeExtent::Ref SeastoreNodeExtent::mutate(context_t c) {
+inline NodeExtentRef SeastoreNodeExtent::mutate(context_t c) {
auto nm = static_cast<SeastoreNodeExtentManager*>(&c.nm);
auto ret = nm->get_tm().get_mutable_extent(c.t, this);
return ret->cast<SeastoreNodeExtent>();
#pragma once
#include "node_extent_manager.h"
-#include "node_impl_replayable.h"
+#include "node_layout_replayable.h"
namespace crimson::os::seastore::onode {
template <typename FieldType, node_type_t NODE_TYPE>
class NodeExtentT {
- enum class state_t {
- NO_RECORDING, // extent_state_t::INITIAL_WRITE_PENDING
- RECORDING, // extent_state_t::MUTATION_PENDING
- PENDING_MUTATE // extent_state_t::CLEAN/DIRTY
- };
-
public:
using layout_t = NodeLayoutReplayableT<FieldType, NODE_TYPE>;
using node_stage_t = typename layout_t::node_stage_t;
using StagedIterator = typename layout_t::StagedIterator;
using value_t = typename layout_t::value_t;
static constexpr auto FIELD_TYPE = layout_t::FIELD_TYPE;
+ enum class state_t {
+ NO_RECORDING, // extent_state_t::INITIAL_WRITE_PENDING
+ RECORDING, // extent_state_t::MUTATION_PENDING
+ PENDING_MUTATE // extent_state_t::CLEAN/DIRTY
+ };
- // TODO: remove
- NodeExtentT() = default;
- NodeExtentT(NodeExtentT&& other) noexcept {
- *this = std::move(other);
- }
- NodeExtentT& operator=(NodeExtentT&& other) noexcept {
- extent = std::move(other.extent);
- state = std::move(other.state);
- node_stage = std::move(other.node_stage);
- mut.emplace(*other.mut);
- return *this;
+ NodeExtentT(state_t state, NodeExtentRef extent)
+ : state{state}, extent{extent},
+ node_stage{reinterpret_cast<const FieldType*>(extent->get_read())} {
+ if (state == state_t::NO_RECORDING) {
+ assert(!mut.has_value());
+ mut.emplace(extent->get_mutable());
+ // TODO: recorder = nullptr;
+ } else if (state == state_t::RECORDING) {
+ assert(!mut.has_value());
+ mut.emplace(extent->get_mutable());
+ // TODO: get recorder from extent
+ } else if (state == state_t::PENDING_MUTATE) {
+ // TODO: recorder = nullptr;
+ } else {
+ ceph_abort("impossible path");
+ }
}
+ ~NodeExtentT() = default;
+ NodeExtentT(const NodeExtentT&) = delete;
+ NodeExtentT(NodeExtentT&&) = delete;
+ NodeExtentT& operator=(const NodeExtentT&) = delete;
+ NodeExtentT& operator=(NodeExtentT&&) = delete;
const node_stage_t& read() const { return node_stage; }
laddr_t get_laddr() const { return extent->get_laddr(); }
insert_pos, insert_stage, insert_size);
}
- void prepare_internal_split_replayable(
- const laddr_t left_child_addr,
- const laddr_t right_child_addr,
- laddr_t* p_split_addr) {
+ void update_child_addr_replayable(
+ const laddr_t new_addr, laddr_t* p_addr) {
assert(state != state_t::PENDING_MUTATE);
// TODO: encode params to recorder as delta
- return layout_t::prepare_internal_split(
- *mut, read(), left_child_addr, right_child_addr, p_split_addr);
+ return layout_t::update_child_addr(*mut, new_addr, p_addr);
}
void test_copy_to(NodeExtentMutable& to) const {
std::memcpy(to.get_write(), extent->get_read(), extent->get_length());
}
- static NodeExtentT load(NodeExtent::Ref extent) {
+ static state_t loaded(NodeExtentRef extent) {
state_t state;
if (extent->is_initial_pending()) {
state = state_t::NO_RECORDING;
} else {
ceph_abort("invalid extent");
}
- return NodeExtentT(extent, state);
+ return state;
}
- struct fresh_extent_t {
- NodeExtentT extent;
- NodeExtentMutable mut;
- };
- using alloc_ertr = NodeExtentManager::tm_ertr;
- static alloc_ertr::future<fresh_extent_t>
- allocate(context_t c, level_t level, bool is_level_tail) {
- // NOTE:
- // *option1: all types of node have the same length;
- // option2: length is defined by node/field types;
- // option3: length is totally flexible;
- return c.nm.alloc_extent(c.t, node_stage_t::EXTENT_SIZE
- ).safe_then([level, is_level_tail](auto extent) {
- assert(extent->is_initial_pending());
- auto mut = extent->get_mutable();
- node_stage_t::bootstrap_extent(
- mut, FIELD_TYPE, NODE_TYPE, is_level_tail, level);
- return fresh_extent_t{NodeExtentT(extent, state_t::NO_RECORDING), mut};
- });
+ static std::tuple<state_t, NodeExtentMutable> allocated(
+ NodeExtentRef extent, bool is_level_tail, level_t level) {
+ assert(extent->is_initial_pending());
+ auto mut = extent->get_mutable();
+ node_stage_t::bootstrap_extent(
+ mut, FIELD_TYPE, NODE_TYPE, is_level_tail, level);
+ return {state_t::NO_RECORDING, mut};
}
private:
- NodeExtentT(NodeExtent::Ref extent, state_t state)
- : extent{extent}, state{state},
- node_stage{reinterpret_cast<const FieldType*>(extent->get_read())} {
- if (state == state_t::NO_RECORDING) {
- assert(!mut.has_value());
- mut.emplace(extent->get_mutable());
- // TODO: recorder = nullptr;
- } else if (state == state_t::RECORDING) {
- assert(!mut.has_value());
- mut.emplace(extent->get_mutable());
- // TODO: get recorder from extent
- } else if (state == state_t::PENDING_MUTATE) {
- // TODO: recorder = nullptr;
- } else {
- ceph_abort("impossible path");
- }
- }
-
- NodeExtent::Ref extent;
state_t state;
+ NodeExtentRef extent;
node_stage_t node_stage;
std::optional<NodeExtentMutable> mut;
// TODO: DeltaRecorderT* recorder;
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "node_impl.h"
+#include "node_layout.h"
+
+namespace crimson::os::seastore::onode {
+
+// XXX: branchless allocation
+InternalNodeImpl::alloc_ertr::future<InternalNodeImpl::fresh_impl_t>
+InternalNodeImpl::allocate(
+ context_t c, field_type_t type, bool is_level_tail, level_t level) {
+ if (type == field_type_t::N0) {
+ return InternalNode0::allocate(c, is_level_tail, level);
+ } else if (type == field_type_t::N1) {
+ return InternalNode1::allocate(c, is_level_tail, level);
+ } else if (type == field_type_t::N2) {
+ return InternalNode2::allocate(c, is_level_tail, level);
+ } else if (type == field_type_t::N3) {
+ return InternalNode3::allocate(c, is_level_tail, level);
+ } else {
+ assert(false && "impossible path");
+ }
+}
+
+LeafNodeImpl::alloc_ertr::future<LeafNodeImpl::fresh_impl_t>
+LeafNodeImpl::allocate(
+ context_t c, field_type_t type, bool is_level_tail) {
+ if (type == field_type_t::N0) {
+ return LeafNode0::allocate(c, is_level_tail, 0);
+ } else if (type == field_type_t::N1) {
+ return LeafNode1::allocate(c, is_level_tail, 0);
+ } else if (type == field_type_t::N2) {
+ return LeafNode2::allocate(c, is_level_tail, 0);
+ } else if (type == field_type_t::N3) {
+ return LeafNode3::allocate(c, is_level_tail, 0);
+ } else {
+ assert(false && "impossible path");
+ }
+}
+
+InternalNodeImplURef InternalNodeImpl::load(
+ NodeExtentRef extent, field_type_t type, bool expect_is_level_tail) {
+ if (type == field_type_t::N0) {
+ return InternalNode0::load(extent, expect_is_level_tail);
+ } else if (type == field_type_t::N1) {
+ return InternalNode1::load(extent, expect_is_level_tail);
+ } else if (type == field_type_t::N2) {
+ return InternalNode2::load(extent, expect_is_level_tail);
+ } else if (type == field_type_t::N3) {
+ return InternalNode3::load(extent, expect_is_level_tail);
+ } else {
+ assert(false && "impossible path");
+ }
+}
+
+LeafNodeImplURef LeafNodeImpl::load(
+ NodeExtentRef extent, field_type_t type, bool expect_is_level_tail) {
+ if (type == field_type_t::N0) {
+ return LeafNode0::load(extent, expect_is_level_tail);
+ } else if (type == field_type_t::N1) {
+ return LeafNode1::load(extent, expect_is_level_tail);
+ } else if (type == field_type_t::N2) {
+ return LeafNode2::load(extent, expect_is_level_tail);
+ } else if (type == field_type_t::N3) {
+ return LeafNode3::load(extent, expect_is_level_tail);
+ } else {
+ assert(false && "impossible path");
+ }
+}
+
+}
#pragma once
-// TODO: remove
-#include <iostream>
#include <ostream>
-#include "common/likely.h"
-#include "node.h"
-#include "node_extent_visitor.h"
-#include "stages/node_layout.h"
+#include "node_extent_mutable.h"
+#include "node_types.h"
+#include "stages/stage_types.h"
namespace crimson::os::seastore::onode {
+struct key_hobj_t;
+struct key_view_t;
class NodeExtentMutable;
-// TODO: decouple NodeT with Node
-
-template <typename FieldType, node_type_t _NODE_TYPE, typename ConcreteType>
-class NodeT : virtual public Node {
+class NodeImpl {
public:
- using extent_t = NodeExtentT<FieldType, _NODE_TYPE>;
- using node_ertr = Node::node_ertr;
- template <class... ValuesT>
- using node_future = Node::node_future<ValuesT...>;
- using node_stage_t = typename extent_t::node_stage_t;
- using position_t = typename extent_t::position_t;
- using value_t = typename extent_t::value_t;
- static constexpr auto FIELD_TYPE = extent_t::FIELD_TYPE;
- static constexpr auto NODE_TYPE = _NODE_TYPE;
-
- struct fresh_node_t {
- Ref<ConcreteType> node;
- NodeExtentMutable mut;
- std::pair<Ref<Node>, NodeExtentMutable> make_pair() {
- return std::make_pair(Ref<Node>(node), mut);
- }
- };
-
- virtual ~NodeT() = default;
-
- bool is_level_tail() const override final { return extent.read().is_level_tail(); }
- field_type_t field_type() const override final { return FIELD_TYPE; }
- laddr_t laddr() const override final { return extent.get_laddr(); }
- level_t level() const override final { return extent.read().level(); }
-
- full_key_t<KeyT::VIEW> get_key_view(
- const search_position_t& position) const override final {
- full_key_t<KeyT::VIEW> ret;
- STAGE_T::get_key_view(
- extent.read(), cast_down<STAGE_T::STAGE>(position), ret);
- return ret;
- }
-
- full_key_t<KeyT::VIEW> get_largest_key_view() const override final {
- full_key_t<KeyT::VIEW> key_view;
- STAGE_T::lookup_largest_index(extent.read(), key_view);
- return key_view;
- }
-
- std::ostream& dump(std::ostream& os) const override final {
- auto& node_stage = extent.read();
- auto p_start = node_stage.p_start();
- os << *this << ":";
- os << "\n header: " << node_stage_t::header_size() << "B";
- size_t size = 0u;
- if (node_stage.keys()) {
- STAGE_T::dump(node_stage, os, " ", size, p_start);
- } else {
- if constexpr (NODE_TYPE == node_type_t::LEAF) {
- return os << " empty!";
- } else { // internal node
- if (!is_level_tail()) {
- return os << " empty!";
- } else {
- size += node_stage_t::header_size();
- }
- }
- }
- if constexpr (NODE_TYPE == node_type_t::INTERNAL) {
- if (is_level_tail()) {
- size += sizeof(laddr_t);
- auto value_ptr = node_stage.get_end_p_laddr();
- int offset = reinterpret_cast<const char*>(value_ptr) - p_start;
- os << "\n tail value: 0x"
- << std::hex << *value_ptr << std::dec
- << " " << size << "B"
- << " @" << offset << "B";
- }
- }
- return os;
- }
-
- std::ostream& dump_brief(std::ostream& os) const override final {
- auto& node_stage = extent.read();
- os << "Node" << NODE_TYPE << FIELD_TYPE
- << "@0x" << std::hex << laddr()
- << "+" << node_stage_t::EXTENT_SIZE << std::dec
- << (is_level_tail() ? "$" : "")
- << "(level=" << (unsigned)level()
- << ", filled=" << node_stage.total_size() - node_stage.free_size() << "B"
- << ", free=" << node_stage.free_size() << "B"
- << ")";
- return os;
- }
-
- const value_t* get_value_ptr(const search_position_t& position) const {
- auto& node_stage = extent.read();
- if constexpr (NODE_TYPE == node_type_t::INTERNAL) {
- if (position.is_end()) {
- assert(is_level_tail());
- return node_stage.get_end_p_laddr();
- }
- } else {
- assert(!position.is_end());
- }
- return STAGE_T::get_p_value(node_stage, cast_down<STAGE_T::STAGE>(position));
- }
-
- void test_make_destructable(
- context_t c, NodeExtentMutable& mut, Super::URef&& _super) override final {
- node_stage_t::update_is_level_tail(mut, extent.read(), true);
- make_root(c, std::move(_super));
- }
-
- static Ref<Node> load(NodeExtent::Ref extent, bool expect_is_level_tail) {
- Ref<ConcreteType> ret = new ConcreteType();
- ret->extent = extent_t::load(extent);
- assert(ret->is_level_tail() == expect_is_level_tail);
- return ret;
- }
+ using alloc_ertr = crimson::errorator<
+ crimson::ct_error::input_output_error,
+ crimson::ct_error::invarg,
+ crimson::ct_error::enoent,
+ crimson::ct_error::erange>;
+ virtual ~NodeImpl() = default;
+
+ virtual field_type_t field_type() const = 0;
+ virtual laddr_t laddr() const = 0;
+ virtual void prepare_mutate(context_t) = 0;
+ virtual bool is_level_tail() const = 0;
+ virtual bool is_empty() const = 0;
+ virtual level_t level() const = 0;
+ virtual node_offset_t free_size() const = 0;
+ virtual key_view_t get_key_view(const search_position_t&) const = 0;
+ virtual key_view_t get_largest_key_view() const = 0;
+
+ virtual std::ostream& dump(std::ostream&) const = 0;
+ virtual std::ostream& dump_brief(std::ostream&) const = 0;
+
+ virtual void test_copy_to(NodeExtentMutable&) const = 0;
+ virtual void test_set_tail(NodeExtentMutable&) = 0;
protected:
- // TODO: constructor
- extent_t extent;
+ NodeImpl() = default;
};
-template <typename FieldType, typename ConcreteType>
-class InternalNodeT : public InternalNode,
- public NodeT<FieldType, node_type_t::INTERNAL, ConcreteType> {
+class InternalNodeImpl : public NodeImpl {
public:
- using parent_t = NodeT<FieldType, node_type_t::INTERNAL, ConcreteType>;
- using extent_t = typename parent_t::extent_t;
- using fresh_node_t = typename parent_t::fresh_node_t;
- using node_stage_t = typename parent_t::node_stage_t;
- using position_t = typename parent_t::position_t;
-
- virtual ~InternalNodeT() = default;
+ struct internal_marker_t {};
+ virtual ~InternalNodeImpl() = default;
- node_future<search_result_t> do_lower_bound(
- context_t c, const full_key_t<KeyT::HOBJ>& key,
- MatchHistory& history) override final {
- auto result = STAGE_T::lower_bound_normalized(
- this->extent.read(), key, history);
- auto& position = result.position;
- laddr_t child_addr;
- if (position.is_end()) {
- assert(this->is_level_tail());
- child_addr = *this->get_value_ptr(position);
- } else {
- assert(result.p_value);
- child_addr = *result.p_value;
- }
- return get_or_track_child(c, position, child_addr
- ).safe_then([c, &key, &history](auto child) {
- // XXX(multi-type): pass result.mstat to child
- return child->do_lower_bound(c, key, history);
- });
+ #pragma GCC diagnostic ignored "-Woverloaded-virtual"
+ virtual const laddr_t* get_p_value(
+ const search_position_t&, internal_marker_t={}) const {
+ assert(false && "impossible path");
}
-
- node_future<Ref<tree_cursor_t>> lookup_smallest(context_t c) override final {
- auto position = search_position_t::begin();
- laddr_t child_addr = *this->get_value_ptr(position);
- return get_or_track_child(c, position, child_addr).safe_then([c](auto child) {
- return child->lookup_smallest(c);
- });
+ #pragma GCC diagnostic ignored "-Woverloaded-virtual"
+ virtual lookup_result_t<node_type_t::INTERNAL> lower_bound(
+ const key_hobj_t&, MatchHistory&, internal_marker_t={}) const {
+ assert(false && "impossible path");
}
-
- node_future<Ref<tree_cursor_t>> lookup_largest(context_t c) override final {
- // NOTE: unlike LeafNodeT::lookup_largest(), this only works for the tail
- // internal node to return the tail child address.
- auto position = search_position_t::end();
- laddr_t child_addr = *this->get_value_ptr(position);
- return get_or_track_child(c, position, child_addr).safe_then([c](auto child) {
- return child->lookup_largest(c);
- });
+ #pragma GCC diagnostic ignored "-Woverloaded-virtual"
+ virtual const laddr_t* insert(
+ const key_view_t&, const laddr_t&, search_position_t&, match_stage_t&, node_offset_t&) {
+ assert(false && "impossible path");
}
-
- node_future<> apply_child_split(
- context_t c, const search_position_t& pos,
- const full_key_t<KeyT::VIEW>& left_key, Ref<Node> left_child,
- Ref<Node> right_child) override final {
- this->extent.prepare_mutate(c);
- auto& node_stage = this->extent.read();
-
- // update pos => l_addr to r_addr
- auto left_laddr = left_child->laddr();
- auto right_laddr = right_child->laddr();
- const laddr_t* p_rvalue = this->get_value_ptr(pos);
- this->extent.prepare_internal_split_replayable(
- left_laddr, right_laddr, const_cast<laddr_t*>(p_rvalue));
- this->replace_track(pos, left_child, right_child);
-
- // evaluate insertion
- position_t insert_pos = cast_down<STAGE_T::STAGE>(pos);
- match_stage_t insert_stage;
- node_offset_t insert_size;
- if (unlikely(!node_stage.keys())) {
- assert(insert_pos.is_end());
- insert_stage = STAGE_T::STAGE;
- insert_size = STAGE_T::template insert_size<KeyT::VIEW>(left_key, left_laddr);
- } else {
- std::tie(insert_stage, insert_size) =
- STAGE_T::evaluate_insert(node_stage, left_key, left_laddr, insert_pos, true);
- }
-
- // TODO: common part begin, move to NodeT
- auto free_size = node_stage.free_size();
- if (free_size >= insert_size) {
- auto p_value = this->extent.template insert_replayable<KeyT::VIEW>(
- left_key, left_laddr, insert_pos, insert_stage, insert_size);
- assert(node_stage.free_size() == free_size - insert_size);
- // TODO: common part end, move to NodeT
-
- assert(*p_value == left_laddr);
- auto insert_pos_normalized = normalize(std::move(insert_pos));
- assert(insert_pos_normalized <= pos);
- assert(get_key_view(insert_pos_normalized) == left_key);
- track_insert(insert_pos_normalized, insert_stage, left_child, right_child);
- this->validate_tracked_children();
- return node_ertr::now();
- }
-
- std::cout << " try insert at: " << insert_pos
- << ", insert_stage=" << (int)insert_stage
- << ", insert_size=" << insert_size
- << ", values=0x" << std::hex << left_laddr
- << ",0x" << right_laddr << std::dec << std::endl;
-
- Ref<InternalNodeT> this_ref = this;
- return (is_root() ? this->upgrade_root(c) : node_ertr::now()
- ).safe_then([this, c] {
- return ConcreteType::allocate(c, this->level(), this->is_level_tail());
- }).safe_then([this_ref, this, c, left_key, left_child, right_child, left_laddr,
- insert_pos, insert_stage, insert_size](auto fresh_right) mutable {
- auto& node_stage = this->extent.read();
- size_t empty_size = node_stage.size_before(0);
- size_t available_size = node_stage.total_size() - empty_size;
- size_t target_split_size = empty_size + (available_size + insert_size) / 2;
- // TODO adjust NODE_BLOCK_SIZE according to this requirement
- assert(insert_size < available_size / 2);
- typename STAGE_T::StagedIterator split_at;
- bool insert_left = STAGE_T::locate_split(
- node_stage, target_split_size, insert_pos, insert_stage, insert_size, split_at);
-
- std::cout << " split at: " << split_at << ", insert_left=" << insert_left
- << ", now insert at: " << insert_pos
- << std::endl;
-
- auto append_at = split_at;
- // TODO(cross-node string dedup)
- typename STAGE_T::template StagedAppender<KeyT::VIEW> right_appender;
- right_appender.init(&fresh_right.mut, fresh_right.mut.get_write());
- const laddr_t* p_value = nullptr;
- if (!insert_left) {
- // right node: append [start(append_at), insert_pos)
- STAGE_T::template append_until<KeyT::VIEW>(
- append_at, right_appender, insert_pos, insert_stage);
- std::cout << "insert to right: " << insert_pos
- << ", insert_stage=" << (int)insert_stage << std::endl;
- // right node: append [insert_pos(key, value)]
- bool is_front_insert = (insert_pos == position_t::begin());
- bool is_end = STAGE_T::template append_insert<KeyT::VIEW>(
- left_key, left_laddr, append_at, right_appender,
- is_front_insert, insert_stage, p_value);
- assert(append_at.is_end() == is_end);
- }
-
- // right node: append (insert_pos, end)
- auto pos_end = position_t::end();
- STAGE_T::template append_until<KeyT::VIEW>(
- append_at, right_appender, pos_end, STAGE_T::STAGE);
- assert(append_at.is_end());
- right_appender.wrap();
- fresh_right.node->dump(std::cout) << std::endl;
-
- // mutate left node
- if (insert_left) {
- p_value = this->extent.template split_insert_replayable<KeyT::VIEW>(
- split_at, left_key, left_laddr, insert_pos, insert_stage, insert_size);
- } else {
- this->extent.split_replayable(split_at);
- }
- this->dump(std::cout) << std::endl;
- assert(p_value);
- // TODO: common part end, move to NodeT
-
- auto split_pos_normalized = normalize(split_at.get_pos());
- auto insert_pos_normalized = normalize(std::move(insert_pos));
- std::cout << "split at " << split_pos_normalized
- << ", insert at " << insert_pos_normalized
- << ", insert_left=" << insert_left
- << ", insert_stage=" << (int)insert_stage << std::endl;
- track_split(split_pos_normalized, fresh_right.node);
- if (insert_left) {
- track_insert(insert_pos_normalized, insert_stage, left_child);
- } else {
- fresh_right.node->track_insert(insert_pos_normalized, insert_stage, left_child);
- }
-
- this->validate_tracked_children();
- fresh_right.node->validate_tracked_children();
-
- // propagate index to parent
- return this->insert_parent(c, fresh_right.node);
- // TODO (optimize)
- // try to acquire space from siblings before split... see btrfs
- });
- }
-
- static node_future<fresh_node_t> allocate(
- context_t c, level_t level, bool is_level_tail) {
- assert(level != 0u);
- return extent_t::allocate(c, level, is_level_tail
- ).safe_then([](auto&& fresh_extent) {
- auto ret = Ref<ConcreteType>(new ConcreteType());
- ret->extent = std::move(fresh_extent.extent);
- return fresh_node_t{ret, fresh_extent.mut};
- });
- }
-
- private:
- const laddr_t* get_p_value(const search_position_t& pos) const override final {
- return this->get_value_ptr(pos);
+ #pragma GCC diagnostic ignored "-Woverloaded-virtual"
+ virtual std::tuple<search_position_t, bool, const laddr_t*> split_insert(
+ NodeExtentMutable&, NodeImpl&, const key_view_t&, const laddr_t&,
+ search_position_t&, match_stage_t&, node_offset_t&) {
+ assert(false && "impossible path");
}
-};
+ virtual void replace_child_addr(const search_position_t&, laddr_t dst, laddr_t src) = 0;
+ virtual std::tuple<match_stage_t, node_offset_t> evaluate_insert(
+ const key_view_t&, const laddr_t&, search_position_t&) const = 0;
-class InternalNode0 final : public InternalNodeT<node_fields_0_t, InternalNode0> {
- public:
- node_future<> test_clone_root(
- context_t c_other, RootNodeTracker& tracker_other) const override final {
- assert(is_root());
- assert(is_level_tail());
- Ref<const InternalNode0> this_ref = this;
- return InternalNode0::allocate(c_other, level(), true
- ).safe_then([this, c_other, &tracker_other](auto fresh_other) {
- this->extent.test_copy_to(fresh_other.mut);
- auto cloned_root = fresh_other.node;
- return c_other.nm.get_super(c_other.t, tracker_other
- ).safe_then([c_other, cloned_root](auto&& super_other) {
- cloned_root->make_root_new(c_other, std::move(super_other));
- return cloned_root;
- });
- }).safe_then([this_ref, this, c_other](auto cloned_root) {
- // In some unit tests, the children are stubbed out that they
- // don't exist in NodeExtentManager, and are only tracked in memory.
- return test_clone_children(c_other, cloned_root);
- });
- }
+ struct fresh_impl_t {
+ InternalNodeImplURef impl;
+ NodeExtentMutable mut;
+ std::pair<NodeImplURef, NodeExtentMutable> make_pair() {
+ return {std::move(impl), mut};
+ }
+ };
+ static alloc_ertr::future<fresh_impl_t> allocate(context_t, field_type_t, bool, level_t);
+ static InternalNodeImplURef load(NodeExtentRef, field_type_t, bool);
- static node_future<Ref<InternalNode0>> allocate_root(
- context_t c, level_t old_root_level,
- laddr_t old_root_addr, Super::URef&& super) {
- return allocate(c, old_root_level + 1, true
- ).safe_then([c, old_root_addr,
- super = std::move(super)](auto fresh_root) mutable {
- auto root = fresh_root.node;
- const laddr_t* p_value = root->get_value_ptr(search_position_t::end());
- fresh_root.mut.copy_in_absolute(
- const_cast<laddr_t*>(p_value), old_root_addr);
- root->make_root_from(c, std::move(super), old_root_addr);
- return root;
- });
- }
+ protected:
+ InternalNodeImpl() = default;
};
-class InternalNode1 final : public InternalNodeT<node_fields_1_t, InternalNode1> {};
-class InternalNode2 final : public InternalNodeT<node_fields_2_t, InternalNode2> {};
-class InternalNode3 final : public InternalNodeT<internal_fields_3_t, InternalNode3> {};
-
-template <typename FieldType, typename ConcreteType>
-class LeafNodeT: public LeafNode,
- public NodeT<FieldType, node_type_t::LEAF, ConcreteType> {
+class LeafNodeImpl : public NodeImpl {
public:
- using parent_t = NodeT<FieldType, node_type_t::LEAF, ConcreteType>;
- using extent_t = typename parent_t::extent_t;
- using fresh_node_t = typename parent_t::fresh_node_t;
- using node_stage_t = typename parent_t::node_stage_t;
- using position_t = typename parent_t::position_t;
-
- virtual ~LeafNodeT() = default;
-
- node_future<search_result_t> do_lower_bound(
- context_t, const full_key_t<KeyT::HOBJ>& key,
- MatchHistory& history) override final {
- auto& node_stage = this->extent.read();
- if (unlikely(node_stage.keys() == 0)) {
- assert(this->is_root());
- history.set<STAGE_LEFT>(MatchKindCMP::NE);
- auto p_cursor = get_or_track_cursor(search_position_t::end(), nullptr);
- return node_ertr::make_ready_future<search_result_t>(
- search_result_t{p_cursor, MatchKindBS::NE});
- }
-
- auto result = STAGE_T::lower_bound_normalized(node_stage, key, history);
- if (result.is_end()) {
- assert(this->is_level_tail());
- } else {
- assert(result.p_value);
- }
- auto p_cursor = get_or_track_cursor(result.position, result.p_value);
- return node_ertr::make_ready_future<search_result_t>(
- search_result_t{p_cursor, result.match()});
- }
-
- node_future<Ref<tree_cursor_t>> lookup_smallest(context_t) override final {
- auto& node_stage = this->extent.read();
- if (unlikely(node_stage.keys() == 0)) {
- assert(this->is_root());
- auto pos = search_position_t::end();
- return node_ertr::make_ready_future<Ref<tree_cursor_t>>(
- get_or_track_cursor(pos, nullptr));
- }
-
- auto pos = search_position_t::begin();
- const onode_t* p_value = this->get_value_ptr(pos);
- return node_ertr::make_ready_future<Ref<tree_cursor_t>>(
- get_or_track_cursor(pos, p_value));
- }
-
- node_future<Ref<tree_cursor_t>> lookup_largest(context_t) override final {
- auto& node_stage = this->extent.read();
- if (unlikely(node_stage.keys() == 0)) {
- assert(this->is_root());
- auto pos = search_position_t::end();
- return node_ertr::make_ready_future<Ref<tree_cursor_t>>(
- get_or_track_cursor(pos, nullptr));
- }
-
- search_position_t pos;
- const onode_t* p_value = nullptr;
- STAGE_T::lookup_largest_normalized(node_stage, pos, p_value);
- return node_ertr::make_ready_future<Ref<tree_cursor_t>>(
- get_or_track_cursor(pos, p_value));
- }
-
- node_future<Ref<tree_cursor_t>> insert_value(
- context_t c, const full_key_t<KeyT::HOBJ>& key, const onode_t& value,
- const search_position_t& pos, const MatchHistory& history) override final {
-#ifndef NDEBUG
- if (pos.is_end()) {
- assert(this->is_level_tail());
- }
-#endif
- this->extent.prepare_mutate(c);
- auto& node_stage = this->extent.read();
-
- position_t insert_pos = cast_down<STAGE_T::STAGE>(pos);
- auto [insert_stage, insert_size] =
- STAGE_T::evaluate_insert(key, value, history, insert_pos);
-
- // TODO: common part begin, move to NodeT
- auto free_size = node_stage.free_size();
- if (free_size >= insert_size) {
- auto p_value = this->extent.template insert_replayable<KeyT::HOBJ>(
- key, value, insert_pos, insert_stage, insert_size);
- assert(node_stage.free_size() == free_size - insert_size);
- // TODO: common part end, move to NodeT
-
- assert(p_value->size == value.size);
- auto insert_pos_normalized = normalize(std::move(insert_pos));
- assert(insert_pos_normalized <= pos);
- assert(get_key_view(insert_pos_normalized) == key);
- auto ret = track_insert(insert_pos_normalized, insert_stage, p_value);
- this->validate_tracked_cursors();
- return node_ertr::make_ready_future<Ref<tree_cursor_t>>(ret);
+ struct leaf_marker_t {};
+ virtual ~LeafNodeImpl() = default;
+
+ #pragma GCC diagnostic ignored "-Woverloaded-virtual"
+ virtual const onode_t* get_p_value(
+ const search_position_t&, leaf_marker_t={}) const {
+ assert(false && "impossible path");
+ }
+ #pragma GCC diagnostic ignored "-Woverloaded-virtual"
+ virtual lookup_result_t<node_type_t::LEAF> lower_bound(
+ const key_hobj_t&, MatchHistory&, leaf_marker_t={}) const {
+ assert(false && "impossible path");
+ }
+ #pragma GCC diagnostic ignored "-Woverloaded-virtual"
+ virtual const onode_t* insert(
+ const key_hobj_t&, const onode_t&, search_position_t&, match_stage_t&, node_offset_t&) {
+ assert(false && "impossible path");
+ }
+ #pragma GCC diagnostic ignored "-Woverloaded-virtual"
+ virtual std::tuple<search_position_t, bool, const onode_t*> split_insert(
+ NodeExtentMutable&, NodeImpl&, const key_hobj_t&, const onode_t&,
+ search_position_t&, match_stage_t&, node_offset_t&) {
+ assert(false && "impossible path");
+ }
+
+ virtual void get_largest_value(search_position_t&, const onode_t*&) const = 0;
+ virtual std::tuple<match_stage_t, node_offset_t> evaluate_insert(
+ const key_hobj_t&, const onode_t&,
+ const MatchHistory&, search_position_t&) const = 0;
+
+ struct fresh_impl_t {
+ LeafNodeImplURef impl;
+ NodeExtentMutable mut;
+ std::pair<NodeImplURef, NodeExtentMutable> make_pair() {
+ return {std::move(impl), mut};
}
+ };
+ static alloc_ertr::future<fresh_impl_t> allocate(context_t, field_type_t, bool);
+ static LeafNodeImplURef load(NodeExtentRef, field_type_t, bool);
- std::cout << " try insert at: " << insert_pos
- << ", insert_stage=" << (int)insert_stage
- << ", insert_size=" << insert_size
- << std::endl;
-
- Ref<LeafNodeT> this_ref = this;
- return (is_root() ? this->upgrade_root(c) : node_ertr::now()
- ).safe_then([this, c] {
- return ConcreteType::allocate(c, this->is_level_tail());
- }).safe_then([this_ref, this, c, &key, &value, &history,
- insert_pos, insert_stage, insert_size](auto fresh_right) mutable {
- auto& node_stage = this->extent.read();
- size_t empty_size = node_stage.size_before(0);
- size_t available_size = node_stage.total_size() - empty_size;
- size_t target_split_size = empty_size + (available_size + insert_size) / 2;
- // TODO adjust NODE_BLOCK_SIZE according to this requirement
- assert(insert_size < available_size / 2);
- typename STAGE_T::StagedIterator split_at;
- bool insert_left = STAGE_T::locate_split(
- node_stage, target_split_size, insert_pos, insert_stage, insert_size, split_at);
-
- std::cout << " split at: " << split_at << ", insert_left=" << insert_left
- << ", now insert at: " << insert_pos
- << std::endl;
-
- auto append_at = split_at;
- // TODO(cross-node string dedup)
- typename STAGE_T::template StagedAppender<KeyT::HOBJ> right_appender;
- right_appender.init(&fresh_right.mut, fresh_right.mut.get_write());
- const onode_t* p_value = nullptr;
- if (!insert_left) {
- // right node: append [start(append_at), insert_pos)
- STAGE_T::template append_until<KeyT::HOBJ>(
- append_at, right_appender, insert_pos, insert_stage);
- std::cout << "insert to right: " << insert_pos
- << ", insert_stage=" << (int)insert_stage << std::endl;
- // right node: append [insert_pos(key, value)]
- bool is_front_insert = (insert_pos == position_t::begin());
- bool is_end = STAGE_T::template append_insert<KeyT::HOBJ>(
- key, value, append_at, right_appender,
- is_front_insert, insert_stage, p_value);
- assert(append_at.is_end() == is_end);
- }
-
- // right node: append (insert_pos, end)
- auto pos_end = position_t::end();
- STAGE_T::template append_until<KeyT::HOBJ>(
- append_at, right_appender, pos_end, STAGE_T::STAGE);
- assert(append_at.is_end());
- right_appender.wrap();
- fresh_right.node->dump(std::cout) << std::endl;
-
- // mutate left node
- if (insert_left) {
- p_value = this->extent.template split_insert_replayable<KeyT::HOBJ>(
- split_at, key, value, insert_pos, insert_stage, insert_size);
- } else {
- this->extent.split_replayable(split_at);
- }
- this->dump(std::cout) << std::endl;
- assert(p_value);
- // TODO: common part end, move to NodeT
-
- auto split_pos_normalized = normalize(split_at.get_pos());
- auto insert_pos_normalized = normalize(std::move(insert_pos));
- std::cout << "split at " << split_pos_normalized
- << ", insert at " << insert_pos_normalized
- << ", insert_left=" << insert_left
- << ", insert_stage=" << (int)insert_stage << std::endl;
- track_split(split_pos_normalized, fresh_right.node);
- Ref<tree_cursor_t> ret;
- if (insert_left) {
- assert(this->get_key_view(insert_pos_normalized) == key);
- ret = track_insert(insert_pos_normalized, insert_stage, p_value);
- } else {
- assert(fresh_right.node->get_key_view(insert_pos_normalized) == key);
- ret = fresh_right.node->track_insert(insert_pos_normalized, insert_stage, p_value);
- }
-
- this->validate_tracked_cursors();
- fresh_right.node->validate_tracked_cursors();
-
- // propagate index to parent
- return this->insert_parent(c, fresh_right.node).safe_then([ret] {
- return ret;
- });
- // TODO (optimize)
- // try to acquire space from siblings before split... see btrfs
- });
- }
-
- static node_future<fresh_node_t> allocate(context_t c, bool is_level_tail) {
- return extent_t::allocate(c, 0u, is_level_tail
- ).safe_then([](auto&& fresh_extent) {
- auto ret = Ref<ConcreteType>(new ConcreteType());
- ret->extent = std::move(fresh_extent.extent);
- return fresh_node_t{ret, fresh_extent.mut};
- });
- }
-
- private:
- const onode_t* get_p_value(const search_position_t& pos) const override final {
- return this->get_value_ptr(pos);
- }
-};
-class LeafNode0 final : public LeafNodeT<node_fields_0_t, LeafNode0> {
- public:
- node_future<> test_clone_root(
- context_t c_other, RootNodeTracker& tracker_other) const override final {
- assert(this->is_root());
- assert(is_level_tail());
- Ref<const LeafNode0> this_ref = this;
- return LeafNode0::allocate(c_other, true
- ).safe_then([this, c_other, &tracker_other](auto fresh_other) {
- this->extent.test_copy_to(fresh_other.mut);
- auto cloned_root = fresh_other.node;
- return c_other.nm.get_super(c_other.t, tracker_other
- ).safe_then([c_other, cloned_root](auto&& super_other) {
- cloned_root->make_root_new(c_other, std::move(super_other));
- });
- }).safe_then([this_ref]{});
- }
-
- static node_future<> mkfs(context_t c, RootNodeTracker& root_tracker) {
- return allocate(c, true
- ).safe_then([c, &root_tracker](auto fresh_node) {
- auto root = fresh_node.node;
- return c.nm.get_super(c.t, root_tracker
- ).safe_then([c, root](auto&& super) {
- root->make_root_new(c, std::move(super));
- });
- });
- }
+ protected:
+ LeafNodeImpl() = default;
};
-class LeafNode1 final : public LeafNodeT<node_fields_1_t, LeafNode1> {};
-class LeafNode2 final : public LeafNodeT<node_fields_2_t, LeafNode2> {};
-class LeafNode3 final : public LeafNodeT<leaf_fields_3_t, LeafNode3> {};
-
-inline Node::node_future<Ref<Node>> load_node(
- context_t c, laddr_t addr, bool expect_is_level_tail) {
- // NOTE:
- // *option1: all types of node have the same length;
- // option2: length is defined by node/field types;
- // option3: length is totally flexible;
- return c.nm.read_extent(c.t, addr, NODE_BLOCK_SIZE
- ).safe_then([expect_is_level_tail](auto extent) {
- const auto header = reinterpret_cast<const node_header_t*>(extent->get_read());
- auto _field_type = header->get_field_type();
- if (!_field_type.has_value()) {
- throw std::runtime_error("load failed: bad field type");
- }
- auto _node_type = header->get_node_type();
- if (_field_type == field_type_t::N0) {
- if (_node_type == node_type_t::LEAF) {
- return LeafNode0::load(extent, expect_is_level_tail);
- } else {
- return InternalNode0::load(extent, expect_is_level_tail);
- }
- } else if (_field_type == field_type_t::N1) {
- if (_node_type == node_type_t::LEAF) {
- return LeafNode1::load(extent, expect_is_level_tail);
- } else {
- return InternalNode1::load(extent, expect_is_level_tail);
- }
- } else if (_field_type == field_type_t::N2) {
- if (_node_type == node_type_t::LEAF) {
- return LeafNode2::load(extent, expect_is_level_tail);
- } else {
- return InternalNode2::load(extent, expect_is_level_tail);
- }
- } else if (_field_type == field_type_t::N3) {
- if (_node_type == node_type_t::LEAF) {
- return LeafNode3::load(extent, expect_is_level_tail);
- } else {
- return InternalNode3::load(extent, expect_is_level_tail);
- }
- } else {
- assert(false);
- }
- });
-}
}
+++ /dev/null
-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
-// vim: ts=8 sw=2 smarttab
-
-#pragma once
-
-// TODO: remove
-#include <iostream>
-
-#include "node_extent_mutable.h"
-#include "stages/node_stage.h"
-#include "stages/stage.h"
-
-#define STAGE_T node_to_stage_t<node_stage_t>
-
-namespace crimson::os::seastore::onode {
-
-template <typename FieldType, node_type_t NODE_TYPE>
-struct NodeLayoutReplayableT {
- using node_stage_t = node_extent_t<FieldType, NODE_TYPE>;
- using position_t = typename STAGE_T::position_t;
- using StagedIterator = typename STAGE_T::StagedIterator;
- using value_t = value_type_t<NODE_TYPE>;
- static constexpr auto FIELD_TYPE = FieldType::FIELD_TYPE;
-
- template <KeyT KT>
- static const value_t* insert(
- NodeExtentMutable& mut,
- const node_stage_t& node_stage,
- const full_key_t<KT>& key,
- const value_t& value,
- position_t& insert_pos,
- match_stage_t& insert_stage,
- node_offset_t& insert_size) {
- auto p_value = STAGE_T::template proceed_insert<KT, false>(
- mut, node_stage, key, value, insert_pos, insert_stage, insert_size);
- return p_value;
- }
-
- static void split(
- NodeExtentMutable& mut,
- const node_stage_t& node_stage,
- StagedIterator& split_at) {
- node_stage_t::update_is_level_tail(mut, node_stage, false);
- STAGE_T::trim(mut, split_at);
- }
-
- template <KeyT KT>
- static const value_t* split_insert(
- NodeExtentMutable& mut,
- const node_stage_t& node_stage,
- StagedIterator& split_at,
- const full_key_t<KT>& key,
- const value_t& value,
- position_t& insert_pos,
- match_stage_t& insert_stage,
- node_offset_t& insert_size) {
- node_stage_t::update_is_level_tail(mut, node_stage, false);
- STAGE_T::trim(mut, split_at);
- std::cout << "insert to left: " << insert_pos
- << ", insert_stage=" << (int)insert_stage << std::endl;
- auto p_value = STAGE_T::template proceed_insert<KT, true>(
- mut, node_stage, key, value, insert_pos, insert_stage, insert_size);
- return p_value;
- }
-
- static void prepare_internal_split(
- NodeExtentMutable& mut,
- const node_stage_t& node_stage,
- const laddr_t left_child_addr,
- const laddr_t right_child_addr,
- laddr_t* p_split_addr) {
- assert(NODE_TYPE == node_type_t::INTERNAL);
- assert(*p_split_addr == left_child_addr);
- mut.copy_in_absolute(p_split_addr, right_child_addr);
- }
-
-};
-
-}
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+// TODO: remove
+#include <iostream>
+#include <ostream>
+
+#include "common/likely.h"
+#include "node_extent_visitor.h"
+#include "node_impl.h"
+#include "stages/node_stage_layout.h"
+
+namespace crimson::os::seastore::onode {
+
+template <node_type_t NODE_TYPE> struct insert_key_type;
+template <> struct insert_key_type<node_type_t::INTERNAL> {
+ static constexpr auto type = KeyT::VIEW; };
+template <> struct insert_key_type<node_type_t::LEAF> {
+ static constexpr auto type = KeyT::HOBJ; };
+
+template <node_type_t NODE_TYPE> struct node_impl_type;
+template <> struct node_impl_type<node_type_t::INTERNAL> {
+ using type = InternalNodeImpl; };
+template <> struct node_impl_type<node_type_t::LEAF> {
+ using type = LeafNodeImpl; };
+
+template <node_type_t NODE_TYPE> struct node_marker_type;
+template <> struct node_marker_type<node_type_t::INTERNAL> {
+ using type = InternalNodeImpl::internal_marker_t; };
+template <> struct node_marker_type<node_type_t::LEAF> {
+ using type = LeafNodeImpl::leaf_marker_t; };
+
+template <typename FieldType, node_type_t NODE_TYPE>
+class NodeLayoutT final : public InternalNodeImpl, public LeafNodeImpl {
+ public:
+ using URef = std::unique_ptr<NodeLayoutT>;
+ using extent_t = NodeExtentT<FieldType, NODE_TYPE>;
+ using parent_t = typename node_impl_type<NODE_TYPE>::type;
+ using marker_t = typename node_marker_type<NODE_TYPE>::type;
+ using node_stage_t = typename extent_t::node_stage_t;
+ using position_t = typename extent_t::position_t;
+ using value_t = typename extent_t::value_t;
+ static constexpr auto FIELD_TYPE = extent_t::FIELD_TYPE;
+ static constexpr auto KEY_TYPE = insert_key_type<NODE_TYPE>::type;
+ static constexpr auto STAGE = STAGE_T::STAGE;
+
+ NodeLayoutT(const NodeLayoutT&) = delete;
+ NodeLayoutT(NodeLayoutT&&) = delete;
+ NodeLayoutT& operator=(const NodeLayoutT&) = delete;
+ NodeLayoutT& operator=(NodeLayoutT&&) = delete;
+ ~NodeLayoutT() override = default;
+
+ static URef load(NodeExtentRef extent, bool expect_is_level_tail) {
+ std::unique_ptr<NodeLayoutT> ret(
+ new NodeLayoutT(extent_t::loaded(extent), extent));
+ assert(ret->is_level_tail() == expect_is_level_tail);
+ return ret;
+ }
+
+ using alloc_ertr = NodeExtentManager::tm_ertr;
+ static alloc_ertr::future<typename parent_t::fresh_impl_t> allocate(
+ context_t c, bool is_level_tail, level_t level) {
+ // NOTE:
+ // *option1: all types of node have the same length;
+ // option2: length is defined by node/field types;
+ // option3: length is totally flexible;
+ return c.nm.alloc_extent(c.t, node_stage_t::EXTENT_SIZE
+ ).safe_then([is_level_tail, level](auto extent) {
+ auto [state, mut] = extent_t::allocated(extent, is_level_tail, level);
+ return typename parent_t::fresh_impl_t{
+ std::unique_ptr<parent_t>(new NodeLayoutT(state, extent)), mut};
+ });
+ }
+
+ protected:
+ /*
+ * NodeImpl
+ */
+ field_type_t field_type() const override { return FIELD_TYPE; }
+ laddr_t laddr() const override { return extent.get_laddr(); }
+ void prepare_mutate(context_t c) override { return extent.prepare_mutate(c); }
+ bool is_level_tail() const override { return extent.read().is_level_tail(); }
+ bool is_empty() const override { return extent.read().keys() == 0; }
+ level_t level() const override { return extent.read().level(); }
+ node_offset_t free_size() const override { return extent.read().free_size(); }
+
+ key_view_t get_key_view(const search_position_t& position) const override {
+ key_view_t ret;
+ STAGE_T::get_key_view(extent.read(), cast_down<STAGE>(position), ret);
+ return ret;
+ }
+
+ key_view_t get_largest_key_view() const override {
+ key_view_t ret;
+ STAGE_T::lookup_largest_index(extent.read(), ret);
+ return ret;
+ }
+
+ std::ostream& dump(std::ostream& os) const override {
+ auto& node_stage = extent.read();
+ auto p_start = node_stage.p_start();
+ dump_brief(os);
+ os << ":\n header: " << node_stage_t::header_size() << "B";
+ size_t size = 0u;
+ if (node_stage.keys()) {
+ STAGE_T::dump(node_stage, os, " ", size, p_start);
+ } else {
+ if constexpr (NODE_TYPE == node_type_t::LEAF) {
+ return os << " empty!";
+ } else { // internal node
+ if (!node_stage.is_level_tail()) {
+ return os << " empty!";
+ } else {
+ size += node_stage_t::header_size();
+ }
+ }
+ }
+ if constexpr (NODE_TYPE == node_type_t::INTERNAL) {
+ if (node_stage.is_level_tail()) {
+ size += sizeof(laddr_t);
+ auto value_ptr = node_stage.get_end_p_laddr();
+ int offset = reinterpret_cast<const char*>(value_ptr) - p_start;
+ os << "\n tail value: 0x"
+ << std::hex << *value_ptr << std::dec
+ << " " << size << "B"
+ << " @" << offset << "B";
+ }
+ }
+ return os;
+ }
+
+ std::ostream& dump_brief(std::ostream& os) const override {
+ auto& node_stage = extent.read();
+ os << "Node" << NODE_TYPE << FIELD_TYPE
+ << "@0x" << std::hex << extent.get_laddr()
+ << "+" << node_stage_t::EXTENT_SIZE << std::dec
+ << (node_stage.is_level_tail() ? "$" : "")
+ << "(level=" << (unsigned)node_stage.level()
+ << ", filled=" << node_stage.total_size() - node_stage.free_size() << "B"
+ << ", free=" << node_stage.free_size() << "B"
+ << ")";
+ return os;
+ }
+
+ void test_copy_to(NodeExtentMutable& to) const override {
+ extent.test_copy_to(to);
+ }
+
+ void test_set_tail(NodeExtentMutable& mut) override {
+ node_stage_t::update_is_level_tail(mut, extent.read(), true);
+ }
+
+ /*
+ * Common
+ */
+ const value_t* get_p_value(
+ const search_position_t& position, marker_t={}) const override {
+ auto& node_stage = extent.read();
+ if constexpr (NODE_TYPE == node_type_t::INTERNAL) {
+ if (position.is_end()) {
+ assert(is_level_tail());
+ return node_stage.get_end_p_laddr();
+ }
+ } else {
+ assert(!position.is_end());
+ }
+ return STAGE_T::get_p_value(node_stage, cast_down<STAGE>(position));
+ }
+
+ lookup_result_t<NODE_TYPE> lower_bound(
+ const key_hobj_t& key, MatchHistory& history, marker_t={}) const override {
+ auto& node_stage = extent.read();
+ if constexpr (NODE_TYPE == node_type_t::LEAF) {
+ if (unlikely(node_stage.keys() == 0)) {
+ history.set<STAGE_LEFT>(MatchKindCMP::NE);
+ return lookup_result_t<NODE_TYPE>::end();
+ }
+ }
+ auto result = STAGE_T::lower_bound_normalized(node_stage, key, history);
+ if (result.is_end()) {
+ assert(node_stage.is_level_tail());
+ assert(result.p_value == nullptr);
+ if constexpr (NODE_TYPE == node_type_t::INTERNAL) {
+ result.p_value = node_stage.get_end_p_laddr();
+ }
+ } else {
+ assert(result.p_value != nullptr);
+ }
+ return result;
+ }
+
+ const value_t* insert(
+ const full_key_t<KEY_TYPE>& key, const value_t& value,
+ search_position_t& insert_pos, match_stage_t& insert_stage,
+ node_offset_t& insert_size) override {
+ auto ret = extent.template insert_replayable<KEY_TYPE>(
+ key, value, cast_down<STAGE>(insert_pos), insert_stage, insert_size);
+ assert(get_key_view(insert_pos) == key);
+ return ret;
+ }
+
+ std::tuple<search_position_t, bool, const value_t*> split_insert(
+ NodeExtentMutable& right_mut, NodeImpl& right_impl,
+ const full_key_t<KEY_TYPE>& key, const value_t& value,
+ search_position_t& _insert_pos, match_stage_t& insert_stage,
+ node_offset_t& insert_size) override {
+ auto& insert_pos = cast_down<STAGE>(_insert_pos);
+ auto& node_stage = extent.read();
+ size_t empty_size = node_stage.size_before(0);
+ size_t available_size = node_stage.total_size() - empty_size;
+ size_t target_split_size = empty_size + (available_size + insert_size) / 2;
+ // TODO adjust NODE_BLOCK_SIZE according to this requirement
+ assert(insert_size < available_size / 2);
+ typename STAGE_T::StagedIterator split_at;
+ bool is_insert_left = STAGE_T::locate_split(
+ node_stage, target_split_size, insert_pos, insert_stage, insert_size, split_at);
+
+ std::cout << " split at: " << split_at << ", is_insert_left=" << is_insert_left
+ << ", now insert at: " << insert_pos
+ << std::endl;
+
+ auto append_at = split_at;
+ // TODO(cross-node string dedup)
+ typename STAGE_T::template StagedAppender<KEY_TYPE> right_appender;
+ right_appender.init(&right_mut, right_mut.get_write());
+ const value_t* p_value = nullptr;
+ if (!is_insert_left) {
+ // right node: append [start(append_at), insert_pos)
+ STAGE_T::template append_until<KEY_TYPE>(
+ append_at, right_appender, insert_pos, insert_stage);
+ std::cout << "insert to right: " << insert_pos
+ << ", insert_stage=" << (int)insert_stage << std::endl;
+ // right node: append [insert_pos(key, value)]
+ bool is_front_insert = (insert_pos == position_t::begin());
+ bool is_end = STAGE_T::template append_insert<KEY_TYPE>(
+ key, value, append_at, right_appender,
+ is_front_insert, insert_stage, p_value);
+ assert(append_at.is_end() == is_end);
+ }
+
+ // right node: append (insert_pos, end)
+ auto pos_end = position_t::end();
+ STAGE_T::template append_until<KEY_TYPE>(
+ append_at, right_appender, pos_end, STAGE);
+ assert(append_at.is_end());
+ right_appender.wrap();
+ right_impl.dump(std::cout) << std::endl;
+
+ // mutate left node
+ if (is_insert_left) {
+ p_value = extent.template split_insert_replayable<KEY_TYPE>(
+ split_at, key, value, insert_pos, insert_stage, insert_size);
+ assert(get_key_view(_insert_pos) == key);
+ } else {
+ assert(right_impl.get_key_view(_insert_pos) == key);
+ extent.split_replayable(split_at);
+ }
+ dump(std::cout) << std::endl;
+ assert(p_value);
+
+ auto split_pos = normalize(split_at.get_pos());
+ std::cout << "split at " << split_pos
+ << ", insert at " << _insert_pos
+ << ", is_insert_left=" << is_insert_left
+ << ", insert_stage=" << (int)insert_stage << std::endl;
+ return {split_pos, is_insert_left, p_value};
+ }
+
+ /*
+ * InternalNodeImpl
+ */
+ void replace_child_addr(
+ const search_position_t& pos, laddr_t dst, laddr_t src) override {
+ if constexpr (NODE_TYPE == node_type_t::INTERNAL) {
+ const laddr_t* p_value = get_p_value(pos);
+ assert(*p_value == src);
+ extent.update_child_addr_replayable(dst, const_cast<laddr_t*>(p_value));
+ } else {
+ assert(false && "impossible path");
+ }
+ }
+
+ std::tuple<match_stage_t, node_offset_t> evaluate_insert(
+ const key_view_t& key, const laddr_t& value,
+ search_position_t& insert_pos) const override {
+ if constexpr (NODE_TYPE == node_type_t::INTERNAL) {
+ auto& node_stage = extent.read();
+ match_stage_t insert_stage;
+ node_offset_t insert_size;
+ if (unlikely(!node_stage.keys())) {
+ assert(insert_pos.is_end());
+ insert_stage = STAGE;
+ insert_size = STAGE_T::template insert_size<KeyT::VIEW>(key, value);
+ } else {
+ std::tie(insert_stage, insert_size) = STAGE_T::evaluate_insert(
+ node_stage, key, value, cast_down<STAGE>(insert_pos), true);
+ }
+ return {insert_stage, insert_size};
+ } else {
+ assert(false && "impossible path");
+ }
+ }
+
+ /*
+ * LeafNodeImpl
+ */
+ void get_largest_value(search_position_t& pos, const onode_t*& p_value) const override {
+ if constexpr (NODE_TYPE == node_type_t::LEAF) {
+ STAGE_T::lookup_largest_normalized(extent.read(), pos, p_value);
+ } else {
+ assert(false && "impossible path");
+ }
+ }
+
+ std::tuple<match_stage_t, node_offset_t> evaluate_insert(
+ const key_hobj_t& key, const onode_t& value, const MatchHistory& history,
+ search_position_t& insert_pos) const override {
+ if constexpr (NODE_TYPE == node_type_t::LEAF) {
+ return STAGE_T::evaluate_insert(
+ key, value, history, cast_down<STAGE>(insert_pos));
+ } else {
+ assert(false && "impossible path");
+ }
+ }
+
+ private:
+ NodeLayoutT(typename extent_t::state_t state, NodeExtentRef extent)
+ : extent{state, extent} {}
+
+ extent_t extent;
+};
+
+using InternalNode0 = NodeLayoutT<node_fields_0_t, node_type_t::INTERNAL>;
+using InternalNode1 = NodeLayoutT<node_fields_1_t, node_type_t::INTERNAL>;
+using InternalNode2 = NodeLayoutT<node_fields_2_t, node_type_t::INTERNAL>;
+using InternalNode3 = NodeLayoutT<internal_fields_3_t, node_type_t::INTERNAL>;
+using LeafNode0 = NodeLayoutT<node_fields_0_t, node_type_t::LEAF>;
+using LeafNode1 = NodeLayoutT<node_fields_1_t, node_type_t::LEAF>;
+using LeafNode2 = NodeLayoutT<node_fields_2_t, node_type_t::LEAF>;
+using LeafNode3 = NodeLayoutT<leaf_fields_3_t, node_type_t::LEAF>;
+
+}
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+// TODO: remove
+#include <iostream>
+
+#include "node_extent_mutable.h"
+#include "stages/node_stage.h"
+#include "stages/stage.h"
+
+#define STAGE_T node_to_stage_t<node_stage_t>
+
+namespace crimson::os::seastore::onode {
+
+template <typename FieldType, node_type_t NODE_TYPE>
+struct NodeLayoutReplayableT {
+ using node_stage_t = node_extent_t<FieldType, NODE_TYPE>;
+ using position_t = typename STAGE_T::position_t;
+ using StagedIterator = typename STAGE_T::StagedIterator;
+ using value_t = value_type_t<NODE_TYPE>;
+ static constexpr auto FIELD_TYPE = FieldType::FIELD_TYPE;
+
+ template <KeyT KT>
+ static const value_t* insert(
+ NodeExtentMutable& mut,
+ const node_stage_t& node_stage,
+ const full_key_t<KT>& key,
+ const value_t& value,
+ position_t& insert_pos,
+ match_stage_t& insert_stage,
+ node_offset_t& insert_size) {
+ auto p_value = STAGE_T::template proceed_insert<KT, false>(
+ mut, node_stage, key, value, insert_pos, insert_stage, insert_size);
+ return p_value;
+ }
+
+ static void split(
+ NodeExtentMutable& mut,
+ const node_stage_t& node_stage,
+ StagedIterator& split_at) {
+ node_stage_t::update_is_level_tail(mut, node_stage, false);
+ STAGE_T::trim(mut, split_at);
+ }
+
+ template <KeyT KT>
+ static const value_t* split_insert(
+ NodeExtentMutable& mut,
+ const node_stage_t& node_stage,
+ StagedIterator& split_at,
+ const full_key_t<KT>& key,
+ const value_t& value,
+ position_t& insert_pos,
+ match_stage_t& insert_stage,
+ node_offset_t& insert_size) {
+ node_stage_t::update_is_level_tail(mut, node_stage, false);
+ STAGE_T::trim(mut, split_at);
+ std::cout << "insert to left: " << insert_pos
+ << ", insert_stage=" << (int)insert_stage << std::endl;
+ auto p_value = STAGE_T::template proceed_insert<KT, true>(
+ mut, node_stage, key, value, insert_pos, insert_stage, insert_size);
+ return p_value;
+ }
+
+ static void update_child_addr(
+ NodeExtentMutable& mut, const laddr_t new_addr, laddr_t* p_addr) {
+ assert(NODE_TYPE == node_type_t::INTERNAL);
+ mut.copy_in_absolute(p_addr, new_addr);
+ }
+
+};
+
+}
return os;
}
-using level_t = uint8_t;
-
}
class key_view_t;
class key_hobj_t;
enum class KeyT { VIEW, HOBJ };
-template <KeyT> struct _key_type;
-template<> struct _key_type<KeyT::VIEW> { using type = key_view_t; };
-template<> struct _key_type<KeyT::HOBJ> { using type = key_hobj_t; };
+template <KeyT> struct _full_key_type;
+template<> struct _full_key_type<KeyT::VIEW> { using type = key_view_t; };
+template<> struct _full_key_type<KeyT::HOBJ> { using type = key_hobj_t; };
template <KeyT type>
-using full_key_t = typename _key_type<type>::type;
+using full_key_t = typename _full_key_type<type>::type;
// TODO: consider alignments
struct shard_pool_t {
+++ /dev/null
-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
-// vim: ts=8 sw=2 smarttab
-
-#include "node_layout.h"
-
-#include "crimson/os/seastore/onode_manager/staged-fltree/node_extent_mutable.h"
-
-namespace crimson::os::seastore::onode {
-
-void node_header_t::bootstrap_extent(
- NodeExtentMutable& mut,
- field_type_t field_type, node_type_t node_type,
- bool is_level_tail, level_t level) {
- node_header_t header;
- header.set_field_type(field_type);
- header.set_node_type(node_type);
- header.set_is_level_tail(is_level_tail);
- header.level = level;
- mut.copy_in_relative(0, header);
-}
-
-void node_header_t::update_is_level_tail(
- NodeExtentMutable& mut, const node_header_t& header, bool value) {
- auto& _header = const_cast<node_header_t&>(header);
- _header.set_is_level_tail(value);
- mut.validate_inplace_update(_header);
-}
-
-#define F013_T _node_fields_013_t<SlotType>
-#define F013_INST(ST) _node_fields_013_t<ST>
-#define F013_TEMPLATE(ST) template struct F013_INST(ST)
-F013_TEMPLATE(slot_0_t);
-F013_TEMPLATE(slot_1_t);
-F013_TEMPLATE(slot_3_t);
-
-template <typename SlotType>
-void F013_T::update_size_at(
- NodeExtentMutable& mut, const me_t& node, size_t index, int change) {
- assert(index <= node.num_keys);
- for (const auto* p_slot = &node.slots[index];
- p_slot < &node.slots[node.num_keys];
- ++p_slot) {
- node_offset_t offset = p_slot->right_offset;
- mut.copy_in_absolute(
- (void*)&(p_slot->right_offset),
- node_offset_t(offset - change));
- }
-}
-
-template <typename SlotType>
-void F013_T::append_key(
- NodeExtentMutable& mut, const key_t& key, char*& p_append) {
- mut.copy_in_absolute(p_append, key);
- p_append += sizeof(key_t);
-}
-
-template <typename SlotType>
-void F013_T::append_offset(
- NodeExtentMutable& mut, node_offset_t offset_to_right, char*& p_append) {
- mut.copy_in_absolute(p_append, offset_to_right);
- p_append += sizeof(node_offset_t);
-}
-
-template <typename SlotType>
-template <KeyT KT>
-void F013_T::insert_at(
- NodeExtentMutable& mut, const full_key_t<KT>& key,
- const me_t& node, size_t index, node_offset_t size_right) {
- assert(index <= node.num_keys);
- update_size_at(mut, node, index, size_right);
- auto p_insert = const_cast<char*>(fields_start(node)) +
- node.get_key_start_offset(index);
- auto p_shift_end = fields_start(node) + node.get_key_start_offset(node.num_keys);
- mut.shift_absolute(p_insert, p_shift_end - p_insert, estimate_insert_one());
- mut.copy_in_absolute((void*)&node.num_keys, num_keys_t(node.num_keys + 1));
- append_key(mut, key_t::template from_key<KT>(key), p_insert);
- append_offset(mut, node.get_item_end_offset(index) - size_right, p_insert);
-}
-#define IA_TEMPLATE(ST, KT) template void F013_INST(ST):: \
- insert_at<KT>(NodeExtentMutable&, const full_key_t<KT>&, \
- const F013_INST(ST)&, size_t, node_offset_t)
-IA_TEMPLATE(slot_0_t, KeyT::VIEW);
-IA_TEMPLATE(slot_1_t, KeyT::VIEW);
-IA_TEMPLATE(slot_3_t, KeyT::VIEW);
-IA_TEMPLATE(slot_0_t, KeyT::HOBJ);
-IA_TEMPLATE(slot_1_t, KeyT::HOBJ);
-IA_TEMPLATE(slot_3_t, KeyT::HOBJ);
-
-void node_fields_2_t::append_offset(
- NodeExtentMutable& mut, node_offset_t offset_to_right, char*& p_append) {
- mut.copy_in_absolute(p_append, offset_to_right);
- p_append += sizeof(node_offset_t);
-}
-
-}
+++ /dev/null
-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
-// vim: ts=8 sw=2 smarttab
-
-#pragma once
-
-#include "key_layout.h"
-#include "crimson/os/seastore/onode_manager/staged-fltree/node_types.h"
-
-namespace crimson::os::seastore::onode {
-
-class NodeExtentMutable;
-
-struct node_header_t {
- static constexpr unsigned FIELD_TYPE_BITS = 6u;
- static_assert(static_cast<uint8_t>(field_type_t::_MAX) <= 1u << FIELD_TYPE_BITS);
- static constexpr unsigned NODE_TYPE_BITS = 1u;
- static constexpr unsigned B_LEVEL_TAIL_BITS = 1u;
- using bits_t = uint8_t;
-
- node_header_t() {}
- std::optional<field_type_t> get_field_type() const {
- if (field_type >= FIELD_TYPE_MAGIC &&
- field_type < static_cast<uint8_t>(field_type_t::_MAX)) {
- return static_cast<field_type_t>(field_type);
- } else {
- return std::nullopt;
- }
- }
- node_type_t get_node_type() const {
- return static_cast<node_type_t>(node_type);
- }
- bool get_is_level_tail() const {
- return is_level_tail;
- }
-
- static void bootstrap_extent(
- NodeExtentMutable&, field_type_t, node_type_t, bool, level_t);
-
- static void update_is_level_tail(NodeExtentMutable&, const node_header_t&, bool);
-
- bits_t field_type : FIELD_TYPE_BITS;
- bits_t node_type : NODE_TYPE_BITS;
- bits_t is_level_tail : B_LEVEL_TAIL_BITS;
- static_assert(sizeof(bits_t) * 8 ==
- FIELD_TYPE_BITS + NODE_TYPE_BITS + B_LEVEL_TAIL_BITS);
- level_t level;
-
- private:
- void set_field_type(field_type_t type) {
- field_type = static_cast<uint8_t>(type);
- }
- void set_node_type(node_type_t type) {
- node_type = static_cast<uint8_t>(type);
- }
- void set_is_level_tail(bool value) {
- is_level_tail = static_cast<uint8_t>(value);
- }
-} __attribute__((packed));
-
-template <typename FixedKeyType, field_type_t _FIELD_TYPE>
-struct _slot_t {
- using key_t = FixedKeyType;
- static constexpr field_type_t FIELD_TYPE = _FIELD_TYPE;
-
- key_t key;
- node_offset_t right_offset;
-} __attribute__((packed));
-using slot_0_t = _slot_t<shard_pool_crush_t, field_type_t::N0>;
-using slot_1_t = _slot_t<crush_t, field_type_t::N1>;
-using slot_3_t = _slot_t<snap_gen_t, field_type_t::N3>;
-
-struct node_range_t {
- node_offset_t start;
- node_offset_t end;
-};
-
-template <typename FieldType>
-const char* fields_start(const FieldType& node) {
- return reinterpret_cast<const char*>(&node);
-}
-
-template <node_type_t NODE_TYPE, typename FieldType>
-node_range_t fields_free_range_before(
- const FieldType& node, size_t index) {
- assert(index <= node.num_keys);
- node_offset_t offset_start = node.get_key_start_offset(index);
- node_offset_t offset_end =
- (index == 0 ? FieldType::SIZE
- : node.get_item_start_offset(index - 1));
- if constexpr (NODE_TYPE == node_type_t::INTERNAL) {
- if (node.is_level_tail() && index == node.num_keys) {
- offset_end -= sizeof(laddr_t);
- }
- }
- assert(offset_start <= offset_end);
- assert(offset_end - offset_start < FieldType::SIZE);
- return {offset_start, offset_end};
-}
-
-// internal/leaf node N0, N1; leaf node N3
-template <typename SlotType>
-struct _node_fields_013_t {
- // TODO: decide by NODE_BLOCK_SIZE, sizeof(SlotType), sizeof(laddr_t)
- // and the minimal size of variable_key.
- using num_keys_t = uint8_t;
- using key_t = typename SlotType::key_t;
- using key_get_type = const key_t&;
- using me_t = _node_fields_013_t<SlotType>;
- static constexpr field_type_t FIELD_TYPE = SlotType::FIELD_TYPE;
- static constexpr node_offset_t SIZE = NODE_BLOCK_SIZE;
- static constexpr node_offset_t HEADER_SIZE =
- sizeof(node_header_t) + sizeof(num_keys_t);
-
- bool is_level_tail() const { return header.get_is_level_tail(); }
- size_t total_size() const { return SIZE; }
- key_get_type get_key(size_t index) const {
- assert(index < num_keys);
- return slots[index].key;
- }
- node_offset_t get_key_start_offset(size_t index) const {
- assert(index <= num_keys);
- auto offset = HEADER_SIZE + sizeof(SlotType) * index;
- assert(offset < SIZE);
- return offset;
- }
- node_offset_t get_item_start_offset(size_t index) const {
- assert(index < num_keys);
- auto offset = slots[index].right_offset;
- assert(offset <= SIZE);
- return offset;
- }
- const void* p_offset(size_t index) const {
- assert(index < num_keys);
- return &slots[index].right_offset;
- }
- node_offset_t get_item_end_offset(size_t index) const {
- return index == 0 ? SIZE : get_item_start_offset(index - 1);
- }
- template <node_type_t NODE_TYPE>
- node_offset_t free_size_before(size_t index) const {
- auto range = fields_free_range_before<NODE_TYPE>(*this, index);
- return range.end - range.start;
- }
-
-#if 0
- template <node_type_t NODE_TYPE>
- void fill_unused(NodeExtentMutable& mut) const {
- auto range = fields_free_range_before<NODE_TYPE>(*this, num_keys);
- for (auto i = range.start; i < range.end; ++i) {
- mut.copy_in_relative(i, uint8_t(0xc5));
- }
- }
-
- template <node_type_t NODE_TYPE>
- void validate_unused() const {
- auto range = fields_free_range_before<NODE_TYPE>(*this, num_keys);
- for (auto i = fields_start(*this) + range.start;
- i < fields_start(*this) + range.end;
- ++i) {
- assert(*i == char(0xc5));
- }
- }
-#endif
-
- static node_offset_t estimate_insert_one() { return sizeof(SlotType); }
- template <KeyT KT>
- static void insert_at(
- NodeExtentMutable&, const full_key_t<KT>& key,
- const me_t& node, size_t index, node_offset_t size_right);
- static void update_size_at(
- NodeExtentMutable&, const me_t& node, size_t index, int change);
- static void append_key(
- NodeExtentMutable&, const key_t& key, char*& p_append);
- template <KeyT KT>
- static void append_key(
- NodeExtentMutable& mut, const full_key_t<KT>& key, char*& p_append) {
- append_key(mut, key_t::template from_key<KT>(key), p_append);
- }
- static void append_offset(
- NodeExtentMutable& mut, node_offset_t offset_to_right, char*& p_append);
-
- node_header_t header;
- num_keys_t num_keys = 0u;
- SlotType slots[];
-} __attribute__((packed));
-using node_fields_0_t = _node_fields_013_t<slot_0_t>;
-using node_fields_1_t = _node_fields_013_t<slot_1_t>;
-
-// internal/leaf node N2
-struct node_fields_2_t {
- // TODO: decide by NODE_BLOCK_SIZE, sizeof(node_off_t), sizeof(laddr_t)
- // and the minimal size of variable_key.
- using num_keys_t = uint8_t;
- using key_t = ns_oid_view_t;
- using key_get_type = key_t;
- static constexpr field_type_t FIELD_TYPE = field_type_t::N2;
- static constexpr node_offset_t SIZE = NODE_BLOCK_SIZE;
- static constexpr node_offset_t HEADER_SIZE =
- sizeof(node_header_t) + sizeof(num_keys_t);
-
- bool is_level_tail() const { return header.get_is_level_tail(); }
- size_t total_size() const { return SIZE; }
- key_get_type get_key(size_t index) const {
- assert(index < num_keys);
- node_offset_t item_end_offset =
- (index == 0 ? SIZE : offsets[index - 1]);
- assert(item_end_offset <= SIZE);
- const char* p_start = fields_start(*this);
- return key_t(p_start + item_end_offset);
- }
- node_offset_t get_key_start_offset(size_t index) const {
- assert(index <= num_keys);
- auto offset = HEADER_SIZE + sizeof(node_offset_t) * num_keys;
- assert(offset <= SIZE);
- return offset;
- }
- node_offset_t get_item_start_offset(size_t index) const {
- assert(index < num_keys);
- auto offset = offsets[index];
- assert(offset <= SIZE);
- return offset;
- }
- const void* p_offset(size_t index) const {
- assert(index < num_keys);
- return &offsets[index];
- }
- node_offset_t get_item_end_offset(size_t index) const {
- return index == 0 ? SIZE : get_item_start_offset(index - 1);
- }
- template <node_type_t NODE_TYPE>
- node_offset_t free_size_before(size_t index) const {
- auto range = fields_free_range_before<NODE_TYPE>(*this, index);
- return range.end - range.start;
- }
-
-#if 0
- template <node_type_t NODE_TYPE>
- void fill_unused(NodeExtentMutable& mut) const {
- auto range = fields_free_range_before<NODE_TYPE>(*this, num_keys);
- for (auto i = range.start; i < range.end; ++i) {
- mut.copy_in_relative(i, uint8_t(0xc5));
- }
- }
-
- template <node_type_t NODE_TYPE>
- void validate_unused() const {
- auto range = fields_free_range_before<NODE_TYPE>(*this, num_keys);
- for (auto i = fields_start(*this) + range.start;
- i < fields_start(*this) + range.end;
- ++i) {
- assert(*i == char(0xc5));
- }
- }
-#endif
-
- static node_offset_t estimate_insert_one() { return sizeof(node_offset_t); }
- template <KeyT KT>
- static void insert_at(
- NodeExtentMutable& mut, const full_key_t<KT>& key,
- const node_fields_2_t& node, size_t index, node_offset_t size_right) {
- assert(false && "not implemented");
- }
- static void update_size_at(
- NodeExtentMutable& mut, const node_fields_2_t& node, size_t index, int change) {
- assert(false && "not implemented");
- }
- static void append_key(
- NodeExtentMutable& mut, const key_t& key, char*& p_append) {
- ns_oid_view_t::append(mut, key, p_append);
- }
- template <KeyT KT>
- static void append_key(
- NodeExtentMutable& mut, const full_key_t<KT>& key, char*& p_append) {
- ns_oid_view_t::append<KT>(mut, key, p_append);
- }
- static void append_offset(
- NodeExtentMutable& mut, node_offset_t offset_to_right, char*& p_append);
-
- node_header_t header;
- num_keys_t num_keys = 0u;
- node_offset_t offsets[];
-} __attribute__((packed));
-
-// TODO: decide by NODE_BLOCK_SIZE, sizeof(snap_gen_t), sizeof(laddr_t)
-static constexpr unsigned MAX_NUM_KEYS_I3 = 170u;
-template <unsigned MAX_NUM_KEYS>
-struct _internal_fields_3_t {
- using key_get_type = const snap_gen_t&;
- using me_t = _internal_fields_3_t<MAX_NUM_KEYS>;
- // TODO: decide by NODE_BLOCK_SIZE, sizeof(snap_gen_t), sizeof(laddr_t)
- using num_keys_t = uint8_t;
- static constexpr field_type_t FIELD_TYPE = field_type_t::N3;
- static constexpr node_offset_t SIZE = sizeof(me_t);
- static constexpr node_offset_t HEADER_SIZE =
- sizeof(node_header_t) + sizeof(num_keys_t);
-
- bool is_level_tail() const { return header.get_is_level_tail(); }
- size_t total_size() const {
- if (is_level_tail()) {
- return SIZE - sizeof(snap_gen_t);
- } else {
- return SIZE;
- }
- }
- key_get_type get_key(size_t index) const {
- assert(index < num_keys);
- return keys[index];
- }
- template <node_type_t NODE_TYPE>
- std::enable_if_t<NODE_TYPE == node_type_t::INTERNAL, node_offset_t>
- free_size_before(size_t index) const {
- assert(index <= num_keys);
- auto allowed_num_keys = is_level_tail() ? MAX_NUM_KEYS - 1 : MAX_NUM_KEYS;
- assert(num_keys <= allowed_num_keys);
- auto free = (MAX_NUM_KEYS - index) * (sizeof(snap_gen_t) + sizeof(laddr_t));
- if (is_level_tail() && index == num_keys) {
- free -= (sizeof(snap_gen_t) + sizeof(laddr_t));
- }
- assert(free < SIZE);
- return free;
- }
-
-#if 0
- template <node_type_t NODE_TYPE>
- void fill_unused(NodeExtentMutable& mut) const {
- node_offset_t begin = (const char*)&keys[num_keys] - fields_start(*this);
- node_offset_t end = (const char*)&child_addrs[0] - fields_start(*this);
- for (auto i = begin; i < end; ++i) {
- mut.copy_in_relative(i, uint8_t(0xc5));
- }
- begin = (const char*)&child_addrs[num_keys] - fields_start(*this);
- end = NODE_BLOCK_SIZE;
- if (is_level_tail()) {
- begin += sizeof(laddr_t);
- }
- for (auto i = begin; i < end; ++i) {
- mut.copy_in_relative(i, uint8_t(0xc5));
- }
- }
-
- template <node_type_t NODE_TYPE>
- void validate_unused() const {
- auto begin = (const char*)&keys[num_keys];
- auto end = (const char*)&child_addrs[0];
- for (auto i = begin; i < end; ++i) {
- assert(*i == uint8_t(0xc5));
- }
- begin = (const char*)&child_addrs[num_keys];
- end = fields_start(*this) + NODE_BLOCK_SIZE;
- if (is_level_tail()) {
- begin += sizeof(laddr_t);
- }
- for (auto i = begin; i < end; ++i) {
- assert(*i == char(0xc5));
- }
- }
-#endif
-
- static node_offset_t estimate_insert_one() {
- return sizeof(snap_gen_t) + sizeof(laddr_t);
- }
- template <KeyT KT>
- static void insert_at(
- NodeExtentMutable& mut, const full_key_t<KT>& key,
- const me_t& node, size_t index, node_offset_t size_right) {
- assert(false && "not implemented");
- }
- static void update_size_at(
- NodeExtentMutable& mut, const me_t& node, size_t index, int change) {
- assert(false && "not implemented");
- }
-
- node_header_t header;
- num_keys_t num_keys = 0u;
- snap_gen_t keys[MAX_NUM_KEYS];
- laddr_t child_addrs[MAX_NUM_KEYS];
-} __attribute__((packed));
-static_assert(_internal_fields_3_t<MAX_NUM_KEYS_I3>::SIZE <= NODE_BLOCK_SIZE &&
- _internal_fields_3_t<MAX_NUM_KEYS_I3 + 1>::SIZE > NODE_BLOCK_SIZE);
-using internal_fields_3_t = _internal_fields_3_t<MAX_NUM_KEYS_I3>;
-
-using leaf_fields_3_t = _node_fields_013_t<slot_3_t>;
-
-/*
- * block layout of a variable-sized item (right-side)
- *
- * for internal node type 0, 1:
- * previous off (block boundary) -----------------------------+
- * current off --+ |
- * | |
- * V V
- * <==== | sub |fix|sub |fix|oid char|ns char|colli-|
- * (next-item) |...addr|key|addr|key|array & |array &|-sion |(prv-item)...
- * <==== | 1 |1 |0 |0 |len |len |offset|
- * ^ |
- * | |
- * +------------ next collision ----------+
- * see item_iterator_t<node_type_t::INTERNAL>
- *
- * for internal node type 2:
- * previous off (block boundary) ----------------------+
- * current off --+ |
- * | |
- * V V
- * <==== | sub |fix|sub |fix|oid char|ns char|
- * (next-item) |...addr|key|addr|key|array & |array &|(prv-item)...
- * <==== | 1 |1 |0 |0 |len |len |
- * see sub_items_t<node_type_t::INTERNAL>
- *
- * for leaf node type 0, 1:
- * previous off (block boundary) ----------------------------------------+
- * current off --+ |
- * | |
- * V V
- * <==== | fix|o- |fix| off|off|num |oid char|ns char|colli-|
- * (next-item) |...key|node|key|...set|set|sub |array & |array &|-sion |(prv-item)
- * <==== | 1 |0 |0 | 1 |0 |keys|len |len |offset|
- * ^ |
- * | |
- * +------------ next collision ----------------------+
- * see item_iterator_t<node_type_t::LEAF>
- *
- * for leaf node type 2:
- * previous off (block boundary) ---------------------------------+
- * current off --+ |
- * | |
- * V V
- * <==== | fix|o- |fix| off|off|num |oid char|ns char|
- * (next-item) |...key|node|key|...set|set|sub |array & |array &|(prv-item)
- * <==== | 1 |0 |0 | 1 |0 |keys|len |len |
- * see sub_items_t<node_type_t::LEAF>
- */
-
-}
#include "node_stage.h"
#include "crimson/os/seastore/onode_manager/staged-fltree/node_extent_mutable.h"
-#include "node_layout.h"
+#include "node_stage_layout.h"
namespace crimson::os::seastore::onode {
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "node_stage_layout.h"
+
+#include "crimson/os/seastore/onode_manager/staged-fltree/node_extent_mutable.h"
+
+namespace crimson::os::seastore::onode {
+
+void node_header_t::bootstrap_extent(
+ NodeExtentMutable& mut,
+ field_type_t field_type, node_type_t node_type,
+ bool is_level_tail, level_t level) {
+ node_header_t header;
+ header.set_field_type(field_type);
+ header.set_node_type(node_type);
+ header.set_is_level_tail(is_level_tail);
+ header.level = level;
+ mut.copy_in_relative(0, header);
+}
+
+void node_header_t::update_is_level_tail(
+ NodeExtentMutable& mut, const node_header_t& header, bool value) {
+ auto& _header = const_cast<node_header_t&>(header);
+ _header.set_is_level_tail(value);
+ mut.validate_inplace_update(_header);
+}
+
+#define F013_T _node_fields_013_t<SlotType>
+#define F013_INST(ST) _node_fields_013_t<ST>
+#define F013_TEMPLATE(ST) template struct F013_INST(ST)
+F013_TEMPLATE(slot_0_t);
+F013_TEMPLATE(slot_1_t);
+F013_TEMPLATE(slot_3_t);
+
+template <typename SlotType>
+void F013_T::update_size_at(
+ NodeExtentMutable& mut, const me_t& node, size_t index, int change) {
+ assert(index <= node.num_keys);
+ for (const auto* p_slot = &node.slots[index];
+ p_slot < &node.slots[node.num_keys];
+ ++p_slot) {
+ node_offset_t offset = p_slot->right_offset;
+ mut.copy_in_absolute(
+ (void*)&(p_slot->right_offset),
+ node_offset_t(offset - change));
+ }
+}
+
+template <typename SlotType>
+void F013_T::append_key(
+ NodeExtentMutable& mut, const key_t& key, char*& p_append) {
+ mut.copy_in_absolute(p_append, key);
+ p_append += sizeof(key_t);
+}
+
+template <typename SlotType>
+void F013_T::append_offset(
+ NodeExtentMutable& mut, node_offset_t offset_to_right, char*& p_append) {
+ mut.copy_in_absolute(p_append, offset_to_right);
+ p_append += sizeof(node_offset_t);
+}
+
+template <typename SlotType>
+template <KeyT KT>
+void F013_T::insert_at(
+ NodeExtentMutable& mut, const full_key_t<KT>& key,
+ const me_t& node, size_t index, node_offset_t size_right) {
+ assert(index <= node.num_keys);
+ update_size_at(mut, node, index, size_right);
+ auto p_insert = const_cast<char*>(fields_start(node)) +
+ node.get_key_start_offset(index);
+ auto p_shift_end = fields_start(node) + node.get_key_start_offset(node.num_keys);
+ mut.shift_absolute(p_insert, p_shift_end - p_insert, estimate_insert_one());
+ mut.copy_in_absolute((void*)&node.num_keys, num_keys_t(node.num_keys + 1));
+ append_key(mut, key_t::template from_key<KT>(key), p_insert);
+ append_offset(mut, node.get_item_end_offset(index) - size_right, p_insert);
+}
+#define IA_TEMPLATE(ST, KT) template void F013_INST(ST):: \
+ insert_at<KT>(NodeExtentMutable&, const full_key_t<KT>&, \
+ const F013_INST(ST)&, size_t, node_offset_t)
+IA_TEMPLATE(slot_0_t, KeyT::VIEW);
+IA_TEMPLATE(slot_1_t, KeyT::VIEW);
+IA_TEMPLATE(slot_3_t, KeyT::VIEW);
+IA_TEMPLATE(slot_0_t, KeyT::HOBJ);
+IA_TEMPLATE(slot_1_t, KeyT::HOBJ);
+IA_TEMPLATE(slot_3_t, KeyT::HOBJ);
+
+void node_fields_2_t::append_offset(
+ NodeExtentMutable& mut, node_offset_t offset_to_right, char*& p_append) {
+ mut.copy_in_absolute(p_append, offset_to_right);
+ p_append += sizeof(node_offset_t);
+}
+
+}
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include "key_layout.h"
+#include "crimson/os/seastore/onode_manager/staged-fltree/node_types.h"
+
+namespace crimson::os::seastore::onode {
+
+class NodeExtentMutable;
+
+struct node_header_t {
+ static constexpr unsigned FIELD_TYPE_BITS = 6u;
+ static_assert(static_cast<uint8_t>(field_type_t::_MAX) <= 1u << FIELD_TYPE_BITS);
+ static constexpr unsigned NODE_TYPE_BITS = 1u;
+ static constexpr unsigned B_LEVEL_TAIL_BITS = 1u;
+ using bits_t = uint8_t;
+
+ node_header_t() {}
+ std::optional<field_type_t> get_field_type() const {
+ if (field_type >= FIELD_TYPE_MAGIC &&
+ field_type < static_cast<uint8_t>(field_type_t::_MAX)) {
+ return static_cast<field_type_t>(field_type);
+ } else {
+ return std::nullopt;
+ }
+ }
+ node_type_t get_node_type() const {
+ return static_cast<node_type_t>(node_type);
+ }
+ bool get_is_level_tail() const {
+ return is_level_tail;
+ }
+
+ static void bootstrap_extent(
+ NodeExtentMutable&, field_type_t, node_type_t, bool, level_t);
+
+ static void update_is_level_tail(NodeExtentMutable&, const node_header_t&, bool);
+
+ bits_t field_type : FIELD_TYPE_BITS;
+ bits_t node_type : NODE_TYPE_BITS;
+ bits_t is_level_tail : B_LEVEL_TAIL_BITS;
+ static_assert(sizeof(bits_t) * 8 ==
+ FIELD_TYPE_BITS + NODE_TYPE_BITS + B_LEVEL_TAIL_BITS);
+ level_t level;
+
+ private:
+ void set_field_type(field_type_t type) {
+ field_type = static_cast<uint8_t>(type);
+ }
+ void set_node_type(node_type_t type) {
+ node_type = static_cast<uint8_t>(type);
+ }
+ void set_is_level_tail(bool value) {
+ is_level_tail = static_cast<uint8_t>(value);
+ }
+} __attribute__((packed));
+
+template <typename FixedKeyType, field_type_t _FIELD_TYPE>
+struct _slot_t {
+ using key_t = FixedKeyType;
+ static constexpr field_type_t FIELD_TYPE = _FIELD_TYPE;
+
+ key_t key;
+ node_offset_t right_offset;
+} __attribute__((packed));
+using slot_0_t = _slot_t<shard_pool_crush_t, field_type_t::N0>;
+using slot_1_t = _slot_t<crush_t, field_type_t::N1>;
+using slot_3_t = _slot_t<snap_gen_t, field_type_t::N3>;
+
+struct node_range_t {
+ node_offset_t start;
+ node_offset_t end;
+};
+
+template <typename FieldType>
+const char* fields_start(const FieldType& node) {
+ return reinterpret_cast<const char*>(&node);
+}
+
+template <node_type_t NODE_TYPE, typename FieldType>
+node_range_t fields_free_range_before(
+ const FieldType& node, size_t index) {
+ assert(index <= node.num_keys);
+ node_offset_t offset_start = node.get_key_start_offset(index);
+ node_offset_t offset_end =
+ (index == 0 ? FieldType::SIZE
+ : node.get_item_start_offset(index - 1));
+ if constexpr (NODE_TYPE == node_type_t::INTERNAL) {
+ if (node.is_level_tail() && index == node.num_keys) {
+ offset_end -= sizeof(laddr_t);
+ }
+ }
+ assert(offset_start <= offset_end);
+ assert(offset_end - offset_start < FieldType::SIZE);
+ return {offset_start, offset_end};
+}
+
+// internal/leaf node N0, N1; leaf node N3
+template <typename SlotType>
+struct _node_fields_013_t {
+ // TODO: decide by NODE_BLOCK_SIZE, sizeof(SlotType), sizeof(laddr_t)
+ // and the minimal size of variable_key.
+ using num_keys_t = uint8_t;
+ using key_t = typename SlotType::key_t;
+ using key_get_type = const key_t&;
+ using me_t = _node_fields_013_t<SlotType>;
+ static constexpr field_type_t FIELD_TYPE = SlotType::FIELD_TYPE;
+ static constexpr node_offset_t SIZE = NODE_BLOCK_SIZE;
+ static constexpr node_offset_t HEADER_SIZE =
+ sizeof(node_header_t) + sizeof(num_keys_t);
+
+ bool is_level_tail() const { return header.get_is_level_tail(); }
+ size_t total_size() const { return SIZE; }
+ key_get_type get_key(size_t index) const {
+ assert(index < num_keys);
+ return slots[index].key;
+ }
+ node_offset_t get_key_start_offset(size_t index) const {
+ assert(index <= num_keys);
+ auto offset = HEADER_SIZE + sizeof(SlotType) * index;
+ assert(offset < SIZE);
+ return offset;
+ }
+ node_offset_t get_item_start_offset(size_t index) const {
+ assert(index < num_keys);
+ auto offset = slots[index].right_offset;
+ assert(offset <= SIZE);
+ return offset;
+ }
+ const void* p_offset(size_t index) const {
+ assert(index < num_keys);
+ return &slots[index].right_offset;
+ }
+ node_offset_t get_item_end_offset(size_t index) const {
+ return index == 0 ? SIZE : get_item_start_offset(index - 1);
+ }
+ template <node_type_t NODE_TYPE>
+ node_offset_t free_size_before(size_t index) const {
+ auto range = fields_free_range_before<NODE_TYPE>(*this, index);
+ return range.end - range.start;
+ }
+
+#if 0
+ template <node_type_t NODE_TYPE>
+ void fill_unused(NodeExtentMutable& mut) const {
+ auto range = fields_free_range_before<NODE_TYPE>(*this, num_keys);
+ for (auto i = range.start; i < range.end; ++i) {
+ mut.copy_in_relative(i, uint8_t(0xc5));
+ }
+ }
+
+ template <node_type_t NODE_TYPE>
+ void validate_unused() const {
+ auto range = fields_free_range_before<NODE_TYPE>(*this, num_keys);
+ for (auto i = fields_start(*this) + range.start;
+ i < fields_start(*this) + range.end;
+ ++i) {
+ assert(*i == char(0xc5));
+ }
+ }
+#endif
+
+ static node_offset_t estimate_insert_one() { return sizeof(SlotType); }
+ template <KeyT KT>
+ static void insert_at(
+ NodeExtentMutable&, const full_key_t<KT>& key,
+ const me_t& node, size_t index, node_offset_t size_right);
+ static void update_size_at(
+ NodeExtentMutable&, const me_t& node, size_t index, int change);
+ static void append_key(
+ NodeExtentMutable&, const key_t& key, char*& p_append);
+ template <KeyT KT>
+ static void append_key(
+ NodeExtentMutable& mut, const full_key_t<KT>& key, char*& p_append) {
+ append_key(mut, key_t::template from_key<KT>(key), p_append);
+ }
+ static void append_offset(
+ NodeExtentMutable& mut, node_offset_t offset_to_right, char*& p_append);
+
+ node_header_t header;
+ num_keys_t num_keys = 0u;
+ SlotType slots[];
+} __attribute__((packed));
+using node_fields_0_t = _node_fields_013_t<slot_0_t>;
+using node_fields_1_t = _node_fields_013_t<slot_1_t>;
+
+// internal/leaf node N2
+struct node_fields_2_t {
+ // TODO: decide by NODE_BLOCK_SIZE, sizeof(node_off_t), sizeof(laddr_t)
+ // and the minimal size of variable_key.
+ using num_keys_t = uint8_t;
+ using key_t = ns_oid_view_t;
+ using key_get_type = key_t;
+ static constexpr field_type_t FIELD_TYPE = field_type_t::N2;
+ static constexpr node_offset_t SIZE = NODE_BLOCK_SIZE;
+ static constexpr node_offset_t HEADER_SIZE =
+ sizeof(node_header_t) + sizeof(num_keys_t);
+
+ bool is_level_tail() const { return header.get_is_level_tail(); }
+ size_t total_size() const { return SIZE; }
+ key_get_type get_key(size_t index) const {
+ assert(index < num_keys);
+ node_offset_t item_end_offset =
+ (index == 0 ? SIZE : offsets[index - 1]);
+ assert(item_end_offset <= SIZE);
+ const char* p_start = fields_start(*this);
+ return key_t(p_start + item_end_offset);
+ }
+ node_offset_t get_key_start_offset(size_t index) const {
+ assert(index <= num_keys);
+ auto offset = HEADER_SIZE + sizeof(node_offset_t) * num_keys;
+ assert(offset <= SIZE);
+ return offset;
+ }
+ node_offset_t get_item_start_offset(size_t index) const {
+ assert(index < num_keys);
+ auto offset = offsets[index];
+ assert(offset <= SIZE);
+ return offset;
+ }
+ const void* p_offset(size_t index) const {
+ assert(index < num_keys);
+ return &offsets[index];
+ }
+ node_offset_t get_item_end_offset(size_t index) const {
+ return index == 0 ? SIZE : get_item_start_offset(index - 1);
+ }
+ template <node_type_t NODE_TYPE>
+ node_offset_t free_size_before(size_t index) const {
+ auto range = fields_free_range_before<NODE_TYPE>(*this, index);
+ return range.end - range.start;
+ }
+
+#if 0
+ template <node_type_t NODE_TYPE>
+ void fill_unused(NodeExtentMutable& mut) const {
+ auto range = fields_free_range_before<NODE_TYPE>(*this, num_keys);
+ for (auto i = range.start; i < range.end; ++i) {
+ mut.copy_in_relative(i, uint8_t(0xc5));
+ }
+ }
+
+ template <node_type_t NODE_TYPE>
+ void validate_unused() const {
+ auto range = fields_free_range_before<NODE_TYPE>(*this, num_keys);
+ for (auto i = fields_start(*this) + range.start;
+ i < fields_start(*this) + range.end;
+ ++i) {
+ assert(*i == char(0xc5));
+ }
+ }
+#endif
+
+ static node_offset_t estimate_insert_one() { return sizeof(node_offset_t); }
+ template <KeyT KT>
+ static void insert_at(
+ NodeExtentMutable& mut, const full_key_t<KT>& key,
+ const node_fields_2_t& node, size_t index, node_offset_t size_right) {
+ assert(false && "not implemented");
+ }
+ static void update_size_at(
+ NodeExtentMutable& mut, const node_fields_2_t& node, size_t index, int change) {
+ assert(false && "not implemented");
+ }
+ static void append_key(
+ NodeExtentMutable& mut, const key_t& key, char*& p_append) {
+ ns_oid_view_t::append(mut, key, p_append);
+ }
+ template <KeyT KT>
+ static void append_key(
+ NodeExtentMutable& mut, const full_key_t<KT>& key, char*& p_append) {
+ ns_oid_view_t::append<KT>(mut, key, p_append);
+ }
+ static void append_offset(
+ NodeExtentMutable& mut, node_offset_t offset_to_right, char*& p_append);
+
+ node_header_t header;
+ num_keys_t num_keys = 0u;
+ node_offset_t offsets[];
+} __attribute__((packed));
+
+// TODO: decide by NODE_BLOCK_SIZE, sizeof(snap_gen_t), sizeof(laddr_t)
+static constexpr unsigned MAX_NUM_KEYS_I3 = 170u;
+template <unsigned MAX_NUM_KEYS>
+struct _internal_fields_3_t {
+ using key_get_type = const snap_gen_t&;
+ using me_t = _internal_fields_3_t<MAX_NUM_KEYS>;
+ // TODO: decide by NODE_BLOCK_SIZE, sizeof(snap_gen_t), sizeof(laddr_t)
+ using num_keys_t = uint8_t;
+ static constexpr field_type_t FIELD_TYPE = field_type_t::N3;
+ static constexpr node_offset_t SIZE = sizeof(me_t);
+ static constexpr node_offset_t HEADER_SIZE =
+ sizeof(node_header_t) + sizeof(num_keys_t);
+
+ bool is_level_tail() const { return header.get_is_level_tail(); }
+ size_t total_size() const {
+ if (is_level_tail()) {
+ return SIZE - sizeof(snap_gen_t);
+ } else {
+ return SIZE;
+ }
+ }
+ key_get_type get_key(size_t index) const {
+ assert(index < num_keys);
+ return keys[index];
+ }
+ template <node_type_t NODE_TYPE>
+ std::enable_if_t<NODE_TYPE == node_type_t::INTERNAL, node_offset_t>
+ free_size_before(size_t index) const {
+ assert(index <= num_keys);
+ auto allowed_num_keys = is_level_tail() ? MAX_NUM_KEYS - 1 : MAX_NUM_KEYS;
+ assert(num_keys <= allowed_num_keys);
+ auto free = (MAX_NUM_KEYS - index) * (sizeof(snap_gen_t) + sizeof(laddr_t));
+ if (is_level_tail() && index == num_keys) {
+ free -= (sizeof(snap_gen_t) + sizeof(laddr_t));
+ }
+ assert(free < SIZE);
+ return free;
+ }
+
+#if 0
+ template <node_type_t NODE_TYPE>
+ void fill_unused(NodeExtentMutable& mut) const {
+ node_offset_t begin = (const char*)&keys[num_keys] - fields_start(*this);
+ node_offset_t end = (const char*)&child_addrs[0] - fields_start(*this);
+ for (auto i = begin; i < end; ++i) {
+ mut.copy_in_relative(i, uint8_t(0xc5));
+ }
+ begin = (const char*)&child_addrs[num_keys] - fields_start(*this);
+ end = NODE_BLOCK_SIZE;
+ if (is_level_tail()) {
+ begin += sizeof(laddr_t);
+ }
+ for (auto i = begin; i < end; ++i) {
+ mut.copy_in_relative(i, uint8_t(0xc5));
+ }
+ }
+
+ template <node_type_t NODE_TYPE>
+ void validate_unused() const {
+ auto begin = (const char*)&keys[num_keys];
+ auto end = (const char*)&child_addrs[0];
+ for (auto i = begin; i < end; ++i) {
+ assert(*i == uint8_t(0xc5));
+ }
+ begin = (const char*)&child_addrs[num_keys];
+ end = fields_start(*this) + NODE_BLOCK_SIZE;
+ if (is_level_tail()) {
+ begin += sizeof(laddr_t);
+ }
+ for (auto i = begin; i < end; ++i) {
+ assert(*i == char(0xc5));
+ }
+ }
+#endif
+
+ static node_offset_t estimate_insert_one() {
+ return sizeof(snap_gen_t) + sizeof(laddr_t);
+ }
+ template <KeyT KT>
+ static void insert_at(
+ NodeExtentMutable& mut, const full_key_t<KT>& key,
+ const me_t& node, size_t index, node_offset_t size_right) {
+ assert(false && "not implemented");
+ }
+ static void update_size_at(
+ NodeExtentMutable& mut, const me_t& node, size_t index, int change) {
+ assert(false && "not implemented");
+ }
+
+ node_header_t header;
+ num_keys_t num_keys = 0u;
+ snap_gen_t keys[MAX_NUM_KEYS];
+ laddr_t child_addrs[MAX_NUM_KEYS];
+} __attribute__((packed));
+static_assert(_internal_fields_3_t<MAX_NUM_KEYS_I3>::SIZE <= NODE_BLOCK_SIZE &&
+ _internal_fields_3_t<MAX_NUM_KEYS_I3 + 1>::SIZE > NODE_BLOCK_SIZE);
+using internal_fields_3_t = _internal_fields_3_t<MAX_NUM_KEYS_I3>;
+
+using leaf_fields_3_t = _node_fields_013_t<slot_3_t>;
+
+/*
+ * block layout of a variable-sized item (right-side)
+ *
+ * for internal node type 0, 1:
+ * previous off (block boundary) -----------------------------+
+ * current off --+ |
+ * | |
+ * V V
+ * <==== | sub |fix|sub |fix|oid char|ns char|colli-|
+ * (next-item) |...addr|key|addr|key|array & |array &|-sion |(prv-item)...
+ * <==== | 1 |1 |0 |0 |len |len |offset|
+ * ^ |
+ * | |
+ * +------------ next collision ----------+
+ * see item_iterator_t<node_type_t::INTERNAL>
+ *
+ * for internal node type 2:
+ * previous off (block boundary) ----------------------+
+ * current off --+ |
+ * | |
+ * V V
+ * <==== | sub |fix|sub |fix|oid char|ns char|
+ * (next-item) |...addr|key|addr|key|array & |array &|(prv-item)...
+ * <==== | 1 |1 |0 |0 |len |len |
+ * see sub_items_t<node_type_t::INTERNAL>
+ *
+ * for leaf node type 0, 1:
+ * previous off (block boundary) ----------------------------------------+
+ * current off --+ |
+ * | |
+ * V V
+ * <==== | fix|o- |fix| off|off|num |oid char|ns char|colli-|
+ * (next-item) |...key|node|key|...set|set|sub |array & |array &|-sion |(prv-item)
+ * <==== | 1 |0 |0 | 1 |0 |keys|len |len |offset|
+ * ^ |
+ * | |
+ * +------------ next collision ----------------------+
+ * see item_iterator_t<node_type_t::LEAF>
+ *
+ * for leaf node type 2:
+ * previous off (block boundary) ---------------------------------+
+ * current off --+ |
+ * | |
+ * V V
+ * <==== | fix|o- |fix| off|off|num |oid char|ns char|
+ * (next-item) |...key|node|key|...set|set|sub |array & |array &|(prv-item)
+ * <==== | 1 |0 |0 | 1 |0 |keys|len |len |
+ * see sub_items_t<node_type_t::LEAF>
+ */
+
+}
return {rbegin, MatchKindBS::NE};
}
-using match_stat_t = int8_t;
-constexpr match_stat_t MSTAT_END = -2; // index is search_position_t::end()
-constexpr match_stat_t MSTAT_EQ = -1; // key == index
-constexpr match_stat_t MSTAT_NE0 = 0; // key == index [pool/shard crush ns/oid]; key < index [snap/gen]
-constexpr match_stat_t MSTAT_NE1 = 1; // key == index [pool/shard crush]; key < index [ns/oid]
-constexpr match_stat_t MSTAT_NE2 = 2; // key < index [pool/shard crush ns/oid] ||
- // key == index [pool/shard]; key < index [crush]
-constexpr match_stat_t MSTAT_NE3 = 3; // key < index [pool/shard]
-constexpr match_stat_t MSTAT_MIN = MSTAT_END;
-constexpr match_stat_t MSTAT_MAX = MSTAT_NE3;
-
inline bool matchable(field_type_t type, match_stat_t mstat) {
assert(mstat >= MSTAT_MIN && mstat <= MSTAT_MAX);
/*
}
}
-template <node_type_t NODE_TYPE, match_stage_t STAGE>
-struct staged_result_t {
- using me_t = staged_result_t<NODE_TYPE, STAGE>;
- bool is_end() const { return position.is_end(); }
- MatchKindBS match() const {
- assert(mstat >= MSTAT_MIN && mstat <= MSTAT_MAX);
- return (mstat == MSTAT_EQ ? MatchKindBS::EQ : MatchKindBS::NE);
- }
-
- static me_t end() {
- return {staged_position_t<STAGE>::end(), nullptr, MSTAT_END};
- }
- template <typename T = me_t>
- static std::enable_if_t<STAGE != STAGE_BOTTOM, T> from_nxt(
- size_t index, const staged_result_t<NODE_TYPE, STAGE - 1>& nxt_stage_result) {
- return {{index, nxt_stage_result.position},
- nxt_stage_result.p_value,
- nxt_stage_result.mstat};
- }
-
- staged_position_t<STAGE> position;
- const value_type_t<NODE_TYPE>* p_value;
- match_stat_t mstat;
-};
-
-template <node_type_t NODE_TYPE>
-staged_result_t<NODE_TYPE, STAGE_TOP>&& normalize(
- staged_result_t<NODE_TYPE, STAGE_TOP>&& result) { return std::move(result); }
-
-template <node_type_t NODE_TYPE, match_stage_t STAGE,
- typename = std::enable_if_t<STAGE != STAGE_TOP>>
-staged_result_t<NODE_TYPE, STAGE_TOP> normalize(
- staged_result_t<NODE_TYPE, STAGE>&& result) {
- // FIXME: assert result.mstat correct
- return {normalize(std::move(result.position)), result.p_value, result.mstat};
-}
-
-/*
- * staged infrastructure
- */
-
-template <node_type_t _NODE_TYPE>
-struct staged_params_subitems {
- using container_t = sub_items_t<_NODE_TYPE>;
- static constexpr auto NODE_TYPE = _NODE_TYPE;
- static constexpr auto STAGE = STAGE_RIGHT;
-
- // dummy type in order to make our type system work
- // any better solution to get rid of this?
- using next_param_t = staged_params_subitems<NODE_TYPE>;
-};
-
-template <node_type_t _NODE_TYPE>
-struct staged_params_item_iterator {
- using container_t = item_iterator_t<_NODE_TYPE>;
- static constexpr auto NODE_TYPE = _NODE_TYPE;
- static constexpr auto STAGE = STAGE_STRING;
-
- using next_param_t = staged_params_subitems<NODE_TYPE>;
-};
-
-template <typename NodeType>
-struct staged_params_node_01 {
- using container_t = NodeType;
- static constexpr auto NODE_TYPE = NodeType::NODE_TYPE;
- static constexpr auto STAGE = STAGE_LEFT;
-
- using next_param_t = staged_params_item_iterator<NODE_TYPE>;
-};
-
-template <typename NodeType>
-struct staged_params_node_2 {
- using container_t = NodeType;
- static constexpr auto NODE_TYPE = NodeType::NODE_TYPE;
- static constexpr auto STAGE = STAGE_STRING;
-
- using next_param_t = staged_params_subitems<NODE_TYPE>;
-};
-
-template <typename NodeType>
-struct staged_params_node_3 {
- using container_t = NodeType;
- static constexpr auto NODE_TYPE = NodeType::NODE_TYPE;
- static constexpr auto STAGE = STAGE_RIGHT;
-
- // dummy type in order to make our type system work
- // any better solution to get rid of this?
- using next_param_t = staged_params_node_3<NodeType>;
-};
-
#define NXT_STAGE_T staged<next_param_t>
enum class TrimType { BEFORE, AFTER, AT };
assert(false);
}
- static staged_result_t<NODE_TYPE, STAGE_TOP> lower_bound_normalized(
+ static lookup_result_t<NODE_TYPE> lower_bound_normalized(
const container_t& container,
const full_key_t<KeyT::HOBJ>& key,
MatchHistory& history) {
}
};
+/*
+ * staged infrastructure
+ */
+
+template <node_type_t _NODE_TYPE>
+struct staged_params_subitems {
+ using container_t = sub_items_t<_NODE_TYPE>;
+ static constexpr auto NODE_TYPE = _NODE_TYPE;
+ static constexpr auto STAGE = STAGE_RIGHT;
+
+ // dummy type in order to make our type system work
+ // any better solution to get rid of this?
+ using next_param_t = staged_params_subitems<NODE_TYPE>;
+};
+
+template <node_type_t _NODE_TYPE>
+struct staged_params_item_iterator {
+ using container_t = item_iterator_t<_NODE_TYPE>;
+ static constexpr auto NODE_TYPE = _NODE_TYPE;
+ static constexpr auto STAGE = STAGE_STRING;
+
+ using next_param_t = staged_params_subitems<NODE_TYPE>;
+};
+
+template <typename NodeType>
+struct staged_params_node_01 {
+ using container_t = NodeType;
+ static constexpr auto NODE_TYPE = NodeType::NODE_TYPE;
+ static constexpr auto STAGE = STAGE_LEFT;
+
+ using next_param_t = staged_params_item_iterator<NODE_TYPE>;
+};
+
+template <typename NodeType>
+struct staged_params_node_2 {
+ using container_t = NodeType;
+ static constexpr auto NODE_TYPE = NodeType::NODE_TYPE;
+ static constexpr auto STAGE = STAGE_STRING;
+
+ using next_param_t = staged_params_subitems<NODE_TYPE>;
+};
+
+template <typename NodeType>
+struct staged_params_node_3 {
+ using container_t = NodeType;
+ static constexpr auto NODE_TYPE = NodeType::NODE_TYPE;
+ static constexpr auto STAGE = STAGE_RIGHT;
+
+ // dummy type in order to make our type system work
+ // any better solution to get rid of this?
+ using next_param_t = staged_params_node_3<NodeType>;
+};
+
template <typename NodeType, typename Enable = void> struct _node_to_stage_t;
template <typename NodeType>
struct _node_to_stage_t<NodeType,
#include <ostream>
#include "crimson/os/seastore/onode_manager/staged-fltree/fwd.h"
+#include "crimson/os/seastore/onode_manager/staged-fltree/node_types.h"
namespace crimson::os::seastore::onode {
template <node_type_t NODE_TYPE>
using value_type_t = typename value_type<NODE_TYPE>::type;
+using match_stat_t = int8_t;
+constexpr match_stat_t MSTAT_END = -2; // index is search_position_t::end()
+constexpr match_stat_t MSTAT_EQ = -1; // key == index
+constexpr match_stat_t MSTAT_NE0 = 0; // key == index [pool/shard crush ns/oid]; key < index [snap/gen]
+constexpr match_stat_t MSTAT_NE1 = 1; // key == index [pool/shard crush]; key < index [ns/oid]
+constexpr match_stat_t MSTAT_NE2 = 2; // key < index [pool/shard crush ns/oid] ||
+ // key == index [pool/shard]; key < index [crush]
+constexpr match_stat_t MSTAT_NE3 = 3; // key < index [pool/shard]
+constexpr match_stat_t MSTAT_MIN = MSTAT_END;
+constexpr match_stat_t MSTAT_MAX = MSTAT_NE3;
+
+template <node_type_t NODE_TYPE, match_stage_t STAGE>
+struct staged_result_t {
+ using me_t = staged_result_t<NODE_TYPE, STAGE>;
+ bool is_end() const { return position.is_end(); }
+ MatchKindBS match() const {
+ assert(mstat >= MSTAT_MIN && mstat <= MSTAT_MAX);
+ return (mstat == MSTAT_EQ ? MatchKindBS::EQ : MatchKindBS::NE);
+ }
+
+ static me_t end() {
+ return {staged_position_t<STAGE>::end(), nullptr, MSTAT_END};
+ }
+ template <typename T = me_t>
+ static std::enable_if_t<STAGE != STAGE_BOTTOM, T> from_nxt(
+ size_t index, const staged_result_t<NODE_TYPE, STAGE - 1>& nxt_stage_result) {
+ return {{index, nxt_stage_result.position},
+ nxt_stage_result.p_value,
+ nxt_stage_result.mstat};
+ }
+
+ staged_position_t<STAGE> position;
+ const value_type_t<NODE_TYPE>* p_value;
+ match_stat_t mstat;
+};
+
+template <node_type_t NODE_TYPE>
+using lookup_result_t = staged_result_t<NODE_TYPE, STAGE_TOP>;
+
+template <node_type_t NODE_TYPE>
+lookup_result_t<NODE_TYPE>&& normalize(
+ lookup_result_t<NODE_TYPE>&& result) { return std::move(result); }
+
+template <node_type_t NODE_TYPE, match_stage_t STAGE,
+ typename = std::enable_if_t<STAGE != STAGE_TOP>>
+lookup_result_t<NODE_TYPE> normalize(
+ staged_result_t<NODE_TYPE, STAGE>&& result) {
+ // FIXME: assert result.mstat correct
+ return {normalize(std::move(result.position)), result.p_value, result.mstat};
+}
+
}
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*-
// vim: ts=8 sw=2 smarttab
+#include <array>
#include <cstring>
#include <memory>
#include <random>
#include <vector>
#include "crimson/common/log.h"
+#include "crimson/os/seastore/onode_manager/staged-fltree/node.h"
#include "crimson/os/seastore/onode_manager/staged-fltree/node_extent_manager.h"
-#include "crimson/os/seastore/onode_manager/staged-fltree/node_impl.h"
-#include "crimson/os/seastore/onode_manager/staged-fltree/stages/node_stage.h"
-#include "crimson/os/seastore/onode_manager/staged-fltree/stages/stage.h"
+#include "crimson/os/seastore/onode_manager/staged-fltree/node_layout.h"
#include "crimson/os/seastore/onode_manager/staged-fltree/tree.h"
#include "test/crimson/gtest_seastar.h"
auto nm = NodeExtentManager::create_dummy();
auto t = make_transaction();
context_t c{*nm, *t};
- std::vector<std::pair<Ref<Node>, NodeExtentMutable>> nodes = {
- InternalNode0::allocate(c, 1u, false).unsafe_get0().make_pair(),
- InternalNode1::allocate(c, 1u, false).unsafe_get0().make_pair(),
- InternalNode2::allocate(c, 1u, false).unsafe_get0().make_pair(),
- InternalNode3::allocate(c, 1u, false).unsafe_get0().make_pair(),
- InternalNode0::allocate(c, 1u, true).unsafe_get0().make_pair(),
- InternalNode1::allocate(c, 1u, true).unsafe_get0().make_pair(),
- InternalNode2::allocate(c, 1u, true).unsafe_get0().make_pair(),
- InternalNode3::allocate(c, 1u, true).unsafe_get0().make_pair(),
- LeafNode0::allocate(c, false).unsafe_get0().make_pair(),
- LeafNode1::allocate(c, false).unsafe_get0().make_pair(),
- LeafNode2::allocate(c, false).unsafe_get0().make_pair(),
- LeafNode3::allocate(c, false).unsafe_get0().make_pair(),
- LeafNode0::allocate(c, true).unsafe_get0().make_pair(),
- LeafNode1::allocate(c, true).unsafe_get0().make_pair(),
- LeafNode2::allocate(c, true).unsafe_get0().make_pair(),
- LeafNode3::allocate(c, true).unsafe_get0().make_pair()
+ std::array<std::pair<NodeImplURef, NodeExtentMutable>, 16> nodes = {
+ InternalNode0::allocate(c, false, 1u).unsafe_get0().make_pair(),
+ InternalNode1::allocate(c, false, 1u).unsafe_get0().make_pair(),
+ InternalNode2::allocate(c, false, 1u).unsafe_get0().make_pair(),
+ InternalNode3::allocate(c, false, 1u).unsafe_get0().make_pair(),
+ InternalNode0::allocate(c, true, 1u).unsafe_get0().make_pair(),
+ InternalNode1::allocate(c, true, 1u).unsafe_get0().make_pair(),
+ InternalNode2::allocate(c, true, 1u).unsafe_get0().make_pair(),
+ InternalNode3::allocate(c, true, 1u).unsafe_get0().make_pair(),
+ LeafNode0::allocate(c, false, 0u).unsafe_get0().make_pair(),
+ LeafNode1::allocate(c, false, 0u).unsafe_get0().make_pair(),
+ LeafNode2::allocate(c, false, 0u).unsafe_get0().make_pair(),
+ LeafNode3::allocate(c, false, 0u).unsafe_get0().make_pair(),
+ LeafNode0::allocate(c, true, 0u).unsafe_get0().make_pair(),
+ LeafNode1::allocate(c, true, 0u).unsafe_get0().make_pair(),
+ LeafNode2::allocate(c, true, 0u).unsafe_get0().make_pair(),
+ LeafNode3::allocate(c, true, 0u).unsafe_get0().make_pair()
};
std::ostringstream oss;
oss << "\nallocated nodes:";
- auto node_tracker = RootNodeTracker::create(c.nm.is_read_isolated());
- assert(node_tracker->is_clean());
for (auto iter = nodes.begin(); iter != nodes.end(); ++iter) {
+ oss << "\n ";
auto& ref_node = iter->first;
- auto& mut = iter->second;
- oss << "\n " << *ref_node;
- ref_node->test_make_destructable(
- c, mut, c.nm.get_super(c.t, *node_tracker).unsafe_get0());
- assert(!node_tracker->is_clean());
- iter->first.reset();
- assert(node_tracker->is_clean());
+ ref_node->dump_brief(oss);
}
logger().info("{}", oss.str());
});
namespace crimson::os::seastore::onode {
class DummyChildPool {
- class DummyChild final : public Node {
+ class DummyChildImpl final : public NodeImpl {
public:
- virtual ~DummyChild() {
+ using URef = std::unique_ptr<DummyChildImpl>;
+ DummyChildImpl(const std::set<onode_key_t>& keys, bool is_level_tail, laddr_t laddr)
+ : keys{keys}, _is_level_tail{is_level_tail}, _laddr{laddr} {
+ std::tie(key_view, p_mem_key_view) = build_key_view(*keys.crbegin());
+ }
+ ~DummyChildImpl() override {
+ std::free(p_mem_key_view);
+ }
+
+ const std::set<onode_key_t>& get_keys() const { return keys; }
+
+ void reset(const std::set<onode_key_t>& _keys, bool level_tail) {
+ keys = _keys;
+ _is_level_tail = level_tail;
std::free(p_mem_key_view);
+ std::tie(key_view, p_mem_key_view) = build_key_view(*keys.crbegin());
}
+ public:
+ laddr_t laddr() const override { return _laddr; }
+ bool is_level_tail() const override { return _is_level_tail; }
+
+ protected:
+ field_type_t field_type() const override { return field_type_t::N0; }
+ level_t level() const override { return 0u; }
+ key_view_t get_largest_key_view() const override { return key_view; }
+ void prepare_mutate(context_t) override {
+ assert(false && "impossible path"); }
+ bool is_empty() const override {
+ assert(false && "impossible path"); }
+ node_offset_t free_size() const override {
+ assert(false && "impossible path"); }
+ key_view_t get_key_view(const search_position_t&) const override {
+ assert(false && "impossible path"); }
+ std::ostream& dump(std::ostream&) const override {
+ assert(false && "impossible path"); }
+ std::ostream& dump_brief(std::ostream&) const override {
+ assert(false && "impossible path"); }
+ void test_copy_to(NodeExtentMutable&) const override {
+ assert(false && "impossible path"); }
+ void test_set_tail(NodeExtentMutable&) override {
+ assert(false && "impossible path"); }
+
+ private:
+ std::set<onode_key_t> keys;
+ bool _is_level_tail;
+ laddr_t _laddr;
+
+ key_view_t key_view;
+ void* p_mem_key_view;
+ };
+
+ class DummyChild final : public Node {
+ public:
+ ~DummyChild() override = default;
+
node_future<> populate_split(
context_t c, std::set<Ref<DummyChild>>& splitable_nodes) {
assert(can_split());
assert(splitable_nodes.find(this) != splitable_nodes.end());
size_t index;
+ const auto& keys = impl->get_keys();
if (keys.size() == 2) {
index = 1;
} else {
std::set<onode_key_t> left_keys(keys.begin(), iter);
std::set<onode_key_t> right_keys(iter, keys.end());
- bool right_is_tail = _is_level_tail;
- reset(left_keys, false);
- auto right_child = DummyChild::create(right_keys, right_is_tail, pool);
+ bool right_is_tail = impl->is_level_tail();
+ impl->reset(left_keys, false);
+ auto right_child = DummyChild::create_new(right_keys, right_is_tail, pool);
if (!can_split()) {
splitable_nodes.erase(this);
}
if (right_child->can_split()) {
splitable_nodes.insert(right_child);
}
- return this->insert_parent(c, right_child);
+ return insert_parent(c, right_child);
}
node_future<> insert_and_split(
context_t c, const onode_key_t& insert_key,
std::set<Ref<DummyChild>>& splitable_nodes) {
+ const auto& keys = impl->get_keys();
assert(keys.size() == 1);
auto& key = *keys.begin();
assert(insert_key < key);
std::set<onode_key_t> new_keys;
new_keys.insert(insert_key);
new_keys.insert(key);
- reset(new_keys, _is_level_tail);
+ impl->reset(new_keys, impl->is_level_tail());
splitable_nodes.clear();
splitable_nodes.insert(this);
}
static Ref<DummyChild> create(
+ const std::set<onode_key_t>& keys, bool is_level_tail,
+ laddr_t addr, DummyChildPool& pool) {
+ auto ref_impl = std::make_unique<DummyChildImpl>(keys, is_level_tail, addr);
+ return new DummyChild(ref_impl.get(), std::move(ref_impl), pool);
+ }
+
+ static Ref<DummyChild> create_new(
const std::set<onode_key_t>& keys, bool is_level_tail, DummyChildPool& pool) {
static laddr_t seed = 0;
- return new DummyChild(keys, is_level_tail, seed++, pool);
+ return create(keys, is_level_tail, seed++, pool);
}
static node_future<Ref<DummyChild>> create_initial(
context_t c, const std::set<onode_key_t>& keys,
DummyChildPool& pool, RootNodeTracker& root_tracker) {
- auto initial = create(keys, true, pool);
+ auto initial = create_new(keys, true, pool);
return c.nm.get_super(c.t, root_tracker
).safe_then([c, &pool, initial](auto super) {
initial->make_root_new(c, std::move(super));
});
}
- static Ref<DummyChild> create_clone(
- const std::set<onode_key_t>& keys, bool is_level_tail,
- laddr_t addr, DummyChildPool& pool) {
- return new DummyChild(keys, is_level_tail, addr, pool);
- }
-
protected:
- bool is_level_tail() const override { return _is_level_tail; }
- field_type_t field_type() const override { return field_type_t::N0; }
- laddr_t laddr() const override { return _laddr; }
- level_t level() const override { return 0u; }
- key_view_t get_key_view(const search_position_t&) const override {
- assert(false && "impossible path"); }
- key_view_t get_largest_key_view() const override { return key_view; }
- std::ostream& dump(std::ostream&) const override {
- assert(false && "impossible path"); }
- std::ostream& dump_brief(std::ostream&) const override {
- assert(false && "impossible path"); }
- node_future<search_result_t> do_lower_bound(
- context_t, const key_hobj_t&, MatchHistory&) override {
- assert(false && "impossible path"); }
- node_future<Ref<tree_cursor_t>> lookup_smallest(context_t) override {
- assert(false && "impossible path"); }
- node_future<Ref<tree_cursor_t>> lookup_largest(context_t) override {
- assert(false && "impossible path"); }
- void test_make_destructable(
- context_t, NodeExtentMutable&, Super::URef&&) override {
- assert(false && "impossible path"); }
node_future<> test_clone_non_root(
context_t, Ref<InternalNode> new_parent) const override {
assert(!is_root());
auto p_pool_clone = pool.pool_clone_in_progress;
assert(p_pool_clone);
- auto clone = create_clone(keys, _is_level_tail, _laddr, *p_pool_clone);
+ auto clone = create(
+ impl->get_keys(), impl->is_level_tail(), impl->laddr(), *p_pool_clone);
clone->as_child(parent_info().position, new_parent);
- clone->_laddr = _laddr;
return node_ertr::now();
}
+ node_future<Ref<tree_cursor_t>> lookup_smallest(context_t) override {
+ assert(false && "impossible path"); }
+ node_future<Ref<tree_cursor_t>> lookup_largest(context_t) override {
+ assert(false && "impossible path"); }
+ node_future<> test_clone_root(context_t, RootNodeTracker&) const override {
+ assert(false && "impossible path"); }
+ node_future<search_result_t> lower_bound_tracked(
+ context_t, const key_hobj_t&, MatchHistory&) override {
+ assert(false && "impossible path"); }
private:
- DummyChild(const std::set<onode_key_t>& keys,
- bool is_level_tail, laddr_t laddr, DummyChildPool& pool)
- : keys{keys}, _is_level_tail{is_level_tail}, _laddr{laddr}, pool{pool} {
- std::tie(key_view, p_mem_key_view) = build_key_view(*keys.crbegin());
+ DummyChild(DummyChildImpl* impl, DummyChildImpl::URef&& ref, DummyChildPool& pool)
+ : Node(std::move(ref)), impl{impl}, pool{pool} {
pool.track_node(this);
}
- bool can_split() const { return keys.size() > 1; }
+ bool can_split() const { return impl->get_keys().size() > 1; }
- void reset(const std::set<onode_key_t>& _keys, bool level_tail) {
- keys = _keys;
- _is_level_tail = level_tail;
- std::free(p_mem_key_view);
- std::tie(key_view, p_mem_key_view) = build_key_view(*keys.crbegin());
- }
-
- mutable std::random_device rd;
- std::set<onode_key_t> keys;
- bool _is_level_tail;
- laddr_t _laddr;
+ DummyChildImpl* impl;
DummyChildPool& pool;
-
- key_view_t key_view;
- void* p_mem_key_view;
+ mutable std::random_device rd;
};
public: