Skip to content

Commit 1c4dc6a

Browse files
Chen-Yifanjhunsaker
authored andcommitted
Fix traverse_blocking to call up() correctly on failure path
traverse_blocking was not calling up() to restore the TraverseMachine state when failing. This ensures the state machine is properly restored on both success and failure paths. Asserts that the state machine is at the top most level after blocking traverse.
1 parent 1ffe058 commit 1c4dc6a

File tree

2 files changed

+74
-24
lines changed

2 files changed

+74
-24
lines changed

category/mpt/test/db_test.cpp

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -873,8 +873,7 @@ TEST(DbTest, history_length_adjustment_never_under_min)
873873
remove(dbname);
874874
}
875875

876-
TEST_F(
877-
OnDiskDbWithFileFixture, read_only_db_traverse_fail_upon_version_expiration)
876+
TEST_F(OnDiskDbWithFileFixture, read_only_db_traverse_as_version_expire)
878877
{
879878
struct TraverseMachinePruneHistory final : public TraverseMachine
880879
{
@@ -927,6 +926,12 @@ TEST_F(
927926
{
928927
return std::make_unique<TraverseMachinePruneHistory>(*this);
929928
}
929+
930+
void reset()
931+
{
932+
path = Nibbles{};
933+
has_done_callback = false;
934+
}
930935
};
931936

932937
EXPECT_EQ(db.get_history_length(), MPT_TEST_HISTORY_LENGTH);
@@ -942,24 +947,51 @@ TEST_F(
942947
root = db.upsert(std::move(root), std::move(ls), version);
943948
};
944949

945-
while (version < MPT_TEST_HISTORY_LENGTH - 1) {
950+
while (version < MPT_TEST_HISTORY_LENGTH) {
946951
upsert_once();
947952
++version;
948953
}
949954
// traverse
950-
AsyncIOContext io_ctx{ReadOnlyOnDiskDbConfig{.dbname_paths = {dbname}}};
951-
Db ro_db{io_ctx};
952955
TraverseMachinePruneHistory traverse_machine{upsert_once};
953-
auto const read_version = ro_db.get_earliest_version();
954-
ASSERT_EQ(read_version, 0);
955-
NodeCursor const root_cursor{ro_db.load_root_for_version(read_version)};
956-
ASSERT_EQ(
957-
ro_db.traverse(root_cursor, traverse_machine, read_version), true);
958-
EXPECT_EQ(ro_db.get_earliest_version(), read_version);
959-
++version;
960-
ASSERT_EQ(
961-
ro_db.traverse(root_cursor, traverse_machine, read_version), false);
962-
EXPECT_GT(ro_db.get_earliest_version(), read_version);
956+
957+
// Helper to test both traverse APIs
958+
auto test_traverse = [&](auto &db, auto traverse_fn) {
959+
traverse_machine.reset();
960+
version = db.get_latest_version();
961+
auto const latest_version = db.get_latest_version();
962+
auto const earliest_version = db.get_earliest_version();
963+
auto root_cursor = db.find({}, latest_version).value();
964+
ASSERT_EQ(
965+
traverse_fn(root_cursor, traverse_machine, latest_version), true);
966+
EXPECT_EQ(traverse_machine.path.nibble_size(), 0u);
967+
EXPECT_EQ(db.get_earliest_version(), earliest_version);
968+
969+
// traverse earliest will erase earliest version
970+
root_cursor = db.find({}, earliest_version).value();
971+
++version;
972+
traverse_machine.reset();
973+
ASSERT_EQ(
974+
traverse_fn(root_cursor, traverse_machine, earliest_version),
975+
false);
976+
EXPECT_GT(db.get_earliest_version(), earliest_version);
977+
EXPECT_EQ(traverse_machine.path.nibble_size(), 0u);
978+
};
979+
980+
{
981+
AsyncIOContext io_ctx{ReadOnlyOnDiskDbConfig{.dbname_paths = {dbname}}};
982+
Db ro_db{io_ctx};
983+
// Test both APIs
984+
test_traverse(ro_db, [&](auto &&...args) {
985+
return ro_db.traverse_blocking(args...);
986+
});
987+
test_traverse(
988+
ro_db, [&](auto &&...args) { return ro_db.traverse(args...); });
989+
}
990+
{
991+
RODb ro_db(ReadOnlyOnDiskDbConfig{.dbname_paths = {dbname}});
992+
test_traverse(
993+
ro_db, [&](auto &&...args) { return ro_db.traverse(args...); });
994+
}
963995
}
964996

965997
TEST_F(OnDiskDbWithFileFixture, benchmark_blocking_parallel_traverse)
@@ -1568,6 +1600,7 @@ TYPED_TEST(DbTraverseTest, trimmed_traverse)
15681600
// Trimmed traversal
15691601
struct TrimmedTraverse : public TraverseMachine
15701602
{
1603+
unsigned curr_level{0};
15711604
size_t &num_leaves;
15721605

15731606
TrimmedTraverse(size_t &num_leaves)
@@ -1584,10 +1617,14 @@ TYPED_TEST(DbTraverseTest, trimmed_traverse)
15841617
if (node.has_value()) {
15851618
++num_leaves;
15861619
}
1620+
++curr_level;
15871621
return true;
15881622
}
15891623

1590-
virtual void up(unsigned char const, Node const &) override {}
1624+
virtual void up(unsigned char const, Node const &) override
1625+
{
1626+
--curr_level;
1627+
}
15911628

15921629
virtual std::unique_ptr<TraverseMachine> clone() const override
15931630
{
@@ -1610,13 +1647,15 @@ TYPED_TEST(DbTraverseTest, trimmed_traverse)
16101647
TrimmedTraverse traverse{num_leaves};
16111648
ASSERT_TRUE(
16121649
this->db.traverse(res_cursor.value(), traverse, this->block_id));
1650+
ASSERT_EQ(traverse.curr_level, 0);
16131651
EXPECT_EQ(num_leaves, 2);
16141652
}
16151653
{
16161654
size_t num_leaves = 0;
16171655
TrimmedTraverse traverse{num_leaves};
16181656
ASSERT_TRUE(this->db.traverse_blocking(
16191657
res_cursor.value(), traverse, this->block_id));
1658+
ASSERT_EQ(traverse.curr_level, 0);
16201659
EXPECT_EQ(num_leaves, 2);
16211660
}
16221661
}

category/mpt/traverse.hpp

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,20 +74,29 @@ namespace detail
7474
--traverse.level;
7575
return true;
7676
}
77-
for (auto const [idx, branch] : NodeChildrenRange(node.mask)) {
78-
if (traverse.should_visit(node, branch)) {
77+
for (auto const [idx, next_branch] : NodeChildrenRange(node.mask)) {
78+
if (traverse.should_visit(node, next_branch)) {
7979
if (Node::SharedPtr const &next = node.next(idx);
8080
next != nullptr) {
81-
preorder_traverse_blocking_impl(
82-
aux, branch, *next, traverse, version);
81+
if (!preorder_traverse_blocking_impl(
82+
aux, next_branch, *next, traverse, version)) {
83+
--traverse.level;
84+
traverse.up(branch, node);
85+
return false;
86+
}
8387
continue;
8488
}
8589
MONAD_ASSERT(aux.is_on_disk());
8690
Node::UniquePtr next_node_ondisk =
8791
read_node_blocking(aux, node.fnext(idx), version);
88-
if (!next_node_ondisk ||
89-
!preorder_traverse_blocking_impl(
90-
aux, branch, *next_node_ondisk, traverse, version)) {
92+
if (!next_node_ondisk || !preorder_traverse_blocking_impl(
93+
aux,
94+
next_branch,
95+
*next_node_ondisk,
96+
traverse,
97+
version)) {
98+
--traverse.level;
99+
traverse.up(branch, node);
91100
return false;
92101
}
93102
}
@@ -368,8 +377,10 @@ inline bool preorder_traverse_blocking(
368377
UpdateAuxImpl &aux, Node const &node, TraverseMachine &traverse,
369378
uint64_t const version)
370379
{
371-
return detail::preorder_traverse_blocking_impl(
380+
auto const ret = detail::preorder_traverse_blocking_impl(
372381
aux, INVALID_BRANCH, node, traverse, version);
382+
MONAD_ASSERT(traverse.level == 0);
383+
return ret;
373384
}
374385

375386
inline bool preorder_traverse_ondisk(

0 commit comments

Comments
 (0)