Improve active elections update loop
This commit is contained in:
parent
1c5b29a88a
commit
c311aa67af
3 changed files with 87 additions and 62 deletions
|
|
@ -53,9 +53,18 @@ nano::active_elections::active_elections (nano::node & node_a, nano::ledger_noti
|
|||
// Notify observers about cemented blocks on a background thread
|
||||
workers.post ([this, results = std::move (results)] () {
|
||||
auto transaction = node.ledger.tx_begin_read ();
|
||||
for (auto const & [status, votes] : results)
|
||||
for (auto const & [election, status, votes] : results)
|
||||
{
|
||||
transaction.refresh_if_needed ();
|
||||
|
||||
// Dependent elections are implicitly confirmed when their block is cemented
|
||||
// TODO: Should this be the case? Maybe just cancel the election and issue block cemented notification?
|
||||
if (election)
|
||||
{
|
||||
node.stats.inc (nano::stat::type::active_elections, nano::stat::detail::confirm_dependent);
|
||||
election->try_confirm (status.winner->hash ()); // TODO: This should either confirm or cancel the election
|
||||
}
|
||||
|
||||
notify_observers (transaction, status, votes);
|
||||
}
|
||||
});
|
||||
|
|
@ -165,27 +174,6 @@ auto nano::active_elections::insert (std::shared_ptr<nano::block> const & block,
|
|||
|
||||
node.vote_router.connect (hash, result.election);
|
||||
|
||||
auto should_activate_immediately = [&] () {
|
||||
// Broadcast votes immediately for priority elections
|
||||
if (behavior == nano::election_behavior::priority)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Skip passive phase for blocks without cached votes to avoid bootstrap delays
|
||||
if (!node.vote_cache.contains (hash))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
bool activate_immediately = should_activate_immediately ();
|
||||
if (activate_immediately)
|
||||
{
|
||||
node.stats.inc (nano::stat::type::active_elections, nano::stat::detail::activate_immediately);
|
||||
result.election->transition_active ();
|
||||
}
|
||||
|
||||
node.stats.inc (nano::stat::type::active_elections, nano::stat::detail::started);
|
||||
node.stats.inc (nano::stat::type::active_elections_started, to_stat_detail (behavior));
|
||||
|
||||
|
|
@ -193,11 +181,10 @@ auto nano::active_elections::insert (std::shared_ptr<nano::block> const & block,
|
|||
nano::log::arg{ "behavior", behavior },
|
||||
nano::log::arg{ "election", result.election });
|
||||
|
||||
node.logger.debug (nano::log::type::active_elections, "Started new election for root: {} with blocks: {} (behavior: {}, active immediately: {})",
|
||||
node.logger.debug (nano::log::type::active_elections, "Started new election for root: {} with blocks: {} (behavior: {})",
|
||||
root,
|
||||
fmt::join (result.election->blocks_hashes (), ", "), // TODO: Lazy eval
|
||||
to_string (behavior),
|
||||
activate_immediately);
|
||||
to_string (behavior));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -231,6 +218,23 @@ auto nano::active_elections::insert (std::shared_ptr<nano::block> const & block,
|
|||
{
|
||||
release_assert (result.election);
|
||||
|
||||
auto should_activate_immediately = [&] () {
|
||||
// Skip passive phase for blocks without cached votes to avoid bootstrap delays
|
||||
if (!node.vote_cache.contains (hash))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Transition to active (broadcasting votes, sending confirm reqs) state if needed
|
||||
bool activate_immediately = should_activate_immediately ();
|
||||
if (activate_immediately)
|
||||
{
|
||||
node.stats.inc (nano::stat::type::active_elections, nano::stat::detail::activate_immediately);
|
||||
result.election->transition_active ();
|
||||
}
|
||||
|
||||
// Notifications
|
||||
node.observers.active_started.notify (hash);
|
||||
vacancy_updated.notify ();
|
||||
|
|
@ -382,12 +386,7 @@ auto nano::active_elections::block_cemented (std::shared_ptr<nano::block> const
|
|||
debug_assert (node.block_confirmed (block->hash ()));
|
||||
|
||||
// Dependent elections are implicitly confirmed when their block is cemented
|
||||
auto dependend_election = election_impl (block->qualified_root ());
|
||||
if (dependend_election)
|
||||
{
|
||||
node.stats.inc (nano::stat::type::active_elections, nano::stat::detail::confirm_dependent);
|
||||
dependend_election->try_confirm (block->hash ()); // TODO: This should either confirm or cancel the election
|
||||
}
|
||||
auto election = election_impl (block->qualified_root ());
|
||||
|
||||
nano::election_status status;
|
||||
std::vector<nano::vote_with_weight_info> votes;
|
||||
|
|
@ -401,7 +400,7 @@ auto nano::active_elections::block_cemented (std::shared_ptr<nano::block> const
|
|||
votes = source_election->votes_with_weight ();
|
||||
status.type = nano::election_status_type::active_confirmed_quorum;
|
||||
}
|
||||
else if (dependend_election)
|
||||
else if (election)
|
||||
{
|
||||
status.type = nano::election_status_type::active_confirmation_height;
|
||||
}
|
||||
|
|
@ -420,7 +419,7 @@ auto nano::active_elections::block_cemented (std::shared_ptr<nano::block> const
|
|||
nano::log::arg{ "confirmation_root", confirmation_root },
|
||||
nano::log::arg{ "source_election", source_election });
|
||||
|
||||
return { status, votes };
|
||||
return { election, status, votes };
|
||||
}
|
||||
|
||||
void nano::active_elections::notify_observers (nano::secure::transaction const & transaction, nano::election_status const & status, std::vector<nano::vote_with_weight_info> const & votes) const
|
||||
|
|
@ -461,11 +460,31 @@ void nano::active_elections::notify_observers (nano::secure::transaction const &
|
|||
}
|
||||
}
|
||||
|
||||
bool nano::active_elections::trigger (nano::qualified_root const & root)
|
||||
{
|
||||
bool triggered = false;
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard{ mutex };
|
||||
if (auto election = index.election (root))
|
||||
{
|
||||
triggered = index.trigger (election);
|
||||
}
|
||||
}
|
||||
if (triggered)
|
||||
{
|
||||
condition.notify_all ();
|
||||
}
|
||||
return triggered;
|
||||
}
|
||||
|
||||
void nano::active_elections::tick_elections (nano::unique_lock<nano::mutex> & lock)
|
||||
{
|
||||
debug_assert (lock.owns_lock ());
|
||||
|
||||
auto const election_list = list_active_impl ();
|
||||
auto const now = std::chrono::steady_clock::now ();
|
||||
auto const cutoff = now - node.network_params.network.aec_loop_interval;
|
||||
auto const election_list = index.list (cutoff, now);
|
||||
debug_assert (!election_list.empty ()); // Shouldn't be called if there are no elections to process
|
||||
|
||||
lock.unlock ();
|
||||
|
||||
|
|
@ -510,25 +529,33 @@ void nano::active_elections::tick_elections (nano::unique_lock<nano::mutex> & lo
|
|||
}
|
||||
}
|
||||
|
||||
bool nano::active_elections::predicate () const
|
||||
{
|
||||
debug_assert (!mutex.try_lock ());
|
||||
|
||||
auto cutoff = std::chrono::steady_clock::now () - node.network_params.network.aec_loop_interval;
|
||||
return index.any (cutoff);
|
||||
}
|
||||
|
||||
void nano::active_elections::run ()
|
||||
{
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
while (!stopped)
|
||||
{
|
||||
auto const stamp = std::chrono::steady_clock::now ();
|
||||
if (predicate ())
|
||||
{
|
||||
node.stats.inc (nano::stat::type::active, nano::stat::detail::loop);
|
||||
|
||||
node.stats.inc (nano::stat::type::active, nano::stat::detail::loop);
|
||||
|
||||
tick_elections (lock);
|
||||
debug_assert (!lock.owns_lock ());
|
||||
lock.lock ();
|
||||
|
||||
auto const min_sleep = node.network_params.network.aec_loop_interval / 2;
|
||||
auto const wakeup = std::max (stamp + node.network_params.network.aec_loop_interval, std::chrono::steady_clock::now () + min_sleep);
|
||||
|
||||
condition.wait_until (lock, wakeup, [this, wakeup] {
|
||||
return stopped || std::chrono::steady_clock::now () >= wakeup;
|
||||
});
|
||||
tick_elections (lock);
|
||||
debug_assert (!lock.owns_lock ());
|
||||
lock.lock ();
|
||||
}
|
||||
else
|
||||
{
|
||||
condition.wait_for (lock, node.network_params.network.aec_loop_interval / 2, [this] {
|
||||
return stopped || predicate ();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,9 @@ public:
|
|||
// Notify this container about a new block (potential fork)
|
||||
bool publish (std::shared_ptr<nano::block> const &);
|
||||
|
||||
// Trigger an immediate election update (e.g. after it is confirmed)
|
||||
bool trigger (nano::qualified_root const &);
|
||||
|
||||
/// Is the root of this block in the roots container
|
||||
bool active (nano::block const &) const;
|
||||
bool active (nano::qualified_root const &) const;
|
||||
|
|
@ -111,13 +114,20 @@ public: // Events
|
|||
nano::observer_set<> vacancy_updated;
|
||||
|
||||
private:
|
||||
bool predicate () const;
|
||||
void run ();
|
||||
void tick_elections (nano::unique_lock<nano::mutex> &);
|
||||
|
||||
// Erase all blocks from active and, if not confirmed, clear digests from network filters
|
||||
void erase_election (nano::unique_lock<nano::mutex> & lock_a, std::shared_ptr<nano::election>);
|
||||
|
||||
using block_cemented_result = std::pair<nano::election_status, std::vector<nano::vote_with_weight_info>>;
|
||||
struct block_cemented_result
|
||||
{
|
||||
std::shared_ptr<nano::election> election;
|
||||
nano::election_status status;
|
||||
std::vector<nano::vote_with_weight_info> votes;
|
||||
};
|
||||
|
||||
block_cemented_result block_cemented (std::shared_ptr<nano::block> const & block, nano::block_hash const & confirmation_root, std::shared_ptr<nano::election> const & source_election);
|
||||
void notify_observers (nano::secure::transaction const &, nano::election_status const & status, std::vector<nano::vote_with_weight_info> const & votes) const;
|
||||
|
||||
|
|
@ -152,22 +162,8 @@ private:
|
|||
nano::interval bootstrap_stale_interval;
|
||||
nano::interval warning_interval;
|
||||
|
||||
friend class election;
|
||||
|
||||
public: // Tests
|
||||
void clear ();
|
||||
|
||||
friend class node_fork_storm_Test;
|
||||
friend class system_block_sequence_Test;
|
||||
friend class node_mass_block_new_Test;
|
||||
friend class active_elections_vote_replays_Test;
|
||||
friend class frontiers_confirmation_prioritize_frontiers_Test;
|
||||
friend class frontiers_confirmation_prioritize_frontiers_max_optimistic_elections_Test;
|
||||
friend class confirmation_height_prioritize_frontiers_overwrite_Test;
|
||||
friend class active_elections_confirmation_consistency_Test;
|
||||
friend class node_deferred_dependent_elections_Test;
|
||||
friend class active_elections_pessimistic_elections_Test;
|
||||
friend class frontiers_confirmation_expired_optimistic_elections_removal_Test;
|
||||
};
|
||||
|
||||
nano::stat::type to_stat_type (nano::election_state);
|
||||
|
|
|
|||
|
|
@ -78,6 +78,8 @@ void nano::election::confirm_once (nano::unique_lock<nano::mutex> & lock)
|
|||
|
||||
lock.unlock ();
|
||||
|
||||
node.active.trigger (qualified_root);
|
||||
|
||||
node.election_workers.post ([status_l, confirmation_action_l = confirmation_action] () {
|
||||
if (confirmation_action_l)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue