Skip to content

Commit 0e83188

Browse files
committed
Track the number of active elections by each bucket and cap at the bucket maximum.
The number of elections started for a particular bucket is now limited on a per-bucket basis rather than globally across all buckets.
1 parent f5d8c39 commit 0e83188

15 files changed

+125
-386
lines changed

nano/core_test/active_elections.cpp

Lines changed: 6 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ TEST (active_elections, keep_local)
163163
nano::node_config node_config = system.default_config ();
164164
node_config.enable_voting = false;
165165
// Bound to 2, won't drop wallet created transactions, but good to test dropping remote
166-
node_config.active_elections.size = 2;
166+
node_config.priority_scheduler.bucket_maximum = 2;
167167
// Disable frontier confirmation to allow the test to finish before
168168
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
169169

@@ -192,7 +192,10 @@ TEST (active_elections, keep_local)
192192
ASSERT_NE (nullptr, send6);
193193

194194
// force-confirm blocks
195-
nano::test::confirm (node.ledger, send6);
195+
auto election = nano::test::start_election (system, node, send6->hash ());
196+
ASSERT_NE (nullptr, election);
197+
election->force_confirm ();
198+
ASSERT_TIMELY (5s, node.active.empty ());
196199

197200
nano::state_block_builder builder{};
198201
const auto receive1 = builder.make_block ()
@@ -227,8 +230,7 @@ TEST (active_elections, keep_local)
227230
node.process_active (receive3);
228231

229232
/// bound elections, should drop after one loop
230-
ASSERT_TIMELY_EQ (5s, node.active.size (), node_config.active_elections.size);
231-
// ASSERT_EQ (1, node.scheduler.size ());
233+
ASSERT_TIMELY_EQ (5s, node.active.size (), node_config.priority_scheduler.bucket_maximum);
232234
}
233235

234236
TEST (inactive_votes_cache, basic)
@@ -1389,101 +1391,3 @@ TEST (active_elections, limit_vote_hinted_elections)
13891391
// Ensure there was no overflow of elections
13901392
ASSERT_EQ (0, node.stats.count (nano::stat::type::active_dropped, nano::stat::detail::priority));
13911393
}
1392-
1393-
/*
1394-
* Tests that when AEC is running at capacity from normal elections, it is still possible to schedule a limited number of hinted elections
1395-
*/
1396-
TEST (active_elections, allow_limited_overflow)
1397-
{
1398-
nano::test::system system;
1399-
nano::node_config config = system.default_config ();
1400-
const int aec_limit = 20;
1401-
config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
1402-
config.active_elections.size = aec_limit;
1403-
config.active_elections.hinted_limit_percentage = 20; // Should give us a limit of 4 hinted elections
1404-
auto & node = *system.add_node (config);
1405-
1406-
auto blocks = nano::test::setup_independent_blocks (system, node, aec_limit * 4);
1407-
1408-
// Split blocks in two halves
1409-
std::vector<std::shared_ptr<nano::block>> blocks1 (blocks.begin (), blocks.begin () + blocks.size () / 2);
1410-
std::vector<std::shared_ptr<nano::block>> blocks2 (blocks.begin () + blocks.size () / 2, blocks.end ());
1411-
1412-
// Even though automatic frontier confirmation is disabled, AEC is doing funny stuff and inserting elections, clear that
1413-
WAIT (1s);
1414-
node.active.clear ();
1415-
ASSERT_TRUE (node.active.empty ());
1416-
1417-
// Insert the first part of the blocks into normal election scheduler
1418-
for (auto const & block : blocks1)
1419-
{
1420-
node.scheduler.priority.activate (node.ledger.tx_begin_read (), block->account ());
1421-
}
1422-
1423-
// Ensure number of active elections reaches AEC limit and there is no overfill
1424-
ASSERT_TIMELY_EQ (5s, node.active.size (), node.active.limit (nano::election_behavior::priority));
1425-
// And it stays that way without increasing
1426-
ASSERT_ALWAYS (1s, node.active.size () == node.active.limit (nano::election_behavior::priority));
1427-
1428-
// Insert votes for the second part of the blocks, so that those are scheduled as hinted elections
1429-
for (auto const & block : blocks2)
1430-
{
1431-
// Non-final vote, so it stays in the AEC without getting confirmed
1432-
auto vote = nano::test::make_vote (nano::dev::genesis_key, { block });
1433-
node.vote_cache.insert (vote);
1434-
}
1435-
1436-
// Ensure active elections overfill AEC only up to normal + hinted limit
1437-
ASSERT_TIMELY_EQ (5s, node.active.size (), node.active.limit (nano::election_behavior::priority) + node.active.limit (nano::election_behavior::hinted));
1438-
// And it stays that way without increasing
1439-
ASSERT_ALWAYS (1s, node.active.size () == node.active.limit (nano::election_behavior::priority) + node.active.limit (nano::election_behavior::hinted));
1440-
}
1441-
1442-
/*
1443-
* Tests that when hinted elections are present in the AEC, normal scheduler adapts not to exceed the limit of all elections
1444-
*/
1445-
TEST (active_elections, allow_limited_overflow_adapt)
1446-
{
1447-
nano::test::system system;
1448-
nano::node_config config = system.default_config ();
1449-
const int aec_limit = 20;
1450-
config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
1451-
config.active_elections.size = aec_limit;
1452-
config.active_elections.hinted_limit_percentage = 20; // Should give us a limit of 4 hinted elections
1453-
auto & node = *system.add_node (config);
1454-
1455-
auto blocks = nano::test::setup_independent_blocks (system, node, aec_limit * 4);
1456-
1457-
// Split blocks in two halves
1458-
std::vector<std::shared_ptr<nano::block>> blocks1 (blocks.begin (), blocks.begin () + blocks.size () / 2);
1459-
std::vector<std::shared_ptr<nano::block>> blocks2 (blocks.begin () + blocks.size () / 2, blocks.end ());
1460-
1461-
// Even though automatic frontier confirmation is disabled, AEC is doing funny stuff and inserting elections, clear that
1462-
WAIT (1s);
1463-
node.active.clear ();
1464-
ASSERT_TRUE (node.active.empty ());
1465-
1466-
// Insert votes for the second part of the blocks, so that those are scheduled as hinted elections
1467-
for (auto const & block : blocks2)
1468-
{
1469-
// Non-final vote, so it stays in the AEC without getting confirmed
1470-
auto vote = nano::test::make_vote (nano::dev::genesis_key, { block });
1471-
node.vote_cache.insert (vote);
1472-
}
1473-
1474-
// Ensure hinted election amount is bounded by hinted limit
1475-
ASSERT_TIMELY_EQ (5s, node.active.size (), node.active.limit (nano::election_behavior::hinted));
1476-
// And it stays that way without increasing
1477-
ASSERT_ALWAYS (1s, node.active.size () == node.active.limit (nano::election_behavior::hinted));
1478-
1479-
// Insert the first part of the blocks into normal election scheduler
1480-
for (auto const & block : blocks1)
1481-
{
1482-
node.scheduler.priority.activate (node.ledger.tx_begin_read (), block->account ());
1483-
}
1484-
1485-
// Ensure number of active elections reaches AEC limit and there is no overfill
1486-
ASSERT_TIMELY_EQ (5s, node.active.size (), node.active.limit (nano::election_behavior::priority));
1487-
// And it stays that way without increasing
1488-
ASSERT_ALWAYS (1s, node.active.size () == node.active.limit (nano::election_behavior::priority));
1489-
}

nano/core_test/election_scheduler.cpp

Lines changed: 0 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -53,96 +53,3 @@ TEST (election_scheduler, activate_one_flush)
5353
system.nodes[0]->scheduler.priority.activate (system.nodes[0]->ledger.tx_begin_read (), nano::dev::genesis_key.pub);
5454
ASSERT_TIMELY (5s, system.nodes[0]->active.election (send1->qualified_root ()));
5555
}
56-
57-
/**
58-
* Tests that the election scheduler and the active transactions container (AEC)
59-
* work in sync with regards to the node configuration value "active_elections.size".
60-
*
61-
* The test sets up two forcefully cemented blocks -- a send on the genesis account and a receive on a second account.
62-
* It then creates two other blocks, each a successor to one of the previous two,
63-
* and processes them locally (without the node starting elections for them, but just saving them to disk).
64-
*
65-
* Elections for these latter two (B1 and B2) are started by the test code manually via `election_scheduler::activate`.
66-
* The test expects E1 to start right off and take its seat into the AEC.
67-
* E2 is expected not to start though (because the AEC is full), so B2 should be awaiting in the scheduler's queue.
68-
*
69-
* As soon as the test code manually confirms E1 (and thus evicts it out of the AEC),
70-
* it is expected that E2 begins and the scheduler's queue becomes empty again.
71-
*/
72-
TEST (election_scheduler, no_vacancy)
73-
{
74-
nano::test::system system{};
75-
76-
nano::node_config config = system.default_config ();
77-
config.active_elections.size = 1;
78-
config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
79-
80-
auto & node = *system.add_node (config);
81-
nano::state_block_builder builder{};
82-
nano::keypair key{};
83-
84-
// Activating accounts depends on confirmed dependencies. First, prepare 2 accounts
85-
auto send = builder.make_block ()
86-
.account (nano::dev::genesis_key.pub)
87-
.previous (nano::dev::genesis->hash ())
88-
.representative (nano::dev::genesis_key.pub)
89-
.link (key.pub)
90-
.balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio)
91-
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
92-
.work (*system.work.generate (nano::dev::genesis->hash ()))
93-
.build ();
94-
ASSERT_EQ (nano::block_status::progress, node.process (send));
95-
node.process_confirmed (nano::election_status{ send });
96-
97-
auto receive = builder.make_block ()
98-
.account (key.pub)
99-
.previous (0)
100-
.representative (key.pub)
101-
.link (send->hash ())
102-
.balance (nano::Gxrb_ratio)
103-
.sign (key.prv, key.pub)
104-
.work (*system.work.generate (key.pub))
105-
.build ();
106-
ASSERT_EQ (nano::block_status::progress, node.process (receive));
107-
node.process_confirmed (nano::election_status{ receive });
108-
109-
ASSERT_TIMELY (5s, nano::test::confirmed (node, { send, receive }));
110-
111-
// Second, process two eligible transactions
112-
auto block1 = builder.make_block ()
113-
.account (nano::dev::genesis_key.pub)
114-
.previous (send->hash ())
115-
.representative (nano::dev::genesis_key.pub)
116-
.link (nano::dev::genesis_key.pub)
117-
.balance (nano::dev::constants.genesis_amount - 2 * nano::Gxrb_ratio)
118-
.sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub)
119-
.work (*system.work.generate (send->hash ()))
120-
.build ();
121-
ASSERT_EQ (nano::block_status::progress, node.process (block1));
122-
123-
// There is vacancy so it should be inserted
124-
node.scheduler.priority.activate (node.ledger.tx_begin_read (), nano::dev::genesis_key.pub);
125-
std::shared_ptr<nano::election> election{};
126-
ASSERT_TIMELY (5s, (election = node.active.election (block1->qualified_root ())) != nullptr);
127-
128-
auto block2 = builder.make_block ()
129-
.account (key.pub)
130-
.previous (receive->hash ())
131-
.representative (key.pub)
132-
.link (key.pub)
133-
.balance (0)
134-
.sign (key.prv, key.pub)
135-
.work (*system.work.generate (receive->hash ()))
136-
.build ();
137-
ASSERT_EQ (nano::block_status::progress, node.process (block2));
138-
139-
// There is no vacancy so it should stay queued
140-
node.scheduler.priority.activate (node.ledger.tx_begin_read (), key.pub);
141-
ASSERT_TIMELY_EQ (5s, node.scheduler.priority.size (), 1);
142-
ASSERT_EQ (node.active.election (block2->qualified_root ()), nullptr);
143-
144-
// Election confirmed, next in queue should begin
145-
election->force_confirm ();
146-
ASSERT_TIMELY (5s, node.active.election (block2->qualified_root ()) != nullptr);
147-
ASSERT_TRUE (node.scheduler.priority.empty ());
148-
}

0 commit comments

Comments
 (0)