Skip to content

Commit

Permalink
Improve automatic frontiers confirmation (#2686)
Browse files Browse the repository at this point in the history
* Improve automatic frontiers confirmation

* Refactor code into function and move up a level (Gui comment)

* Typo semi-colon

* Conditions need reversing
  • Loading branch information
wezrule committed Mar 27, 2020
1 parent df74fb7 commit ea1a43c
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 55 deletions.
2 changes: 1 addition & 1 deletion nano/core_test/confirmation_height.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1000,7 +1000,7 @@ TEST (confirmation_height, prioritize_frontiers)
transaction.refresh ();
node->active.prioritize_frontiers_for_confirmation (transaction, std::chrono::seconds (1), std::chrono::seconds (1));
ASSERT_TRUE (priority_orders_match (node->active.priority_wallet_cementable_frontiers, std::array<nano::account, num_accounts>{ key3.pub, nano::genesis_account, key4.pub, key1.pub, key2.pub }));
node->active.search_frontiers (transaction);
node->active.confirm_prioritized_frontiers (transaction);

// Check that the active transactions roots contains the frontiers
system.deadline_set (std::chrono::seconds (10));
Expand Down
110 changes: 58 additions & 52 deletions nano/node/active_transactions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ thread ([this]() {
this->block_cemented_callback (callback_block_a);
});

// Register a callback which will get called after a batch of blocks is written and observer calls finished
// Register a callback which will get called if a block is already cemented
confirmation_height_processor.add_block_already_cemented_observer ([this](nano::block_hash const & hash_a) {
this->block_already_cemented_callback (hash_a);
});
Expand All @@ -46,27 +46,21 @@ nano::active_transactions::~active_transactions ()
stop ();
}

void nano::active_transactions::search_frontiers (nano::transaction const & transaction_a)
void nano::active_transactions::confirm_prioritized_frontiers (nano::transaction const & transaction_a)
{
// Limit maximum count of elections to start
auto rep_counts (node.wallets.rep_counts ());
bool representative (node.config.enable_voting && rep_counts.voting > 0);
bool half_princpal_representative (representative && rep_counts.half_principal > 0);
/* Check less frequently for regular nodes in auto mode */
bool agressive_mode (half_princpal_representative || node.config.frontiers_confirmation == nano::frontiers_confirmation_mode::always);
auto request_interval (std::chrono::milliseconds (node.network_params.network.request_interval_ms));
auto agressive_factor = request_interval * (agressive_mode ? 20 : 100);
// Decrease check time for test network
auto is_test_network = node.network_params.network.is_test_network ();
int test_network_factor = is_test_network ? 1000 : 1;
auto roots_size = size ();
nano::unique_lock<std::mutex> lk (mutex);
auto check_time_exceeded = std::chrono::steady_clock::now () >= next_frontier_check;
lk.unlock ();
auto max_elections = 1000;
auto low_active_elections = roots_size < max_elections;
bool wallets_check_required = (!skip_wallets || !priority_wallet_cementable_frontiers.empty ()) && !agressive_mode;
// Minimise dropping real-time transactions, set the number of frontiers added to a factor of the total number of active elections
// Minimise dropping real-time transactions, set the number of frontiers added to a factor of the maximum number of possible active elections
auto max_active = node.config.active_elections_size / 20;
if (roots_size <= max_active && (check_time_exceeded || wallets_check_required || (!is_test_network && low_active_elections && agressive_mode)))
{
Expand All @@ -76,13 +70,8 @@ void nano::active_transactions::search_frontiers (nano::transaction const & tran
max_elections = max_active - roots_size;
}

// Spend time prioritizing accounts to reduce voting traffic
auto time_spent_prioritizing_ledger_accounts = request_interval / 10;
auto time_spent_prioritizing_wallet_accounts = request_interval / 25;
prioritize_frontiers_for_confirmation (transaction_a, is_test_network ? std::chrono::milliseconds (50) : time_spent_prioritizing_ledger_accounts, time_spent_prioritizing_wallet_accounts);

size_t elections_count (0);
lk.lock ();
nano::unique_lock<std::mutex> lk (mutex);
auto start_elections_for_prioritized_frontiers = [&transaction_a, &elections_count, max_elections, &lk, &representative, this](prioritize_num_uncemented & cementable_frontiers) {
while (!cementable_frontiers.empty () && !this->stopped && elections_count < max_elections)
{
Expand All @@ -91,25 +80,28 @@ void nano::active_transactions::search_frontiers (nano::transaction const & tran
cementable_frontiers.get<tag_uncemented> ().erase (cementable_account_front_it);
lk.unlock ();
nano::account_info info;
auto error = node.store.account_get (transaction_a, cementable_account.account, info);
auto error = this->node.store.account_get (transaction_a, cementable_account.account, info);
if (!error)
{
nano::confirmation_height_info confirmation_height_info;
error = node.store.confirmation_height_get (transaction_a, cementable_account.account, confirmation_height_info);
release_assert (!error);

if (info.block_count > confirmation_height_info.height && !this->confirmation_height_processor.is_processing_block (info.head))
if (!this->confirmation_height_processor.is_processing_block (info.head))
{
auto block (this->node.store.block_get (transaction_a, info.head));
auto election = this->insert (block);
if (election.inserted)
nano::confirmation_height_info confirmation_height_info;
error = this->node.store.confirmation_height_get (transaction_a, cementable_account.account, confirmation_height_info);
release_assert (!error);

if (info.block_count > confirmation_height_info.height)
{
election.election->transition_active ();
++elections_count;
// Calculate votes for local representatives
if (election.prioritized && representative)
auto block (this->node.store.block_get (transaction_a, info.head));
auto election_insert_result = this->insert (block);
if (election_insert_result.inserted)
{
this->node.block_processor.generator.add (info.head);
election_insert_result.election->transition_active ();
++elections_count;
// Calculate votes for local representatives
if (election_insert_result.prioritized && representative)
{
this->node.block_processor.generator.add (info.head);
}
}
}
}
Expand All @@ -119,7 +111,13 @@ void nano::active_transactions::search_frontiers (nano::transaction const & tran
};
start_elections_for_prioritized_frontiers (priority_cementable_frontiers);
start_elections_for_prioritized_frontiers (priority_wallet_cementable_frontiers);
next_frontier_check = steady_clock::now () + (agressive_factor / test_network_factor);

auto request_interval (std::chrono::milliseconds (node.network_params.network.request_interval_ms));
auto rel_time_next_frontier_check = request_interval * (agressive_mode ? 20 : 60);
// Decrease check time for test network
int test_network_factor = is_test_network ? 1000 : 1;

next_frontier_check = steady_clock::now () + (rel_time_next_frontier_check / test_network_factor);
}
}

Expand Down Expand Up @@ -208,24 +206,6 @@ void nano::active_transactions::block_already_cemented_callback (nano::block_has
void nano::active_transactions::request_confirm (nano::unique_lock<std::mutex> & lock_a)
{
debug_assert (!mutex.try_lock ());
/*
* Confirm frontiers when there aren't many confirmations already pending and node finished initial bootstrap
* In auto mode start confirm only if node contains almost principal representative (half of required for principal weight)
*/

// Due to the confirmation height processor working asynchronously and compressing several roots into one frontier, probably_unconfirmed_frontiers can be wrong
{
auto pending_confirmation_height_size (confirmation_height_processor.awaiting_processing_size ());
bool probably_unconfirmed_frontiers (node.ledger.cache.block_count > node.ledger.cache.cemented_count + roots.size () + pending_confirmation_height_size);
bool bootstrap_weight_reached (node.ledger.cache.block_count >= node.ledger.bootstrap_weight_max_blocks);
if (node.config.frontiers_confirmation != nano::frontiers_confirmation_mode::disabled && bootstrap_weight_reached && probably_unconfirmed_frontiers && pending_confirmation_height_size < confirmed_frontiers_max_pending_cut_off)
{
lock_a.unlock ();
search_frontiers (node.store.tx_begin_read ());
lock_a.lock ();
update_adjusted_difficulty (); // New roots sorting
}
}

// Only representatives ready to receive batched confirm_req
nano::confirmation_solicitor solicitor (node.network, node.network_params.network);
Expand Down Expand Up @@ -293,6 +273,30 @@ void nano::active_transactions::request_confirm (nano::unique_lock<std::mutex> &
}
}

void nano::active_transactions::frontiers_confirmation (nano::unique_lock<std::mutex> & lock_a)
{
/*
* Confirm frontiers when there aren't many confirmations already pending and node finished initial bootstrap
*/
auto pending_confirmation_height_size (confirmation_height_processor.awaiting_processing_size ());
auto bootstrap_weight_reached (node.ledger.cache.block_count >= node.ledger.bootstrap_weight_max_blocks);
auto disabled_confirmation_mode = (node.config.frontiers_confirmation == nano::frontiers_confirmation_mode::disabled);
auto conf_height_capacity_reached = pending_confirmation_height_size > confirmed_frontiers_max_pending_size;
auto all_cemented = node.ledger.cache.block_count == node.ledger.cache.cemented_count;
if (!disabled_confirmation_mode && bootstrap_weight_reached && !conf_height_capacity_reached && !all_cemented)
{
// Spend some time prioritizing accounts with the most uncemented blocks to reduce voting traffic
auto request_interval = std::chrono::milliseconds (node.network_params.network.request_interval_ms);
auto time_spent_prioritizing_ledger_accounts = request_interval / 100;
auto time_spent_prioritizing_wallet_accounts = request_interval / 250;
lock_a.unlock ();
auto transaction = node.store.tx_begin_read ();
prioritize_frontiers_for_confirmation (transaction, node.network_params.network.is_test_network () ? std::chrono::milliseconds (50) : time_spent_prioritizing_ledger_accounts, time_spent_prioritizing_wallet_accounts);
confirm_prioritized_frontiers (transaction);
lock_a.lock ();
}
}

void nano::active_transactions::request_loop ()
{
nano::unique_lock<std::mutex> lock (mutex);
Expand All @@ -312,6 +316,8 @@ void nano::active_transactions::request_loop ()
const auto wakeup_l (std::chrono::steady_clock::now () + std::chrono::milliseconds (node.network_params.network.request_interval_ms));

update_adjusted_difficulty ();
// frontiers_confirmation should be above update_active_difficulty to ensure new sorted roots are updated
frontiers_confirmation (lock);
update_active_difficulty (lock);
request_confirm (lock);

Expand Down Expand Up @@ -364,10 +370,10 @@ void nano::active_transactions::prioritize_account_for_confirmation (nano::activ
}
}

void nano::active_transactions::prioritize_frontiers_for_confirmation (nano::transaction const & transaction_a, std::chrono::milliseconds ledger_accounts_time_a, std::chrono::milliseconds wallet_account_time_a)
void nano::active_transactions::prioritize_frontiers_for_confirmation (nano::transaction const & transaction_a, std::chrono::milliseconds ledger_account_traversal_max_time_a, std::chrono::milliseconds wallet_account_traversal_max_time_a)
{
// Don't try to prioritize when there are a large number of pending confirmation heights as blocks can be cemented in the meantime, making the prioritization less reliable
if (confirmation_height_processor.awaiting_processing_size () < confirmed_frontiers_max_pending_cut_off)
if (confirmation_height_processor.awaiting_processing_size () < confirmed_frontiers_max_pending_size)
{
size_t priority_cementable_frontiers_size;
size_t priority_wallet_cementable_frontiers_size;
Expand Down Expand Up @@ -423,7 +429,7 @@ void nano::active_transactions::prioritize_frontiers_for_confirmation (nano::tra

prioritize_account_for_confirmation (priority_wallet_cementable_frontiers, priority_wallet_cementable_frontiers_size, account, info, confirmation_height_info.height);

if (wallet_account_timer.since_start () >= wallet_account_time_a)
if (wallet_account_timer.since_start () >= wallet_account_traversal_max_time_a)
{
break;
}
Expand Down Expand Up @@ -465,7 +471,7 @@ void nano::active_transactions::prioritize_frontiers_for_confirmation (nano::tra
}
}
next_frontier_account = account.number () + 1;
if (timer.since_start () >= ledger_accounts_time_a)
if (timer.since_start () >= ledger_account_traversal_max_time_a)
{
break;
}
Expand Down
5 changes: 3 additions & 2 deletions nano/node/active_transactions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,9 @@ class active_transactions final
nano::election_insertion_result insert_impl (std::shared_ptr<nano::block>, std::function<void(std::shared_ptr<nano::block>)> const & = [](std::shared_ptr<nano::block>) {});
// clang-format on
void request_loop ();
void search_frontiers (nano::transaction const &);
void confirm_prioritized_frontiers (nano::transaction const & transaction_a);
void request_confirm (nano::unique_lock<std::mutex> &);
void frontiers_confirmation (nano::unique_lock<std::mutex> &);
nano::account next_frontier_account{ 0 };
std::chrono::steady_clock::time_point next_frontier_check{ std::chrono::steady_clock::now () };
nano::condition_variable condition;
Expand Down Expand Up @@ -204,7 +205,7 @@ class active_transactions final
bool skip_wallets{ false };
void prioritize_account_for_confirmation (prioritize_num_uncemented &, size_t &, nano::account const &, nano::account_info const &, uint64_t);
static size_t constexpr max_priority_cementable_frontiers{ 100000 };
static size_t constexpr confirmed_frontiers_max_pending_cut_off{ 1000 };
static size_t constexpr confirmed_frontiers_max_pending_size{ 10000 };
std::deque<nano::block_hash> adjust_difficulty_list;
// clang-format off
using ordered_cache = boost::multi_index_container<nano::inactive_cache_information,
Expand Down

0 comments on commit ea1a43c

Please sign in to comment.