Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hinted scheduler improvements #4312

Merged
merged 13 commits into from
Oct 17, 2023
57 changes: 31 additions & 26 deletions nano/core_test/active_transactions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ TEST (active_transactions, inactive_votes_cache)
.build_shared ();
auto vote (std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::vote::timestamp_max, nano::vote::duration_max, std::vector<nano::block_hash> (1, send->hash ())));
node.vote_processor.vote (vote, std::make_shared<nano::transport::inproc::channel> (node, node));
ASSERT_TIMELY (5s, node.inactive_vote_cache.cache_size () == 1);
ASSERT_TIMELY (5s, node.vote_cache.size () == 1);
node.process_active (send);
node.block_processor.flush ();
ASSERT_TIMELY (5s, node.ledger.block_confirmed (node.store.tx_begin_read (), send->hash ()));
Expand All @@ -264,7 +264,7 @@ TEST (active_transactions, inactive_votes_cache_non_final)
// Non-final vote
auto vote = std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, 0, 0, std::vector<nano::block_hash> (1, send->hash ()));
node.vote_processor.vote (vote, std::make_shared<nano::transport::inproc::channel> (node, node));
ASSERT_TIMELY (5s, node.inactive_vote_cache.cache_size () == 1);
ASSERT_TIMELY (5s, node.vote_cache.size () == 1);

node.process_active (send);
std::shared_ptr<nano::election> election;
Expand Down Expand Up @@ -301,7 +301,7 @@ TEST (active_transactions, inactive_votes_cache_fork)

auto const vote = std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::vote::timestamp_max, nano::vote::duration_max, std::vector<nano::block_hash> (1, send1->hash ()));
node.vote_processor.vote (vote, std::make_shared<nano::transport::inproc::channel> (node, node));
ASSERT_TIMELY (5s, node.inactive_vote_cache.cache_size () == 1);
ASSERT_TIMELY (5s, node.vote_cache.size () == 1);

node.process_active (send2);

Expand Down Expand Up @@ -356,10 +356,10 @@ TEST (active_transactions, inactive_votes_cache_existing_vote)
ASSERT_EQ (nano::vote::timestamp_min * 1, last_vote1.timestamp);
// Attempt to change vote with inactive_votes_cache
nano::unique_lock<nano::mutex> active_lock (node.active.mutex);
node.inactive_vote_cache.vote (send->hash (), vote1);
auto cache = node.inactive_vote_cache.find (send->hash ());
node.vote_cache.vote (send->hash (), vote1);
auto cache = node.vote_cache.find (send->hash ());
ASSERT_TRUE (cache);
ASSERT_EQ (1, cache->voters.size ());
ASSERT_EQ (1, cache->voters ().size ());
cache->fill (election);
// Check that election data is not changed
ASSERT_EQ (2, election->votes ().size ());
Expand Down Expand Up @@ -416,9 +416,9 @@ TEST (active_transactions, inactive_votes_cache_multiple_votes)
auto vote2 (std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, 0, 0, std::vector<nano::block_hash> (1, send1->hash ())));
node.vote_processor.vote (vote2, std::make_shared<nano::transport::inproc::channel> (node, node));

ASSERT_TIMELY (5s, node.inactive_vote_cache.find (send1->hash ()));
ASSERT_TIMELY (5s, node.inactive_vote_cache.find (send1->hash ())->voters.size () == 2);
ASSERT_EQ (1, node.inactive_vote_cache.cache_size ());
ASSERT_TIMELY (5s, node.vote_cache.find (send1->hash ()));
ASSERT_TIMELY (5s, node.vote_cache.find (send1->hash ())->voters ().size () == 2);
ASSERT_EQ (1, node.vote_cache.size ());
node.scheduler.priority.activate (nano::dev::genesis_key.pub, node.store.tx_begin_read ());
std::shared_ptr<nano::election> election;
ASSERT_TIMELY (5s, election = node.active.election (send1->qualified_root ()));
Expand All @@ -438,7 +438,7 @@ TEST (active_transactions, inactive_votes_cache_election_start)
nano::send_block_builder send_block_builder;
nano::state_block_builder state_block_builder;
// Enough weight to trigger election hinting but not enough to confirm block on its own
auto amount = ((node.online_reps.trended () / 100) * node.config.election_hint_weight_percent) / 2 + 1000 * nano::Gxrb_ratio;
auto amount = ((node.online_reps.trended () / 100) * node.config.hinted_scheduler.hinting_threshold_percent) / 2 + 1000 * nano::Gxrb_ratio;
auto send1 = send_block_builder.make_block ()
.previous (latest)
.destination (key1.pub)
Expand Down Expand Up @@ -493,28 +493,33 @@ TEST (active_transactions, inactive_votes_cache_election_start)
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
.work (*system.work.generate (send3->hash ()))
.build_shared ();

// Inactive votes
std::vector<nano::block_hash> hashes{ open1->hash (), open2->hash (), send4->hash () };
auto vote1 (std::make_shared<nano::vote> (key1.pub, key1.prv, 0, 0, hashes));
auto vote1 = nano::test::make_vote (key1, { open1, open2, send4 });
node.vote_processor.vote (vote1, std::make_shared<nano::transport::inproc::channel> (node, node));
ASSERT_TIMELY (5s, node.inactive_vote_cache.cache_size () == 3);
ASSERT_TIMELY (5s, node.vote_cache.size () == 3);
ASSERT_TRUE (node.active.empty ());
ASSERT_EQ (1, node.ledger.cache.cemented_count);

// 2 votes are required to start election (dev network)
auto vote2 (std::make_shared<nano::vote> (key2.pub, key2.prv, 0, 0, hashes));
auto vote2 = nano::test::make_vote (key2, { open1, open2, send4 });
node.vote_processor.vote (vote2, std::make_shared<nano::transport::inproc::channel> (node, node));
// Only open1 & open2 blocks elections should start (send4 is missing previous block in ledger)
ASSERT_TIMELY (5s, 2 == node.active.size ());
// Only election for send1 should start, other blocks are missing dependencies and don't have enough final weight
ASSERT_TIMELY_EQ (5s, 1, node.active.size ());
ASSERT_TRUE (node.active.active (send1->hash ()));

// Confirm elections with weight quorum
auto vote0 (std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::vote::timestamp_max, nano::vote::duration_max, hashes)); // Final vote for confirmation
auto vote0 = nano::test::make_final_vote (nano::dev::genesis_key, { open1, open2, send4 });
node.vote_processor.vote (vote0, std::make_shared<nano::transport::inproc::channel> (node, node));
ASSERT_TIMELY (5s, node.active.empty ());
ASSERT_TIMELY_EQ (5s, 0, node.active.size ());
ASSERT_TIMELY (5s, 5 == node.ledger.cache.cemented_count);
ASSERT_TRUE (nano::test::confirmed (node, { send1, send2, open1, open2 }));

// A late block arrival also checks the inactive votes cache
ASSERT_TRUE (node.active.empty ());
auto send4_cache (node.inactive_vote_cache.find (send4->hash ()));
auto send4_cache (node.vote_cache.find (send4->hash ()));
ASSERT_TRUE (send4_cache);
ASSERT_EQ (3, send4_cache->voters.size ());
ASSERT_EQ (3, send4_cache->voters ().size ());
node.process_active (send3);
// An election is started for send6 but does not
ASSERT_FALSE (node.block_confirmed_or_being_confirmed (send3->hash ()));
Expand Down Expand Up @@ -960,8 +965,8 @@ TEST (active_transactions, fork_replacement_tally)
node1.vote_processor.vote (vote, std::make_shared<nano::transport::inproc::channel> (node1, node1));
node1.vote_processor.flush ();
// ensure vote arrives before the block
ASSERT_TIMELY (5s, node1.inactive_vote_cache.find (send_last->hash ()));
ASSERT_TIMELY (5s, 1 == node1.inactive_vote_cache.find (send_last->hash ())->size ());
ASSERT_TIMELY (5s, node1.vote_cache.find (send_last->hash ()));
ASSERT_TIMELY (5s, 1 == node1.vote_cache.find (send_last->hash ())->size ());
node1.network.publish_filter.clear ();
node2.network.flood_block (send_last);
ASSERT_TIMELY (5s, node1.stats.count (nano::stat::type::message, nano::stat::detail::publish, nano::stat::dir::in) > 1);
Expand Down Expand Up @@ -1427,7 +1432,7 @@ TEST (active_transactions, limit_vote_hinted_elections)

// Setup representatives
// Enough weight to trigger election hinting but not enough to confirm block on its own
const auto amount = ((node.online_reps.trended () / 100) * node.config.election_hint_weight_percent) + 1000 * nano::Gxrb_ratio;
const auto amount = ((node.online_reps.trended () / 100) * node.config.hinted_scheduler.hinting_threshold_percent) + 1000 * nano::Gxrb_ratio;
nano::keypair rep1 = nano::test::setup_rep (system, node, amount / 2);
nano::keypair rep2 = nano::test::setup_rep (system, node, amount / 2);

Expand All @@ -1444,7 +1449,7 @@ TEST (active_transactions, limit_vote_hinted_elections)
auto vote1 = nano::test::make_vote (rep1, { open0, open1 });
node.vote_processor.vote (vote1, nano::test::fake_channel (node));
// Ensure new inactive vote cache entries were created
ASSERT_TIMELY (5s, node.inactive_vote_cache.cache_size () == 2);
ASSERT_TIMELY (5s, node.vote_cache.size () == 2);
// And no elections are getting started yet
ASSERT_ALWAYS (1s, node.active.empty ());
// And nothing got confirmed yet
Expand Down Expand Up @@ -1517,7 +1522,7 @@ TEST (active_transactions, allow_limited_overflow)
{
// Non-final vote, so it stays in the AEC without getting confirmed
auto vote = nano::test::make_vote (nano::dev::genesis_key, { block });
node.inactive_vote_cache.vote (block->hash (), vote);
node.vote_cache.vote (block->hash (), vote);
}

// Ensure active elections overfill AEC only up to normal + hinted limit
Expand Down Expand Up @@ -1555,7 +1560,7 @@ TEST (active_transactions, allow_limited_overflow_adapt)
{
// Non-final vote, so it stays in the AEC without getting confirmed
auto vote = nano::test::make_vote (nano::dev::genesis_key, { block });
node.inactive_vote_cache.vote (block->hash (), vote);
node.vote_cache.vote (block->hash (), vote);
}

// Ensure hinted election amount is bounded by hinted limit
Expand Down
56 changes: 23 additions & 33 deletions nano/core_test/toml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ TEST (toml, daemon_config_deserialize_defaults)
ASSERT_EQ (conf.node.secondary_work_peers, defaults.node.secondary_work_peers);
ASSERT_EQ (conf.node.online_weight_minimum, defaults.node.online_weight_minimum);
ASSERT_EQ (conf.node.rep_crawler_weight_minimum, defaults.node.rep_crawler_weight_minimum);
ASSERT_EQ (conf.node.election_hint_weight_percent, defaults.node.election_hint_weight_percent);
ASSERT_EQ (conf.node.password_fanout, defaults.node.password_fanout);
ASSERT_EQ (conf.node.peering_port, defaults.node.peering_port);
ASSERT_EQ (conf.node.pow_sleep_interval, defaults.node.pow_sleep_interval);
Expand Down Expand Up @@ -272,6 +271,13 @@ TEST (toml, daemon_config_deserialize_defaults)
ASSERT_EQ (conf.node.optimistic_scheduler.enabled, defaults.node.optimistic_scheduler.enabled);
ASSERT_EQ (conf.node.optimistic_scheduler.gap_threshold, defaults.node.optimistic_scheduler.gap_threshold);
ASSERT_EQ (conf.node.optimistic_scheduler.max_size, defaults.node.optimistic_scheduler.max_size);

ASSERT_EQ (conf.node.hinted_scheduler.hinting_threshold_percent, defaults.node.hinted_scheduler.hinting_threshold_percent);
ASSERT_EQ (conf.node.hinted_scheduler.check_interval.count (), defaults.node.hinted_scheduler.check_interval.count ());
ASSERT_EQ (conf.node.hinted_scheduler.block_cooldown.count (), defaults.node.hinted_scheduler.block_cooldown.count ());

ASSERT_EQ (conf.node.vote_cache.max_size, defaults.node.vote_cache.max_size);
ASSERT_EQ (conf.node.vote_cache.max_voters, defaults.node.vote_cache.max_voters);
}

TEST (toml, optional_child)
Expand Down Expand Up @@ -425,7 +431,6 @@ TEST (toml, daemon_config_deserialize_no_defaults)
background_threads = 999
online_weight_minimum = "999"
rep_crawler_weight_minimum = "999"
election_hint_weight_percent = 19
password_fanout = 999
peering_port = 999
pow_sleep_interval= 999
Expand Down Expand Up @@ -534,6 +539,11 @@ TEST (toml, daemon_config_deserialize_no_defaults)
gap_threshold = 999
max_size = 999

[node.hinted_scheduler]
hinting_threshold = 99
check_interval = 999
block_cooldown = 999

[node.rocksdb]
enable = true
memory_multiplier = 3
Expand All @@ -544,6 +554,10 @@ TEST (toml, daemon_config_deserialize_no_defaults)
max_pruning_age = 999
max_pruning_depth = 999

[node.vote_cache]
max_size = 999
max_voters = 999

[opencl]
device = 999
enable = true
Expand Down Expand Up @@ -606,7 +620,6 @@ TEST (toml, daemon_config_deserialize_no_defaults)
ASSERT_NE (conf.node.max_pruning_depth, defaults.node.max_pruning_depth);
ASSERT_NE (conf.node.online_weight_minimum, defaults.node.online_weight_minimum);
ASSERT_NE (conf.node.rep_crawler_weight_minimum, defaults.node.rep_crawler_weight_minimum);
ASSERT_NE (conf.node.election_hint_weight_percent, defaults.node.election_hint_weight_percent);
ASSERT_NE (conf.node.password_fanout, defaults.node.password_fanout);
ASSERT_NE (conf.node.peering_port, defaults.node.peering_port);
ASSERT_NE (conf.node.pow_sleep_interval, defaults.node.pow_sleep_interval);
Expand Down Expand Up @@ -703,6 +716,13 @@ TEST (toml, daemon_config_deserialize_no_defaults)
ASSERT_NE (conf.node.optimistic_scheduler.enabled, defaults.node.optimistic_scheduler.enabled);
ASSERT_NE (conf.node.optimistic_scheduler.gap_threshold, defaults.node.optimistic_scheduler.gap_threshold);
ASSERT_NE (conf.node.optimistic_scheduler.max_size, defaults.node.optimistic_scheduler.max_size);

ASSERT_NE (conf.node.hinted_scheduler.hinting_threshold_percent, defaults.node.hinted_scheduler.hinting_threshold_percent);
ASSERT_NE (conf.node.hinted_scheduler.check_interval.count (), defaults.node.hinted_scheduler.check_interval.count ());
ASSERT_NE (conf.node.hinted_scheduler.block_cooldown.count (), defaults.node.hinted_scheduler.block_cooldown.count ());

ASSERT_NE (conf.node.vote_cache.max_size, defaults.node.vote_cache.max_size);
ASSERT_NE (conf.node.vote_cache.max_voters, defaults.node.vote_cache.max_voters);
}

/** There should be no required values **/
Expand Down Expand Up @@ -835,36 +855,6 @@ TEST (toml, daemon_config_deserialize_errors)
ASSERT_EQ (conf.node.frontiers_confirmation, nano::frontiers_confirmation_mode::invalid);
}

{
std::stringstream ss;
ss << R"toml(
[node]
election_hint_weight_percent = 4
)toml";

nano::tomlconfig toml;
toml.read (ss);
nano::daemon_config conf;
conf.deserialize_toml (toml);

ASSERT_EQ (toml.get_error ().get_message (), "election_hint_weight_percent must be a number between 5 and 50");
}

{
std::stringstream ss;
ss << R"toml(
[node]
election_hint_weight_percent = 51
)toml";

nano::tomlconfig toml;
toml.read (ss);
nano::daemon_config conf;
conf.deserialize_toml (toml);

ASSERT_EQ (toml.get_error ().get_message (), "election_hint_weight_percent must be a number between 5 and 50");
}

{
std::stringstream ss;
ss << R"toml(
Expand Down
Loading
Loading