Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move get_block abi serialization off the main thread #696

Merged
merged 10 commits into from
Feb 15, 2023
64 changes: 36 additions & 28 deletions libraries/chain/abi_serializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,14 @@ namespace eosio { namespace chain {
);
}

abi_serializer::abi_serializer( const abi_def& abi, const yield_function_t& yield ) {
abi_serializer::abi_serializer( abi_def&& abi, const yield_function_t& yield ) {
configure_built_in_types();
set_abi(abi, yield);
set_abi(std::move(abi), yield);
}

abi_serializer::abi_serializer( const abi_def& abi, const fc::microseconds& max_serialization_time) {
abi_serializer::abi_serializer( abi_def&& abi, const fc::microseconds& max_serialization_time) {
configure_built_in_types();
set_abi(abi, create_yield_function(max_serialization_time));
set_abi(std::move(abi), create_yield_function(max_serialization_time));
}

void abi_serializer::add_specialized_unpack_pack( const string& name,
Expand Down Expand Up @@ -128,11 +128,19 @@ namespace eosio { namespace chain {
built_in_types.emplace("extended_asset", pack_unpack<extended_asset>());
}

void abi_serializer::set_abi(const abi_def& abi, const yield_function_t& yield) {
void abi_serializer::set_abi(abi_def&& abi, const yield_function_t& yield) {
impl::abi_traverse_context ctx(yield);

EOS_ASSERT(starts_with(abi.version, "eosio::abi/1."), unsupported_abi_version_exception, "ABI has an unsupported version");

size_t types_size = abi.types.size();
size_t structs_size = abi.structs.size();
size_t actions_size = abi.actions.size();
size_t tables_size = abi.tables.size();
size_t error_messages_size = abi.error_messages.size();
size_t variants_size = abi.variants.value.size();
size_t action_results_size = abi.action_results.value.size();

typedefs.clear();
structs.clear();
actions.clear();
Expand All @@ -141,47 +149,47 @@ namespace eosio { namespace chain {
variants.clear();
action_results.clear();

for( const auto& st : abi.structs )
structs[st.name] = st;
for( auto& st : abi.structs )
structs[st.name] = std::move(st);

for( const auto& td : abi.types ) {
for( auto& td : abi.types ) {
EOS_ASSERT(!_is_type(td.new_type_name, ctx), duplicate_abi_type_def_exception,
"type already exists", ("new_type_name",impl::limit_size(td.new_type_name)));
typedefs[td.new_type_name] = td.type;
typedefs[std::move(td.new_type_name)] = std::move(td.type);
}

for( const auto& a : abi.actions )
actions[a.name] = a.type;
for( auto& a : abi.actions )
actions[std::move(a.name)] = std::move(a.type);

for( const auto& t : abi.tables )
tables[t.name] = t.type;
for( auto& t : abi.tables )
tables[std::move(t.name)] = std::move(t.type);

for( const auto& e : abi.error_messages )
error_messages[e.error_code] = e.error_msg;
for( auto& e : abi.error_messages )
error_messages[std::move(e.error_code)] = std::move(e.error_msg);

for( const auto& v : abi.variants.value )
variants[v.name] = v;
for( auto& v : abi.variants.value )
variants[v.name] = std::move(v);

for( const auto& r : abi.action_results.value )
action_results[r.name] = r.result_type;
for( auto& r : abi.action_results.value )
action_results[std::move(r.name)] = std::move(r.result_type);

/**
* The ABI vector may contain duplicates which would make it
* an invalid ABI
*/
EOS_ASSERT( typedefs.size() == abi.types.size(), duplicate_abi_type_def_exception, "duplicate type definition detected" );
EOS_ASSERT( structs.size() == abi.structs.size(), duplicate_abi_struct_def_exception, "duplicate struct definition detected" );
EOS_ASSERT( actions.size() == abi.actions.size(), duplicate_abi_action_def_exception, "duplicate action definition detected" );
EOS_ASSERT( tables.size() == abi.tables.size(), duplicate_abi_table_def_exception, "duplicate table definition detected" );
EOS_ASSERT( error_messages.size() == abi.error_messages.size(), duplicate_abi_err_msg_def_exception, "duplicate error message definition detected" );
EOS_ASSERT( variants.size() == abi.variants.value.size(), duplicate_abi_variant_def_exception, "duplicate variant definition detected" );
EOS_ASSERT( action_results.size() == abi.action_results.value.size(), duplicate_abi_action_results_def_exception, "duplicate action results definition detected" );
EOS_ASSERT( typedefs.size() == types_size, duplicate_abi_type_def_exception, "duplicate type definition detected" );
EOS_ASSERT( structs.size() == structs_size, duplicate_abi_struct_def_exception, "duplicate struct definition detected" );
EOS_ASSERT( actions.size() == actions_size, duplicate_abi_action_def_exception, "duplicate action definition detected" );
EOS_ASSERT( tables.size() == tables_size, duplicate_abi_table_def_exception, "duplicate table definition detected" );
EOS_ASSERT( error_messages.size() == error_messages_size, duplicate_abi_err_msg_def_exception, "duplicate error message definition detected" );
EOS_ASSERT( variants.size() == variants_size, duplicate_abi_variant_def_exception, "duplicate variant definition detected" );
EOS_ASSERT( action_results.size() == action_results_size, duplicate_abi_action_results_def_exception, "duplicate action results definition detected" );

validate(ctx);
}

void abi_serializer::set_abi(const abi_def& abi, const fc::microseconds& max_serialization_time) {
return set_abi(abi, create_yield_function(max_serialization_time));
void abi_serializer::set_abi(abi_def&& abi, const fc::microseconds& max_serialization_time) {
return set_abi(std::move(abi), create_yield_function(max_serialization_time));
}

bool abi_serializer::is_builtin_type(const std::string_view& type)const {
Expand Down
8 changes: 4 additions & 4 deletions libraries/chain/include/eosio/chain/abi_serializer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ struct abi_serializer {
using yield_function_t = fc::optional_delegate<void(size_t)>;

abi_serializer(){ configure_built_in_types(); }
abi_serializer( const abi_def& abi, const yield_function_t& yield );
abi_serializer( abi_def&& abi, const yield_function_t& yield );
[[deprecated("use the overload with yield_function_t[=create_yield_function(max_serialization_time)]")]]
abi_serializer( const abi_def& abi, const fc::microseconds& max_serialization_time );
void set_abi( const abi_def& abi, const yield_function_t& yield );
abi_serializer( abi_def&& abi, const fc::microseconds& max_serialization_time );
void set_abi( abi_def&& abi, const yield_function_t& yield );
[[deprecated("use the overload with yield_function_t[=create_yield_function(max_serialization_time)]")]]
void set_abi(const abi_def& abi, const fc::microseconds& max_serialization_time);
void set_abi( abi_def&& abi, const fc::microseconds& max_serialization_time);

/// @return string_view of `t` or internal string type
std::string_view resolve_type(const std::string_view& t)const;
Expand Down
5 changes: 2 additions & 3 deletions libraries/chain/include/eosio/chain/controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,9 +348,8 @@ namespace eosio { namespace chain {
if( n.good() ) {
try {
const auto& a = get_account( n );
abi_def abi;
if( abi_serializer::to_abi( a.abi, abi ))
return abi_serializer( abi, yield );
if( abi_def abi; abi_serializer::to_abi( a.abi, abi ))
return abi_serializer( std::move(abi), yield );
} FC_CAPTURE_AND_LOG((n))
}
return std::optional<abi_serializer>();
Expand Down
5 changes: 2 additions & 3 deletions libraries/testing/include/eosio/testing/tester.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,9 +341,8 @@ namespace eosio { namespace testing {
return [this]( const account_name& name ) -> std::optional<abi_serializer> {
try {
const auto& accnt = control->db().get<account_object, by_name>( name );
abi_def abi;
if( abi_serializer::to_abi( accnt.abi, abi )) {
return abi_serializer( abi, abi_serializer::create_yield_function( abi_serializer_max_time ) );
if( abi_def abi; abi_serializer::to_abi( accnt.abi, abi )) {
return abi_serializer( std::move(abi), abi_serializer::create_yield_function( abi_serializer_max_time ) );
}
return std::optional<abi_serializer>();
} FC_RETHROW_EXCEPTIONS( error, "Failed to find or parse ABI for ${name}", ("name", name))
Expand Down
2 changes: 1 addition & 1 deletion libraries/testing/tester.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ namespace eosio { namespace testing {
const variant_object& data )const { try {
const auto& acnt = control->get_account(code);
auto abi = acnt.get_abi();
chain::abi_serializer abis(abi, abi_serializer::create_yield_function( abi_serializer_max_time ));
chain::abi_serializer abis(std::move(abi), abi_serializer::create_yield_function( abi_serializer_max_time ));

string action_type_name = abis.get_action_type(acttype);
FC_ASSERT( action_type_name != string(), "unknown action type ${a}", ("a",acttype) );
Expand Down
41 changes: 37 additions & 4 deletions plugins/chain_api_plugin/chain_api_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,20 +98,18 @@ void chain_api_plugin::plugin_startup() {
ilog( "starting chain_api_plugin" );
my.reset(new chain_api_plugin_impl(app().get_plugin<chain_plugin>().chain()));
auto& chain = app().get_plugin<chain_plugin>();
auto& http = app().get_plugin<http_plugin>();
fc::microseconds max_response_time = http.get_max_response_time();
auto& _http_plugin = app().get_plugin<http_plugin>();
fc::microseconds max_response_time = _http_plugin.get_max_response_time();

auto ro_api = chain.get_read_only_api(max_response_time);
auto rw_api = chain.get_read_write_api(max_response_time);

auto& _http_plugin = app().get_plugin<http_plugin>();
ro_api.set_shorten_abi_errors( !http_plugin::verbose_errors() );

_http_plugin.add_api( {
CHAIN_RO_CALL(get_info, 200, http_params_types::no_params)}, appbase::priority::medium_high);
_http_plugin.add_api({
CHAIN_RO_CALL(get_activated_protocol_features, 200, http_params_types::possible_no_params),
CHAIN_RO_CALL(get_block, 200, http_params_types::params_required),
CHAIN_RO_CALL(get_block_info, 200, http_params_types::params_required),
CHAIN_RO_CALL(get_block_header_state, 200, http_params_types::params_required),
CHAIN_RO_CALL(get_account, 200, http_params_types::params_required),
Expand Down Expand Up @@ -151,6 +149,41 @@ void chain_api_plugin::plugin_startup() {
CHAIN_RO_CALL_WITH_400(get_transaction_status, 200, http_params_types::params_required),
});
}

_http_plugin.add_api({
{ std::string("/v1/chain/get_block"),
[ro_api, &_http_plugin, max_time=std::min(chain.get_abi_serializer_max_time(),max_response_time)]
( string, string body, url_response_callback cb ) mutable {
auto deadline = ro_api.start();
try {
auto start = fc::time_point::now();
auto params = parse_params<chain_apis::read_only::get_block_params, http_params_types::params_required>(body);
FC_CHECK_DEADLINE( deadline );
chain::signed_block_ptr block = ro_api.get_block( params, deadline );

auto abi_cache = ro_api.get_block_serializers( block, max_time );
FC_CHECK_DEADLINE( deadline );

auto post_time = fc::time_point::now();
auto remaining_time = max_time - (post_time - start);
_http_plugin.post_http_thread_pool(
[ro_api, cb, deadline, post_time, remaining_time, abi_cache{std::move(abi_cache)}, block{std::move( block )}]() {
try {
auto new_deadline = deadline + (fc::time_point::now() - post_time);

fc::variant result = ro_api.convert_block( block, abi_cache, remaining_time );

cb( 200, new_deadline, std::move( result ) );
} catch( ... ) {
http_plugin::handle_exception( "chain", "get_block", "", cb );
}
} );
} catch( ... ) {
http_plugin::handle_exception("chain", "get_block", body, cb);
}
}
}
} );
}

void chain_api_plugin::plugin_shutdown() {}
Expand Down
Loading