Skip to content

Commit

Permalink
Bound the growth of election_winner_details set (#4720)
Browse files Browse the repository at this point in the history
* Bound the growth of `election_winner_details` set

* Notify about vacancy change

* Make election buckets always respect AEC vacancy

* Build script
  • Loading branch information
pwojcikdev authored and clemahieu committed Sep 5, 2024
1 parent bb1604e commit 6288e88
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 28 deletions.
4 changes: 2 additions & 2 deletions ci/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ SRC=${SRC:-${PWD}}
OS=$(uname)

CMAKE_BACKTRACE=""
if [[ "$OS" == 'Linux' ]]; then
if [[ ${OS} == 'Linux' ]]; then
CMAKE_BACKTRACE="-DNANO_STACKTRACE_BACKTRACE=ON"

if [[ "$COMPILER" == 'clang' ]]; then
if [[ ${COMPILER:-} == 'clang' ]]; then
CMAKE_BACKTRACE="${CMAKE_BACKTRACE} -DNANO_BACKTRACE_INCLUDE=</tmp/backtrace.h>"
fi
fi
Expand Down
50 changes: 50 additions & 0 deletions nano/core_test/active_elections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1388,3 +1388,53 @@ TEST (active_elections, limit_vote_hinted_elections)
// Ensure there was no overflow of elections
ASSERT_EQ (0, node.stats.count (nano::stat::type::active_elections_dropped, nano::stat::detail::priority));
}

/*
* Ensures that election winners set won't grow without bounds when cementing is slower that the rate of confirming new elections
*/
TEST (active_elections, bound_election_winners)
{
nano::test::system system;
nano::node_config config = system.default_config ();
// Set election winner limit to a low value
config.active_elections.max_election_winners = 5;
auto & node = *system.add_node (config);

// Start elections for a couple of blocks, number of elections is larger than the election winner set limit
auto blocks = nano::test::setup_independent_blocks (system, node, 10);
ASSERT_TIMELY (5s, std::all_of (blocks.begin (), blocks.end (), [&node] (auto & block) {
return node.active.active (*block);
}));

{
// Prevent cementing of confirmed blocks
auto guard = node.ledger.tx_begin_write ({}, nano::store::writer::testing);

// Ensure that when the number of election winners reaches the limit, AEC vacancy reflects that
ASSERT_TRUE (node.active.vacancy (nano::election_behavior::priority) > 0);

int index = 0;
for (; index < config.active_elections.max_election_winners; ++index)
{
auto election = node.vote_router.election (blocks[index]->hash ());
ASSERT_TRUE (election);
election->force_confirm ();
}

ASSERT_TIMELY_EQ (5s, node.active.vacancy (nano::election_behavior::priority), 0);

// Confirming more elections should make the vacancy negative
for (; index < blocks.size (); ++index)
{
auto election = node.vote_router.election (blocks[index]->hash ());
ASSERT_TRUE (election);
election->force_confirm ();
}

ASSERT_TIMELY (5s, node.active.vacancy (nano::election_behavior::priority) < 0);

// Release the guard to allow cementing, there should be some vacancy now
}

ASSERT_TIMELY (5s, node.active.vacancy (nano::election_behavior::priority) > 0);
}
52 changes: 33 additions & 19 deletions nano/node/active_elections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,14 +193,19 @@ void nano::active_elections::add_election_winner_details (nano::block_hash const

std::shared_ptr<nano::election> nano::active_elections::remove_election_winner_details (nano::block_hash const & hash_a)
{
nano::lock_guard<nano::mutex> guard{ election_winner_details_mutex };
std::shared_ptr<nano::election> result;
auto existing = election_winner_details.find (hash_a);
if (existing != election_winner_details.end ())
{
result = existing->second;
election_winner_details.erase (existing);
nano::lock_guard<nano::mutex> guard{ election_winner_details_mutex };
auto existing = election_winner_details.find (hash_a);
if (existing != election_winner_details.end ())
{
result = existing->second;
election_winner_details.erase (existing);
}
}

vacancy_update ();

return result;
}

Expand Down Expand Up @@ -243,19 +248,27 @@ int64_t nano::active_elections::limit (nano::election_behavior behavior) const

int64_t nano::active_elections::vacancy (nano::election_behavior behavior) const
{
nano::lock_guard<nano::mutex> guard{ mutex };
switch (behavior)
{
case nano::election_behavior::manual:
return std::numeric_limits<int64_t>::max ();
case nano::election_behavior::priority:
return limit (nano::election_behavior::priority) - static_cast<int64_t> (roots.size ());
case nano::election_behavior::hinted:
case nano::election_behavior::optimistic:
return limit (behavior) - count_by_behavior[behavior];
}
debug_assert (false); // Unknown enum
return 0;
auto election_vacancy = [this] (nano::election_behavior behavior) -> int64_t {
nano::lock_guard<nano::mutex> guard{ mutex };
switch (behavior)
{
case nano::election_behavior::manual:
return std::numeric_limits<int64_t>::max ();
case nano::election_behavior::priority:
return limit (nano::election_behavior::priority) - static_cast<int64_t> (roots.size ());
case nano::election_behavior::hinted:
case nano::election_behavior::optimistic:
return limit (behavior) - count_by_behavior[behavior];
}
debug_assert (false); // Unknown enum
return 0;
};

auto election_winners_vacancy = [this] () -> int64_t {
return static_cast<int64_t> (config.max_election_winners) - static_cast<int64_t> (election_winner_details_size ());
};

return std::min (election_vacancy (behavior), election_winners_vacancy ());
}

void nano::active_elections::request_confirm (nano::unique_lock<nano::mutex> & lock_a)
Expand Down Expand Up @@ -336,6 +349,7 @@ void nano::active_elections::cleanup_election (nano::unique_lock<nano::mutex> &
{
entry.erased_callback (election);
}

vacancy_update ();

for (auto const & [hash, block] : blocks_l)
Expand Down Expand Up @@ -558,7 +572,7 @@ bool nano::active_elections::publish (std::shared_ptr<nano::block> const & block
return result;
}

std::size_t nano::active_elections::election_winner_details_size ()
std::size_t nano::active_elections::election_winner_details_size () const
{
nano::lock_guard<nano::mutex> guard{ election_winner_details_mutex };
return election_winner_details.size ();
Expand Down
6 changes: 4 additions & 2 deletions nano/node/active_elections.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ class active_elections_config final
std::size_t confirmation_history_size{ 2048 };
// Maximum cache size for recently_confirmed
std::size_t confirmation_cache{ 65536 };
// Maximum size of election winner details set
std::size_t max_election_winners{ 1024 * 16 };
};

/**
Expand Down Expand Up @@ -138,7 +140,7 @@ class active_elections final
int64_t vacancy (nano::election_behavior behavior) const;
std::function<void ()> vacancy_update{ [] () {} };

std::size_t election_winner_details_size ();
std::size_t election_winner_details_size () const;
void add_election_winner_details (nano::block_hash const &, std::shared_ptr<nano::election> const &);
std::shared_ptr<nano::election> remove_election_winner_details (nano::block_hash const &);

Expand Down Expand Up @@ -170,7 +172,7 @@ class active_elections final
mutable nano::mutex mutex{ mutex_identifier (mutexes::active) };

private:
nano::mutex election_winner_details_mutex{ mutex_identifier (mutexes::election_winner_details) };
mutable nano::mutex election_winner_details_mutex{ mutex_identifier (mutexes::election_winner_details) };
std::unordered_map<nano::block_hash, std::shared_ptr<nano::election>> election_winner_details;

// Maximum time an election can be kept active if it is extending the container
Expand Down
6 changes: 1 addition & 5 deletions nano/node/scheduler/bucket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,7 @@ bool nano::scheduler::bucket::election_vacancy (priority_t candidate) const
{
debug_assert (!mutex.try_lock ());

if (elections.size () < config.reserved_elections)
{
return true;
}
if (elections.size () < config.max_elections)
if (elections.size () < config.reserved_elections || elections.size () < config.max_elections)
{
return active.vacancy (nano::election_behavior::priority) > 0;
}
Expand Down

0 comments on commit 6288e88

Please sign in to comment.