From 6f8055718f89daef02f217907d3c1b2f6009fdb3 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Thu, 9 May 2024 16:24:49 +0100 Subject: [PATCH 01/14] Splitting manual election_behavior from normal. --- nano/core_test/active_elections.cpp | 6 +++--- nano/core_test/election.cpp | 2 +- nano/lib/stats_enums.hpp | 1 + nano/node/active_elections.cpp | 6 ++++++ nano/node/election.cpp | 2 ++ nano/node/election_behavior.hpp | 1 + nano/node/json_handler.cpp | 4 ++++ nano/node/scheduler/manual.cpp | 2 +- 8 files changed, 19 insertions(+), 5 deletions(-) diff --git a/nano/core_test/active_elections.cpp b/nano/core_test/active_elections.cpp index d36d764a5b..a99feb48d1 100644 --- a/nano/core_test/active_elections.cpp +++ b/nano/core_test/active_elections.cpp @@ -669,7 +669,7 @@ TEST (active_elections, dropped_cleanup) ASSERT_FALSE (node.network.publish_filter.apply (block_bytes.data (), block_bytes.size ())); // An election was recently dropped - ASSERT_EQ (1, node.stats.count (nano::stat::type::active_dropped, nano::stat::detail::normal)); + ASSERT_EQ (1, node.stats.count (nano::stat::type::active_dropped, nano::stat::detail::manual)); // Block cleared from active ASSERT_FALSE (node.vote_router.active (hash)); @@ -687,7 +687,7 @@ TEST (active_elections, dropped_cleanup) ASSERT_TRUE (node.network.publish_filter.apply (block_bytes.data (), block_bytes.size ())); // Not dropped - ASSERT_EQ (1, node.stats.count (nano::stat::type::active_dropped, nano::stat::detail::normal)); + ASSERT_EQ (1, node.stats.count (nano::stat::type::active_dropped, nano::stat::detail::manual)); // Block cleared from active ASSERT_FALSE (node.vote_router.active (hash)); @@ -1418,7 +1418,7 @@ TEST (active_elections, fifo) ASSERT_TIMELY_EQ (5s, node.active.size (), 1); // Ensure overflow stats have been incremented - ASSERT_EQ (1, node.stats.count (nano::stat::type::active_dropped, nano::stat::detail::normal)); + ASSERT_EQ (1, node.stats.count (nano::stat::type::active_dropped, nano::stat::detail::manual)); // Ensure the surviving transaction is the least recently inserted ASSERT_TIMELY (1s, node.active.election (receive2->qualified_root ()) != nullptr); diff --git a/nano/core_test/election.cpp b/nano/core_test/election.cpp index c285e12c6d..7d8bda40d7 100644 --- a/nano/core_test/election.cpp +++ b/nano/core_test/election.cpp @@ -27,7 +27,7 @@ TEST (election, behavior) auto chain = nano::test::setup_chain (system, *system.nodes[0], 1, nano::dev::genesis_key, false); auto election = nano::test::start_election (system, *system.nodes[0], chain[0]->hash ()); ASSERT_NE (nullptr, election); - ASSERT_EQ (nano::election_behavior::normal, election->behavior ()); + ASSERT_EQ (nano::election_behavior::manual, election->behavior ()); } TEST (election, quorum_minimum_flip_success) diff --git a/nano/lib/stats_enums.hpp b/nano/lib/stats_enums.hpp index 616a8a573e..397443c426 100644 --- a/nano/lib/stats_enums.hpp +++ b/nano/lib/stats_enums.hpp @@ -219,6 +219,7 @@ enum class detail broadcast_block_repeat, // election types + manual, normal, hinted, optimistic, diff --git a/nano/node/active_elections.cpp b/nano/node/active_elections.cpp index 3c223752df..378974d419 100644 --- a/nano/node/active_elections.cpp +++ b/nano/node/active_elections.cpp @@ -187,6 +187,10 @@ int64_t nano::active_elections::limit (nano::election_behavior behavior) const { switch (behavior) { + case nano::election_behavior::manual: + { + return std::numeric_limits::max (); + } case nano::election_behavior::normal: { return static_cast (config.size); @@ -212,6 +216,8 @@ int64_t nano::active_elections::vacancy (nano::election_behavior behavior) const nano::lock_guard guard{ mutex }; switch (behavior) { + case nano::election_behavior::manual: + return std::numeric_limits::max (); case nano::election_behavior::normal: return limit () - static_cast (roots.size ()); case nano::election_behavior::hinted: diff --git a/nano/node/election.cpp b/nano/node/election.cpp index 56415165a1..404458ba0e 100644 --- a/nano/node/election.cpp +++ b/nano/node/election.cpp @@ -142,6 +142,7 @@ std::chrono::milliseconds nano::election::confirm_req_time () const { switch (behavior ()) { + case election_behavior::manual: case election_behavior::normal: case election_behavior::hinted: return base_latency () * 5; @@ -295,6 +296,7 @@ std::chrono::milliseconds nano::election::time_to_live () const { switch (behavior ()) { + case election_behavior::manual: case election_behavior::normal: return std::chrono::milliseconds (5 * 60 * 1000); case election_behavior::hinted: diff --git a/nano/node/election_behavior.hpp b/nano/node/election_behavior.hpp index c38b1c8de6..a4f1dee910 100644 --- a/nano/node/election_behavior.hpp +++ b/nano/node/election_behavior.hpp @@ -8,6 +8,7 @@ namespace nano { enum class election_behavior { + manual, normal, /** * Hinted elections: diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index d1978048ef..fb76b3b5ec 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -2009,6 +2009,7 @@ void nano::json_handler::confirmation_active () void nano::json_handler::election_statistics () { auto active_elections = node.active.list_active (); + unsigned manual_count = 0; unsigned normal_count = 0; unsigned hinted_count = 0; unsigned optimistic_count = 0; @@ -2027,6 +2028,9 @@ void nano::json_handler::election_statistics () switch (election->behavior ()) { + case election_behavior::manual: + manual_count++; + break; case election_behavior::normal: normal_count++; break; diff --git a/nano/node/scheduler/manual.cpp b/nano/node/scheduler/manual.cpp index 85108e6a82..3235a91c20 100644 --- a/nano/node/scheduler/manual.cpp +++ b/nano/node/scheduler/manual.cpp @@ -42,7 +42,7 @@ void nano::scheduler::manual::notify () void nano::scheduler::manual::push (std::shared_ptr const & block_a, boost::optional const & previous_balance_a) { nano::lock_guard lock{ mutex }; - queue.push_back (std::make_tuple (block_a, previous_balance_a, nano::election_behavior::normal)); + queue.push_back (std::make_tuple (block_a, previous_balance_a, nano::election_behavior::manual)); notify (); } From 97696ea8a0df7a2649cb2ee0dd2208798fb6ecd2 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Fri, 10 May 2024 09:26:05 +0100 Subject: [PATCH 02/14] Renaming election_behavior::normal to election_behavior::priority --- nano/core_test/active_elections.cpp | 2 +- nano/core_test/confirmation_solicitor.cpp | 10 +++++----- nano/core_test/election.cpp | 2 +- nano/lib/stats_enums.hpp | 2 +- nano/node/active_elections.cpp | 6 +++--- nano/node/active_elections.hpp | 6 +++--- nano/node/election.cpp | 4 ++-- nano/node/election.hpp | 2 +- nano/node/election_behavior.hpp | 2 +- nano/node/json_handler.cpp | 8 ++++---- nano/rpc_test/rpc.cpp | 2 +- 11 files changed, 23 insertions(+), 23 deletions(-) diff --git a/nano/core_test/active_elections.cpp b/nano/core_test/active_elections.cpp index a99feb48d1..bfc162b4af 100644 --- a/nano/core_test/active_elections.cpp +++ b/nano/core_test/active_elections.cpp @@ -1487,7 +1487,7 @@ TEST (active_elections, limit_vote_hinted_elections) ASSERT_TIMELY (5s, nano::test::active (node, { open1 })); // Ensure there was no overflow of elections - ASSERT_EQ (0, node.stats.count (nano::stat::type::active_dropped, nano::stat::detail::normal)); + ASSERT_EQ (0, node.stats.count (nano::stat::type::active_dropped, nano::stat::detail::priority)); } /* diff --git a/nano/core_test/confirmation_solicitor.cpp b/nano/core_test/confirmation_solicitor.cpp index f3609a36a6..ca48a2ee8a 100644 --- a/nano/core_test/confirmation_solicitor.cpp +++ b/nano/core_test/confirmation_solicitor.cpp @@ -46,11 +46,11 @@ TEST (confirmation_solicitor, batches) nano::lock_guard guard (node2.active.mutex); for (size_t i (0); i < nano::network::confirm_req_hashes_max; ++i) { - auto election (std::make_shared (node2, send, nullptr, nullptr, nano::election_behavior::normal)); + auto election (std::make_shared (node2, send, nullptr, nullptr, nano::election_behavior::priority)); ASSERT_FALSE (solicitor.add (*election)); } // Reached the maximum amount of requests for the channel - auto election (std::make_shared (node2, send, nullptr, nullptr, nano::election_behavior::normal)); + auto election (std::make_shared (node2, send, nullptr, nullptr, nano::election_behavior::priority)); // Broadcasting should be immediate ASSERT_EQ (0, node2.stats.count (nano::stat::type::message, nano::stat::detail::publish, nano::stat::dir::out)); ASSERT_FALSE (solicitor.broadcast (*election)); @@ -92,7 +92,7 @@ TEST (confirmation_solicitor, different_hash) .work (*system.work.generate (nano::dev::genesis->hash ())) .build (); send->sideband_set ({}); - auto election (std::make_shared (node2, send, nullptr, nullptr, nano::election_behavior::normal)); + auto election (std::make_shared (node2, send, nullptr, nullptr, nano::election_behavior::priority)); // Add a vote for something else, not the winner election->last_votes[representative.account] = { std::chrono::steady_clock::now (), 1, 1 }; // Ensure the request and broadcast goes through @@ -136,7 +136,7 @@ TEST (confirmation_solicitor, bypass_max_requests_cap) .work (*system.work.generate (nano::dev::genesis->hash ())) .build (); send->sideband_set ({}); - auto election (std::make_shared (node2, send, nullptr, nullptr, nano::election_behavior::normal)); + auto election (std::make_shared (node2, send, nullptr, nullptr, nano::election_behavior::priority)); // Add a vote for something else, not the winner for (auto const & rep : representatives) { @@ -149,7 +149,7 @@ TEST (confirmation_solicitor, bypass_max_requests_cap) ASSERT_TIMELY_EQ (6s, max_representatives + 1, node2.stats.count (nano::stat::type::message, nano::stat::detail::confirm_req, nano::stat::dir::out)); solicitor.prepare (representatives); - auto election2 (std::make_shared (node2, send, nullptr, nullptr, nano::election_behavior::normal)); + auto election2 (std::make_shared (node2, send, nullptr, nullptr, nano::election_behavior::priority)); ASSERT_FALSE (solicitor.add (*election2)); ASSERT_FALSE (solicitor.broadcast (*election2)); diff --git a/nano/core_test/election.cpp b/nano/core_test/election.cpp index 7d8bda40d7..82bcb7f131 100644 --- a/nano/core_test/election.cpp +++ b/nano/core_test/election.cpp @@ -18,7 +18,7 @@ TEST (election, construction) nano::test::system system (1); auto & node = *system.nodes[0]; auto election = std::make_shared ( - node, nano::dev::genesis, [] (auto const &) {}, [] (auto const &) {}, nano::election_behavior::normal); + node, nano::dev::genesis, [] (auto const &) {}, [] (auto const &) {}, nano::election_behavior::priority); } TEST (election, behavior) diff --git a/nano/lib/stats_enums.hpp b/nano/lib/stats_enums.hpp index 397443c426..5c6b4f3f84 100644 --- a/nano/lib/stats_enums.hpp +++ b/nano/lib/stats_enums.hpp @@ -220,7 +220,7 @@ enum class detail // election types manual, - normal, + priority, hinted, optimistic, diff --git a/nano/node/active_elections.cpp b/nano/node/active_elections.cpp index 378974d419..2275e0d586 100644 --- a/nano/node/active_elections.cpp +++ b/nano/node/active_elections.cpp @@ -191,7 +191,7 @@ int64_t nano::active_elections::limit (nano::election_behavior behavior) const { return std::numeric_limits::max (); } - case nano::election_behavior::normal: + case nano::election_behavior::priority: { return static_cast (config.size); } @@ -218,7 +218,7 @@ int64_t nano::active_elections::vacancy (nano::election_behavior behavior) const { case nano::election_behavior::manual: return std::numeric_limits::max (); - case nano::election_behavior::normal: + case nano::election_behavior::priority: return limit () - static_cast (roots.size ()); case nano::election_behavior::hinted: case nano::election_behavior::optimistic: @@ -563,7 +563,7 @@ std::unique_ptr nano::collect_container_info (ac auto composite = std::make_unique (name); composite->add_component (std::make_unique (container_info{ "roots", active_elections.roots.size (), sizeof (decltype (active_elections.roots)::value_type) })); composite->add_component (std::make_unique (container_info{ "election_winner_details", active_elections.election_winner_details_size (), sizeof (decltype (active_elections.election_winner_details)::value_type) })); - composite->add_component (std::make_unique (container_info{ "normal", static_cast (active_elections.count_by_behavior[nano::election_behavior::normal]), 0 })); + composite->add_component (std::make_unique (container_info{ "normal", static_cast (active_elections.count_by_behavior[nano::election_behavior::priority]), 0 })); composite->add_component (std::make_unique (container_info{ "hinted", static_cast (active_elections.count_by_behavior[nano::election_behavior::hinted]), 0 })); composite->add_component (std::make_unique (container_info{ "optimistic", static_cast (active_elections.count_by_behavior[nano::election_behavior::optimistic]), 0 })); diff --git a/nano/node/active_elections.hpp b/nano/node/active_elections.hpp index 5631b8c2f8..26340ea748 100644 --- a/nano/node/active_elections.hpp +++ b/nano/node/active_elections.hpp @@ -108,7 +108,7 @@ class active_elections final /** * Starts new election with a specified behavior type */ - nano::election_insertion_result insert (std::shared_ptr const &, nano::election_behavior = nano::election_behavior::normal); + nano::election_insertion_result insert (std::shared_ptr const &, nano::election_behavior = nano::election_behavior::priority); // Is the root of this block in the roots container bool active (nano::block const &) const; bool active (nano::qualified_root const &) const; @@ -128,11 +128,11 @@ class active_elections final * Maximum number of elections that should be present in this container * NOTE: This is only a soft limit, it is possible for this container to exceed this count */ - int64_t limit (nano::election_behavior behavior = nano::election_behavior::normal) const; + int64_t limit (nano::election_behavior behavior = nano::election_behavior::priority) const; /** * How many election slots are available for specified election type */ - int64_t vacancy (nano::election_behavior behavior = nano::election_behavior::normal) const; + int64_t vacancy (nano::election_behavior behavior = nano::election_behavior::priority) const; std::function vacancy_update{ [] () {} }; std::size_t election_winner_details_size (); diff --git a/nano/node/election.cpp b/nano/node/election.cpp index 404458ba0e..02d6a0cb1a 100644 --- a/nano/node/election.cpp +++ b/nano/node/election.cpp @@ -143,7 +143,7 @@ std::chrono::milliseconds nano::election::confirm_req_time () const switch (behavior ()) { case election_behavior::manual: - case election_behavior::normal: + case election_behavior::priority: case election_behavior::hinted: return base_latency () * 5; case election_behavior::optimistic: @@ -297,7 +297,7 @@ std::chrono::milliseconds nano::election::time_to_live () const switch (behavior ()) { case election_behavior::manual: - case election_behavior::normal: + case election_behavior::priority: return std::chrono::milliseconds (5 * 60 * 1000); case election_behavior::hinted: case election_behavior::optimistic: diff --git a/nano/node/election.hpp b/nano/node/election.hpp index 2ebe729b8f..a8ab8c05e5 100644 --- a/nano/node/election.hpp +++ b/nano/node/election.hpp @@ -172,7 +172,7 @@ class election final : public std::enable_shared_from_this mutable nano::uint128_t final_weight{ 0 }; mutable std::unordered_map last_tally; - nano::election_behavior const behavior_m{ nano::election_behavior::normal }; + nano::election_behavior const behavior_m{ nano::election_behavior::priority }; std::chrono::steady_clock::time_point const election_start{ std::chrono::steady_clock::now () }; mutable nano::mutex mutex; diff --git a/nano/node/election_behavior.hpp b/nano/node/election_behavior.hpp index a4f1dee910..1224b1787d 100644 --- a/nano/node/election_behavior.hpp +++ b/nano/node/election_behavior.hpp @@ -9,7 +9,7 @@ namespace nano enum class election_behavior { manual, - normal, + priority, /** * Hinted elections: * - shorter timespan diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index fb76b3b5ec..62a5ee193c 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -2010,7 +2010,7 @@ void nano::json_handler::election_statistics () { auto active_elections = node.active.list_active (); unsigned manual_count = 0; - unsigned normal_count = 0; + unsigned priority_count = 0; unsigned hinted_count = 0; unsigned optimistic_count = 0; unsigned total_count = 0; @@ -2031,8 +2031,8 @@ void nano::json_handler::election_statistics () case election_behavior::manual: manual_count++; break; - case election_behavior::normal: - normal_count++; + case election_behavior::priority: + priority_count++; break; case election_behavior::hinted: hinted_count++; @@ -2050,7 +2050,7 @@ void nano::json_handler::election_statistics () std::stringstream stream_utilization; stream_utilization << std::fixed << std::setprecision (2) << utilization_percentage; - response_l.put ("normal", normal_count); + response_l.put ("priority", priority_count); response_l.put ("hinted", hinted_count); response_l.put ("optimistic", optimistic_count); response_l.put ("total", total_count); diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 37e3e66cf2..3093ca9ab6 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -6962,7 +6962,7 @@ TEST (rpc, election_statistics) request.put ("action", "election_statistics"); auto response = wait_response (system, rpc_ctx, request); - ASSERT_EQ ("1", response.get ("normal")); + ASSERT_EQ ("1", response.get ("priority")); ASSERT_EQ ("0", response.get ("hinted")); ASSERT_EQ ("0", response.get ("optimistic")); ASSERT_EQ ("1", response.get ("total")); From 6690d1fe2414af930d8d26b0d2240adc71efcb74 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Fri, 10 May 2024 09:58:22 +0100 Subject: [PATCH 03/14] Remove default nano::election_behavior arguments --- nano/core_test/active_elections.cpp | 18 +++++++++--------- nano/node/active_elections.cpp | 4 ++-- nano/node/active_elections.hpp | 4 ++-- nano/node/election.hpp | 4 ++-- nano/node/scheduler/manual.hpp | 2 +- nano/node/scheduler/priority.cpp | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/nano/core_test/active_elections.cpp b/nano/core_test/active_elections.cpp index bfc162b4af..d110111cc5 100644 --- a/nano/core_test/active_elections.cpp +++ b/nano/core_test/active_elections.cpp @@ -1325,18 +1325,18 @@ TEST (active_elections, vacancy) .build (); node.active.vacancy_update = [&updated] () { updated = true; }; ASSERT_EQ (nano::block_status::progress, node.process (send)); - ASSERT_EQ (1, node.active.vacancy ()); + ASSERT_EQ (1, node.active.vacancy (nano::election_behavior::priority)); ASSERT_EQ (0, node.active.size ()); node.scheduler.priority.activate (node.ledger.tx_begin_read (), nano::dev::genesis_key.pub); ASSERT_TIMELY (1s, updated); updated = false; - ASSERT_EQ (0, node.active.vacancy ()); + ASSERT_EQ (0, node.active.vacancy (nano::election_behavior::priority)); ASSERT_EQ (1, node.active.size ()); auto election1 = node.active.election (send->qualified_root ()); ASSERT_NE (nullptr, election1); election1->force_confirm (); ASSERT_TIMELY (1s, updated); - ASSERT_EQ (1, node.active.vacancy ()); + ASSERT_EQ (1, node.active.vacancy (nano::election_behavior::priority)); ASSERT_EQ (0, node.active.size ()); } } @@ -1521,9 +1521,9 @@ TEST (active_elections, allow_limited_overflow) } // Ensure number of active elections reaches AEC limit and there is no overfill - ASSERT_TIMELY_EQ (5s, node.active.size (), node.active.limit ()); + ASSERT_TIMELY_EQ (5s, node.active.size (), node.active.limit (nano::election_behavior::priority)); // And it stays that way without increasing - ASSERT_ALWAYS (1s, node.active.size () == node.active.limit ()); + ASSERT_ALWAYS (1s, node.active.size () == node.active.limit (nano::election_behavior::priority)); // Insert votes for the second part of the blocks, so that those are scheduled as hinted elections for (auto const & block : blocks2) @@ -1534,9 +1534,9 @@ TEST (active_elections, allow_limited_overflow) } // Ensure active elections overfill AEC only up to normal + hinted limit - ASSERT_TIMELY_EQ (5s, node.active.size (), node.active.limit () + node.active.limit (nano::election_behavior::hinted)); + ASSERT_TIMELY_EQ (5s, node.active.size (), node.active.limit (nano::election_behavior::priority) + node.active.limit (nano::election_behavior::hinted)); // And it stays that way without increasing - ASSERT_ALWAYS (1s, node.active.size () == node.active.limit () + node.active.limit (nano::election_behavior::hinted)); + ASSERT_ALWAYS (1s, node.active.size () == node.active.limit (nano::election_behavior::priority) + node.active.limit (nano::election_behavior::hinted)); } /* @@ -1583,7 +1583,7 @@ TEST (active_elections, allow_limited_overflow_adapt) } // Ensure number of active elections reaches AEC limit and there is no overfill - ASSERT_TIMELY_EQ (5s, node.active.size (), node.active.limit ()); + ASSERT_TIMELY_EQ (5s, node.active.size (), node.active.limit (nano::election_behavior::priority)); // And it stays that way without increasing - ASSERT_ALWAYS (1s, node.active.size () == node.active.limit ()); + ASSERT_ALWAYS (1s, node.active.size () == node.active.limit (nano::election_behavior::priority)); } diff --git a/nano/node/active_elections.cpp b/nano/node/active_elections.cpp index 2275e0d586..88eb6398e4 100644 --- a/nano/node/active_elections.cpp +++ b/nano/node/active_elections.cpp @@ -219,7 +219,7 @@ int64_t nano::active_elections::vacancy (nano::election_behavior behavior) const case nano::election_behavior::manual: return std::numeric_limits::max (); case nano::election_behavior::priority: - return limit () - static_cast (roots.size ()); + return limit (nano::election_behavior::priority) - static_cast (roots.size ()); case nano::election_behavior::hinted: case nano::election_behavior::optimistic: return limit (behavior) - count_by_behavior[behavior]; @@ -372,7 +372,7 @@ void nano::active_elections::trim () * However, it is possible that AEC will be temporarily overfilled in case it's running at full capacity and election hinting or manual queue kicks in. * That case will lead to unwanted churning of elections, so this allows for AEC to be overfilled to 125% until erasing of elections happens. */ - while (vacancy () < -(limit () / 4)) + while (vacancy (nano::election_behavior::priority) < -(limit (nano::election_behavior::priority) / 4)) { node.stats.inc (nano::stat::type::active, nano::stat::detail::erase_oldest); erase_oldest (); diff --git a/nano/node/active_elections.hpp b/nano/node/active_elections.hpp index 26340ea748..5e4b0aff05 100644 --- a/nano/node/active_elections.hpp +++ b/nano/node/active_elections.hpp @@ -128,11 +128,11 @@ class active_elections final * Maximum number of elections that should be present in this container * NOTE: This is only a soft limit, it is possible for this container to exceed this count */ - int64_t limit (nano::election_behavior behavior = nano::election_behavior::priority) const; + int64_t limit (nano::election_behavior behavior) const; /** * How many election slots are available for specified election type */ - int64_t vacancy (nano::election_behavior behavior = nano::election_behavior::priority) const; + int64_t vacancy (nano::election_behavior behavior) const; std::function vacancy_update{ [] () {} }; std::size_t election_winner_details_size (); diff --git a/nano/node/election.hpp b/nano/node/election.hpp index a8ab8c05e5..6111191e2b 100644 --- a/nano/node/election.hpp +++ b/nano/node/election.hpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -17,6 +16,7 @@ namespace nano class block; class channel; class confirmation_solicitor; +enum class election_behavior; class inactive_cache_information; class node; enum class vote_code; @@ -172,7 +172,7 @@ class election final : public std::enable_shared_from_this mutable nano::uint128_t final_weight{ 0 }; mutable std::unordered_map last_tally; - nano::election_behavior const behavior_m{ nano::election_behavior::priority }; + nano::election_behavior const behavior_m; std::chrono::steady_clock::time_point const election_start{ std::chrono::steady_clock::now () }; mutable nano::mutex mutex; diff --git a/nano/node/scheduler/manual.hpp b/nano/node/scheduler/manual.hpp index ee6c060048..648a7e934c 100644 --- a/nano/node/scheduler/manual.hpp +++ b/nano/node/scheduler/manual.hpp @@ -1,7 +1,6 @@ #pragma once #include #include -#include #include @@ -12,6 +11,7 @@ namespace nano { class block; +enum class election_behavior; class node; } diff --git a/nano/node/scheduler/priority.cpp b/nano/node/scheduler/priority.cpp index 0a91f8e560..d16d33fe56 100644 --- a/nano/node/scheduler/priority.cpp +++ b/nano/node/scheduler/priority.cpp @@ -95,7 +95,7 @@ bool nano::scheduler::priority::empty () const bool nano::scheduler::priority::predicate () const { - return node.active.vacancy () > 0 && !buckets->empty (); + return node.active.vacancy (nano::election_behavior::priority) > 0 && !buckets->empty (); } void nano::scheduler::priority::run () From fd65cd9e14eb055d8440bfc040a44dde6aa3b129 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Fri, 10 May 2024 10:20:10 +0100 Subject: [PATCH 04/14] Remove active_elections::trim Each scheduler checks its own limits with calls to active_elections::vacancy. Trim is problematic as it indiscriminately cancels elections without consideration to why it was scheduled or its priority. --- nano/core_test/active_elections.cpp | 83 ----------------------------- nano/node/active_elections.cpp | 26 --------- nano/node/active_elections.hpp | 3 -- 3 files changed, 112 deletions(-) diff --git a/nano/core_test/active_elections.cpp b/nano/core_test/active_elections.cpp index d110111cc5..9818755d07 100644 --- a/nano/core_test/active_elections.cpp +++ b/nano/core_test/active_elections.cpp @@ -1341,89 +1341,6 @@ TEST (active_elections, vacancy) } } -// Ensure transactions in excess of capacity are removed in fifo order -TEST (active_elections, fifo) -{ - nano::test::system system{}; - - nano::node_config config = system.default_config (); - config.active_elections.size = 1; - config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - - auto & node = *system.add_node (config); - auto latest_hash = nano::dev::genesis->hash (); - nano::keypair key0{}; - nano::state_block_builder builder{}; - - // Construct two pending entries that can be received simultaneously - auto send1 = builder.make_block () - .previous (latest_hash) - .account (nano::dev::genesis_key.pub) - .representative (nano::dev::genesis_key.pub) - .link (key0.pub) - .balance (nano::dev::constants.genesis_amount - 1) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (latest_hash)) - .build (); - ASSERT_EQ (nano::block_status::progress, node.process (send1)); - node.process_confirmed (nano::election_status{ send1 }); - ASSERT_TIMELY (5s, node.block_confirmed (send1->hash ())); - - nano::keypair key1{}; - latest_hash = send1->hash (); - auto send2 = builder.make_block () - .previous (latest_hash) - .account (nano::dev::genesis_key.pub) - .representative (nano::dev::genesis_key.pub) - .link (key1.pub) - .balance (nano::dev::constants.genesis_amount - 2) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (latest_hash)) - .build (); - ASSERT_EQ (nano::block_status::progress, node.process (send2)); - node.process_confirmed (nano::election_status{ send2 }); - ASSERT_TIMELY (5s, node.block_confirmed (send2->hash ())); - - auto receive1 = builder.make_block () - .previous (0) - .account (key0.pub) - .representative (nano::dev::genesis_key.pub) - .link (send1->hash ()) - .balance (1) - .sign (key0.prv, key0.pub) - .work (*system.work.generate (key0.pub)) - .build (); - ASSERT_EQ (nano::block_status::progress, node.process (receive1)); - - auto receive2 = builder.make_block () - .previous (0) - .account (key1.pub) - .representative (nano::dev::genesis_key.pub) - .link (send2->hash ()) - .balance (1) - .sign (key1.prv, key1.pub) - .work (*system.work.generate (key1.pub)) - .build (); - ASSERT_EQ (nano::block_status::progress, node.process (receive2)); - - // Ensure first transaction becomes active - node.scheduler.manual.push (receive1); - ASSERT_TIMELY (5s, node.active.election (receive1->qualified_root ()) != nullptr); - - // Ensure second transaction becomes active - node.scheduler.manual.push (receive2); - ASSERT_TIMELY (5s, node.active.election (receive2->qualified_root ()) != nullptr); - - // Ensure excess transactions get trimmed - ASSERT_TIMELY_EQ (5s, node.active.size (), 1); - - // Ensure overflow stats have been incremented - ASSERT_EQ (1, node.stats.count (nano::stat::type::active_dropped, nano::stat::detail::manual)); - - // Ensure the surviving transaction is the least recently inserted - ASSERT_TIMELY (1s, node.active.election (receive2->qualified_root ()) != nullptr); -} - /* * Ensures we limit the number of vote hinted elections in AEC */ diff --git a/nano/node/active_elections.cpp b/nano/node/active_elections.cpp index 88eb6398e4..b0e3507306 100644 --- a/nano/node/active_elections.cpp +++ b/nano/node/active_elections.cpp @@ -365,20 +365,6 @@ void nano::active_elections::request_loop () } } -void nano::active_elections::trim () -{ - /* - * Both normal and hinted election schedulers are well-behaved, meaning they first check for AEC vacancy before inserting new elections. - * However, it is possible that AEC will be temporarily overfilled in case it's running at full capacity and election hinting or manual queue kicks in. - * That case will lead to unwanted churning of elections, so this allows for AEC to be overfilled to 125% until erasing of elections happens. - */ - while (vacancy (nano::election_behavior::priority) < -(limit (nano::election_behavior::priority) / 4)) - { - node.stats.inc (nano::stat::type::active, nano::stat::detail::erase_oldest); - erase_oldest (); - } -} - nano::election_insertion_result nano::active_elections::insert (std::shared_ptr const & block_a, nano::election_behavior election_behavior_a) { debug_assert (block_a); @@ -449,8 +435,6 @@ nano::election_insertion_result nano::active_elections::insert (std::shared_ptr< result.election->broadcast_vote (); } - trim (); - return result; } @@ -495,16 +479,6 @@ bool nano::active_elections::erase (nano::qualified_root const & root_a) return false; } -void nano::active_elections::erase_oldest () -{ - nano::unique_lock lock{ mutex }; - if (!roots.empty ()) - { - auto item = roots.get ().front (); - cleanup_election (lock, item.election); - } -} - bool nano::active_elections::empty () const { nano::lock_guard lock{ mutex }; diff --git a/nano/node/active_elections.hpp b/nano/node/active_elections.hpp index 5e4b0aff05..869912f5c8 100644 --- a/nano/node/active_elections.hpp +++ b/nano/node/active_elections.hpp @@ -117,7 +117,6 @@ class active_elections final std::vector> list_active (std::size_t = std::numeric_limits::max ()); bool erase (nano::block const &); bool erase (nano::qualified_root const &); - void erase_oldest (); bool empty () const; std::size_t size () const; bool publish (std::shared_ptr const &); @@ -140,8 +139,6 @@ class active_elections final std::shared_ptr remove_election_winner_details (nano::block_hash const &); private: - // Erase elections if we're over capacity - void trim (); void request_loop (); void request_confirm (nano::unique_lock &); // Erase all blocks from active and, if not confirmed, clear digests from network filters From 6b308b3c699cb99a882a7a1b82c1c761c966e212 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Wed, 1 May 2024 01:32:28 +0100 Subject: [PATCH 05/14] Remove debug thread yield. --- nano/node/scheduler/priority.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/nano/node/scheduler/priority.cpp b/nano/node/scheduler/priority.cpp index d16d33fe56..0635824f67 100644 --- a/nano/node/scheduler/priority.cpp +++ b/nano/node/scheduler/priority.cpp @@ -106,7 +106,6 @@ void nano::scheduler::priority::run () condition.wait (lock, [this] () { return stopped || predicate (); }); - debug_assert ((std::this_thread::yield (), true)); // Introduce some random delay in debug builds if (!stopped) { stats.inc (nano::stat::type::election_scheduler, nano::stat::detail::loop); From b554bfbdb15630722586cea2469b88c8d8fb3f84 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Wed, 1 May 2024 01:20:15 +0100 Subject: [PATCH 06/14] Priority scheduler maintains its own quantity of elections to keep open rather than using vacancy() Remove election_scheduler.no_vacancy --- nano/core_test/active_elections.cpp | 133 -------------------------- nano/core_test/election_scheduler.cpp | 93 ------------------ nano/node/node.cpp | 1 - 3 files changed, 227 deletions(-) diff --git a/nano/core_test/active_elections.cpp b/nano/core_test/active_elections.cpp index 9818755d07..a52659c6e7 100644 --- a/nano/core_test/active_elections.cpp +++ b/nano/core_test/active_elections.cpp @@ -1305,42 +1305,6 @@ TEST (active_elections, list_active) auto active = node.active.list_active (); } -TEST (active_elections, vacancy) -{ - std::atomic updated = false; - { - nano::test::system system; - nano::node_config config = system.default_config (); - config.active_elections.size = 1; - auto & node = *system.add_node (config); - nano::state_block_builder builder; - auto send = builder.make_block () - .account (nano::dev::genesis_key.pub) - .previous (nano::dev::genesis->hash ()) - .representative (nano::dev::genesis_key.pub) - .link (nano::dev::genesis_key.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (nano::dev::genesis->hash ())) - .build (); - node.active.vacancy_update = [&updated] () { updated = true; }; - ASSERT_EQ (nano::block_status::progress, node.process (send)); - ASSERT_EQ (1, node.active.vacancy (nano::election_behavior::priority)); - ASSERT_EQ (0, node.active.size ()); - node.scheduler.priority.activate (node.ledger.tx_begin_read (), nano::dev::genesis_key.pub); - ASSERT_TIMELY (1s, updated); - updated = false; - ASSERT_EQ (0, node.active.vacancy (nano::election_behavior::priority)); - ASSERT_EQ (1, node.active.size ()); - auto election1 = node.active.election (send->qualified_root ()); - ASSERT_NE (nullptr, election1); - election1->force_confirm (); - ASSERT_TIMELY (1s, updated); - ASSERT_EQ (1, node.active.vacancy (nano::election_behavior::priority)); - ASSERT_EQ (0, node.active.size ()); - } -} - /* * Ensures we limit the number of vote hinted elections in AEC */ @@ -1407,100 +1371,3 @@ TEST (active_elections, limit_vote_hinted_elections) ASSERT_EQ (0, node.stats.count (nano::stat::type::active_dropped, nano::stat::detail::priority)); } -/* - * Tests that when AEC is running at capacity from normal elections, it is still possible to schedule a limited number of hinted elections - */ -TEST (active_elections, allow_limited_overflow) -{ - nano::test::system system; - nano::node_config config = system.default_config (); - const int aec_limit = 20; - config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - config.active_elections.size = aec_limit; - config.active_elections.hinted_limit_percentage = 20; // Should give us a limit of 4 hinted elections - auto & node = *system.add_node (config); - - auto blocks = nano::test::setup_independent_blocks (system, node, aec_limit * 4); - - // Split blocks in two halves - std::vector> blocks1 (blocks.begin (), blocks.begin () + blocks.size () / 2); - std::vector> blocks2 (blocks.begin () + blocks.size () / 2, blocks.end ()); - - // Even though automatic frontier confirmation is disabled, AEC is doing funny stuff and inserting elections, clear that - WAIT (1s); - node.active.clear (); - ASSERT_TRUE (node.active.empty ()); - - // Insert the first part of the blocks into normal election scheduler - for (auto const & block : blocks1) - { - node.scheduler.priority.activate (node.ledger.tx_begin_read (), block->account ()); - } - - // Ensure number of active elections reaches AEC limit and there is no overfill - ASSERT_TIMELY_EQ (5s, node.active.size (), node.active.limit (nano::election_behavior::priority)); - // And it stays that way without increasing - ASSERT_ALWAYS (1s, node.active.size () == node.active.limit (nano::election_behavior::priority)); - - // Insert votes for the second part of the blocks, so that those are scheduled as hinted elections - for (auto const & block : blocks2) - { - // Non-final vote, so it stays in the AEC without getting confirmed - auto vote = nano::test::make_vote (nano::dev::genesis_key, { block }); - node.vote_cache.insert (vote); - } - - // Ensure active elections overfill AEC only up to normal + hinted limit - ASSERT_TIMELY_EQ (5s, node.active.size (), node.active.limit (nano::election_behavior::priority) + node.active.limit (nano::election_behavior::hinted)); - // And it stays that way without increasing - ASSERT_ALWAYS (1s, node.active.size () == node.active.limit (nano::election_behavior::priority) + node.active.limit (nano::election_behavior::hinted)); -} - -/* - * Tests that when hinted elections are present in the AEC, normal scheduler adapts not to exceed the limit of all elections - */ -TEST (active_elections, allow_limited_overflow_adapt) -{ - nano::test::system system; - nano::node_config config = system.default_config (); - const int aec_limit = 20; - config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - config.active_elections.size = aec_limit; - config.active_elections.hinted_limit_percentage = 20; // Should give us a limit of 4 hinted elections - auto & node = *system.add_node (config); - - auto blocks = nano::test::setup_independent_blocks (system, node, aec_limit * 4); - - // Split blocks in two halves - std::vector> blocks1 (blocks.begin (), blocks.begin () + blocks.size () / 2); - std::vector> blocks2 (blocks.begin () + blocks.size () / 2, blocks.end ()); - - // Even though automatic frontier confirmation is disabled, AEC is doing funny stuff and inserting elections, clear that - WAIT (1s); - node.active.clear (); - ASSERT_TRUE (node.active.empty ()); - - // Insert votes for the second part of the blocks, so that those are scheduled as hinted elections - for (auto const & block : blocks2) - { - // Non-final vote, so it stays in the AEC without getting confirmed - auto vote = nano::test::make_vote (nano::dev::genesis_key, { block }); - node.vote_cache.insert (vote); - } - - // Ensure hinted election amount is bounded by hinted limit - ASSERT_TIMELY_EQ (5s, node.active.size (), node.active.limit (nano::election_behavior::hinted)); - // And it stays that way without increasing - ASSERT_ALWAYS (1s, node.active.size () == node.active.limit (nano::election_behavior::hinted)); - - // Insert the first part of the blocks into normal election scheduler - for (auto const & block : blocks1) - { - node.scheduler.priority.activate (node.ledger.tx_begin_read (), block->account ()); - } - - // Ensure number of active elections reaches AEC limit and there is no overfill - ASSERT_TIMELY_EQ (5s, node.active.size (), node.active.limit (nano::election_behavior::priority)); - // And it stays that way without increasing - ASSERT_ALWAYS (1s, node.active.size () == node.active.limit (nano::election_behavior::priority)); -} diff --git a/nano/core_test/election_scheduler.cpp b/nano/core_test/election_scheduler.cpp index 02a37bec89..8c525e9e1a 100644 --- a/nano/core_test/election_scheduler.cpp +++ b/nano/core_test/election_scheduler.cpp @@ -53,96 +53,3 @@ TEST (election_scheduler, activate_one_flush) system.nodes[0]->scheduler.priority.activate (system.nodes[0]->ledger.tx_begin_read (), nano::dev::genesis_key.pub); ASSERT_TIMELY (5s, system.nodes[0]->active.election (send1->qualified_root ())); } - -/** - * Tests that the election scheduler and the active transactions container (AEC) - * work in sync with regards to the node configuration value "active_elections.size". - * - * The test sets up two forcefully cemented blocks -- a send on the genesis account and a receive on a second account. - * It then creates two other blocks, each a successor to one of the previous two, - * and processes them locally (without the node starting elections for them, but just saving them to disk). - * - * Elections for these latter two (B1 and B2) are started by the test code manually via `election_scheduler::activate`. - * The test expects E1 to start right off and take its seat into the AEC. - * E2 is expected not to start though (because the AEC is full), so B2 should be awaiting in the scheduler's queue. - * - * As soon as the test code manually confirms E1 (and thus evicts it out of the AEC), - * it is expected that E2 begins and the scheduler's queue becomes empty again. - */ -TEST (election_scheduler, no_vacancy) -{ - nano::test::system system{}; - - nano::node_config config = system.default_config (); - config.active_elections.size = 1; - config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - - auto & node = *system.add_node (config); - nano::state_block_builder builder{}; - nano::keypair key{}; - - // Activating accounts depends on confirmed dependencies. First, prepare 2 accounts - auto send = builder.make_block () - .account (nano::dev::genesis_key.pub) - .previous (nano::dev::genesis->hash ()) - .representative (nano::dev::genesis_key.pub) - .link (key.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (nano::dev::genesis->hash ())) - .build (); - ASSERT_EQ (nano::block_status::progress, node.process (send)); - node.process_confirmed (nano::election_status{ send }); - - auto receive = builder.make_block () - .account (key.pub) - .previous (0) - .representative (key.pub) - .link (send->hash ()) - .balance (nano::Gxrb_ratio) - .sign (key.prv, key.pub) - .work (*system.work.generate (key.pub)) - .build (); - ASSERT_EQ (nano::block_status::progress, node.process (receive)); - node.process_confirmed (nano::election_status{ receive }); - - ASSERT_TIMELY (5s, nano::test::confirmed (node, { send, receive })); - - // Second, process two eligible transactions - auto block1 = builder.make_block () - .account (nano::dev::genesis_key.pub) - .previous (send->hash ()) - .representative (nano::dev::genesis_key.pub) - .link (nano::dev::genesis_key.pub) - .balance (nano::dev::constants.genesis_amount - 2 * nano::Gxrb_ratio) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*system.work.generate (send->hash ())) - .build (); - ASSERT_EQ (nano::block_status::progress, node.process (block1)); - - // There is vacancy so it should be inserted - node.scheduler.priority.activate (node.ledger.tx_begin_read (), nano::dev::genesis_key.pub); - std::shared_ptr election{}; - ASSERT_TIMELY (5s, (election = node.active.election (block1->qualified_root ())) != nullptr); - - auto block2 = builder.make_block () - .account (key.pub) - .previous (receive->hash ()) - .representative (key.pub) - .link (key.pub) - .balance (0) - .sign (key.prv, key.pub) - .work (*system.work.generate (receive->hash ())) - .build (); - ASSERT_EQ (nano::block_status::progress, node.process (block2)); - - // There is no vacancy so it should stay queued - node.scheduler.priority.activate (node.ledger.tx_begin_read (), key.pub); - ASSERT_TIMELY_EQ (5s, node.scheduler.priority.size (), 1); - ASSERT_EQ (node.active.election (block2->qualified_root ()), nullptr); - - // Election confirmed, next in queue should begin - election->force_confirm (); - ASSERT_TIMELY (5s, node.active.election (block2->qualified_root ()) != nullptr); - ASSERT_TRUE (node.scheduler.priority.empty ()); -} diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 40040c18ad..ce4de6125f 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -254,7 +254,6 @@ nano::node::node (std::shared_ptr io_ctx_a, std::filesy { // Notify election schedulers when AEC frees election slot active.vacancy_update = [this] () { - scheduler.priority.notify (); scheduler.hinted.notify (); scheduler.optimistic.notify (); }; From 5d9c7289bbf266cdd8fc13970c00f2f9d5935049 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Wed, 1 May 2024 03:23:13 +0100 Subject: [PATCH 07/14] Replace vacancy_update with an election_stopped observer_set. Schedulers only get notifications when an election is closed i.e. vacancy has increased. --- nano/node/active_elections.cpp | 4 +--- nano/node/active_elections.hpp | 2 +- nano/node/node.cpp | 5 +++-- nano/node/scheduler/hinted.hpp | 3 ++- nano/node/scheduler/optimistic.hpp | 3 ++- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/nano/node/active_elections.cpp b/nano/node/active_elections.cpp index b0e3507306..bbf0a7e6ee 100644 --- a/nano/node/active_elections.cpp +++ b/nano/node/active_elections.cpp @@ -292,7 +292,7 @@ void nano::active_elections::cleanup_election (nano::unique_lock & node.stats.sample (nano::stat::sample::active_election_duration, { 0, 1000 * 60 * 10 /* 0-10 minutes range */ }, election->duration ().count ()); - vacancy_update (); + election_stopped.notify (election); for (auto const & [hash, block] : blocks_l) { @@ -426,7 +426,6 @@ nano::election_insertion_result nano::active_elections::insert (std::shared_ptr< node.vote_router.trigger_vote_cache (hash); node.observers.active_started.notify (hash); - vacancy_update (); } // Votes are generated for inserted or ongoing elections @@ -527,7 +526,6 @@ void nano::active_elections::clear () nano::lock_guard guard{ mutex }; roots.clear (); } - vacancy_update (); } std::unique_ptr nano::collect_container_info (active_elections & active_elections, std::string const & name) diff --git a/nano/node/active_elections.hpp b/nano/node/active_elections.hpp index 869912f5c8..ddcb2645c8 100644 --- a/nano/node/active_elections.hpp +++ b/nano/node/active_elections.hpp @@ -132,7 +132,7 @@ class active_elections final * How many election slots are available for specified election type */ int64_t vacancy (nano::election_behavior behavior) const; - std::function vacancy_update{ [] () {} }; + nano::observer_set> election_stopped; std::size_t election_winner_details_size (); void add_election_winner_details (nano::block_hash const &, std::shared_ptr const &); diff --git a/nano/node/node.cpp b/nano/node/node.cpp index ce4de6125f..34422edcd9 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -253,10 +253,11 @@ nano::node::node (std::shared_ptr io_ctx_a, std::filesy if (!init_error ()) { // Notify election schedulers when AEC frees election slot - active.vacancy_update = [this] () { + active.election_stopped.add ([this] (std::shared_ptr election) { + scheduler.priority.notify (); scheduler.hinted.notify (); scheduler.optimistic.notify (); - }; + }); wallets.observer = [this] (bool active) { observers.wallet.notify (active); diff --git a/nano/node/scheduler/hinted.hpp b/nano/node/scheduler/hinted.hpp index e26772bda2..330e894055 100644 --- a/nano/node/scheduler/hinted.hpp +++ b/nano/node/scheduler/hinted.hpp @@ -61,7 +61,8 @@ class hinted final void stop (); /* - * Notify about changes in AEC vacancy + * Notify when AEC has become more empty + * Notify when scheduler is stopped */ void notify (); diff --git a/nano/node/scheduler/optimistic.hpp b/nano/node/scheduler/optimistic.hpp index 14318773ab..411c2d4dc5 100644 --- a/nano/node/scheduler/optimistic.hpp +++ b/nano/node/scheduler/optimistic.hpp @@ -63,7 +63,8 @@ class optimistic final bool activate (nano::secure::transaction const & transaction, nano::account const & account); /** - * Notify about changes in AEC vacancy + * Notify when AEC has become more empty + * Notify when scheduler is stopped */ void notify (); From 8e0a4496bd40d0e6917a7917c2024e7bbdf9216c Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Tue, 23 Apr 2024 09:52:22 +0100 Subject: [PATCH 08/14] Bucket limiting and depth parameterization. Convert vacancy_update to election_stopped observer. Reworking buckets. Fixing bucket WIP WIP Commenting removal selection Scheduler logging. Increase timestamp precision. Formatting. Removing completed elections. --- nano/core_test/blockprocessor.cpp | 117 +++++++++++++++ nano/core_test/scheduler_buckets.cpp | 160 ++++----------------- nano/lib/logging_enums.hpp | 5 +- nano/lib/stats_enums.hpp | 1 + nano/lib/thread_roles.cpp | 3 - nano/lib/thread_roles.hpp | 1 - nano/node/CMakeLists.txt | 2 - nano/node/blockprocessor.cpp | 22 +++ nano/node/node.cpp | 2 +- nano/node/nodeconfig.hpp | 2 + nano/node/process_live_dispatcher.cpp | 4 +- nano/node/scheduler/bucket.cpp | 64 +++------ nano/node/scheduler/bucket.hpp | 51 +++++-- nano/node/scheduler/buckets.cpp | 150 ------------------- nano/node/scheduler/buckets.hpp | 60 -------- nano/node/scheduler/component.cpp | 2 - nano/node/scheduler/priority.cpp | 198 ++++++++++++++------------ nano/node/scheduler/priority.hpp | 48 ++++--- nano/secure/common.hpp | 3 +- nano/slow_test/node.cpp | 2 +- 20 files changed, 373 insertions(+), 524 deletions(-) delete mode 100644 nano/node/scheduler/buckets.cpp delete mode 100644 nano/node/scheduler/buckets.hpp diff --git a/nano/core_test/blockprocessor.cpp b/nano/core_test/blockprocessor.cpp index 57c777062b..4b4a4d98bc 100644 --- a/nano/core_test/blockprocessor.cpp +++ b/nano/core_test/blockprocessor.cpp @@ -1,9 +1,12 @@ #include #include +#include +#include #include #include #include #include +#include #include #include @@ -40,3 +43,117 @@ TEST (block_processor, broadcast_block_on_arrival) // Checks whether the block was broadcast. ASSERT_TIMELY (5s, node2->block_or_pruned_exists (send1->hash ())); } + +TEST (block_processor, rollback_overflow) +{ + nano::test::system system; + nano::node_config config; + config.priority_scheduler.depth = 1; + auto node = system.add_node (config); + nano::state_block_builder builder; + nano::keypair key1; + auto send1 = builder.make_block () + .account (nano::dev::genesis_key.pub) + .previous (nano::dev::genesis->hash ()) + .representative (nano::dev::genesis_key.pub) + .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) + .link (key1.pub) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (nano::dev::genesis->hash ())) + .build (); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (node->ledger.tx_begin_write (), send1)); + node->ledger.confirm (node->ledger.tx_begin_write (), send1->hash ()); + nano::keypair key2; + auto send2 = builder.make_block () + .account (nano::dev::genesis_key.pub) + .previous (send1->hash ()) + .representative (nano::dev::genesis_key.pub) + .balance (nano::dev::constants.genesis_amount - 2 * nano::Gxrb_ratio) + .link (key2.pub) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (send1->hash ())) + .build (); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (node->ledger.tx_begin_write (), send2)); + node->ledger.confirm (node->ledger.tx_begin_write (), send2->hash ()); + + auto open1 = builder.make_block () + .account (key1.pub) + .previous (0) + .representative (nano::dev::genesis_key.pub) + .balance (nano::Gxrb_ratio) + .link (send1->hash ()) + .sign (key1.prv, key1.pub) + .work (*system.work.generate (key1.pub)) + .build (); + std::optional status; + ASSERT_TRUE ((status = node->block_processor.add_blocking (open1, nano::block_source::live), status && status.value () == nano::block_status::progress)); + auto open2 = builder.make_block () + .account (key2.pub) + .previous (0) + .representative (nano::dev::genesis_key.pub) + .balance (nano::Gxrb_ratio) + .link (send2->hash ()) + .sign (key2.prv, key2.pub) + .work (*system.work.generate (key2.pub)) + .build (); + ASSERT_TRUE ((status = node->block_processor.add_blocking (open2, nano::block_source::live), status && status.value () == nano::block_status::overflow)); +} + +TEST (block_processor, scheduler_confirmed_space) +{ + nano::test::system system; + nano::node_config config; + config.priority_scheduler.depth = 1; + auto node = system.add_node (config); + nano::state_block_builder builder; + nano::keypair key1; + auto send1 = builder.make_block () + .account (nano::dev::genesis_key.pub) + .previous (nano::dev::genesis->hash ()) + .representative (nano::dev::genesis_key.pub) + .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) + .link (key1.pub) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (nano::dev::genesis->hash ())) + .build (); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (node->ledger.tx_begin_write (), send1)); + node->ledger.confirm (node->ledger.tx_begin_write (), send1->hash ()); + nano::keypair key2; + auto send2 = builder.make_block () + .account (nano::dev::genesis_key.pub) + .previous (send1->hash ()) + .representative (nano::dev::genesis_key.pub) + .balance (nano::dev::constants.genesis_amount - 2 * nano::Gxrb_ratio) + .link (key2.pub) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*system.work.generate (send1->hash ())) + .build (); + ASSERT_EQ (nano::block_status::progress, node->ledger.process (node->ledger.tx_begin_write (), send2)); + node->ledger.confirm (node->ledger.tx_begin_write (), send2->hash ()); + + auto open1 = builder.make_block () + .account (key1.pub) + .previous (0) + .representative (nano::dev::genesis_key.pub) + .balance (nano::Gxrb_ratio) + .link (send1->hash ()) + .sign (key1.prv, key1.pub) + .work (*system.work.generate (key1.pub)) + .build (); + std::optional status; + ASSERT_TRUE ((status = node->block_processor.add_blocking (open1, nano::block_source::live), status && status.value () == nano::block_status::progress)); + auto election = node->active.election (open1->qualified_root ()); + ASSERT_NE (nullptr, election); + election->force_confirm (); + ASSERT_TIMELY (5s, node->active.empty ()); + auto open2 = builder.make_block () + .account (key2.pub) + .previous (0) + .representative (nano::dev::genesis_key.pub) + .balance (nano::Gxrb_ratio) + .link (send2->hash ()) + .sign (key2.prv, key2.pub) + .work (*system.work.generate (key2.pub)) + .build (); + ASSERT_TRUE ((status = node->block_processor.add_blocking (open2, nano::block_source::live), status && status.value () == nano::block_status::progress)); +} diff --git a/nano/core_test/scheduler_buckets.cpp b/nano/core_test/scheduler_buckets.cpp index 43912e29b9..47c18ec2ed 100644 --- a/nano/core_test/scheduler_buckets.cpp +++ b/nano/core_test/scheduler_buckets.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include @@ -107,148 +107,50 @@ std::shared_ptr & block3 () return result; } -TEST (buckets, construction) +TEST (bucket, construction) { - nano::scheduler::buckets buckets; - ASSERT_EQ (0, buckets.size ()); - ASSERT_TRUE (buckets.empty ()); - ASSERT_EQ (62, buckets.bucket_count ()); + nano::scheduler::bucket bucket{ 0 }; } -TEST (buckets, index_min) +TEST (bucket, insert_zero) { - nano::scheduler::buckets buckets; - ASSERT_EQ (0, buckets.index (std::numeric_limits::min ())); + nano::scheduler::bucket bucket{ 0 }; + auto block = block0 (); + auto drop = bucket.insert (std::chrono::steady_clock::time_point{} + std::chrono::milliseconds{ 0 }, block); + ASSERT_EQ (drop, block); } -TEST (buckets, index_max) +TEST (bucket, push_available) { - nano::scheduler::buckets buckets; - ASSERT_EQ (buckets.bucket_count () - 1, buckets.index (std::numeric_limits::max ())); + nano::scheduler::bucket bucket{ 1 }; + auto block = block0 (); + ASSERT_EQ (nullptr, bucket.insert (std::chrono::steady_clock::time_point{} + std::chrono::milliseconds{ 1000 }, block)); } -TEST (buckets, insert_Gxrb) +TEST (bucket, push_overflow_other) { - nano::scheduler::buckets buckets; - buckets.push (1000, block0 (), nano::Gxrb_ratio); - ASSERT_EQ (1, buckets.size ()); - ASSERT_EQ (1, buckets.bucket_size (48)); + nano::scheduler::bucket bucket{ 1 }; + auto block0 = ::block0 (); + auto block1 = ::block1 (); + ASSERT_EQ (nullptr, bucket.insert (std::chrono::steady_clock::time_point{} + std::chrono::milliseconds{ 1000 }, block0)); + ASSERT_EQ (block0, bucket.insert (std::chrono::steady_clock::time_point{} + std::chrono::milliseconds{ 900 }, block1)); } -TEST (buckets, insert_Mxrb) +TEST (bucket, push_overflow_self) { - nano::scheduler::buckets buckets; - buckets.push (1000, block1 (), nano::Mxrb_ratio); - ASSERT_EQ (1, buckets.size ()); - ASSERT_EQ (1, buckets.bucket_size (13)); + nano::scheduler::bucket bucket{ 1 }; + auto block0 = ::block0 (); + auto block1 = ::block1 (); + ASSERT_EQ (nullptr, bucket.insert (std::chrono::steady_clock::time_point{} + std::chrono::milliseconds{ 1000 }, block0)); + ASSERT_EQ (block1, bucket.insert (std::chrono::steady_clock::time_point{} + std::chrono::milliseconds{ 1100 }, block1)); } -// Test two blocks with the same priority -TEST (buckets, insert_same_priority) +// Inserting duplicate block should not return an overflow or reject +TEST (bucket, accept_duplicate) { - nano::scheduler::buckets buckets; - buckets.push (1000, block0 (), nano::Gxrb_ratio); - buckets.push (1000, block2 (), nano::Gxrb_ratio); - ASSERT_EQ (2, buckets.size ()); - ASSERT_EQ (2, buckets.bucket_size (48)); -} - -// Test the same block inserted multiple times -TEST (buckets, insert_duplicate) -{ - nano::scheduler::buckets buckets; - buckets.push (1000, block0 (), nano::Gxrb_ratio); - buckets.push (1000, block0 (), nano::Gxrb_ratio); - ASSERT_EQ (1, buckets.size ()); - ASSERT_EQ (1, buckets.bucket_size (48)); -} - -TEST (buckets, insert_older) -{ - nano::scheduler::buckets buckets; - buckets.push (1000, block0 (), nano::Gxrb_ratio); - buckets.push (1100, block2 (), nano::Gxrb_ratio); - ASSERT_EQ (block0 (), buckets.top ()); - buckets.pop (); - ASSERT_EQ (block2 (), buckets.top ()); - buckets.pop (); -} - -TEST (buckets, pop) -{ - nano::scheduler::buckets buckets; - ASSERT_TRUE (buckets.empty ()); - buckets.push (1000, block0 (), nano::Gxrb_ratio); - ASSERT_FALSE (buckets.empty ()); - buckets.pop (); - ASSERT_TRUE (buckets.empty ()); -} - -TEST (buckets, top_one) -{ - nano::scheduler::buckets buckets; - buckets.push (1000, block0 (), nano::Gxrb_ratio); - ASSERT_EQ (block0 (), buckets.top ()); -} - -TEST (buckets, top_two) -{ - nano::scheduler::buckets buckets; - buckets.push (1000, block0 (), nano::Gxrb_ratio); - buckets.push (1, block1 (), nano::Mxrb_ratio); - ASSERT_EQ (block0 (), buckets.top ()); - buckets.pop (); - ASSERT_EQ (block1 (), buckets.top ()); - buckets.pop (); - ASSERT_TRUE (buckets.empty ()); -} - -TEST (buckets, top_round_robin) -{ - nano::scheduler::buckets buckets; - buckets.push (1000, blockzero (), 0); - ASSERT_EQ (blockzero (), buckets.top ()); - buckets.push (1000, block0 (), nano::Gxrb_ratio); - buckets.push (1000, block1 (), nano::Mxrb_ratio); - buckets.push (1100, block3 (), nano::Mxrb_ratio); - buckets.pop (); // blockzero - EXPECT_EQ (block1 (), buckets.top ()); - buckets.pop (); - EXPECT_EQ (block0 (), buckets.top ()); - buckets.pop (); - EXPECT_EQ (block3 (), buckets.top ()); - buckets.pop (); - EXPECT_TRUE (buckets.empty ()); -} - -TEST (buckets, trim_normal) -{ - nano::scheduler::buckets buckets{ 1 }; - buckets.push (1000, block0 (), nano::Gxrb_ratio); - buckets.push (1100, block2 (), nano::Gxrb_ratio); - ASSERT_EQ (1, buckets.size ()); - ASSERT_EQ (block0 (), buckets.top ()); -} - -TEST (buckets, trim_reverse) -{ - nano::scheduler::buckets buckets{ 1 }; - buckets.push (1100, block2 (), nano::Gxrb_ratio); - buckets.push (1000, block0 (), nano::Gxrb_ratio); - ASSERT_EQ (1, buckets.size ()); - ASSERT_EQ (block0 (), buckets.top ()); -} - -TEST (buckets, trim_even) -{ - nano::scheduler::buckets buckets{ 2 }; - buckets.push (1000, block0 (), nano::Gxrb_ratio); - buckets.push (1100, block2 (), nano::Gxrb_ratio); - ASSERT_EQ (1, buckets.size ()); - ASSERT_EQ (block0 (), buckets.top ()); - buckets.push (1000, block1 (), nano::Mxrb_ratio); - ASSERT_EQ (2, buckets.size ()); - ASSERT_EQ (block0 (), buckets.top ()); - buckets.pop (); - ASSERT_EQ (block1 (), buckets.top ()); + nano::scheduler::bucket bucket{ 1 }; + auto block0 = ::block0 (); + ASSERT_EQ (nullptr, bucket.insert (std::chrono::steady_clock::time_point{} + std::chrono::milliseconds{ 1000 }, block0)); + ASSERT_EQ (nullptr, bucket.insert (std::chrono::steady_clock::time_point{} + std::chrono::milliseconds{ 900 }, block0)); + ASSERT_EQ (nullptr, bucket.insert (std::chrono::steady_clock::time_point{} + std::chrono::milliseconds{ 1100 }, block0)); } diff --git a/nano/lib/logging_enums.hpp b/nano/lib/logging_enums.hpp index 662981d0a6..94e7715910 100644 --- a/nano/lib/logging_enums.hpp +++ b/nano/lib/logging_enums.hpp @@ -126,6 +126,9 @@ enum class detail // election_scheduler block_activated, + block_insert, // Block inserted with no overflow + block_overflow, // Block inserted and another block was removed as an overflow + block_reject, // Block was not inserted because of an overflow // vote_generator candidate_processed, @@ -215,4 +218,4 @@ struct magic_enum::customize::enum_range { static constexpr int min = 0; static constexpr int max = 512; -}; \ No newline at end of file +}; diff --git a/nano/lib/stats_enums.hpp b/nano/lib/stats_enums.hpp index 5c6b4f3f84..a5a22cd621 100644 --- a/nano/lib/stats_enums.hpp +++ b/nano/lib/stats_enums.hpp @@ -145,6 +145,7 @@ enum class detail balance_mismatch, representative_mismatch, block_position, + overflow, // blockprocessor process_blocking, diff --git a/nano/lib/thread_roles.cpp b/nano/lib/thread_roles.cpp index 8b1b7618e0..5ba7be622d 100644 --- a/nano/lib/thread_roles.cpp +++ b/nano/lib/thread_roles.cpp @@ -103,9 +103,6 @@ std::string nano::thread_role::get_string (nano::thread_role::name role) case nano::thread_role::name::scheduler_optimistic: thread_role_name_string = "Sched Opt"; break; - case nano::thread_role::name::scheduler_priority: - thread_role_name_string = "Sched Priority"; - break; case nano::thread_role::name::stats: thread_role_name_string = "Stats"; break; diff --git a/nano/lib/thread_roles.hpp b/nano/lib/thread_roles.hpp index ceaeac6652..8949485730 100644 --- a/nano/lib/thread_roles.hpp +++ b/nano/lib/thread_roles.hpp @@ -41,7 +41,6 @@ enum class name scheduler_hinted, scheduler_manual, scheduler_optimistic, - scheduler_priority, rep_crawler, local_block_broadcasting, rep_tiers, diff --git a/nano/node/CMakeLists.txt b/nano/node/CMakeLists.txt index a069e9715e..da45aadffb 100644 --- a/nano/node/CMakeLists.txt +++ b/nano/node/CMakeLists.txt @@ -139,8 +139,6 @@ add_library( request_aggregator.cpp scheduler/bucket.cpp scheduler/bucket.hpp - scheduler/buckets.cpp - scheduler/buckets.hpp scheduler/component.hpp scheduler/component.cpp scheduler/hinted.hpp diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index 80ecefeb62..dd75c99118 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include #include @@ -367,6 +369,22 @@ nano::block_status nano::block_processor::process_one (secure::write_transaction nano::log::arg{ "forced", forced_a }, nano::log::arg{ "block", block }); + if (result == nano::block_status::progress) + { + std::shared_ptr removed; + if (node.ledger.dependents_confirmed (transaction_a, *block)) + { + removed = node.scheduler.priority.activate (transaction_a, block->account ()); + } + if (removed != nullptr && node.ledger.any.block_exists (transaction_a, removed->hash ())) + { + node.ledger.rollback (transaction_a, removed->hash ()); + if (removed->hash () == block->hash ()) + { + return nano::block_status::overflow; + } + } + } switch (result) { case nano::block_status::progress: @@ -444,6 +462,10 @@ nano::block_status nano::block_processor::process_one (secure::write_transaction { break; } + case nano::block_status::overflow: + { + break; + } } return result; } diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 34422edcd9..9dbaeb4807 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -254,7 +254,7 @@ nano::node::node (std::shared_ptr io_ctx_a, std::filesy { // Notify election schedulers when AEC frees election slot active.election_stopped.add ([this] (std::shared_ptr election) { - scheduler.priority.notify (); + scheduler.priority.election_stopped (election); scheduler.hinted.notify (); scheduler.optimistic.notify (); }); diff --git a/nano/node/nodeconfig.hpp b/nano/node/nodeconfig.hpp index a17cecacd5..4b00e5ed6e 100644 --- a/nano/node/nodeconfig.hpp +++ b/nano/node/nodeconfig.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -61,6 +62,7 @@ class node_config std::optional peering_port{}; nano::scheduler::optimistic_config optimistic_scheduler; nano::scheduler::hinted_config hinted_scheduler; + nano::scheduler::priority_config priority_scheduler; std::vector> work_peers; std::vector> secondary_work_peers{ { "127.0.0.1", 8076 } }; /* Default of nano-pow-server */ std::vector preconfigured_peers; diff --git a/nano/node/process_live_dispatcher.cpp b/nano/node/process_live_dispatcher.cpp index 60a80e2154..15a051eff8 100644 --- a/nano/node/process_live_dispatcher.cpp +++ b/nano/node/process_live_dispatcher.cpp @@ -44,10 +44,10 @@ void nano::process_live_dispatcher::inspect (nano::block_status const & result, void nano::process_live_dispatcher::process_live (nano::block const & block, secure::transaction const & transaction) { // Start collecting quorum on block - if (ledger.dependents_confirmed (transaction, block)) + /*if (ledger.dependents_confirmed (transaction, block)) { scheduler.activate (transaction, block.account ()); - } + }*/ if (websocket.server && websocket.server->any_subscriber (nano::websocket::topic::new_unconfirmed_block)) { diff --git a/nano/node/scheduler/bucket.cpp b/nano/node/scheduler/bucket.cpp index 51ba1626a5..b435d5c3aa 100644 --- a/nano/node/scheduler/bucket.cpp +++ b/nano/node/scheduler/bucket.cpp @@ -1,62 +1,34 @@ #include #include -bool nano::scheduler::bucket::value_type::operator< (value_type const & other_a) const +nano::block_hash nano::scheduler::bucket::entry_t::hash () const { - return time < other_a.time || (time == other_a.time && block->hash () < other_a.block->hash ()); + return block->hash (); } -bool nano::scheduler::bucket::value_type::operator== (value_type const & other_a) const +nano::scheduler::bucket::bucket (size_t max) : + max{ max } { - return time == other_a.time && block->hash () == other_a.block->hash (); } -nano::scheduler::bucket::bucket (size_t maximum) : - maximum{ maximum } +std::shared_ptr nano::scheduler::bucket::insert (std::chrono::steady_clock::time_point time, std::shared_ptr block) { - debug_assert (maximum > 0); -} - -nano::scheduler::bucket::~bucket () -{ -} - -std::shared_ptr nano::scheduler::bucket::top () const -{ - debug_assert (!queue.empty ()); - return queue.begin ()->block; -} - -void nano::scheduler::bucket::pop () -{ - debug_assert (!queue.empty ()); - queue.erase (queue.begin ()); -} - -void nano::scheduler::bucket::push (uint64_t time, std::shared_ptr block) -{ - queue.insert ({ time, block }); - if (queue.size () > maximum) + std::lock_guard lock{ mutex }; + backlog.insert (entry_t{ time, block }); + debug_assert (backlog.size () <= max + 1); // One extra at most + if (backlog.size () <= max) { - debug_assert (!queue.empty ()); - queue.erase (--queue.end ()); + return nullptr; } + auto newest = backlog.begin (); // The first item in descending order has the highest timestamp i.e. it is the newest. + auto discard = newest->block; + backlog.erase (newest); + debug_assert (backlog.size () <= max); + return discard; } -size_t nano::scheduler::bucket::size () const -{ - return queue.size (); -} - -bool nano::scheduler::bucket::empty () const +size_t nano::scheduler::bucket::erase (nano::block_hash const & hash) { - return queue.empty (); -} - -void nano::scheduler::bucket::dump () const -{ - for (auto const & item : queue) - { - std::cerr << item.time << ' ' << item.block->hash ().to_string () << '\n'; - } + std::lock_guard lock{ mutex }; + return backlog.get ().erase (hash); } diff --git a/nano/node/scheduler/bucket.hpp b/nano/node/scheduler/bucket.hpp index 2f32c17d59..c2fade950a 100644 --- a/nano/node/scheduler/bucket.hpp +++ b/nano/node/scheduler/bucket.hpp @@ -1,13 +1,24 @@ #pragma once +#include +#include + +#include +#include +#include +#include +#include + +#include #include #include #include -#include +#include namespace nano { class block; +class logger; } namespace nano::scheduler { @@ -15,25 +26,35 @@ namespace nano::scheduler */ class bucket final { - class value_type + class tag_time + { + }; + class tag_hash + { + }; + class entry_t { public: - uint64_t time; + std::chrono::steady_clock::time_point time; std::shared_ptr block; - bool operator< (value_type const & other_a) const; - bool operator== (value_type const & other_a) const; + nano::block_hash hash () const; }; - std::set queue; - size_t const maximum; + using backlog_t = boost::multi_index_container, + boost::multi_index::member, + std::greater>, // Sorted by last-confirmed time in descending order + boost::multi_index::hashed_unique, + boost::multi_index::const_mem_fun>>>; public: - bucket (size_t maximum); - ~bucket (); - std::shared_ptr top () const; - void pop (); - void push (uint64_t time, std::shared_ptr block); - size_t size () const; - bool empty () const; - void dump () const; + bucket (size_t max); + std::shared_ptr insert (std::chrono::steady_clock::time_point time, std::shared_ptr block); + size_t erase (nano::block_hash const & hash); + +private: + backlog_t backlog; + std::mutex mutex; + size_t const max; }; } // namespace nano::scheduler diff --git a/nano/node/scheduler/buckets.cpp b/nano/node/scheduler/buckets.cpp deleted file mode 100644 index c033f6f6ca..0000000000 --- a/nano/node/scheduler/buckets.cpp +++ /dev/null @@ -1,150 +0,0 @@ -#include -#include -#include -#include - -#include - -/** Moves the bucket pointer to the next bucket */ -void nano::scheduler::buckets::next () -{ - ++current; - if (current == buckets_m.end ()) - { - current = buckets_m.begin (); - } -} - -/** Seek to the next non-empty bucket, if one exists */ -void nano::scheduler::buckets::seek () -{ - next (); - for (std::size_t i = 0, n = buckets_m.size (); (*current)->empty () && i < n; ++i) - { - next (); - } -} - -/** - * Prioritization constructor, construct a container containing approximately 'maximum' number of blocks. - * @param maximum number of blocks that this container can hold, this is a soft and approximate limit. - */ -nano::scheduler::buckets::buckets (uint64_t maximum) : - maximum{ maximum } -{ - auto build_region = [this] (uint128_t const & begin, uint128_t const & end, size_t count) { - auto width = (end - begin) / count; - for (auto i = 0; i < count; ++i) - { - minimums.push_back (begin + i * width); - } - }; - minimums.push_back (uint128_t{ 0 }); - build_region (uint128_t{ 1 } << 88, uint128_t{ 1 } << 92, 2); - build_region (uint128_t{ 1 } << 92, uint128_t{ 1 } << 96, 4); - build_region (uint128_t{ 1 } << 96, uint128_t{ 1 } << 100, 8); - build_region (uint128_t{ 1 } << 100, uint128_t{ 1 } << 104, 16); - build_region (uint128_t{ 1 } << 104, uint128_t{ 1 } << 108, 16); - build_region (uint128_t{ 1 } << 108, uint128_t{ 1 } << 112, 8); - build_region (uint128_t{ 1 } << 112, uint128_t{ 1 } << 116, 4); - build_region (uint128_t{ 1 } << 116, uint128_t{ 1 } << 120, 2); - minimums.push_back (uint128_t{ 1 } << 120); - auto bucket_max = std::max (1u, maximum / minimums.size ()); - for (size_t i = 0u, n = minimums.size (); i < n; ++i) - { - buckets_m.push_back (std::make_unique (bucket_max)); - } - current = buckets_m.begin (); -} - -nano::scheduler::buckets::~buckets () -{ -} - -std::size_t nano::scheduler::buckets::index (nano::uint128_t const & balance) const -{ - auto index = std::upper_bound (minimums.begin (), minimums.end (), balance) - minimums.begin () - 1; - return index; -} - -/** - * Push a block and its associated time into the prioritization container. - * The time is given here because sideband might not exist in the case of state blocks. - */ -void nano::scheduler::buckets::push (uint64_t time, std::shared_ptr block, nano::amount const & priority) -{ - auto was_empty = empty (); - auto & bucket = buckets_m[index (priority.number ())]; - bucket->push (time, block); - if (was_empty) - { - seek (); - } -} - -/** Return the highest priority block of the current bucket */ -std::shared_ptr nano::scheduler::buckets::top () const -{ - debug_assert (!empty ()); - auto result = (*current)->top (); - return result; -} - -/** Pop the current block from the container and seek to the next block, if it exists */ -void nano::scheduler::buckets::pop () -{ - debug_assert (!empty ()); - auto & bucket = *current; - bucket->pop (); - seek (); -} - -/** Returns the total number of blocks in buckets */ -std::size_t nano::scheduler::buckets::size () const -{ - std::size_t result{ 0 }; - for (auto const & bucket : buckets_m) - { - result += bucket->size (); - } - return result; -} - -/** Returns number of buckets, 62 by default */ -std::size_t nano::scheduler::buckets::bucket_count () const -{ - return buckets_m.size (); -} - -/** Returns number of items in bucket with index 'index' */ -std::size_t nano::scheduler::buckets::bucket_size (std::size_t index) const -{ - return buckets_m[index]->size (); -} - -/** Returns true if all buckets are empty */ -bool nano::scheduler::buckets::empty () const -{ - return std::all_of (buckets_m.begin (), buckets_m.end (), [] (auto const & bucket) { return bucket->empty (); }); -} - -/** Print the state of the class in stderr */ -void nano::scheduler::buckets::dump () const -{ - for (auto const & bucket : buckets_m) - { - bucket->dump (); - } - std::cerr << "current: " << current - buckets_m.begin () << '\n'; -} - -std::unique_ptr nano::scheduler::buckets::collect_container_info (std::string const & name) -{ - auto composite = std::make_unique (name); - for (auto i = 0; i < buckets_m.size (); ++i) - { - auto const & bucket = buckets_m[i]; - composite->add_component (std::make_unique (container_info{ std::to_string (i), bucket->size (), 0 })); - } - return composite; -} diff --git a/nano/node/scheduler/buckets.hpp b/nano/node/scheduler/buckets.hpp deleted file mode 100644 index 967b4408f7..0000000000 --- a/nano/node/scheduler/buckets.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once -#include -#include - -#include -#include -#include -#include - -namespace nano -{ -class block; -} -namespace nano::scheduler -{ -class bucket; -/** A container for holding blocks and their arrival/creation time. - * - * The container consists of a number of buckets. Each bucket holds an ordered set of 'value_type' items. - * The buckets are accessed in a round robin fashion. The index 'current' holds the index of the bucket to access next. - * When a block is inserted, the bucket to go into is determined by the account balance and the priority inside that - * bucket is determined by its creation/arrival time. - * - * The arrival/creation time is only an approximation and it could even be wildly wrong, - * for example, in the event of bootstrapped blocks. - */ -class buckets final -{ - /** container for the buckets to be read in round robin fashion */ - std::deque> buckets_m; - - /** thresholds that define the bands for each bucket, the minimum balance an account must have to enter a bucket, - * the container writes a block to the lowest indexed bucket that has balance larger than the bucket's minimum value */ - std::deque minimums; - - /** index of bucket to read next */ - decltype (buckets_m)::const_iterator current; - - /** maximum number of blocks in whole container, each bucket's maximum is maximum / bucket_number */ - uint64_t const maximum; - - void next (); - void seek (); - -public: - buckets (uint64_t maximum = 250000u); - ~buckets (); - void push (uint64_t time, std::shared_ptr block, nano::amount const & priority); - std::shared_ptr top () const; - void pop (); - std::size_t size () const; - std::size_t bucket_count () const; - std::size_t bucket_size (std::size_t index) const; - bool empty () const; - void dump () const; - std::size_t index (nano::uint128_t const & balance) const; - - std::unique_ptr collect_container_info (std::string const &); -}; -} // namespace nano::scheduler diff --git a/nano/node/scheduler/component.cpp b/nano/node/scheduler/component.cpp index 0d44042448..57b58a3c02 100644 --- a/nano/node/scheduler/component.cpp +++ b/nano/node/scheduler/component.cpp @@ -26,7 +26,6 @@ void nano::scheduler::component::start () hinted.start (); manual.start (); optimistic.start (); - priority.start (); } void nano::scheduler::component::stop () @@ -34,7 +33,6 @@ void nano::scheduler::component::stop () hinted.stop (); manual.stop (); optimistic.stop (); - priority.stop (); } std::unique_ptr nano::scheduler::component::collect_container_info (std::string const & name) diff --git a/nano/node/scheduler/priority.cpp b/nano/node/scheduler/priority.cpp index 0635824f67..8afb5810db 100644 --- a/nano/node/scheduler/priority.cpp +++ b/nano/node/scheduler/priority.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include #include @@ -10,130 +10,145 @@ nano::scheduler::priority::priority (nano::node & node_a, nano::stats & stats_a) : node{ node_a }, - stats{ stats_a }, - buckets{ std::make_unique () } + stats{ stats_a } { + setup_buckets (); } nano::scheduler::priority::~priority () { - // Thread must be stopped before destruction - debug_assert (!thread.joinable ()); } -void nano::scheduler::priority::start () -{ - debug_assert (!thread.joinable ()); - - thread = std::thread{ [this] () { - nano::thread_role::set (nano::thread_role::name::scheduler_priority); - run (); - } }; -} - -void nano::scheduler::priority::stop () -{ - { - nano::lock_guard lock{ mutex }; - stopped = true; - } - notify (); - nano::join_or_pass (thread); -} - -bool nano::scheduler::priority::activate (secure::transaction const & transaction, nano::account const & account) +std::shared_ptr nano::scheduler::priority::activate (secure::transaction const & transaction, nano::account const & account) { debug_assert (!account.is_zero ()); auto head = node.ledger.confirmed.account_head (transaction, account); if (node.ledger.any.account_head (transaction, account) == head) { - return false; + return nullptr; } auto block = node.ledger.any.block_get (transaction, node.ledger.any.block_successor (transaction, { head.is_zero () ? static_cast (account) : head, head }).value ()); if (!node.ledger.dependents_confirmed (transaction, *block)) { - return false; + return nullptr; } - auto const balance_priority = std::max (block->balance ().number (), node.ledger.confirmed.block_balance (transaction, head).value_or (0).number ()); - auto const time_priority = !head.is_zero () ? node.ledger.confirmed.block_get (transaction, head)->sideband ().timestamp : nano::seconds_since_epoch (); // New accounts get current timestamp i.e. lowest priority + return activate (transaction, block); +} + +std::shared_ptr nano::scheduler::priority::activate (secure::transaction const & transaction, std::shared_ptr const & block) +{ + auto account = block->account (); + auto head = node.ledger.confirmed.account_head (transaction, account); + auto const balance_priority = std::max (block->balance (), node.ledger.any.block_balance (transaction, block->previous ()).value_or (0)); + auto timestamp_calculation = [&] () { + std::chrono::milliseconds diff{ 0 }; + if (!head.is_zero ()) + { + auto timestamp = node.ledger.confirmed.block_get (transaction, head)->sideband ().timestamp; + diff = std::chrono::seconds{ nano::seconds_since_epoch () - timestamp }; + } + // Use clock with higher precision than seconds + auto time = std::chrono::steady_clock::now () - diff; // New accounts get current timestamp i.e. lowest priority + return time; + }; + auto const time_priority = timestamp_calculation (); node.stats.inc (nano::stat::type::election_scheduler, nano::stat::detail::activated); node.logger.trace (nano::log::type::election_scheduler, nano::log::detail::block_activated, nano::log::arg{ "account", account.to_account () }, // TODO: Convert to lazy eval nano::log::arg{ "block", block }, - nano::log::arg{ "time", time_priority }, + nano::log::arg{ "time", time_priority.time_since_epoch ().count () }, nano::log::arg{ "priority", balance_priority }); - nano::lock_guard lock{ mutex }; - buckets->push (time_priority, block, balance_priority); - notify (); - - return true; // Activated -} - -void nano::scheduler::priority::notify () -{ - condition.notify_all (); -} - -std::size_t nano::scheduler::priority::size () const -{ - nano::lock_guard lock{ mutex }; - return buckets->size (); -} + stats.inc (nano::stat::type::election_scheduler, nano::stat::detail::insert_priority); + auto result = node.active.insert (block); + if (result.inserted) + { + stats.inc (nano::stat::type::election_scheduler, nano::stat::detail::insert_priority_success); + } + if (result.election != nullptr) + { + result.election->transition_active (); + } -bool nano::scheduler::priority::empty_locked () const -{ - return buckets->empty (); + nano::unique_lock lock{ mutex }; + if (tracking.find (block->hash ()) != tracking.end ()) + { + return nullptr; + } + auto iter = buckets.upper_bound (balance_priority); + --iter; // Iterator points to bucket after the target priority + debug_assert (iter != buckets.end ()); + auto & bucket = *iter->second; + auto removed = bucket.insert (time_priority, block); + if (removed == nullptr) + { + node.logger.trace (nano::log::type::election_scheduler, nano::log::detail::block_insert, + nano::log::arg{ "block", block->hash ().to_string () }, + nano::log::arg{ "time", time_priority.time_since_epoch ().count () }); + // Bucket was not at full capacity + auto inserted = tracking.emplace (block->hash (), &bucket); + lock.unlock (); + debug_assert (inserted.second); + return nullptr; + } + else if (removed != block) + { + node.logger.trace (nano::log::type::election_scheduler, nano::log::detail::block_overflow, + nano::log::arg{ "block", block->hash ().to_string () }, + nano::log::arg{ "time", time_priority.time_since_epoch ().count () }); + // Bucket was full and another block was lowest priority + auto inserted = tracking.emplace (block->hash (), &bucket); + lock.unlock (); + node.active.erase (*removed); + debug_assert (inserted.second); + return removed; + } + else + { + node.logger.trace (nano::log::type::election_scheduler, nano::log::detail::block_reject, + nano::log::arg{ "block", block->hash ().to_string () }, + nano::log::arg{ "time", time_priority.time_since_epoch ().count () }); + lock.unlock (); + // Bucket was full and block inserted was lowest priority + node.active.erase (*block); + return block; + } } -bool nano::scheduler::priority::empty () const +void nano::scheduler::priority::election_stopped (std::shared_ptr election) { nano::lock_guard lock{ mutex }; - return empty_locked (); -} - -bool nano::scheduler::priority::predicate () const -{ - return node.active.vacancy (nano::election_behavior::priority) > 0 && !buckets->empty (); + for (auto const & [hash, block] : election->blocks ()) + { + if (auto existing = tracking.find (hash); existing != tracking.end ()) + { + auto erased = existing->second->erase (hash); + debug_assert (erased == 1); + tracking.erase (existing); + } + } } -void nano::scheduler::priority::run () +void nano::scheduler::priority::setup_buckets () { - nano::unique_lock lock{ mutex }; - while (!stopped) - { - condition.wait (lock, [this] () { - return stopped || predicate (); - }); - if (!stopped) + auto build_region = [this] (uint128_t const & begin, uint128_t const & end, size_t count) { + auto width = (end - begin) / count; + for (auto i = 0; i < count; ++i) { - stats.inc (nano::stat::type::election_scheduler, nano::stat::detail::loop); - - if (predicate ()) - { - auto block = buckets->top (); - buckets->pop (); - lock.unlock (); - stats.inc (nano::stat::type::election_scheduler, nano::stat::detail::insert_priority); - auto result = node.active.insert (block); - if (result.inserted) - { - stats.inc (nano::stat::type::election_scheduler, nano::stat::detail::insert_priority_success); - } - if (result.election != nullptr) - { - result.election->transition_active (); - } - } - else - { - lock.unlock (); - } - notify (); - lock.lock (); + buckets.emplace (begin + i * width, std::make_unique (node.config.priority_scheduler.depth)); } - } + }; + build_region (0, uint128_t{ 1 } << 88, 1); + build_region (uint128_t{ 1 } << 88, uint128_t{ 1 } << 92, 2); + build_region (uint128_t{ 1 } << 92, uint128_t{ 1 } << 96, 4); + build_region (uint128_t{ 1 } << 96, uint128_t{ 1 } << 100, 8); + build_region (uint128_t{ 1 } << 100, uint128_t{ 1 } << 104, 16); + build_region (uint128_t{ 1 } << 104, uint128_t{ 1 } << 108, 16); + build_region (uint128_t{ 1 } << 108, uint128_t{ 1 } << 112, 8); + build_region (uint128_t{ 1 } << 112, uint128_t{ 1 } << 116, 4); + build_region (uint128_t{ 1 } << 116, uint128_t{ 1 } << 120, 2); + build_region (uint128_t{ 1 } << 120, uint128_t{ 1 } << 127, 1); } std::unique_ptr nano::scheduler::priority::collect_container_info (std::string const & name) @@ -141,6 +156,5 @@ std::unique_ptr nano::scheduler::priority::colle nano::unique_lock lock{ mutex }; auto composite = std::make_unique (name); - composite->add_component (buckets->collect_container_info ("buckets")); return composite; } diff --git a/nano/node/scheduler/priority.hpp b/nano/node/scheduler/priority.hpp index 8de27eaae6..59611e029f 100644 --- a/nano/node/scheduler/priority.hpp +++ b/nano/node/scheduler/priority.hpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -14,6 +15,7 @@ namespace nano { class block; class container_info_component; +class election; class node; class stats; } @@ -21,27 +23,42 @@ namespace nano::secure { class transaction; } +namespace nano::scheduler +{ +class bucket; +} namespace nano::scheduler { -class buckets; +class priority_config final +{ +public: + // nano::error deserialize (nano::tomlconfig & toml); + // nano::error serialize (nano::tomlconfig & toml) const; + +public: + size_t depth{ 128 }; // 62 buckets of 128 items a piece = 7,936 possible elections / backlog +}; class priority final { public: priority (nano::node &, nano::stats &); ~priority (); - void start (); - void stop (); - /** - * Activates the first unconfirmed block of \p account_a - * @return true if account was activated + * Activates the first unconfirmed block of \p account + * @return Block that was evicted when the first unconfirmed block for \p account was activated + */ + std::shared_ptr activate (secure::transaction const & transaction, nano::account const & account); + /** + * Activates the block \p block + * @return Block that was evicted when \p block was activated */ - bool activate (secure::transaction const &, nano::account const &); - void notify (); - std::size_t size () const; - bool empty () const; + std::shared_ptr activate (secure::transaction const & transaction, std::shared_ptr const & block); + /** + * Notify container when election has stopped to free space + */ + void election_stopped (std::shared_ptr election); std::unique_ptr collect_container_info (std::string const & name); @@ -50,15 +67,10 @@ class priority final nano::stats & stats; private: - void run (); - bool empty_locked () const; - bool predicate () const; - - std::unique_ptr buckets; + void setup_buckets (); - bool stopped{ false }; - nano::condition_variable condition; + std::unordered_map tracking; + std::map> buckets; mutable nano::mutex mutex; - std::thread thread; }; } diff --git a/nano/secure/common.hpp b/nano/secure/common.hpp index fd28d30b13..a2b912faa3 100644 --- a/nano/secure/common.hpp +++ b/nano/secure/common.hpp @@ -206,7 +206,8 @@ enum class block_status balance_mismatch, // Balance and amount delta don't match representative_mismatch, // Representative is changed when it is not allowed block_position, // This block cannot follow the previous block - insufficient_work // Insufficient work for this block, even though it passed the minimal validation + insufficient_work, // Insufficient work for this block, even though it passed the minimal validation + overflow // Insufficient space in scheduler }; std::string_view to_string (block_status); diff --git a/nano/slow_test/node.cpp b/nano/slow_test/node.cpp index f5e17a6a4f..27911f9d33 100644 --- a/nano/slow_test/node.cpp +++ b/nano/slow_test/node.cpp @@ -2166,7 +2166,7 @@ TEST (system, block_sequence) std::string message; for (auto i : system.nodes) { - message += boost::str (boost::format ("N:%1% b:%2% c:%3% a:%4% s:%5% p:%6%\n") % std::to_string (i->network.port) % std::to_string (i->ledger.block_count ()) % std::to_string (i->ledger.cemented_count ()) % std::to_string (i->active.size ()) % std::to_string (i->scheduler.priority.size ()) % std::to_string (i->network.size ())); + message += boost::str (boost::format ("N:%1% b:%2% c:%3% a:%4% p:%5%\n") % std::to_string (i->network.port) % std::to_string (i->ledger.block_count ()) % std::to_string (i->ledger.cemented_count ()) % std::to_string (i->active.size ()) % std::to_string (i->network.size ())); nano::lock_guard lock{ i->active.mutex }; for (auto const & j : i->active.roots) { From 9664ad685c7b8f43102d66032096f782610aeb09 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Wed, 1 May 2024 12:39:38 +0100 Subject: [PATCH 09/14] Check if block exists before confirming --- nano/lib/stats_enums.hpp | 1 + nano/node/confirming_set.cpp | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/nano/lib/stats_enums.hpp b/nano/lib/stats_enums.hpp index a5a22cd621..5a7f1257b1 100644 --- a/nano/lib/stats_enums.hpp +++ b/nano/lib/stats_enums.hpp @@ -301,6 +301,7 @@ enum class detail blocks_confirmed, blocks_confirmed_unbounded, blocks_confirmed_bounded, + block_late_removed, // request aggregator aggregator_accepted, diff --git a/nano/node/confirming_set.cpp b/nano/node/confirming_set.cpp index d39cf5791d..6161714a04 100644 --- a/nano/node/confirming_set.cpp +++ b/nano/node/confirming_set.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -80,15 +81,22 @@ void nano::confirming_set::run () { auto item = *i; lock.unlock (); - auto added = ledger.confirm (tx, item); - if (!added.empty ()) + if (ledger.any.block_exists (tx, item)) { - // Confirming this block may implicitly confirm more - cemented.insert (cemented.end (), added.begin (), added.end ()); + auto added = ledger.confirm (tx, item); + if (!added.empty ()) + { + // Confirming this block may implicitly confirm more + cemented.insert (cemented.end (), added.begin (), added.end ()); + } + else + { + already.push_back (item); + } } else { - already.push_back (item); + ledger.stats.inc (nano::stat::type::confirmation_height, nano::stat::detail::block_late_removed); } lock.lock (); } From 522498aab7ec19b0b3340ed8375bbc3328eb9400 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Sat, 4 May 2024 09:56:27 +0100 Subject: [PATCH 10/14] Increasing bucket depth. --- nano/node/scheduler/priority.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nano/node/scheduler/priority.hpp b/nano/node/scheduler/priority.hpp index 59611e029f..20fcbb1cbb 100644 --- a/nano/node/scheduler/priority.hpp +++ b/nano/node/scheduler/priority.hpp @@ -37,7 +37,7 @@ class priority_config final // nano::error serialize (nano::tomlconfig & toml) const; public: - size_t depth{ 128 }; // 62 buckets of 128 items a piece = 7,936 possible elections / backlog + size_t depth{ 4096 }; }; class priority final { From 6833891213a4e70700619c3bb07ed8ea4ae58458 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Mon, 6 May 2024 14:43:08 +0100 Subject: [PATCH 11/14] Add tracing for removed item. --- nano/node/scheduler/priority.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nano/node/scheduler/priority.cpp b/nano/node/scheduler/priority.cpp index 8afb5810db..e9300ea6db 100644 --- a/nano/node/scheduler/priority.cpp +++ b/nano/node/scheduler/priority.cpp @@ -96,6 +96,7 @@ std::shared_ptr nano::scheduler::priority::activate (secure::transa { node.logger.trace (nano::log::type::election_scheduler, nano::log::detail::block_overflow, nano::log::arg{ "block", block->hash ().to_string () }, + nano::log::arg{ "removed", removed->hash ().to_string () }, nano::log::arg{ "time", time_priority.time_since_epoch ().count () }); // Bucket was full and another block was lowest priority auto inserted = tracking.emplace (block->hash (), &bucket); @@ -108,6 +109,7 @@ std::shared_ptr nano::scheduler::priority::activate (secure::transa { node.logger.trace (nano::log::type::election_scheduler, nano::log::detail::block_reject, nano::log::arg{ "block", block->hash ().to_string () }, + nano::log::arg{ "removed", removed->hash ().to_string () }, nano::log::arg{ "time", time_priority.time_since_epoch ().count () }); lock.unlock (); // Bucket was full and block inserted was lowest priority From d4f88073164a49feb2e13686454ec215cd2d6aa4 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Wed, 8 May 2024 12:44:45 +0100 Subject: [PATCH 12/14] Only inspecting live blocks for activation. --- nano/node/blockprocessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index dd75c99118..e7984043e7 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -369,7 +369,7 @@ nano::block_status nano::block_processor::process_one (secure::write_transaction nano::log::arg{ "forced", forced_a }, nano::log::arg{ "block", block }); - if (result == nano::block_status::progress) + if (result == nano::block_status::progress && context.source == nano::block_source::live) { std::shared_ptr removed; if (node.ledger.dependents_confirmed (transaction_a, *block)) From f16c65b4920542439a0ce82b06b8e8f18ffc17e7 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Fri, 10 May 2024 12:49:05 +0100 Subject: [PATCH 13/14] Add stat counters for priority block inserting. --- nano/lib/stats_enums.hpp | 3 +++ nano/node/scheduler/priority.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/nano/lib/stats_enums.hpp b/nano/lib/stats_enums.hpp index 5a7f1257b1..8ec1020b6e 100644 --- a/nano/lib/stats_enums.hpp +++ b/nano/lib/stats_enums.hpp @@ -372,6 +372,9 @@ enum class detail insert_priority, insert_priority_success, erase_oldest, + block_insert, + block_overflow, + block_reject, // handshake invalid_node_id, diff --git a/nano/node/scheduler/priority.cpp b/nano/node/scheduler/priority.cpp index e9300ea6db..4d776bc4bf 100644 --- a/nano/node/scheduler/priority.cpp +++ b/nano/node/scheduler/priority.cpp @@ -83,6 +83,7 @@ std::shared_ptr nano::scheduler::priority::activate (secure::transa auto removed = bucket.insert (time_priority, block); if (removed == nullptr) { + node.stats.inc (nano::stat::type::election_scheduler, nano::stat::detail::block_insert); node.logger.trace (nano::log::type::election_scheduler, nano::log::detail::block_insert, nano::log::arg{ "block", block->hash ().to_string () }, nano::log::arg{ "time", time_priority.time_since_epoch ().count () }); @@ -94,6 +95,7 @@ std::shared_ptr nano::scheduler::priority::activate (secure::transa } else if (removed != block) { + node.stats.inc (nano::stat::type::election_scheduler, nano::stat::detail::block_overflow); node.logger.trace (nano::log::type::election_scheduler, nano::log::detail::block_overflow, nano::log::arg{ "block", block->hash ().to_string () }, nano::log::arg{ "removed", removed->hash ().to_string () }, @@ -107,6 +109,7 @@ std::shared_ptr nano::scheduler::priority::activate (secure::transa } else { + node.stats.inc (nano::stat::type::election_scheduler, nano::stat::detail::block_reject); node.logger.trace (nano::log::type::election_scheduler, nano::log::detail::block_reject, nano::log::arg{ "block", block->hash ().to_string () }, nano::log::arg{ "removed", removed->hash ().to_string () }, From 4444e9bc1525f71c26e4a496ad651b1d45a520de Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Fri, 10 May 2024 14:23:52 +0100 Subject: [PATCH 14/14] Test no rollback. --- nano/node/blockprocessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index e7984043e7..3b0a7b7625 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -378,7 +378,7 @@ nano::block_status nano::block_processor::process_one (secure::write_transaction } if (removed != nullptr && node.ledger.any.block_exists (transaction_a, removed->hash ())) { - node.ledger.rollback (transaction_a, removed->hash ()); + //node.ledger.rollback (transaction_a, removed->hash ()); if (removed->hash () == block->hash ()) { return nano::block_status::overflow;