diff --git a/persistent-data/data-structures/btree_damage_visitor.h b/persistent-data/data-structures/btree_damage_visitor.h index 8656328..46e9d95 100644 --- a/persistent-data/data-structures/btree_damage_visitor.h +++ b/persistent-data/data-structures/btree_damage_visitor.h @@ -82,6 +82,29 @@ namespace persistent_data { block_address damage_begin_; }; + // As we walk a btree we need to know if we've moved into a + // different sub tree (by looking at the btree_path). + class path_tracker { + public: + // returns the old path if the tree has changed. + boost::optional next_path(btree_path const &p) { + if (p != path_) { + btree_path tmp(path_); + path_ = p; + return boost::optional(tmp); + } + + return boost::optional(); + } + + btree_path const ¤t_path() const { + return path_; + } + + private: + btree_path path_; + }; + //---------------------------------------------------------------- // This class implements consistency checking for the btrees. It @@ -125,16 +148,23 @@ namespace persistent_data { bool visit_internal(node_location const &loc, btree_detail::node_ref const &n) { + update_path(loc.path); + return check_internal(loc, n); } bool visit_internal_leaf(node_location const &loc, btree_detail::node_ref const &n) { + update_path(loc.path); + return check_leaf(loc, n); } bool visit_leaf(node_location const &loc, btree_detail::node_ref const &n) { + update_path(loc.path); + + bool r = check_leaf(loc, n); // If anything goes wrong with the checks, we skip @@ -359,28 +389,27 @@ namespace persistent_data { void good_internal(block_address b) { maybe_range64 mr = dt_.good_internal(b); if (mr) - issue_damage(*mr); + issue_damage(path_tracker_.current_path(), *mr); } void good_leaf(block_address b, block_address e) { maybe_range64 mr = dt_.good_leaf(b, e); if (mr) - issue_damage(*mr); + issue_damage(path_tracker_.current_path(), *mr); } + // FIXME: duplicate code void end_walk() { maybe_range64 mr = dt_.end(); if (mr) - issue_damage(*mr); + issue_damage(path_tracker_.current_path(), *mr); } - void issue_damage(range64 const &r) { - // FIXME: we don't really know what level - // the damage is coming from + void issue_damage(btree_path const &path, range64 const &r) { damage d(r, build_damage_desc()); clear_damage_desc(); - damage_visitor_.visit(btree_path(), d); + damage_visitor_.visit(path, d); } std::string build_damage_desc() const { @@ -399,6 +428,22 @@ namespace persistent_data { //-------------------------------- + void update_path(btree_path const &path) { + boost::optional old_path = path_tracker_.next_path(path); + if (old_path) { + // we need to emit any errors that + // were accrued against the old + // path. + + // FIXME: duplicate code with end_walk() + maybe_range64 mr = dt_.end(); + if (mr) + issue_damage(*old_path, *mr); + } + } + + //-------------------------------- + block_counter &counter_; bool avoid_repeated_visits_; @@ -408,6 +453,7 @@ namespace persistent_data { std::set seen_; boost::optional last_leaf_key_[Levels]; + path_tracker path_tracker_; damage_tracker dt_; std::list damage_reasons_; }; diff --git a/unit-tests/btree_damage_visitor_t.cc b/unit-tests/btree_damage_visitor_t.cc index 0f418d4..48076c0 100644 --- a/unit-tests/btree_damage_visitor_t.cc +++ b/unit-tests/btree_damage_visitor_t.cc @@ -420,6 +420,26 @@ namespace { } } + void expect_values_except(unsigned nr_sub_trees, unsigned nr_values, + btree_path const &path, range keys) { + for (unsigned i = 0; i < nr_sub_trees; i++) + expect_sub_tree_values_except(i, nr_values, path, keys); + } + + void expect_sub_tree_values_except(unsigned sub_tree, unsigned nr_values, + btree_path const &path, range keys) { + for (unsigned i = 0; i < nr_values; i++) { + uint64_t key[2] = {sub_tree, i}; + + if (sub_tree == path[0] && keys.contains(i)) + continue; + + btree_path p2; + p2.push_back(sub_tree); + EXPECT_CALL(value_visitor_, visit(Eq(p2), Eq(key_to_value(key)))); + } + } + void expect_damage(btree_path path, range keys) { EXPECT_CALL(damage_visitor_, visit(Eq(path), DamagedKeys(keys))).Times(1); } @@ -599,4 +619,23 @@ TEST_F(BTreeDamageVisitor2Tests, populated_tree_with_no_damage) run(); } +TEST_F(BTreeDamageVisitor2Tests, damaged_leaf) +{ + insert_values(10, 1000); + tree_complete(); + + auto leaf1 = [] (node_info const &n) { + return (n.leaf && n.path.size() == 1 && n.path[0] == 1); + }; + + node_info n = layout_->random_node(leaf1); + cerr << "node: " << n << endl; + trash_block(n.b); + + expect_damage(n.path, n.keys); + expect_values_except(10, 1000, n.path, n.keys); + + run(); +} + //----------------------------------------------------------------