Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Commit

Permalink
Merge pull request #776 from EOSIO/genblock-refactor
Browse files Browse the repository at this point in the history
Refactor block production to have limited execution time and block size
  • Loading branch information
heifner authored Nov 24, 2017
2 parents 6de6290 + a78f8c9 commit 84ae5b2
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 134 deletions.
153 changes: 49 additions & 104 deletions libraries/chain/chain_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ processed_transaction chain_controller::_push_transaction(const signed_transacti
if (!_pending_tx_session.valid())
_pending_tx_session = _db.start_undo_session(true);

FC_ASSERT( _pending_transactions.size() < 1000, "too many pending transactions, try again later" );

auto temp_session = _db.start_undo_session(true);
validate_referenced_accounts(trx);
check_transaction_authorization(trx);
Expand Down Expand Up @@ -308,114 +310,48 @@ signed_block chain_controller::_generate_block(
_pending_tx_session.reset();
_pending_tx_session = _db.start_undo_session(true);

const auto& generated = _db.get_index<generated_transaction_multi_index, generated_transaction_object::by_status>().equal_range(generated_transaction_object::PENDING);

vector<pending_transaction> pending;
std::set<transaction_id_type> invalid_pending;
pending.reserve(std::distance(generated.first, generated.second) + _pending_transactions.size());
for (auto iter = generated.first; iter != generated.second; ++iter) {
const auto& gt = *iter;
pending.emplace_back(std::reference_wrapper<const generated_transaction> {gt.trx});
}
deque<signed_transaction> pending;

for(const auto& st: _pending_transactions) {
pending.emplace_back(std::reference_wrapper<const signed_transaction> {st});
}

auto schedule = scheduler(pending, get_global_properties());
auto start = fc::time_point::now();

signed_block pending_block;
pending_block.cycles.reserve(schedule.cycles.size());

size_t invalid_transaction_count = 0;
size_t valid_transaction_count = 0;

for (const auto &c : schedule.cycles) {
cycle block_cycle;
block_cycle.reserve(c.size());

for (const auto &t : c) {
thread block_thread;
block_thread.user_input.reserve(t.transactions.size());
block_thread.generated_input.reserve(t.transactions.size());
for (const auto &trx : t.transactions) {
try
{
auto temp_session = _db.start_undo_session(true);
if (trx.contains<std::reference_wrapper<const signed_transaction>>()) {
const auto& t = trx.get<std::reference_wrapper<const signed_transaction>>().get();
validate_referenced_accounts(t);
check_transaction_authorization(t);
auto processed = apply_transaction(t);
block_thread.user_input.emplace_back(processed);
} else if (trx.contains<std::reference_wrapper<const generated_transaction>>()) {
const auto& t = trx.get<std::reference_wrapper<const generated_transaction>>().get();
auto processed = apply_transaction(t);
block_thread.generated_input.emplace_back(processed);
} else {
FC_THROW_EXCEPTION(tx_scheduling_exception, "Unknown transaction type in block_schedule");
}

temp_session.squash();
valid_transaction_count++;
}
catch ( const fc::exception& e )
{
// Do nothing, transaction will not be re-applied
elog( "Transaction was not processed while generating block due to ${e}", ("e", e) );
if (trx.contains<std::reference_wrapper<const signed_transaction>>()) {
const auto& t = trx.get<std::reference_wrapper<const signed_transaction>>().get();
wlog( "The transaction was ${t}", ("t", t ) );
invalid_pending.emplace(t.id());
} else if (trx.contains<std::reference_wrapper<const generated_transaction>>()) {
wlog( "The transaction was ${t}", ("t", trx.get<std::reference_wrapper<const generated_transaction>>().get()) );
}
invalid_transaction_count++;
}
}

if (!(block_thread.generated_input.empty() && block_thread.user_input.empty())) {
block_thread.generated_input.shrink_to_fit();
block_thread.user_input.shrink_to_fit();
block_cycle.emplace_back(std::move(block_thread));
}
}

if (!block_cycle.empty()) {
pending_block.cycles.emplace_back(std::move(block_cycle));
}
}
const auto& gprops = get_global_properties();

uint32_t pending_block_size = fc::raw::pack_size( pending_block );
if( _pending_transactions.size() ) {
pending_block.cycles.resize(1);
pending_block.cycles[0].resize(1); // single thread

for( const auto& pending_trx : _pending_transactions ) {
if( (fc::time_point::now() - start) > fc::milliseconds(200) ||
pending_block_size > gprops.configuration.max_blk_size)
{
pending.push_back(pending_trx);
continue;
}
try {
auto temp_session = _db.start_undo_session(true);
validate_referenced_accounts(pending_trx);
check_transaction_authorization(pending_trx);
auto processed = apply_transaction(pending_trx);

size_t postponed_tx_count = pending.size() - valid_transaction_count - invalid_transaction_count;
if( postponed_tx_count > 0 )
{
wlog( "Postponed ${n} transactions due to block size limit", ("n", postponed_tx_count) );
}
pending_block_size += fc::raw::pack_size(processed);

if( invalid_transaction_count > 0 )
{
wlog( "Postponed ${n} transactions errors when processing", ("n", invalid_transaction_count) );

// remove pending transactions determined to be bad during scheduling
if (invalid_pending.size() > 0) {
for (auto itr = _pending_transactions.begin(); itr != _pending_transactions.end(); ) {
auto& tx = *itr;
if (invalid_pending.find(tx.id()) != invalid_pending.end()) {
itr = _pending_transactions.erase(itr);
} else {
++itr;
if( pending_block_size > gprops.configuration.max_blk_size) {
pending.push_back( pending_trx );
continue;
}

temp_session.squash();
pending_block.cycles[0][0].user_input.emplace_back( processed );
} catch ( const fc::exception& e ) {
edump((e.to_detail_string()));
}
}
}

_pending_tx_session.reset();

// We have temporarily broken the invariant that
// _pending_tx_session is the result of applying _pending_tx, as
// _pending_transactions now consists of the set of postponed transactions.
// However, the push_block() call below will re-create the
// _pending_tx_session.
if( pending.size() )
wlog( "${x} pending transactions postponed to future block", ("x", pending.size()) );

pending_block.previous = head_block_id();
pending_block.timestamp = when;
Expand All @@ -429,16 +365,25 @@ signed_block chain_controller::_generate_block(
pending_block.producer_changes = get_global_properties().active_producers - new_schedule;
}

const auto end = fc::time_point::now();
const auto gen_time = end - start;
if( gen_time > fc::milliseconds(10) ) {
ilog("generation took ${x} ms", ("x", gen_time.count() / 1000));
FC_ASSERT(gen_time < fc::milliseconds(250), "block took too long to build");
}

if( !(skip & skip_producer_signature) )
pending_block.sign( block_signing_private_key );

// TODO: Move this to _push_block() so session is restored.
/*
if( !(skip & skip_block_size_check) )
{
FC_ASSERT( fc::raw::pack_size(pending_block) <= get_global_properties().parameters.maximum_block_size );
_pending_tx_session.reset();

_pending_transactions.clear();
for( const auto& t : pending ) {
try {
push_transaction( t );
} catch ( ... ) {
}
}
*/

// push_block( pending_block, skip );

Expand Down
11 changes: 8 additions & 3 deletions tests/slow_tests/slow_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,12 @@ void WithdrawCurrency( testing_blockchain& chain, account_name from, account_nam
//Test account script processing
BOOST_FIXTURE_TEST_CASE(create_script, testing_fixture)
{ try {
chain_controller::txn_msg_limits rate_limit = { fc::time_point_sec(10), 100000, fc::time_point_sec(10), 100000 };

chain_controller::txn_msg_limits rate_limit = {
.per_auth_account_txn_msg_rate_time_frame_sec = fc::time_point_sec(10),
.per_auth_account_txn_msg_rate = 100000,
.per_code_account_txn_msg_rate_time_frame_sec = fc::time_point_sec(10),
.per_code_account_txn_msg_rate = 100000 };
Make_Blockchain(chain, ::eosio::chain_plugin::default_transaction_execution_time,
::eosio::chain_plugin::default_received_block_transaction_execution_time,
::eosio::chain_plugin::default_create_block_transaction_execution_time, rate_limit);
Expand Down Expand Up @@ -345,7 +350,7 @@ BOOST_FIXTURE_TEST_CASE(create_script, testing_fixture)


auto start = fc::time_point::now();
for (uint32_t i = 0; i < 10000; ++i)
for (uint32_t i = 0; i < 500; ++i)
{
eosio::chain::signed_transaction trx;
trx.scope = sort_names({"currency","inita"});
Expand All @@ -358,7 +363,7 @@ BOOST_FIXTURE_TEST_CASE(create_script, testing_fixture)
chain.push_transaction(trx);
}
auto end = fc::time_point::now();
idump((10000*1000000.0 / (end-start).count()));
idump((500*1000000.0 / (end-start).count()));

chain.produce_blocks(10);

Expand Down
Loading

0 comments on commit 84ae5b2

Please sign in to comment.