[btree_node_checker] first draft
Spin-off from btree_damage_visitor
This commit is contained in:
parent
5b3cae824b
commit
4a4dc1a5e0
@ -67,6 +67,7 @@ SOURCE=\
|
||||
persistent-data/data-structures/bitset.cc \
|
||||
persistent-data/data-structures/bloom_filter.cc \
|
||||
persistent-data/data-structures/btree.cc \
|
||||
persistent-data/data-structures/btree_node_checker.cc \
|
||||
persistent-data/error_set.cc \
|
||||
persistent-data/file_utils.cc \
|
||||
persistent-data/hex_dump.cc \
|
||||
|
127
persistent-data/data-structures/btree_node_checker.cc
Normal file
127
persistent-data/data-structures/btree_node_checker.cc
Normal file
@ -0,0 +1,127 @@
|
||||
#include "btree_node_checker.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using persistent_data::btree_detail::btree_node_checker;
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
btree_node_checker::error_type btree_node_checker::get_last_error() {
|
||||
return last_error_;
|
||||
}
|
||||
|
||||
std::string btree_node_checker::get_last_error_string() {
|
||||
switch (last_error_) {
|
||||
case BLOCK_NR_MISMATCH:
|
||||
return block_nr_mismatch_string();
|
||||
case VALUE_SIZES_MISMATCH:
|
||||
return value_sizes_mismatch_string();
|
||||
case MAX_ENTRIES_TOO_LARGE:
|
||||
return max_entries_too_large_string();
|
||||
case MAX_ENTRIES_NOT_DIVISIBLE:
|
||||
return max_entries_not_divisible_string();
|
||||
case NR_ENTRIES_TOO_LARGE:
|
||||
return nr_entries_too_large_string();
|
||||
case NR_ENTRIES_TOO_SMALL:
|
||||
return nr_entries_too_small_string();
|
||||
case KEYS_OUT_OF_ORDER:
|
||||
return keys_out_of_order_string();
|
||||
case PARENT_KEY_MISMATCH:
|
||||
return parent_key_mismatch_string();
|
||||
case LEAF_KEY_OVERLAPPED:
|
||||
return leaf_key_overlapped_string();
|
||||
default:
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
void btree_node_checker::reset() {
|
||||
last_error_ = NO_ERROR;
|
||||
}
|
||||
|
||||
std::string btree_node_checker::block_nr_mismatch_string() {
|
||||
std::ostringstream out;
|
||||
out << "block number mismatch: actually "
|
||||
<< error_location_
|
||||
<< ", claims " << error_block_nr_;
|
||||
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string btree_node_checker::value_sizes_mismatch_string() {
|
||||
std::ostringstream out;
|
||||
out << "value size mismatch: expected " << error_value_sizes_[1]
|
||||
<< ", but got " << error_value_sizes_[0]
|
||||
<< ". This is not the btree you are looking for."
|
||||
<< " (block " << error_location_ << ")";
|
||||
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string btree_node_checker::max_entries_too_large_string() {
|
||||
std::ostringstream out;
|
||||
out << "max entries too large: " << error_max_entries_
|
||||
<< " (block " << error_location_ << ")";
|
||||
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string btree_node_checker::max_entries_not_divisible_string() {
|
||||
std::ostringstream out;
|
||||
out << "max entries is not divisible by 3: " << error_max_entries_
|
||||
<< " (block " << error_location_ << ")";
|
||||
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string btree_node_checker::nr_entries_too_large_string() {
|
||||
std::ostringstream out;
|
||||
out << "bad nr_entries: "
|
||||
<< error_nr_entries_ << " < "
|
||||
<< error_max_entries_
|
||||
<< " (block " << error_location_ << ")";
|
||||
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string btree_node_checker::nr_entries_too_small_string() {
|
||||
std::ostringstream out;
|
||||
out << "too few entries in btree_node: "
|
||||
<< error_nr_entries_
|
||||
<< ", expected at least "
|
||||
<< (error_max_entries_ / 3)
|
||||
<< " (block " << error_location_
|
||||
<< ", max_entries = " << error_max_entries_ << ")";
|
||||
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string btree_node_checker::keys_out_of_order_string() {
|
||||
std::ostringstream out;
|
||||
out << "keys are out of order, "
|
||||
<< error_keys_[0] << " <= " << error_keys_[1]
|
||||
<< " (block " << error_location_ << ")";
|
||||
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string btree_node_checker::parent_key_mismatch_string() {
|
||||
std::ostringstream out;
|
||||
out << "parent key mismatch: parent was " << error_keys_[1]
|
||||
<< ", but lowest in node was " << error_keys_[0]
|
||||
<< " (block " << error_location_ << ")";
|
||||
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string btree_node_checker::leaf_key_overlapped_string() {
|
||||
std::ostringstream out;
|
||||
out << "the last key of the previous leaf was " << error_keys_[1]
|
||||
<< " and the first key of this leaf is " << error_keys_[0]
|
||||
<< " (block " << error_location_ << ")";
|
||||
|
||||
return out.str();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
206
persistent-data/data-structures/btree_node_checker.h
Normal file
206
persistent-data/data-structures/btree_node_checker.h
Normal file
@ -0,0 +1,206 @@
|
||||
#ifndef BTREE_NODE_CHECKER_H
|
||||
#define BTREE_NODE_CHECKER_H
|
||||
|
||||
#include "block-cache/block_cache.h"
|
||||
#include "persistent-data/block.h"
|
||||
#include "persistent-data/data-structures/btree.h"
|
||||
#include "persistent-data/data-structures/btree_disk_structures.h"
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <string>
|
||||
|
||||
using bcache::block_address;
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
namespace persistent_data {
|
||||
namespace btree_detail {
|
||||
class btree_node_checker {
|
||||
public:
|
||||
enum error_type {
|
||||
NO_ERROR,
|
||||
BLOCK_NR_MISMATCH,
|
||||
VALUE_SIZES_MISMATCH,
|
||||
MAX_ENTRIES_TOO_LARGE,
|
||||
MAX_ENTRIES_NOT_DIVISIBLE,
|
||||
NR_ENTRIES_TOO_LARGE,
|
||||
NR_ENTRIES_TOO_SMALL,
|
||||
KEYS_OUT_OF_ORDER,
|
||||
VALUE_SIZE_MISMATCH,
|
||||
PARENT_KEY_MISMATCH,
|
||||
LEAF_KEY_OVERLAPPED,
|
||||
};
|
||||
|
||||
btree_node_checker():
|
||||
last_error_(NO_ERROR),
|
||||
error_location_(0),
|
||||
error_block_nr_(0),
|
||||
error_nr_entries_(0),
|
||||
error_max_entries_(0),
|
||||
error_value_sizes_{0, 0},
|
||||
error_keys_{0, 0} {
|
||||
}
|
||||
|
||||
template <typename ValueTraits>
|
||||
bool check_block_nr(btree_detail::node_ref<ValueTraits> const &n) {
|
||||
if (n.get_location() != n.get_block_nr()) {
|
||||
last_error_ = BLOCK_NR_MISMATCH;
|
||||
error_block_nr_ = n.get_block_nr();
|
||||
error_location_ = n.get_location();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ValueTraits>
|
||||
bool check_value_size(btree_detail::node_ref<ValueTraits> const &n) {
|
||||
if (!n.value_sizes_match()) {
|
||||
last_error_ = VALUE_SIZES_MISMATCH;
|
||||
error_location_ = n.get_location();
|
||||
error_value_sizes_[0] = n.get_value_size();
|
||||
error_value_sizes_[1] = sizeof(typename ValueTraits::disk_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ValueTraits>
|
||||
bool check_max_entries(btree_detail::node_ref<ValueTraits> const &n) {
|
||||
size_t elt_size = sizeof(uint64_t) + n.get_value_size();
|
||||
if (elt_size * n.get_max_entries() + sizeof(node_header) > MD_BLOCK_SIZE) {
|
||||
last_error_ = MAX_ENTRIES_TOO_LARGE;
|
||||
error_location_ = n.get_location();
|
||||
error_max_entries_ = n.get_max_entries();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (n.get_max_entries() % 3) {
|
||||
last_error_ = MAX_ENTRIES_NOT_DIVISIBLE;
|
||||
error_location_ = n.get_location();
|
||||
error_max_entries_ = n.get_max_entries();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ValueTraits>
|
||||
bool check_nr_entries(btree_detail::node_ref<ValueTraits> const &n,
|
||||
bool is_root) {
|
||||
if (n.get_nr_entries() > n.get_max_entries()) {
|
||||
last_error_ = NR_ENTRIES_TOO_LARGE;
|
||||
error_location_ = n.get_location();
|
||||
error_nr_entries_ = n.get_nr_entries();
|
||||
error_max_entries_ = n.get_max_entries();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
block_address min = n.get_max_entries() / 3;
|
||||
if (!is_root && (n.get_nr_entries() < min)) {
|
||||
last_error_ = NR_ENTRIES_TOO_SMALL;
|
||||
error_location_ = n.get_location();
|
||||
error_nr_entries_ = n.get_nr_entries();
|
||||
error_max_entries_ = n.get_max_entries();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ValueTraits>
|
||||
bool check_ordered_keys(btree_detail::node_ref<ValueTraits> const &n) {
|
||||
unsigned nr_entries = n.get_nr_entries();
|
||||
|
||||
if (nr_entries == 0)
|
||||
return true; // can only happen if a root node
|
||||
|
||||
uint64_t last_key = n.key_at(0);
|
||||
|
||||
for (unsigned i = 1; i < nr_entries; i++) {
|
||||
uint64_t k = n.key_at(i);
|
||||
if (k <= last_key) {
|
||||
last_error_ = KEYS_OUT_OF_ORDER;
|
||||
error_location_ = n.get_location();
|
||||
error_keys_[0] = k;
|
||||
error_keys_[1] = last_key;
|
||||
|
||||
return false;
|
||||
}
|
||||
last_key = k;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ValueTraits>
|
||||
bool check_parent_key(btree_detail::node_ref<ValueTraits> const &n,
|
||||
boost::optional<uint64_t> key) {
|
||||
if (!key)
|
||||
return true;
|
||||
|
||||
if (*key > n.key_at(0)) {
|
||||
last_error_ = PARENT_KEY_MISMATCH;
|
||||
error_location_ = n.get_location();
|
||||
error_keys_[0] = n.key_at(0);
|
||||
error_keys_[1] = *key;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ValueTraits>
|
||||
bool check_leaf_key(btree_detail::node_ref<ValueTraits> const &n,
|
||||
boost::optional<uint64_t> key) {
|
||||
if (n.get_nr_entries() == 0)
|
||||
return true; // can only happen if a root node
|
||||
|
||||
if (key && *key >= n.key_at(0)) {
|
||||
last_error_ = LEAF_KEY_OVERLAPPED;
|
||||
error_location_ = n.get_location();
|
||||
error_keys_[0] = n.key_at(0);
|
||||
error_keys_[1] = *key;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
error_type get_last_error();
|
||||
std::string get_last_error_string();
|
||||
void reset();
|
||||
|
||||
private:
|
||||
std::string block_nr_mismatch_string();
|
||||
std::string value_sizes_mismatch_string();
|
||||
std::string max_entries_too_large_string();
|
||||
std::string max_entries_not_divisible_string();
|
||||
std::string nr_entries_too_large_string();
|
||||
std::string nr_entries_too_small_string();
|
||||
std::string keys_out_of_order_string();
|
||||
std::string parent_key_mismatch_string();
|
||||
std::string leaf_key_overlapped_string();
|
||||
|
||||
error_type last_error_;
|
||||
block_address error_location_;
|
||||
block_address error_block_nr_;
|
||||
uint32_t error_nr_entries_;
|
||||
uint32_t error_max_entries_;
|
||||
uint32_t error_value_sizes_[2];
|
||||
uint64_t error_keys_[2];
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user