diff --git a/nano/node/node.cpp b/nano/node/node.cpp index ef7656424d..6926ed5c9c 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -147,6 +147,7 @@ nano::node::node (boost::asio::io_context & io_ctx_a, boost::filesystem::path co startup_time (std::chrono::steady_clock::now ()), node_seq (seq) { + store.unchecked.use_memory = [this] () { return ledger.bootstrap_weight_reached (); }; if (!init_error ()) { telemetry->start (); diff --git a/nano/secure/common.cpp b/nano/secure/common.cpp index 25d01f1653..6737e03780 100644 --- a/nano/secure/common.cpp +++ b/nano/secure/common.cpp @@ -894,6 +894,11 @@ bool nano::unchecked_key::operator== (nano::unchecked_key const & other_a) const return previous == other_a.previous && hash == other_a.hash; } +bool nano::unchecked_key::operator< (nano::unchecked_key const & other_a) const +{ + return previous != other_a.previous ? previous < other_a.previous : hash < other_a.hash; +} + nano::block_hash const & nano::unchecked_key::key () const { return previous; diff --git a/nano/secure/common.hpp b/nano/secure/common.hpp index 078ccdb774..19a7fa84a7 100644 --- a/nano/secure/common.hpp +++ b/nano/secure/common.hpp @@ -175,6 +175,7 @@ class unchecked_key final unchecked_key (nano::uint512_union const &); bool deserialize (nano::stream &); bool operator== (nano::unchecked_key const &) const; + bool operator< (nano::unchecked_key const &) const; nano::block_hash const & key () const; nano::block_hash previous{ 0 }; nano::block_hash hash{ 0 }; diff --git a/nano/secure/store.hpp b/nano/secure/store.hpp index e644e2093e..229e4d670e 100644 --- a/nano/secure/store.hpp +++ b/nano/secure/store.hpp @@ -757,6 +757,8 @@ class unchecked_store nano::transaction const & tx, nano::hash_or_account const & dependency, std::function action, std::function predicate = [] () { return true; }) = 0; virtual size_t count (nano::transaction const &) = 0; + + std::function use_memory = [] () { return true; }; }; /** diff --git a/nano/secure/store/unchecked_store_partial.hpp b/nano/secure/store/unchecked_store_partial.hpp index 5a387f3fdb..c7a36ff992 100644 --- a/nano/secure/store/unchecked_store_partial.hpp +++ b/nano/secure/store/unchecked_store_partial.hpp @@ -2,6 +2,12 @@ #include +#include +#include +#include + +namespace mi = boost::multi_index; + namespace { template @@ -19,10 +25,18 @@ void release_assert_success (store_partial const &, int cons template class unchecked_store_partial : public unchecked_store { + class tag_random_access + { + }; + class tag_root + { + }; + private: nano::store_partial & store; friend void release_assert_success (store_partial const &, int const); + static size_t constexpr mem_block_count_max = 256'000; public: unchecked_store_partial (nano::store_partial & store_a) : @@ -30,55 +44,130 @@ class unchecked_store_partial : public unchecked_store void clear (nano::write_transaction const & transaction_a) override { - auto status = store.drop (transaction_a, tables::unchecked); - release_assert_success (store, status); + nano::lock_guard lock{ mutex }; + if (entries == nullptr) + { + auto status = store.drop (transaction_a, tables::unchecked); + release_assert_success (store, status); + } + else + { + entries->clear (); + } } void put (nano::write_transaction const & transaction_a, nano::unchecked_key const & key_a, nano::unchecked_info const & info_a) override { - if (get (transaction_a, key_a.previous).size () > 1) - return; - nano::db_val info (info_a); - auto status (store.put (transaction_a, tables::unchecked, key_a, info)); - release_assert_success (store, status); + nano::lock_guard lock{ mutex }; + if (entries == nullptr && use_memory ()) + { + auto entries_new = std::make_unique (); + for_each ( + transaction_a, [&entries_new] (nano::unchecked_key const & key, nano::unchecked_info const & info) { entries_new->template get ().insert ({ key, info }); }, [&] () { return entries_new->size () < mem_block_count_max; }); + clear (transaction_a); + entries = std::move (entries_new); + } + if (entries == nullptr) + { + nano::db_val info (info_a); + auto status (store.put (transaction_a, tables::unchecked, key_a, info)); + release_assert_success (store, status); + } + else + { + entries->template get ().insert ({ key_a, info_a }); + while (entries->size () > mem_block_count_max) + { + entries->template get ().pop_front (); + } + } } void put (nano::write_transaction const & transaction_a, nano::block_hash const & hash_a, std::shared_ptr const & block_a) override { - nano::unchecked_key key (hash_a, block_a->hash ()); - nano::unchecked_info info (block_a, block_a->account (), nano::seconds_since_epoch (), nano::signature_verification::unknown); - put (transaction_a, key, info); + nano::lock_guard lock{ mutex }; + if (entries == nullptr) + { + nano::unchecked_key key (hash_a, block_a->hash ()); + nano::unchecked_info info{ block_a, block_a->account (), nano::seconds_since_epoch (), nano::signature_verification::unknown }; + put (transaction_a, key, info); + } + else + { + nano::unchecked_key key (hash_a, block_a->hash ()); + nano::unchecked_info info{ block_a, block_a->account (), nano::seconds_since_epoch (), nano::signature_verification::unknown }; + put (transaction_a, key, info); + } } bool exists (nano::transaction const & transaction_a, nano::unchecked_key const & unchecked_key_a) override { - nano::db_val value; - auto status (store.get (transaction_a, tables::unchecked, nano::db_val (unchecked_key_a), value)); - release_assert (store.success (status) || store.not_found (status)); - return (store.success (status)); + nano::lock_guard lock{ mutex }; + if (entries == nullptr) + { + nano::db_val value; + auto status (store.get (transaction_a, tables::unchecked, nano::db_val (unchecked_key_a), value)); + release_assert (store.success (status) || store.not_found (status)); + return (store.success (status)); + } + else + { + return entries->template get ().count (unchecked_key_a) != 0; + } } void del (nano::write_transaction const & transaction_a, nano::unchecked_key const & key_a) override { - auto status (store.del (transaction_a, tables::unchecked, key_a)); - release_assert_success (store, status); + nano::lock_guard lock{ mutex }; + if (entries == nullptr) + { + auto status (store.del (transaction_a, tables::unchecked, key_a)); + release_assert_success (store, status); + } + else + { + auto erased = entries->template get ().erase (key_a); + release_assert (erased); + } } void for_each ( nano::transaction const & tx, std::function action, std::function predicate = [] () { return true; }) override { - for (auto i = store.template make_iterator (tx, tables::unchecked), n = nano::store_iterator (nullptr); predicate () && i != n; ++i) + nano::lock_guard lock{ mutex }; + if (entries == nullptr) + { + for (auto i = store.template make_iterator (tx, tables::unchecked), n = nano::store_iterator (nullptr); predicate () && i != n; ++i) + { + action (i->first, i->second); + } + } + else { - action (i->first, i->second); + for (auto i = entries->begin (), n = entries->end (); predicate () && i != n; ++i) + { + action (i->key, i->info); + } } } void for_each ( nano::transaction const & tx, nano::hash_or_account const & dependency, std::function action, std::function predicate = [] () { return true; }) override { - for (auto i = store.template make_iterator (tx, tables::unchecked, nano::unchecked_key{ dependency, 0 }), n = nano::store_iterator (nullptr); predicate () && i->first.key () == dependency && i != n; ++i) + nano::lock_guard lock{ mutex }; + if (entries == nullptr) { - action (i->first, i->second); + for (auto i = store.template make_iterator (tx, tables::unchecked, nano::unchecked_key{ dependency, 0 }), n = nano::store_iterator (nullptr); predicate () && i != n && i->first.key () == dependency; ++i) + { + action (i->first, i->second); + } + } + else + { + for (auto i = entries->template get ().lower_bound (nano::unchecked_key{ dependency, 0 }), n = entries->template get ().end (); predicate () && i != n && i->key.key () == dependency; ++i) + { + action (i->key, i->info); + } } } @@ -93,8 +182,31 @@ class unchecked_store_partial : public unchecked_store size_t count (nano::transaction const & transaction_a) override { - return store.count (transaction_a, tables::unchecked); + nano::lock_guard lock{ mutex }; + if (entries == nullptr) + { + return store.count (transaction_a, tables::unchecked); + } + else + { + return entries->size (); + } } + +private: + class entry + { + public: + nano::unchecked_key key; + nano::unchecked_info info; + }; + std::recursive_mutex mutex; + std::unique_ptr>, + mi::ordered_unique, + mi::member>>>> + entries; }; }