Skip to content

Commit

Permalink
Merge pull request #1021 from AntelopeIO/GH-985-interrupt-apply
Browse files Browse the repository at this point in the history
Do not unwind validated blocks on interrupt
  • Loading branch information
heifner authored Nov 13, 2024
2 parents f871bf2 + c7b42ad commit 88c8dbe
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 20 deletions.
35 changes: 21 additions & 14 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4438,8 +4438,9 @@ struct controller_impl {
}
}

auto start = fc::time_point::now();
const auto start_apply_blocks_loop = fc::time_point::now();
for( auto ritr = new_head_branch.rbegin(); ritr != new_head_branch.rend(); ++ritr ) {
const auto start_apply_block = fc::time_point::now();
auto except = std::exception_ptr{};
const auto& bsp = *ritr;
try {
Expand All @@ -4452,7 +4453,7 @@ struct controller_impl {
}
// Break every ~500ms to allow other tasks (e.g. get_info, SHiP) opportunity to run. User expected
// to call apply_blocks again if this returns incomplete.
if (!replaying && fc::time_point::now() - start > fc::milliseconds(500)) {
if (!replaying && fc::time_point::now() - start_apply_blocks_loop > fc::milliseconds(500)) {
result = controller::apply_blocks_result::incomplete;
break;
}
Expand All @@ -4463,7 +4464,11 @@ struct controller_impl {
throw;
} catch (const fc::exception& e) {
if (e.code() == interrupt_exception::code_value) {
ilog("interrupt while applying block ${bn} : ${id}", ("bn", bsp->block_num())("id", bsp->id()));
if (fc::time_point::now() - start_apply_block < fc::milliseconds(2 * config::block_interval_ms)) {
ilog("interrupt while applying block ${bn} : ${id}", ("bn", bsp->block_num())("id", bsp->id()));
throw; // do not want to remove block from fork_db if not interrupting a long, maybe infinite, block
}
ilog("interrupt while applying block, removing block ${bn} : ${id}", ("bn", bsp->block_num())("id", bsp->id()));
} else {
elog("exception thrown while applying block ${bn} : ${id}, previous ${p}, error: ${e}",
("bn", bsp->block_num())("id", bsp->id())("p", bsp->previous())("e", e.to_detail_string()));
Expand All @@ -4480,18 +4485,20 @@ struct controller_impl {
// Remove the block that threw and all forks built off it.
fork_db.remove( (*ritr)->id() );

// pop all blocks from the bad fork, discarding their transactions
// ritr base is a forward itr to the last block successfully applied
auto applied_itr = ritr.base();
for( auto itr = applied_itr; itr != new_head_branch.end(); ++itr ) {
pop_block();
}
EOS_ASSERT( !switch_fork || chain_head.id() == old_head_branch.back()->header.previous, fork_database_exception,
"loss of sync between fork_db and chainbase during fork switch reversal" ); // _should_ never fail
if (switch_fork) {
// pop all blocks from the bad fork, discarding their transactions
// ritr base is a forward itr to the last block successfully applied
auto applied_itr = ritr.base();
for( auto itr = applied_itr; itr != new_head_branch.end(); ++itr ) {
pop_block();
}
EOS_ASSERT( chain_head.id() == old_head_branch.back()->header.previous, fork_database_exception,
"loss of sync between fork_db and chainbase during fork switch reversal" ); // _should_ never fail

// re-apply good blocks
for( auto ritr = old_head_branch.rbegin(); ritr != old_head_branch.rend(); ++ritr ) {
apply_block( *ritr, controller::block_status::validated /* we previously validated these blocks*/, trx_lookup );
// re-apply good blocks
for( auto ritr = old_head_branch.rbegin(); ritr != old_head_branch.rend(); ++ritr ) {
apply_block( *ritr, controller::block_status::validated /* we previously validated these blocks*/, trx_lookup );
}
}
std::rethrow_exception(except);
} // end if exception
Expand Down
10 changes: 4 additions & 6 deletions tests/interrupt_trx_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,19 +71,17 @@
trans=prodNode.pushMessage(contract, action, data, opts, silentErrors=True)
assert trans and not trans[0], "push doitforever action did not fail as expected"

prodNode.processUrllibRequest("test_control", "swap_action", {"from": "doitslow", "to": "doitforever"})

action="doitslow"
trans=prodNode.pushMessage(contract, action, data, opts)
assert trans and trans[0], "Failed to push doitslow action"

prodNode.waitForProducer("defproducera")

prodNode.processUrllibRequest("test_control", "swap_action",
{"from":"doitslow", "to":"doitforever",
"trx_priv_key":EOSIO_ACCT_PRIVATE_DEFAULT_KEY,
"blk_priv_key":cluster.defproduceraAccount.activePrivateKey})

action="doitslow"
trans=prodNode.pushMessage(contract, action, data, opts)
assert trans and trans[0], "Failed to push doitslow action"

assert not prodNode.waitForHeadToAdvance(3), f"prodNode did advance head after doitforever action"

prodNode.interruptAndVerifyExitStatus()
Expand Down

0 comments on commit 88c8dbe

Please sign in to comment.