Merge branch 'develop' into frontier-scan-5
This commit is contained in:
commit
6e68af5b2e
15 changed files with 242 additions and 103 deletions
|
|
@ -37,6 +37,7 @@ add_executable(
|
||||||
node.cpp
|
node.cpp
|
||||||
numbers.cpp
|
numbers.cpp
|
||||||
object_stream.cpp
|
object_stream.cpp
|
||||||
|
observer_set.cpp
|
||||||
optimistic_scheduler.cpp
|
optimistic_scheduler.cpp
|
||||||
processing_queue.cpp
|
processing_queue.cpp
|
||||||
processor_service.cpp
|
processor_service.cpp
|
||||||
|
|
|
||||||
129
nano/core_test/observer_set.cpp
Normal file
129
nano/core_test/observer_set.cpp
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
#include <nano/lib/observer_set.hpp>
|
||||||
|
#include <nano/lib/timer.hpp>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
TEST (observer_set, notify_one)
|
||||||
|
{
|
||||||
|
nano::observer_set<int> set;
|
||||||
|
int value{ 0 };
|
||||||
|
set.add ([&value] (int v) {
|
||||||
|
value = v;
|
||||||
|
});
|
||||||
|
set.notify (1);
|
||||||
|
ASSERT_EQ (1, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST (observer_set, notify_multiple)
|
||||||
|
{
|
||||||
|
nano::observer_set<int> set;
|
||||||
|
int value{ 0 };
|
||||||
|
set.add ([&value] (int v) {
|
||||||
|
value = v;
|
||||||
|
});
|
||||||
|
set.add ([&value] (int v) {
|
||||||
|
value += v;
|
||||||
|
});
|
||||||
|
set.notify (1);
|
||||||
|
ASSERT_EQ (2, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST (observer_set, notify_empty)
|
||||||
|
{
|
||||||
|
nano::observer_set<int> set;
|
||||||
|
set.notify (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST (observer_set, notify_multiple_types)
|
||||||
|
{
|
||||||
|
nano::observer_set<int, std::string> set;
|
||||||
|
int value{ 0 };
|
||||||
|
std::string str;
|
||||||
|
set.add ([&value, &str] (int v, std::string s) {
|
||||||
|
value = v;
|
||||||
|
str = s;
|
||||||
|
});
|
||||||
|
set.notify (1, "test");
|
||||||
|
ASSERT_EQ (1, value);
|
||||||
|
ASSERT_EQ ("test", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST (observer_set, empty_params)
|
||||||
|
{
|
||||||
|
nano::observer_set<> set;
|
||||||
|
set.notify ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure there are no TSAN warnings
|
||||||
|
TEST (observer_set, parallel_notify)
|
||||||
|
{
|
||||||
|
nano::observer_set<int> set;
|
||||||
|
std::atomic<int> value{ 0 };
|
||||||
|
set.add ([&value] (int v) {
|
||||||
|
std::this_thread::sleep_for (100ms);
|
||||||
|
value = v;
|
||||||
|
});
|
||||||
|
nano::timer timer{ nano::timer_state::started };
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
for (int i = 0; i < 10; ++i)
|
||||||
|
{
|
||||||
|
threads.emplace_back ([&set] {
|
||||||
|
set.notify (1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (auto & thread : threads)
|
||||||
|
{
|
||||||
|
thread.join ();
|
||||||
|
}
|
||||||
|
ASSERT_EQ (1, value);
|
||||||
|
// Notification should be done in parallel
|
||||||
|
ASSERT_LT (timer.since_start (), 300ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct move_only
|
||||||
|
{
|
||||||
|
move_only () = default;
|
||||||
|
move_only (move_only &&) = default;
|
||||||
|
move_only & operator= (move_only &&) = default;
|
||||||
|
move_only (move_only const &) = delete;
|
||||||
|
move_only & operator= (move_only const &) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct copy_throw
|
||||||
|
{
|
||||||
|
copy_throw () = default;
|
||||||
|
copy_throw (copy_throw &&) = default;
|
||||||
|
copy_throw & operator= (copy_throw &&) = default;
|
||||||
|
copy_throw (copy_throw const &)
|
||||||
|
{
|
||||||
|
throw std::runtime_error ("copy_throw");
|
||||||
|
}
|
||||||
|
copy_throw & operator= (copy_throw const &) = delete;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that parameters are not unnecessarily copied, this should compile
|
||||||
|
TEST (observer_set, move_only)
|
||||||
|
{
|
||||||
|
nano::observer_set<move_only> set;
|
||||||
|
set.add ([] (move_only const &) {
|
||||||
|
});
|
||||||
|
move_only value;
|
||||||
|
set.notify (value);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST (observer_set, copy_throw)
|
||||||
|
{
|
||||||
|
nano::observer_set<copy_throw> set;
|
||||||
|
set.add ([] (copy_throw const &) {
|
||||||
|
});
|
||||||
|
copy_throw value;
|
||||||
|
ASSERT_NO_THROW (set.notify (value));
|
||||||
|
}
|
||||||
|
|
@ -12,21 +12,25 @@ template <typename... T>
|
||||||
class observer_set final
|
class observer_set final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void add (std::function<void (T...)> const & observer_a)
|
using observer_type = std::function<void (T const &...)>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void add (observer_type observer)
|
||||||
{
|
{
|
||||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||||
observers.push_back (observer_a);
|
observers.push_back (observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void notify (T... args) const
|
void notify (T const &... args) const
|
||||||
{
|
{
|
||||||
|
// Make observers copy to allow parallel notifications
|
||||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||||
auto observers_copy = observers;
|
auto observers_copy = observers;
|
||||||
lock.unlock ();
|
lock.unlock ();
|
||||||
|
|
||||||
for (auto & i : observers_copy)
|
for (auto const & observer : observers_copy)
|
||||||
{
|
{
|
||||||
i (args...);
|
observer (args...);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,7 +57,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutable nano::mutex mutex{ mutex_identifier (mutexes::observer_set) };
|
mutable nano::mutex mutex{ mutex_identifier (mutexes::observer_set) };
|
||||||
std::vector<std::function<void (T...)>> observers;
|
std::vector<observer_type> observers;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,9 @@ std::string nano::thread_role::get_string (nano::thread_role::name role)
|
||||||
case nano::thread_role::name::block_processing:
|
case nano::thread_role::name::block_processing:
|
||||||
thread_role_name_string = "Blck processing";
|
thread_role_name_string = "Blck processing";
|
||||||
break;
|
break;
|
||||||
|
case nano::thread_role::name::block_processing_notifications:
|
||||||
|
thread_role_name_string = "Blck proc notif";
|
||||||
|
break;
|
||||||
case nano::thread_role::name::request_loop:
|
case nano::thread_role::name::request_loop:
|
||||||
thread_role_name_string = "Request loop";
|
thread_role_name_string = "Request loop";
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ enum class name
|
||||||
vote_processing,
|
vote_processing,
|
||||||
vote_cache_processing,
|
vote_cache_processing,
|
||||||
block_processing,
|
block_processing,
|
||||||
|
block_processing_notifications,
|
||||||
request_loop,
|
request_loop,
|
||||||
wallet_actions,
|
wallet_actions,
|
||||||
bootstrap_initiator,
|
bootstrap_initiator,
|
||||||
|
|
|
||||||
|
|
@ -12,36 +12,14 @@
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
/*
|
|
||||||
* block_processor::context
|
|
||||||
*/
|
|
||||||
|
|
||||||
nano::block_processor::context::context (std::shared_ptr<nano::block> block, nano::block_source source_a, callback_t callback_a) :
|
|
||||||
block{ std::move (block) },
|
|
||||||
source{ source_a },
|
|
||||||
callback{ std::move (callback_a) }
|
|
||||||
{
|
|
||||||
debug_assert (source != nano::block_source::unknown);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto nano::block_processor::context::get_future () -> std::future<result_t>
|
|
||||||
{
|
|
||||||
return promise.get_future ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void nano::block_processor::context::set_result (result_t const & result)
|
|
||||||
{
|
|
||||||
promise.set_value (result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* block_processor
|
* block_processor
|
||||||
*/
|
*/
|
||||||
|
|
||||||
nano::block_processor::block_processor (nano::node & node_a) :
|
nano::block_processor::block_processor (nano::node & node_a) :
|
||||||
config{ node_a.config.block_processor },
|
config{ node_a.config.block_processor },
|
||||||
node (node_a),
|
node{ node_a },
|
||||||
next_log (std::chrono::steady_clock::now ())
|
workers{ 1, nano::thread_role::name::block_processing_notifications }
|
||||||
{
|
{
|
||||||
batch_processed.add ([this] (auto const & items) {
|
batch_processed.add ([this] (auto const & items) {
|
||||||
// For every batch item: notify the 'processed' observer.
|
// For every batch item: notify the 'processed' observer.
|
||||||
|
|
@ -84,12 +62,15 @@ nano::block_processor::~block_processor ()
|
||||||
{
|
{
|
||||||
// Thread must be stopped before destruction
|
// Thread must be stopped before destruction
|
||||||
debug_assert (!thread.joinable ());
|
debug_assert (!thread.joinable ());
|
||||||
|
debug_assert (!workers.alive ());
|
||||||
}
|
}
|
||||||
|
|
||||||
void nano::block_processor::start ()
|
void nano::block_processor::start ()
|
||||||
{
|
{
|
||||||
debug_assert (!thread.joinable ());
|
debug_assert (!thread.joinable ());
|
||||||
|
|
||||||
|
workers.start ();
|
||||||
|
|
||||||
thread = std::thread ([this] () {
|
thread = std::thread ([this] () {
|
||||||
nano::thread_role::set (nano::thread_role::name::block_processing);
|
nano::thread_role::set (nano::thread_role::name::block_processing);
|
||||||
run ();
|
run ();
|
||||||
|
|
@ -107,6 +88,7 @@ void nano::block_processor::stop ()
|
||||||
{
|
{
|
||||||
thread.join ();
|
thread.join ();
|
||||||
}
|
}
|
||||||
|
workers.stop ();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove and replace all checks with calls to size (block_source)
|
// TODO: Remove and replace all checks with calls to size (block_source)
|
||||||
|
|
@ -229,13 +211,24 @@ void nano::block_processor::rollback_competitor (secure::write_transaction const
|
||||||
|
|
||||||
void nano::block_processor::run ()
|
void nano::block_processor::run ()
|
||||||
{
|
{
|
||||||
|
nano::interval log_interval;
|
||||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||||
while (!stopped)
|
while (!stopped)
|
||||||
{
|
{
|
||||||
if (!queue.empty ())
|
if (!queue.empty ())
|
||||||
{
|
{
|
||||||
// TODO: Cleaner periodical logging
|
// It's possible that ledger processing happens faster than the notifications can be processed by other components, cooldown here
|
||||||
if (should_log ())
|
while (workers.queued_tasks () >= config.max_queued_notifications)
|
||||||
|
{
|
||||||
|
node.stats.inc (nano::stat::type::blockprocessor, nano::stat::detail::cooldown);
|
||||||
|
condition.wait_for (lock, 100ms, [this] { return stopped; });
|
||||||
|
if (stopped)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log_interval.elapsed (15s))
|
||||||
{
|
{
|
||||||
node.logger.info (nano::log::type::blockprocessor, "{} blocks (+ {} forced) in processing queue",
|
node.logger.info (nano::log::type::blockprocessor, "{} blocks (+ {} forced) in processing queue",
|
||||||
queue.size (),
|
queue.size (),
|
||||||
|
|
@ -244,41 +237,32 @@ void nano::block_processor::run ()
|
||||||
|
|
||||||
auto processed = process_batch (lock);
|
auto processed = process_batch (lock);
|
||||||
debug_assert (!lock.owns_lock ());
|
debug_assert (!lock.owns_lock ());
|
||||||
|
|
||||||
// Set results for futures when not holding the lock
|
|
||||||
for (auto & [result, context] : processed)
|
|
||||||
{
|
|
||||||
if (context.callback)
|
|
||||||
{
|
|
||||||
context.callback (result);
|
|
||||||
}
|
|
||||||
context.set_result (result);
|
|
||||||
}
|
|
||||||
|
|
||||||
batch_processed.notify (processed);
|
|
||||||
|
|
||||||
lock.lock ();
|
lock.lock ();
|
||||||
|
|
||||||
|
// Queue notifications to be dispatched in the background
|
||||||
|
workers.post ([this, processed = std::move (processed)] () mutable {
|
||||||
|
node.stats.inc (nano::stat::type::blockprocessor, nano::stat::detail::notify);
|
||||||
|
// Set results for futures when not holding the lock
|
||||||
|
for (auto & [result, context] : processed)
|
||||||
|
{
|
||||||
|
if (context.callback)
|
||||||
|
{
|
||||||
|
context.callback (result);
|
||||||
|
}
|
||||||
|
context.set_result (result);
|
||||||
|
}
|
||||||
|
batch_processed.notify (processed);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
condition.notify_one ();
|
condition.wait (lock, [this] {
|
||||||
condition.wait (lock);
|
return stopped || !queue.empty ();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nano::block_processor::should_log ()
|
|
||||||
{
|
|
||||||
auto result (false);
|
|
||||||
auto now (std::chrono::steady_clock::now ());
|
|
||||||
if (next_log < now)
|
|
||||||
{
|
|
||||||
next_log = now + std::chrono::seconds (15);
|
|
||||||
result = true;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto nano::block_processor::next () -> context
|
auto nano::block_processor::next () -> context
|
||||||
{
|
{
|
||||||
debug_assert (!mutex.try_lock ());
|
debug_assert (!mutex.try_lock ());
|
||||||
|
|
@ -315,7 +299,7 @@ auto nano::block_processor::process_batch (nano::unique_lock<nano::mutex> & lock
|
||||||
debug_assert (!mutex.try_lock ());
|
debug_assert (!mutex.try_lock ());
|
||||||
debug_assert (!queue.empty ());
|
debug_assert (!queue.empty ());
|
||||||
|
|
||||||
auto batch = next_batch (256);
|
auto batch = next_batch (config.batch_size);
|
||||||
|
|
||||||
lock.unlock ();
|
lock.unlock ();
|
||||||
|
|
||||||
|
|
@ -466,9 +450,32 @@ nano::container_info nano::block_processor::container_info () const
|
||||||
info.put ("blocks", queue.size ());
|
info.put ("blocks", queue.size ());
|
||||||
info.put ("forced", queue.size ({ nano::block_source::forced }));
|
info.put ("forced", queue.size ({ nano::block_source::forced }));
|
||||||
info.add ("queue", queue.container_info ());
|
info.add ("queue", queue.container_info ());
|
||||||
|
info.add ("workers", workers.container_info ());
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* block_processor::context
|
||||||
|
*/
|
||||||
|
|
||||||
|
nano::block_processor::context::context (std::shared_ptr<nano::block> block, nano::block_source source_a, callback_t callback_a) :
|
||||||
|
block{ std::move (block) },
|
||||||
|
source{ source_a },
|
||||||
|
callback{ std::move (callback_a) }
|
||||||
|
{
|
||||||
|
debug_assert (source != nano::block_source::unknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto nano::block_processor::context::get_future () -> std::future<result_t>
|
||||||
|
{
|
||||||
|
return promise.get_future ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void nano::block_processor::context::set_result (result_t const & result)
|
||||||
|
{
|
||||||
|
promise.set_value (result);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* block_processor_config
|
* block_processor_config
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <nano/lib/logging.hpp>
|
#include <nano/lib/logging.hpp>
|
||||||
|
#include <nano/lib/thread_pool.hpp>
|
||||||
#include <nano/node/fair_queue.hpp>
|
#include <nano/node/fair_queue.hpp>
|
||||||
#include <nano/node/fwd.hpp>
|
#include <nano/node/fwd.hpp>
|
||||||
#include <nano/secure/common.hpp>
|
#include <nano/secure/common.hpp>
|
||||||
|
|
@ -46,6 +47,9 @@ public:
|
||||||
size_t priority_live{ 1 };
|
size_t priority_live{ 1 };
|
||||||
size_t priority_bootstrap{ 8 };
|
size_t priority_bootstrap{ 8 };
|
||||||
size_t priority_local{ 16 };
|
size_t priority_local{ 16 };
|
||||||
|
|
||||||
|
size_t batch_size{ 256 };
|
||||||
|
size_t max_queued_notifications{ 8 };
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -89,7 +93,6 @@ public:
|
||||||
bool add (std::shared_ptr<nano::block> const &, nano::block_source = nano::block_source::live, std::shared_ptr<nano::transport::channel> const & channel = nullptr, std::function<void (nano::block_status)> callback = {});
|
bool add (std::shared_ptr<nano::block> const &, nano::block_source = nano::block_source::live, std::shared_ptr<nano::transport::channel> const & channel = nullptr, std::function<void (nano::block_status)> callback = {});
|
||||||
std::optional<nano::block_status> add_blocking (std::shared_ptr<nano::block> const & block, nano::block_source);
|
std::optional<nano::block_status> add_blocking (std::shared_ptr<nano::block> const & block, nano::block_source);
|
||||||
void force (std::shared_ptr<nano::block> const &);
|
void force (std::shared_ptr<nano::block> const &);
|
||||||
bool should_log ();
|
|
||||||
|
|
||||||
nano::container_info container_info () const;
|
nano::container_info container_info () const;
|
||||||
|
|
||||||
|
|
@ -122,11 +125,11 @@ private: // Dependencies
|
||||||
private:
|
private:
|
||||||
nano::fair_queue<context, nano::block_source> queue;
|
nano::fair_queue<context, nano::block_source> queue;
|
||||||
|
|
||||||
std::chrono::steady_clock::time_point next_log;
|
|
||||||
|
|
||||||
bool stopped{ false };
|
bool stopped{ false };
|
||||||
nano::condition_variable condition;
|
nano::condition_variable condition;
|
||||||
mutable nano::mutex mutex{ mutex_identifier (mutexes::block_processor) };
|
mutable nano::mutex mutex{ mutex_identifier (mutexes::block_processor) };
|
||||||
std::thread thread;
|
std::thread thread;
|
||||||
|
|
||||||
|
nano::thread_pool workers;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,6 @@ nano::bootstrap_ascending::service::service (nano::node_config const & node_conf
|
||||||
frontiers_limiter{ config.frontier_rate_limit },
|
frontiers_limiter{ config.frontier_rate_limit },
|
||||||
workers{ 1, nano::thread_role::name::ascending_bootstrap_worker }
|
workers{ 1, nano::thread_role::name::ascending_bootstrap_worker }
|
||||||
{
|
{
|
||||||
// TODO: This is called from a very congested blockprocessor thread. Offload this work to a dedicated processing thread
|
|
||||||
block_processor.batch_processed.add ([this] (auto const & batch) {
|
block_processor.batch_processed.add ([this] (auto const & batch) {
|
||||||
{
|
{
|
||||||
nano::lock_guard<nano::mutex> lock{ mutex };
|
nano::lock_guard<nano::mutex> lock{ mutex };
|
||||||
|
|
@ -266,11 +265,14 @@ void nano::bootstrap_ascending::service::inspect (secure::transaction const & tx
|
||||||
{
|
{
|
||||||
if (source == nano::block_source::bootstrap)
|
if (source == nano::block_source::bootstrap)
|
||||||
{
|
{
|
||||||
const auto account = block.previous ().is_zero () ? block.account_field ().value () : ledger.any.block_account (tx, block.previous ()).value ();
|
const auto account = block.previous ().is_zero () ? block.account_field ().value () : ledger.any.block_account (tx, block.previous ()).value_or (0);
|
||||||
const auto source_hash = block.source_field ().value_or (block.link_field ().value_or (0).as_block_hash ());
|
const auto source_hash = block.source_field ().value_or (block.link_field ().value_or (0).as_block_hash ());
|
||||||
|
|
||||||
// Mark account as blocked because it is missing the source block
|
if (!account.is_zero () && !source_hash.is_zero ())
|
||||||
accounts.block (account, source_hash);
|
{
|
||||||
|
// Mark account as blocked because it is missing the source block
|
||||||
|
accounts.block (account, source_hash);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ nano::confirming_set::confirming_set (confirming_set_config const & config_a, na
|
||||||
ledger{ ledger_a },
|
ledger{ ledger_a },
|
||||||
stats{ stats_a },
|
stats{ stats_a },
|
||||||
logger{ logger_a },
|
logger{ logger_a },
|
||||||
notification_workers{ 1, nano::thread_role::name::confirmation_height_notifications }
|
workers{ 1, nano::thread_role::name::confirmation_height_notifications }
|
||||||
{
|
{
|
||||||
batch_cemented.add ([this] (auto const & cemented) {
|
batch_cemented.add ([this] (auto const & cemented) {
|
||||||
for (auto const & context : cemented)
|
for (auto const & context : cemented)
|
||||||
|
|
@ -55,7 +55,7 @@ void nano::confirming_set::start ()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
notification_workers.start ();
|
workers.start ();
|
||||||
|
|
||||||
thread = std::thread{ [this] () {
|
thread = std::thread{ [this] () {
|
||||||
nano::thread_role::set (nano::thread_role::name::confirmation_height);
|
nano::thread_role::set (nano::thread_role::name::confirmation_height);
|
||||||
|
|
@ -74,7 +74,7 @@ void nano::confirming_set::stop ()
|
||||||
{
|
{
|
||||||
thread.join ();
|
thread.join ();
|
||||||
}
|
}
|
||||||
notification_workers.stop ();
|
workers.stop ();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nano::confirming_set::contains (nano::block_hash const & hash) const
|
bool nano::confirming_set::contains (nano::block_hash const & hash) const
|
||||||
|
|
@ -150,7 +150,7 @@ void nano::confirming_set::run_batch (std::unique_lock<std::mutex> & lock)
|
||||||
std::unique_lock lock{ mutex };
|
std::unique_lock lock{ mutex };
|
||||||
|
|
||||||
// It's possible that ledger cementing happens faster than the notifications can be processed by other components, cooldown here
|
// It's possible that ledger cementing happens faster than the notifications can be processed by other components, cooldown here
|
||||||
while (notification_workers.queued_tasks () >= config.max_queued_notifications)
|
while (workers.queued_tasks () >= config.max_queued_notifications)
|
||||||
{
|
{
|
||||||
stats.inc (nano::stat::type::confirming_set, nano::stat::detail::cooldown);
|
stats.inc (nano::stat::type::confirming_set, nano::stat::detail::cooldown);
|
||||||
condition.wait_for (lock, 100ms, [this] { return stopped.load (); });
|
condition.wait_for (lock, 100ms, [this] { return stopped.load (); });
|
||||||
|
|
@ -160,7 +160,7 @@ void nano::confirming_set::run_batch (std::unique_lock<std::mutex> & lock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
notification_workers.post ([this, batch = std::move (batch)] () {
|
workers.post ([this, batch = std::move (batch)] () {
|
||||||
stats.inc (nano::stat::type::confirming_set, nano::stat::detail::notify);
|
stats.inc (nano::stat::type::confirming_set, nano::stat::detail::notify);
|
||||||
batch_cemented.notify (batch);
|
batch_cemented.notify (batch);
|
||||||
});
|
});
|
||||||
|
|
@ -255,6 +255,7 @@ nano::container_info nano::confirming_set::container_info () const
|
||||||
|
|
||||||
nano::container_info info;
|
nano::container_info info;
|
||||||
info.put ("set", set);
|
info.put ("set", set);
|
||||||
info.add ("notification_workers", notification_workers.container_info ());
|
info.put ("notifications", workers.queued_tasks ());
|
||||||
|
info.add ("workers", workers.container_info ());
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -105,11 +105,11 @@ private:
|
||||||
ordered_entries set;
|
ordered_entries set;
|
||||||
std::unordered_set<nano::block_hash> current;
|
std::unordered_set<nano::block_hash> current;
|
||||||
|
|
||||||
nano::thread_pool notification_workers;
|
|
||||||
|
|
||||||
std::atomic<bool> stopped{ false };
|
std::atomic<bool> stopped{ false };
|
||||||
mutable std::mutex mutex;
|
mutable std::mutex mutex;
|
||||||
std::condition_variable condition;
|
std::condition_variable condition;
|
||||||
std::thread thread;
|
std::thread thread;
|
||||||
|
|
||||||
|
nano::thread_pool workers;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ void iterator::update (int status)
|
||||||
if (status == MDB_SUCCESS)
|
if (status == MDB_SUCCESS)
|
||||||
{
|
{
|
||||||
value_type init;
|
value_type init;
|
||||||
auto status = mdb_cursor_get (cursor, &init.first, &init.second, MDB_GET_CURRENT);
|
auto status = mdb_cursor_get (cursor.get (), &init.first, &init.second, MDB_GET_CURRENT);
|
||||||
release_assert (status == MDB_SUCCESS);
|
release_assert (status == MDB_SUCCESS);
|
||||||
current = init;
|
current = init;
|
||||||
}
|
}
|
||||||
|
|
@ -33,8 +33,10 @@ void iterator::update (int status)
|
||||||
|
|
||||||
iterator::iterator (MDB_txn * tx, MDB_dbi dbi) noexcept
|
iterator::iterator (MDB_txn * tx, MDB_dbi dbi) noexcept
|
||||||
{
|
{
|
||||||
|
MDB_cursor * cursor;
|
||||||
auto open_status = mdb_cursor_open (tx, dbi, &cursor);
|
auto open_status = mdb_cursor_open (tx, dbi, &cursor);
|
||||||
release_assert (open_status == MDB_SUCCESS);
|
release_assert (open_status == MDB_SUCCESS);
|
||||||
|
this->cursor.reset (cursor);
|
||||||
this->current = std::monostate{};
|
this->current = std::monostate{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,7 +55,7 @@ auto iterator::end (MDB_txn * tx, MDB_dbi dbi) -> iterator
|
||||||
auto iterator::lower_bound (MDB_txn * tx, MDB_dbi dbi, MDB_val const & lower_bound) -> iterator
|
auto iterator::lower_bound (MDB_txn * tx, MDB_dbi dbi, MDB_val const & lower_bound) -> iterator
|
||||||
{
|
{
|
||||||
iterator result{ tx, dbi };
|
iterator result{ tx, dbi };
|
||||||
auto status = mdb_cursor_get (result.cursor, const_cast<MDB_val *> (&lower_bound), nullptr, MDB_SET_RANGE);
|
auto status = mdb_cursor_get (result.cursor.get (), const_cast<MDB_val *> (&lower_bound), nullptr, MDB_SET_RANGE);
|
||||||
result.update (status);
|
result.update (status);
|
||||||
return std::move (result);
|
return std::move (result);
|
||||||
}
|
}
|
||||||
|
|
@ -63,18 +65,9 @@ iterator::iterator (iterator && other) noexcept
|
||||||
*this = std::move (other);
|
*this = std::move (other);
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator::~iterator ()
|
|
||||||
{
|
|
||||||
if (cursor)
|
|
||||||
{
|
|
||||||
mdb_cursor_close (cursor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto iterator::operator= (iterator && other) noexcept -> iterator &
|
auto iterator::operator= (iterator && other) noexcept -> iterator &
|
||||||
{
|
{
|
||||||
cursor = other.cursor;
|
cursor = std::move (other.cursor);
|
||||||
other.cursor = nullptr;
|
|
||||||
current = other.current;
|
current = other.current;
|
||||||
other.current = std::monostate{};
|
other.current = std::monostate{};
|
||||||
return *this;
|
return *this;
|
||||||
|
|
@ -83,7 +76,7 @@ auto iterator::operator= (iterator && other) noexcept -> iterator &
|
||||||
auto iterator::operator++ () -> iterator &
|
auto iterator::operator++ () -> iterator &
|
||||||
{
|
{
|
||||||
auto operation = is_end () ? MDB_FIRST : MDB_NEXT;
|
auto operation = is_end () ? MDB_FIRST : MDB_NEXT;
|
||||||
auto status = mdb_cursor_get (cursor, nullptr, nullptr, operation);
|
auto status = mdb_cursor_get (cursor.get (), nullptr, nullptr, operation);
|
||||||
release_assert (status == MDB_SUCCESS || status == MDB_NOTFOUND);
|
release_assert (status == MDB_SUCCESS || status == MDB_NOTFOUND);
|
||||||
update (status);
|
update (status);
|
||||||
return *this;
|
return *this;
|
||||||
|
|
@ -92,7 +85,7 @@ auto iterator::operator++ () -> iterator &
|
||||||
auto iterator::operator-- () -> iterator &
|
auto iterator::operator-- () -> iterator &
|
||||||
{
|
{
|
||||||
auto operation = is_end () ? MDB_LAST : MDB_PREV;
|
auto operation = is_end () ? MDB_LAST : MDB_PREV;
|
||||||
auto status = mdb_cursor_get (cursor, nullptr, nullptr, operation);
|
auto status = mdb_cursor_get (cursor.get (), nullptr, nullptr, operation);
|
||||||
release_assert (status == MDB_SUCCESS || status == MDB_NOTFOUND);
|
release_assert (status == MDB_SUCCESS || status == MDB_NOTFOUND);
|
||||||
update (status);
|
update (status);
|
||||||
return *this;
|
return *this;
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ namespace nano::store::lmdb
|
||||||
*/
|
*/
|
||||||
class iterator
|
class iterator
|
||||||
{
|
{
|
||||||
MDB_cursor * cursor{ nullptr };
|
std::unique_ptr<MDB_cursor, decltype (&mdb_cursor_close)> cursor{ nullptr, mdb_cursor_close };
|
||||||
std::variant<std::monostate, std::pair<MDB_val, MDB_val>> current;
|
std::variant<std::monostate, std::pair<MDB_val, MDB_val>> current;
|
||||||
void update (int status);
|
void update (int status);
|
||||||
iterator (MDB_txn * tx, MDB_dbi dbi) noexcept;
|
iterator (MDB_txn * tx, MDB_dbi dbi) noexcept;
|
||||||
|
|
@ -39,8 +39,6 @@ public:
|
||||||
static auto end (MDB_txn * tx, MDB_dbi dbi) -> iterator;
|
static auto end (MDB_txn * tx, MDB_dbi dbi) -> iterator;
|
||||||
static auto lower_bound (MDB_txn * tx, MDB_dbi dbi, MDB_val const & lower_bound) -> iterator;
|
static auto lower_bound (MDB_txn * tx, MDB_dbi dbi, MDB_val const & lower_bound) -> iterator;
|
||||||
|
|
||||||
~iterator ();
|
|
||||||
|
|
||||||
iterator (iterator const &) = delete;
|
iterator (iterator const &) = delete;
|
||||||
auto operator= (iterator const &) -> iterator & = delete;
|
auto operator= (iterator const &) -> iterator & = delete;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -121,9 +121,7 @@ bool nano::store::lmdb::component::vacuum_after_upgrade (std::filesystem::path c
|
||||||
if (vacuum_success)
|
if (vacuum_success)
|
||||||
{
|
{
|
||||||
// Need to close the database to release the file handle
|
// Need to close the database to release the file handle
|
||||||
mdb_env_sync (env.environment, true);
|
mdb_env_sync (env, true);
|
||||||
mdb_env_close (env.environment);
|
|
||||||
env.environment = nullptr;
|
|
||||||
|
|
||||||
// Replace the ledger file with the vacuumed one
|
// Replace the ledger file with the vacuumed one
|
||||||
std::filesystem::rename (vacuum_path, path_a);
|
std::filesystem::rename (vacuum_path, path_a);
|
||||||
|
|
@ -155,7 +153,7 @@ void nano::store::lmdb::component::serialize_mdb_tracker (boost::property_tree::
|
||||||
void nano::store::lmdb::component::serialize_memory_stats (boost::property_tree::ptree & json)
|
void nano::store::lmdb::component::serialize_memory_stats (boost::property_tree::ptree & json)
|
||||||
{
|
{
|
||||||
MDB_stat stats;
|
MDB_stat stats;
|
||||||
auto status (mdb_env_stat (env.environment, &stats));
|
auto status (mdb_env_stat (env, &stats));
|
||||||
release_assert (status == 0);
|
release_assert (status == 0);
|
||||||
json.put ("branch_pages", stats.ms_branch_pages);
|
json.put ("branch_pages", stats.ms_branch_pages);
|
||||||
json.put ("depth", stats.ms_depth);
|
json.put ("depth", stats.ms_depth);
|
||||||
|
|
@ -448,7 +446,7 @@ std::string nano::store::lmdb::component::error_string (int status) const
|
||||||
|
|
||||||
bool nano::store::lmdb::component::copy_db (std::filesystem::path const & destination_file)
|
bool nano::store::lmdb::component::copy_db (std::filesystem::path const & destination_file)
|
||||||
{
|
{
|
||||||
return !mdb_env_copy2 (env.environment, destination_file.string ().c_str (), MDB_CP_COMPACT);
|
return !mdb_env_copy2 (env, destination_file.string ().c_str (), MDB_CP_COMPACT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void nano::store::lmdb::component::rebuild_db (store::write_transaction const & transaction_a)
|
void nano::store::lmdb::component::rebuild_db (store::write_transaction const & transaction_a)
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,10 @@ void nano::store::lmdb::env::init (bool & error_a, std::filesystem::path const &
|
||||||
nano::set_secure_perm_directory (path_a.parent_path (), error_chmod);
|
nano::set_secure_perm_directory (path_a.parent_path (), error_chmod);
|
||||||
if (!error_mkdir)
|
if (!error_mkdir)
|
||||||
{
|
{
|
||||||
|
MDB_env * environment;
|
||||||
auto status1 (mdb_env_create (&environment));
|
auto status1 (mdb_env_create (&environment));
|
||||||
release_assert (status1 == 0);
|
release_assert (status1 == 0);
|
||||||
|
this->environment.reset (environment);
|
||||||
auto status2 (mdb_env_set_maxdbs (environment, options_a.config.max_databases));
|
auto status2 (mdb_env_set_maxdbs (environment, options_a.config.max_databases));
|
||||||
release_assert (status2 == 0);
|
release_assert (status2 == 0);
|
||||||
auto map_size = options_a.config.map_size;
|
auto map_size = options_a.config.map_size;
|
||||||
|
|
@ -66,13 +68,11 @@ void nano::store::lmdb::env::init (bool & error_a, std::filesystem::path const &
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
error_a = true;
|
error_a = true;
|
||||||
environment = nullptr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
error_a = true;
|
error_a = true;
|
||||||
environment = nullptr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,14 +81,13 @@ nano::store::lmdb::env::~env ()
|
||||||
if (environment != nullptr)
|
if (environment != nullptr)
|
||||||
{
|
{
|
||||||
// Make sure the commits are flushed. This is a no-op unless MDB_NOSYNC is used.
|
// Make sure the commits are flushed. This is a no-op unless MDB_NOSYNC is used.
|
||||||
mdb_env_sync (environment, true);
|
mdb_env_sync (environment.get (), true);
|
||||||
mdb_env_close (environment);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nano::store::lmdb::env::operator MDB_env * () const
|
nano::store::lmdb::env::operator MDB_env * () const
|
||||||
{
|
{
|
||||||
return environment;
|
return environment.get ();
|
||||||
}
|
}
|
||||||
|
|
||||||
nano::store::read_transaction nano::store::lmdb::env::tx_begin_read (store::lmdb::txn_callbacks mdb_txn_callbacks) const
|
nano::store::read_transaction nano::store::lmdb::env::tx_begin_read (store::lmdb::txn_callbacks mdb_txn_callbacks) const
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ public:
|
||||||
store::read_transaction tx_begin_read (txn_callbacks callbacks = txn_callbacks{}) const;
|
store::read_transaction tx_begin_read (txn_callbacks callbacks = txn_callbacks{}) const;
|
||||||
store::write_transaction tx_begin_write (txn_callbacks callbacks = txn_callbacks{}) const;
|
store::write_transaction tx_begin_write (txn_callbacks callbacks = txn_callbacks{}) const;
|
||||||
MDB_txn * tx (store::transaction const & transaction_a) const;
|
MDB_txn * tx (store::transaction const & transaction_a) const;
|
||||||
MDB_env * environment;
|
std::unique_ptr<MDB_env, decltype (&mdb_env_close)> environment{ nullptr, mdb_env_close };
|
||||||
nano::id_t const store_id{ nano::next_id () };
|
nano::id_t const store_id{ nano::next_id () };
|
||||||
};
|
};
|
||||||
} // namespace nano::store::lmdb
|
} // namespace nano::store::lmdb
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue