Skip to content

Commit

Permalink
Merge pull request #1199 from AntelopeIO/gh-1025
Browse files Browse the repository at this point in the history
Improve packed_transaction parsing performance
  • Loading branch information
greg7mdp authored May 19, 2023
2 parents a1c8806 + 3ae59db commit cc523ee
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 29 deletions.
57 changes: 49 additions & 8 deletions libraries/chain/include/eosio/chain/abi_serializer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -736,7 +736,7 @@ namespace impl {
* and can be degraded to the normal ::from_variant(...) processing
*/
template<typename M, typename Resolver, not_require_abi_t<M> = 1>
static void extract( const fc::variant& v, M& o, Resolver, abi_traverse_context& ctx )
static void extract( const fc::variant& v, M& o, const Resolver&, abi_traverse_context& ctx )
{
auto h = ctx.enter_scope();
from_variant(v, o);
Expand Down Expand Up @@ -825,13 +825,14 @@ namespace impl {
from_variant(data, act.data);
valid_empty_data = act.data.empty();
} else if ( data.is_object() ) {
auto abi = resolver(act.account);
if (abi) {
auto type = abi->get_action_type(act.name);
auto abi_optional = resolver(act.account);
if (abi_optional) {
const abi_serializer& abi = *abi_optional;
auto type = abi.get_action_type(act.name);
if (!type.empty()) {
variant_to_binary_context _ctx(*abi, ctx, type);
variant_to_binary_context _ctx(abi, ctx, type);
_ctx.short_path = true; // Just to be safe while avoiding the complexity of threading an override boolean all over the place
act.data = std::move( abi->_variant_to_binary( type, data, _ctx ));
act.data = abi._variant_to_binary( type, data, _ctx );
valid_empty_data = act.data.empty();
}
}
Expand Down Expand Up @@ -1002,6 +1003,7 @@ void abi_serializer::from_variant( const fc::variant& v, T& o, const Resolver& r
} FC_RETHROW_EXCEPTIONS(error, "Failed to deserialize variant", ("variant",v))

using abi_serializer_cache_t = std::unordered_map<account_name, std::optional<abi_serializer>>;
using resolver_fn_t = std::function<std::optional<abi_serializer>(const account_name& name)>;

class abi_resolver {
public:
Expand All @@ -1022,7 +1024,7 @@ class abi_resolver {

class abi_serializer_cache_builder {
public:
explicit abi_serializer_cache_builder(std::function<std::optional<abi_serializer>(const account_name& name)> resolver) :
explicit abi_serializer_cache_builder(resolver_fn_t resolver) :
resolver_(std::move(resolver))
{
}
Expand Down Expand Up @@ -1066,8 +1068,47 @@ class abi_serializer_cache_builder {
}
}

std::function<std::optional<abi_serializer>(const account_name& name)> resolver_;
resolver_fn_t resolver_;
abi_serializer_cache_t abi_serializers;
};

/*
* This is equivalent to a resolver, except that everytime the abi_serializer for an account
* is retrieved, it is stored in an unordered_map, so we won't waste time retrieving it again.
* This is handy when parsing packed_transactions received in a fc::variant.
*/
class caching_resolver {
public:
explicit caching_resolver(resolver_fn_t resolver) :
resolver_(std::move(resolver))
{
}

// make it non-copiable (we should only move it for performance reasons)
caching_resolver(const caching_resolver&) = delete;
caching_resolver& operator=(const caching_resolver&) = delete;

std::optional<std::reference_wrapper<const abi_serializer>> operator()(const account_name& account) const {
auto it = abi_serializers.find(account);
if (it != abi_serializers.end()) {
if (it->second)
return *it->second;
return {};
}
auto serializer = resolver_(account);
auto& dest = abi_serializers[account]; // add entry regardless
if (serializer) {
// we got a serializer, so move it into the cache
dest = abi_serializer_cache_t::mapped_type{std::move(*serializer)};
return *dest; // and return a reference to it
}
return {};
};

private:
const resolver_fn_t resolver_;
mutable abi_serializer_cache_t abi_serializers;
};


} // eosio::chain
36 changes: 17 additions & 19 deletions plugins/chain_plugin/chain_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1897,12 +1897,10 @@ chain::signed_block_ptr read_only::get_raw_block(const read_only::get_raw_block_
std::function<chain::t_or_exception<fc::variant>()> read_only::get_block(const get_raw_block_params& params, const fc::time_point& deadline) const {
chain::signed_block_ptr block = get_raw_block(params, deadline);

auto abi_cache = abi_serializer_cache_builder(make_resolver(db, abi_serializer_max_time, throw_on_yield::no)).add_serializers(block).get();

using return_type = t_or_exception<fc::variant>;
return [this,
resolver = abi_resolver(std::move(abi_cache)),
block = std::move(block)]() mutable -> return_type {
resolver = get_serializers_cache(db, block, abi_serializer_max_time),
block = std::move(block)]() mutable -> return_type {
try {
return convert_block(block, resolver);
} CATCH_AND_RETURN(return_type);
Expand Down Expand Up @@ -1949,7 +1947,7 @@ read_only::get_block_header_result read_only::get_block_header(const read_only::

abi_resolver
read_only::get_block_serializers( const chain::signed_block_ptr& block, const fc::microseconds& max_time ) const {
return abi_resolver(abi_serializer_cache_builder(make_resolver(db, max_time, throw_on_yield::no)).add_serializers(block).get());
return get_serializers_cache(db, block, max_time);
}

fc::variant read_only::convert_block( const chain::signed_block_ptr& block, abi_resolver& resolver ) const {
Expand Down Expand Up @@ -2031,9 +2029,9 @@ void read_write::push_block(read_write::push_block_params&& params, next_functio
void read_write::push_transaction(const read_write::push_transaction_params& params, next_function<read_write::push_transaction_results> next) {
try {
auto pretty_input = std::make_shared<packed_transaction>();
auto resolver = make_resolver(db, abi_serializer_max_time, throw_on_yield::yes);
auto resolver = caching_resolver(make_resolver(db, abi_serializer_max_time, throw_on_yield::yes));
try {
abi_serializer::from_variant(params, *pretty_input, std::move( resolver ), abi_serializer::create_yield_function( abi_serializer_max_time ));
abi_serializer::from_variant(params, *pretty_input, resolver, abi_serializer_max_time);
} EOS_RETHROW_EXCEPTIONS(chain::packed_transaction_type_exception, "Invalid packed transaction")

app().get_method<incoming::methods::transaction_async>()(pretty_input, true, transaction_metadata::trx_type::input, false,
Expand All @@ -2046,9 +2044,8 @@ void read_write::push_transaction(const read_write::push_transaction_params& par
try {
fc::variant output;
try {
auto abi_cache = abi_serializer_cache_builder(make_resolver(db, abi_serializer_max_time, throw_on_yield::no)).add_serializers(trx_trace_ptr).get();
auto resolver = abi_resolver(std::move(abi_cache));
abi_serializer::to_variant(*trx_trace_ptr, output, std::move(resolver), abi_serializer_max_time);
auto resolver = get_serializers_cache(db, trx_trace_ptr, abi_serializer_max_time);
abi_serializer::to_variant(*trx_trace_ptr, output, resolver, abi_serializer_max_time);

// Create map of (closest_unnotified_ancestor_action_ordinal, global_sequence) with action trace
std::map< std::pair<uint32_t, uint64_t>, fc::mutable_variant_object > act_traces_map;
Expand Down Expand Up @@ -2154,9 +2151,9 @@ template<class API, class Result>
void api_base::send_transaction_gen(API &api, send_transaction_params_t params, next_function<Result> next) {
try {
auto ptrx = std::make_shared<packed_transaction>();
auto resolver = make_resolver(api.db, api.abi_serializer_max_time, throw_on_yield::yes);
auto resolver = caching_resolver(make_resolver(api.db, api.abi_serializer_max_time, throw_on_yield::yes));
try {
abi_serializer::from_variant(params.transaction, *ptrx, resolver, abi_serializer::create_yield_function( api.abi_serializer_max_time ));
abi_serializer::from_variant(params.transaction, *ptrx, resolver, api.abi_serializer_max_time);
} EOS_RETHROW_EXCEPTIONS(packed_transaction_type_exception, "Invalid packed transaction")

bool retry = false;
Expand Down Expand Up @@ -2201,13 +2198,14 @@ void api_base::send_transaction_gen(API &api, send_transaction_params_t params,
}
if (!retried) {
// we are still on main thread here. The lambda passed to `next()` below will be executed on the http thread pool
auto abi_cache = abi_serializer_cache_builder(make_resolver(api.db, api.abi_serializer_max_time, throw_on_yield::no)).add_serializers(trx_trace_ptr).get();
using return_type = t_or_exception<Result>;
next([&api, trx_trace_ptr, resolver = abi_resolver(std::move(abi_cache))]() mutable {
next([&api,
trx_trace_ptr,
resolver = get_serializers_cache(api.db, trx_trace_ptr, api.abi_serializer_max_time)]() mutable {
try {
fc::variant output;
try {
abi_serializer::to_variant(*trx_trace_ptr, output, std::move(resolver), api.abi_serializer_max_time);
abi_serializer::to_variant(*trx_trace_ptr, output, resolver, api.abi_serializer_max_time);
} catch( abi_exception& ) {
output = *trx_trace_ptr;
}
Expand Down Expand Up @@ -2498,9 +2496,9 @@ read_only::get_account_return_t read_only::get_account( const get_account_params

read_only::get_required_keys_result read_only::get_required_keys( const get_required_keys_params& params, const fc::time_point& )const {
transaction pretty_input;
auto resolver = make_resolver(db, abi_serializer_max_time, throw_on_yield::yes);
auto resolver = caching_resolver(make_resolver(db, abi_serializer_max_time, throw_on_yield::yes));
try {
abi_serializer::from_variant(params.transaction, pretty_input, resolver, abi_serializer::create_yield_function( abi_serializer_max_time ));
abi_serializer::from_variant(params.transaction, pretty_input, resolver, abi_serializer_max_time);
} EOS_RETHROW_EXCEPTIONS(chain::transaction_type_exception, "Invalid transaction")

auto required_keys_set = db.get_authorization_manager().get_required_keys( pretty_input, params.available_keys, fc::seconds( pretty_input.delay_sec ));
Expand Down Expand Up @@ -2594,7 +2592,7 @@ fc::variant chain_plugin::get_log_trx_trace(const transaction_trace_ptr& trx_tra
fc::variant pretty_output;
try {
abi_serializer::to_log_variant(trx_trace, pretty_output,
make_resolver(chain(), get_abi_serializer_max_time(), throw_on_yield::no),
caching_resolver(make_resolver(chain(), get_abi_serializer_max_time(), throw_on_yield::no)),
get_abi_serializer_max_time());
} catch (...) {
pretty_output = trx_trace;
Expand All @@ -2606,7 +2604,7 @@ fc::variant chain_plugin::get_log_trx(const transaction& trx) const {
fc::variant pretty_output;
try {
abi_serializer::to_log_variant(trx, pretty_output,
make_resolver(chain(), get_abi_serializer_max_time(), throw_on_yield::no),
caching_resolver(make_resolver(chain(), get_abi_serializer_max_time(), throw_on_yield::no)),
get_abi_serializer_max_time());
} catch (...) {
pretty_output = trx;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ namespace eosio {
using chain::action_name;
using chain::abi_def;
using chain::abi_serializer;
using chain::abi_serializer_cache_builder;
using chain::abi_resolver;
using chain::packed_transaction;

Expand All @@ -68,6 +69,11 @@ namespace eosio {
};
}

template<class T>
inline abi_resolver get_serializers_cache(const controller& db, const T& obj, const fc::microseconds& max_time) {
return abi_resolver(abi_serializer_cache_builder(make_resolver(db, max_time, throw_on_yield::no)).add_serializers(obj).get());
}

namespace chain_apis {
struct empty{};

Expand Down
3 changes: 1 addition & 2 deletions plugins/chain_plugin/trx_retry_db.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,7 @@ struct trx_retry_db_impl {
// Convert to variant with abi here and now because abi could change in very next transaction.
// Alternatively, we could store off all the abis needed and do the conversion later, but as this is designed
// to run on an API node, probably the best trade off to perform the abi serialization during block processing.
auto abi_cache = abi_serializer_cache_builder(make_resolver(control, abi_max_time, throw_on_yield::no)).add_serializers(trace).get();
auto resolver = abi_resolver(std::move(abi_cache));
auto resolver = get_serializers_cache(control, trace, abi_max_time);
tt.trx_trace_v.clear();
abi_serializer::to_variant(*trace, tt.trx_trace_v, resolver, abi_max_time);
} catch( chain::abi_exception& ) {
Expand Down

0 comments on commit cc523ee

Please sign in to comment.