From 41479ed1d23ea752d0ce14c2cf5627f43bceb722 Mon Sep 17 00:00:00 2001 From: Andrew Toth Date: Sun, 4 May 2025 13:34:29 -0400 Subject: [PATCH] test: add test for periodic flush inside ActivateBestChain --- src/test/chainstate_write_tests.cpp | 61 ++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/src/test/chainstate_write_tests.cpp b/src/test/chainstate_write_tests.cpp index ccca2f9be10..e1b82ebc121 100644 --- a/src/test/chainstate_write_tests.cpp +++ b/src/test/chainstate_write_tests.cpp @@ -8,6 +8,10 @@ #include +// Taken from validation.cpp +static constexpr auto DATABASE_WRITE_INTERVAL_MIN{50min}; +static constexpr auto DATABASE_WRITE_INTERVAL_MAX{70min}; + BOOST_AUTO_TEST_SUITE(chainstate_write_tests) BOOST_FIXTURE_TEST_CASE(chainstate_write_interval, TestingSetup) @@ -31,15 +35,68 @@ BOOST_FIXTURE_TEST_CASE(chainstate_write_interval, TestingSetup) BOOST_CHECK(!sub->m_did_flush); // The periodic flush interval is between 50 and 70 minutes (inclusive) - SetMockTime(GetTime() + 49min); + SetMockTime(GetTime() + DATABASE_WRITE_INTERVAL_MIN - 1min); chainstate.FlushStateToDisk(state_dummy, FlushStateMode::PERIODIC); m_node.validation_signals->SyncWithValidationInterfaceQueue(); BOOST_CHECK(!sub->m_did_flush); - SetMockTime(GetTime() + 70min); + SetMockTime(GetTime() + DATABASE_WRITE_INTERVAL_MAX); chainstate.FlushStateToDisk(state_dummy, FlushStateMode::PERIODIC); m_node.validation_signals->SyncWithValidationInterfaceQueue(); BOOST_CHECK(sub->m_did_flush); } +// Test that we do PERIODIC flushes inside ActivateBestChain. +// This is necessary for reindex-chainstate to be able to periodically flush +// before reaching chain tip. +BOOST_FIXTURE_TEST_CASE(write_during_multiblock_activation, TestChain100Setup) +{ + struct TestSubscriber final : CValidationInterface + { + const CBlockIndex* m_tip{nullptr}; + const CBlockIndex* m_flushed_at_block{nullptr}; + void ChainStateFlushed(ChainstateRole, const CBlockLocator&) override + { + m_flushed_at_block = m_tip; + } + void UpdatedBlockTip(const CBlockIndex* block_index, const CBlockIndex*, bool) override { + m_tip = block_index; + } + }; + + auto& chainstate{Assert(m_node.chainman)->ActiveChainstate()}; + BlockValidationState state_dummy{}; + + // Pop two blocks from the tip + const CBlockIndex* tip{chainstate.m_chain.Tip()}; + CBlockIndex* second_from_tip{tip->pprev}; + + { + LOCK2(m_node.chainman->GetMutex(), chainstate.MempoolMutex()); + chainstate.DisconnectTip(state_dummy, nullptr); + chainstate.DisconnectTip(state_dummy, nullptr); + } + + BOOST_CHECK_EQUAL(second_from_tip->pprev, chainstate.m_chain.Tip()); + + // Set m_next_write to current time + chainstate.FlushStateToDisk(state_dummy, FlushStateMode::ALWAYS); + m_node.validation_signals->SyncWithValidationInterfaceQueue(); + // The periodic flush interval is between 50 and 70 minutes (inclusive) + // The next call to a PERIODIC write will flush + SetMockTime(GetMockTime() + DATABASE_WRITE_INTERVAL_MAX); + + const auto sub{std::make_shared()}; + m_node.validation_signals->RegisterSharedValidationInterface(sub); + + // ActivateBestChain back to tip + chainstate.ActivateBestChain(state_dummy, nullptr); + BOOST_CHECK_EQUAL(tip, chainstate.m_chain.Tip()); + // Check that we flushed inside ActivateBestChain while we were at the + // second block from tip, since FlushStateToDisk is called with PERIODIC + // inside the outer loop. + m_node.validation_signals->SyncWithValidationInterfaceQueue(); + BOOST_CHECK_EQUAL(sub->m_flushed_at_block, second_from_tip); +} + BOOST_AUTO_TEST_SUITE_END()