Skip to content

Commit

Permalink
Merge pull request #920 from AntelopeIO/get_info_off_main_thread
Browse files Browse the repository at this point in the history
Move get_info off main thread and run in http thread
  • Loading branch information
linh2931 authored Oct 14, 2024
2 parents 43026d4 + 20e3140 commit 8cc763f
Show file tree
Hide file tree
Showing 15 changed files with 308 additions and 103 deletions.
4 changes: 2 additions & 2 deletions libraries/chain/include/eosio/chain/chain_id_type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace eosio {
struct handshake_message;

namespace chain_apis {
class read_only;
class get_info_db;
}

class chain_plugin;
Expand Down Expand Up @@ -49,7 +49,7 @@ namespace chain {
template<typename T>
friend T fc::variant::as()const;

friend class eosio::chain_apis::read_only;
friend class eosio::chain_apis::get_info_db;

friend class eosio::net_plugin_impl;
friend struct eosio::handshake_message;
Expand Down
12 changes: 12 additions & 0 deletions libraries/libfc/include/fc/crypto/hex.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,16 @@ namespace fc {
* @return the number of bytes decoded
*/
size_t from_hex( const std::string& hex_str, char* out_data, size_t out_data_len );

/**
* @return the hex string of `n`
*/
template<typename I>
std::string itoh(I n, size_t hlen = sizeof(I)<<1) {
static const char* digits = "0123456789abcdef";
std::string r(hlen, '0');
for(size_t i = 0, j = (hlen - 1) * 4 ; i < hlen; ++i, j -= 4)
r[i] = digits[(n>>j) & 0x0f];
return r;
}
}
8 changes: 5 additions & 3 deletions plugins/chain_api_plugin/chain_api_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,11 @@ void chain_api_plugin::plugin_startup() {

ro_api.set_shorten_abi_errors( !http_plugin::verbose_errors() );

_http_plugin.add_api( {
CALL_WITH_400(chain, node, ro_api, chain_apis::read_only, get_info, 200, http_params_types::no_params)
}, appbase::exec_queue::read_only, appbase::priority::medium_high);
// Run get_info on http thread only
_http_plugin.add_async_api({
CHAIN_RO_CALL_WITH_400(get_info, 200, http_params_types::no_params)
});

_http_plugin.add_api({
CHAIN_RO_CALL(get_activated_protocol_features, 200, http_params_types::possible_no_params),
CHAIN_RO_CALL_POST(get_block, fc::variant, 200, http_params_types::params_required), // _POST because get_block() returns a lambda to be executed on the http thread pool
Expand Down
1 change: 1 addition & 0 deletions plugins/chain_plugin/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ add_library( chain_plugin
chain_plugin.cpp
trx_retry_db.cpp
tracked_votes.cpp
get_info_db.cpp
${HEADERS} )

if(EOSIO_ENABLE_DEVELOPER_OPTIONS)
Expand Down
58 changes: 20 additions & 38 deletions plugins/chain_plugin/chain_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ class chain_plugin_impl {
std::optional<scoped_connection> applied_transaction_connection;
std::optional<scoped_connection> block_start_connection;

std::optional<chain_apis::get_info_db> _get_info_db;
std::optional<chain_apis::account_query_db> _account_query_db;
std::optional<chain_apis::trx_retry_db> _trx_retry_db;
chain_apis::trx_finality_status_processing_ptr _trx_finality_status_processing;
Expand Down Expand Up @@ -975,12 +976,14 @@ void chain_plugin_impl::plugin_initialize(const variables_map& options) {
}

// only enable last tracked votes if chain_api_plugin enabled, if no http endpoint, no reason to track
bool last_tracked_votes_enabled = false;
bool chain_api_plugin_configured = false;
if (options.count("plugin")) {
const auto& v = options.at("plugin").as<std::vector<std::string>>();
last_tracked_votes_enabled = std::ranges::any_of(v, [](const std::string& p) { return p.find("eosio::chain_api_plugin") != std::string::npos; });
chain_api_plugin_configured = std::ranges::any_of(v, [](const std::string& p) { return p.find("eosio::chain_api_plugin") != std::string::npos; });
}
_last_tracked_votes.emplace(*chain, last_tracked_votes_enabled);
_last_tracked_votes.emplace(*chain, chain_api_plugin_configured);

_get_info_db.emplace(*chain, chain_api_plugin_configured);

// initialize deep mind logging
if ( options.at( "deep-mind" ).as<bool>() ) {
Expand Down Expand Up @@ -1051,6 +1054,10 @@ void chain_plugin_impl::plugin_initialize(const variables_map& options) {
_last_tracked_votes->on_accepted_block(block, id);
}

if (_get_info_db) {
_get_info_db->on_accepted_block();
}

accepted_block_channel.publish( priority::high, t );
} );

Expand All @@ -1065,6 +1072,10 @@ void chain_plugin_impl::plugin_initialize(const variables_map& options) {
_trx_finality_status_processing->signal_irreversible_block(block, id);
}

if (_get_info_db) {
_get_info_db->on_irreversible_block(block, id);
}

irreversible_block_channel.publish( priority::low, t );
} );

Expand Down Expand Up @@ -1196,10 +1207,9 @@ chain_apis::read_write chain_plugin::get_read_write_api(const fc::microseconds&
}

chain_apis::read_only chain_plugin::get_read_only_api(const fc::microseconds& http_max_response_time) const {
return chain_apis::read_only(chain(), my->_account_query_db, my->_last_tracked_votes, get_abi_serializer_max_time(), http_max_response_time, my->_trx_finality_status_processing.get());
return chain_apis::read_only(chain(), my->_get_info_db, my->_account_query_db, my->_last_tracked_votes, get_abi_serializer_max_time(), http_max_response_time, my->_trx_finality_status_processing.get());
}


void chain_plugin::accept_transaction(const chain::packed_transaction_ptr& trx, next_function<chain::transaction_trace_ptr> next) {
my->incoming_transaction_async_method(trx, false, transaction_metadata::trx_type::input, false, std::move(next));
}
Expand Down Expand Up @@ -1281,40 +1291,12 @@ namespace chain_apis {

const string read_only::KEYi64 = "i64";

read_only::get_info_results read_only::get_info(const read_only::get_info_params&, const fc::time_point&) const {
const auto& rm = db.get_resource_limits_manager();

auto head = db.head();
auto head_id = head.id();
auto fork_db_root = db.fork_db_root();
auto fork_db_head = db.fork_db_head();
auto fork_db_root_id = fork_db_root.id();
auto fork_db_head_id = fork_db_head.id();
get_info_db::get_info_results read_only::get_info(const read_only::get_info_params&, const fc::time_point&) const {
EOS_ASSERT(gidb, plugin_config_exception, "get_info being accessed when not enabled");

return {
itoh(static_cast<uint32_t>(app().version())),
db.get_chain_id(),
block_header::num_from_id(head_id),
block_header::num_from_id(fork_db_root_id),
fork_db_root_id,
head_id,
db.head().block_time(),
db.head().producer(),
rm.get_virtual_block_cpu_limit(),
rm.get_virtual_block_net_limit(),
rm.get_block_cpu_limit(),
rm.get_block_net_limit(),
//std::bitset<64>(db.get_dynamic_global_properties().recent_slots_filled).to_string(),
//__builtin_popcountll(db.get_dynamic_global_properties().recent_slots_filled) / 64.0,
app().version_string(),
block_header::num_from_id(fork_db_head_id),
fork_db_head_id,
app().full_version_string(),
rm.get_total_cpu_weight(),
rm.get_total_net_weight(),
db.earliest_available_block_num(),
fork_db_root.block_time()
};
// To be able to run get_info on an http thread, get_info results are stored
// in get_info_db and updated whenever accepted_block signal is received.
return gidb->get_info();
}

read_only::get_transaction_status_results
Expand Down
164 changes: 164 additions & 0 deletions plugins/chain_plugin/get_info_db.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
#include <eosio/chain_plugin/get_info_db.hpp>
#include <eosio/chain/resource_limits.hpp>
#include <eosio/chain/application.hpp>

using namespace eosio;
using namespace eosio::chain;
using namespace appbase;

namespace eosio::chain_apis {
/**
* Implementation details of the get_info results
*/
struct get_info_db_impl {
get_info_db_impl(const chain::controller& controller, bool get_info_enabled)
: controller(controller)
, get_info_enabled(get_info_enabled)
, server_version(fc::itoh(static_cast<uint32_t>(app().version())))
, chain_id(controller.get_chain_id())
, server_version_string(app().version_string())
, server_full_version_string(app().full_version_string()) {}

// Called on accepted_block signal.
void on_accepted_block() {
try {
if (!get_info_enabled) {
return;
}

store_info();
} FC_LOG_AND_DROP(("get_info_db_impl on_accepted_block ERROR"));
}

// Called on irreversible_block signal.
void on_irreversible_block(const chain::signed_block_ptr& block, const block_id_type& id) {
try {
if (!get_info_enabled) {
return;
}

store_info(block, id);
} FC_LOG_AND_DROP(("get_info_db_impl on_irreversible_block ERROR"));
}

// Returns cached get_info results
get_info_db::get_info_results get_info() {
// safely load the info_cache pointer
std::shared_ptr<get_info_db::get_info_results> info = std::atomic_load(&info_cache);

if (info && info->contains_full_data()) {
return *info;
}

// This only happens right after initialization when starts from
// snapshot as no signals are emitted. We need to cache the current states.
store_info();

info = std::atomic_load(&info_cache);
assert(info);
return *info;
}

private:
// A handle to the controller.
const chain::controller& controller;

// Indication whether get_info RPC is enabled.
const bool get_info_enabled = false;

// Cache to store the current get_info results.
// Using std::atomic_load and std::atomic_store to switch pointers.
std::shared_ptr<get_info_db::get_info_results> info_cache = nullptr;

// Fixed data
std::string server_version;
chain::chain_id_type chain_id;
std::string server_version_string;
std::string server_full_version_string;

// Stores common data, and returns fork_db_has_root for future uses to avoid
// multiple mutexes in fork db.
bool store_info_common(const std::shared_ptr<get_info_db::get_info_results>& info) {
assert(info);

// fixed part
info->server_version = server_version;
info->chain_id = chain_id;
info->server_version_string = server_version_string;
info->server_full_version_string = server_full_version_string;

// chain head part
const auto& head = controller.head();
if (head.is_valid()) {
info->head_block_id = head.id();
info->head_block_num = block_header::num_from_id(info->head_block_id);
info->head_block_time = head.block_time();
info->head_block_producer = head.producer();
}

// fork_db part
const auto& fork_db_head = controller.fork_db_head();
bool fork_db_has_root = fork_db_head.is_valid(); // a valid head implies fork_db has root
if (fork_db_has_root) {
info->fork_db_head_block_id = fork_db_head.id();
info->fork_db_head_block_num = block_header::num_from_id(*info->fork_db_head_block_id);
info->earliest_available_block_num = controller.earliest_available_block_num();
}

// resource_limits
const auto& rm = controller.get_resource_limits_manager();
info->virtual_block_cpu_limit = rm.get_virtual_block_cpu_limit();
info->virtual_block_net_limit = rm.get_virtual_block_net_limit();
info->block_cpu_limit = rm.get_block_cpu_limit();
info->block_net_limit = rm.get_block_net_limit();
info->total_cpu_weight = rm.get_total_cpu_weight();
info->total_net_weight = rm.get_total_net_weight();

return fork_db_has_root;
}

void store_info() {
std::shared_ptr<get_info_db::get_info_results> info = std::make_shared<get_info_db::get_info_results>();

bool fork_db_has_root = store_info_common(info); // store_info_common returns fork_db_has_root to avoid mutex in fork db in call to controller.fork_db_has_root()

if (fork_db_has_root) {
const auto& root = controller.fork_db_root(); // avoid multiple mutexes in fork db
info->last_irreversible_block_id = root.id();
info->last_irreversible_block_num = block_header::num_from_id(info->last_irreversible_block_id);
info->last_irreversible_block_time = root.block_time();
}

std::atomic_store(&info_cache, info); // replace current cache safely
}

void store_info(const chain::signed_block_ptr& block, const block_id_type& id) {
std::shared_ptr<get_info_db::get_info_results> info = std::make_shared<get_info_db::get_info_results>();

store_info_common(info);

info->last_irreversible_block_id = id;
info->last_irreversible_block_num = block_header::num_from_id(info->last_irreversible_block_id);
info->last_irreversible_block_time = block->timestamp;

std::atomic_store(&info_cache, info); // replace current cache safely
}
}; // get_info_db_impl

get_info_db::get_info_db( const chain::controller& controller, bool get_info_enabled )
:_impl(std::make_unique<get_info_db_impl>(controller, get_info_enabled)) {}

get_info_db::~get_info_db() = default;

void get_info_db::on_accepted_block() {
_impl->on_accepted_block();
}

void get_info_db::on_irreversible_block(const chain::signed_block_ptr& block, const block_id_type& id) {
_impl->on_irreversible_block(block, id);
}

get_info_db::get_info_results get_info_db::get_info() const {
return _impl->get_info();
}
} // namespace eosio::chain_apis
Loading

0 comments on commit 8cc763f

Please sign in to comment.