From 3f07882ed67ebe816e86805f7c4019bc775ea892 Mon Sep 17 00:00:00 2001 From: Scott Determan Date: Sat, 21 Oct 2023 20:23:46 -0400 Subject: [PATCH] Split tagged cache into .h and .ipp files cc --- include/xrpl/basics/TaggedCache.h | 515 +---------- include/xrpl/basics/TaggedCache.ipp | 819 ++++++++++++++++++ src/test/basics/KeyCache_test.cpp | 1 + src/test/basics/TaggedCache_test.cpp | 1 + src/xrpld/app/ledger/ConsensusTransSetSF.cpp | 1 + src/xrpld/app/ledger/LedgerHistory.cpp | 1 + src/xrpld/app/ledger/detail/LedgerMaster.cpp | 2 +- .../app/ledger/detail/TransactionMaster.cpp | 1 + src/xrpld/app/main/Application.cpp | 1 + src/xrpld/app/misc/NetworkOPs.cpp | 1 + src/xrpld/app/misc/SHAMapStoreImp.h | 2 +- src/xrpld/app/rdb/backend/detail/Node.cpp | 1 + src/xrpld/ledger/detail/CachedView.cpp | 1 + src/xrpld/nodestore/Database.h | 2 +- .../nodestore/detail/DatabaseNodeImp.cpp | 1 + src/xrpld/rpc/handlers/GetCounts.cpp | 1 + src/xrpld/shamap/FullBelowCache.h | 1 + 17 files changed, 863 insertions(+), 489 deletions(-) create mode 100644 include/xrpl/basics/TaggedCache.ipp diff --git a/include/xrpl/basics/TaggedCache.h b/include/xrpl/basics/TaggedCache.h index 1fcdc3707b6..9ff63b1e0ce 100644 --- a/include/xrpl/basics/TaggedCache.h +++ b/include/xrpl/basics/TaggedCache.h @@ -69,230 +69,57 @@ class TaggedCache clock_type& clock, beast::Journal journal, beast::insight::Collector::ptr const& collector = - beast::insight::NullCollector::New()) - : m_journal(journal) - , m_clock(clock) - , m_stats( - name, - std::bind(&TaggedCache::collect_metrics, this), - collector) - , m_name(name) - , m_target_size(size) - , m_target_age(expiration) - , m_cache_count(0) - , m_hits(0) - , m_misses(0) - { - } + beast::insight::NullCollector::New()); public: /** Return the clock associated with the cache. */ clock_type& - clock() - { - return m_clock; - } + clock(); /** Returns the number of items in the container. */ std::size_t - size() const - { - std::lock_guard lock(m_mutex); - return m_cache.size(); - } + size() const; void - setTargetSize(int s) - { - std::lock_guard lock(m_mutex); - m_target_size = s; - - if (s > 0) - { - for (auto& partition : m_cache.map()) - { - partition.rehash(static_cast( - (s + (s >> 2)) / - (partition.max_load_factor() * m_cache.partitions()) + - 1)); - } - } - - JLOG(m_journal.debug()) << m_name << " target size set to " << s; - } + setTargetSize(int s); clock_type::duration - getTargetAge() const - { - std::lock_guard lock(m_mutex); - return m_target_age; - } + getTargetAge() const; void - setTargetAge(clock_type::duration s) - { - std::lock_guard lock(m_mutex); - m_target_age = s; - JLOG(m_journal.debug()) - << m_name << " target age set to " << m_target_age.count(); - } + setTargetAge(clock_type::duration s); int - getCacheSize() const - { - std::lock_guard lock(m_mutex); - return m_cache_count; - } + getCacheSize() const; int - getTrackSize() const - { - std::lock_guard lock(m_mutex); - return m_cache.size(); - } + getTrackSize() const; float - getHitRate() - { - std::lock_guard lock(m_mutex); - auto const total = static_cast(m_hits + m_misses); - return m_hits * (100.0f / std::max(1.0f, total)); - } + getHitRate(); void - clear() - { - std::lock_guard lock(m_mutex); - m_cache.clear(); - m_cache_count = 0; - } + clear(); void - reset() - { - std::lock_guard lock(m_mutex); - m_cache.clear(); - m_cache_count = 0; - m_hits = 0; - m_misses = 0; - } + reset(); /** Refresh the last access time on a key if present. @return `true` If the key was found. */ template bool - touch_if_exists(KeyComparable const& key) - { - std::lock_guard lock(m_mutex); - auto const iter(m_cache.find(key)); - if (iter == m_cache.end()) - { - ++m_stats.misses; - return false; - } - iter->second.touch(m_clock.now()); - ++m_stats.hits; - return true; - } + touch_if_exists(KeyComparable const& key); using SweptPointersVector = std::pair< std::vector>, std::vector>>; void - sweep() - { - // Keep references to all the stuff we sweep - // For performance, each worker thread should exit before the swept data - // is destroyed but still within the main cache lock. - std::vector allStuffToSweep(m_cache.partitions()); - - clock_type::time_point const now(m_clock.now()); - clock_type::time_point when_expire; - - auto const start = std::chrono::steady_clock::now(); - { - std::lock_guard lock(m_mutex); - - if (m_target_size == 0 || - (static_cast(m_cache.size()) <= m_target_size)) - { - when_expire = now - m_target_age; - } - else - { - when_expire = - now - m_target_age * m_target_size / m_cache.size(); - - clock_type::duration const minimumAge(std::chrono::seconds(1)); - if (when_expire > (now - minimumAge)) - when_expire = now - minimumAge; - - JLOG(m_journal.trace()) - << m_name << " is growing fast " << m_cache.size() << " of " - << m_target_size << " aging at " - << (now - when_expire).count() << " of " - << m_target_age.count(); - } - - std::vector workers; - workers.reserve(m_cache.partitions()); - std::atomic allRemovals = 0; - - for (std::size_t p = 0; p < m_cache.partitions(); ++p) - { - workers.push_back(sweepHelper( - when_expire, - now, - m_cache.map()[p], - allStuffToSweep[p], - allRemovals, - lock)); - } - for (std::thread& worker : workers) - worker.join(); - - m_cache_count -= allRemovals; - } - // At this point allStuffToSweep will go out of scope outside the lock - // and decrement the reference count on each strong pointer. - JLOG(m_journal.debug()) - << m_name << " TaggedCache sweep lock duration " - << std::chrono::duration_cast( - std::chrono::steady_clock::now() - start) - .count() - << "ms"; - } + sweep(); bool - del(const key_type& key, bool valid) - { - // Remove from cache, if !valid, remove from map too. Returns true if - // removed from cache - std::lock_guard lock(m_mutex); - - auto cit = m_cache.find(key); - - if (cit == m_cache.end()) - return false; - - Entry& entry = cit->second; - - bool ret = false; - - if (entry.isCached()) - { - --m_cache_count; - entry.ptr.reset(); - ret = true; - } - - if (!valid || entry.isExpired()) - m_cache.erase(cit); - - return ret; - } + del(const key_type& key, bool valid); /** Replace aliased objects with originals. @@ -312,95 +139,18 @@ class TaggedCache canonicalize( const key_type& key, std::shared_ptr& data, - std::function const&)>&& replace) - { - // Return canonical value, store if needed, refresh in cache - // Return values: true=we had the data already - std::lock_guard lock(m_mutex); - - auto cit = m_cache.find(key); - - if (cit == m_cache.end()) - { - m_cache.emplace( - std::piecewise_construct, - std::forward_as_tuple(key), - std::forward_as_tuple(m_clock.now(), data)); - ++m_cache_count; - return false; - } - - Entry& entry = cit->second; - entry.touch(m_clock.now()); - - if (entry.isCached()) - { - if (replace(entry.ptr)) - { - entry.ptr = data; - entry.weak_ptr = data; - } - else - { - data = entry.ptr; - } - - return true; - } - - auto cachedData = entry.lock(); - - if (cachedData) - { - if (replace(entry.ptr)) - { - entry.ptr = data; - entry.weak_ptr = data; - } - else - { - entry.ptr = cachedData; - data = cachedData; - } - - ++m_cache_count; - return true; - } - - entry.ptr = data; - entry.weak_ptr = data; - ++m_cache_count; - - return false; - } + std::function const&)>&& replace); bool canonicalize_replace_cache( const key_type& key, - std::shared_ptr const& data) - { - return canonicalize( - key, - const_cast&>(data), - [](std::shared_ptr const&) { return true; }); - } + std::shared_ptr const& data); bool - canonicalize_replace_client(const key_type& key, std::shared_ptr& data) - { - return canonicalize( - key, data, [](std::shared_ptr const&) { return false; }); - } + canonicalize_replace_client(const key_type& key, std::shared_ptr& data); std::shared_ptr - fetch(const key_type& key) - { - std::lock_guard l(m_mutex); - auto ret = initialFetch(key, l); - if (!ret) - ++m_misses; - return ret; - } + fetch(const key_type& key); /** Insert the element into the container. If the key already exists, nothing happens. @@ -409,26 +159,11 @@ class TaggedCache template auto insert(key_type const& key, T const& value) - -> std::enable_if_t - { - auto p = std::make_shared(std::cref(value)); - return canonicalize_replace_client(key, p); - } + -> std::enable_if_t; template auto - insert(key_type const& key) -> std::enable_if_t - { - std::lock_guard lock(m_mutex); - clock_type::time_point const now(m_clock.now()); - auto [it, inserted] = m_cache.emplace( - std::piecewise_construct, - std::forward_as_tuple(key), - std::forward_as_tuple(now)); - if (!inserted) - it->second.last_access = now; - return inserted; - } + insert(key_type const& key) -> std::enable_if_t; // VFALCO NOTE It looks like this returns a copy of the data in // the output parameter 'data'. This could be expensive. @@ -436,50 +171,18 @@ class TaggedCache // simply return an iterator. // bool - retrieve(const key_type& key, T& data) - { - // retrieve the value of the stored data - auto entry = fetch(key); - - if (!entry) - return false; - - data = *entry; - return true; - } + retrieve(const key_type& key, T& data); mutex_type& - peekMutex() - { - return m_mutex; - } + peekMutex(); std::vector - getKeys() const - { - std::vector v; - - { - std::lock_guard lock(m_mutex); - v.reserve(m_cache.size()); - for (auto const& _ : m_cache) - v.push_back(_.first); - } - - return v; - } + getKeys() const; // CachedSLEs functions. /** Returns the fraction of cache hits. */ double - rate() const - { - std::lock_guard lock(m_mutex); - auto const tot = m_hits + m_misses; - if (tot == 0) - return 0; - return double(m_hits) / tot; - } + rate() const; /** Fetch an item from the cache. If the digest was not found, Handler @@ -488,72 +191,15 @@ class TaggedCache */ template std::shared_ptr - fetch(key_type const& digest, Handler const& h) - { - { - std::lock_guard l(m_mutex); - if (auto ret = initialFetch(digest, l)) - return ret; - } - - auto sle = h(); - if (!sle) - return {}; - - std::lock_guard l(m_mutex); - ++m_misses; - auto const [it, inserted] = - m_cache.emplace(digest, Entry(m_clock.now(), std::move(sle))); - if (!inserted) - it->second.touch(m_clock.now()); - return it->second.ptr; - } + fetch(key_type const& digest, Handler const& h); // End CachedSLEs functions. private: std::shared_ptr - initialFetch(key_type const& key, std::lock_guard const& l) - { - auto cit = m_cache.find(key); - if (cit == m_cache.end()) - return {}; - - Entry& entry = cit->second; - if (entry.isCached()) - { - ++m_hits; - entry.touch(m_clock.now()); - return entry.ptr; - } - entry.ptr = entry.lock(); - if (entry.isCached()) - { - // independent of cache size, so not counted as a hit - ++m_cache_count; - entry.touch(m_clock.now()); - return entry.ptr; - } - - m_cache.erase(cit); - return {}; - } + initialFetch(key_type const& key, std::lock_guard const& l); void - collect_metrics() - { - m_stats.size.set(getCacheSize()); - - { - beast::insight::Gauge::value_type hit_rate(0); - { - std::lock_guard lock(m_mutex); - auto const total(m_hits + m_misses); - if (total != 0) - hit_rate = (m_hits * 100) / total; - } - m_stats.hit_rate.set(hit_rate); - } - } + collect_metrics(); private: struct Stats @@ -657,72 +303,7 @@ class TaggedCache typename KeyValueCacheType::map_type& partition, SweptPointersVector& stuffToSweep, std::atomic& allRemovals, - std::lock_guard const&) - { - return std::thread([&, this]() { - int cacheRemovals = 0; - int mapRemovals = 0; - - // Keep references to all the stuff we sweep - // so that we can destroy them outside the lock. - stuffToSweep.first.reserve(partition.size()); - stuffToSweep.second.reserve(partition.size()); - { - auto cit = partition.begin(); - while (cit != partition.end()) - { - if (cit->second.isWeak()) - { - // weak - if (cit->second.isExpired()) - { - stuffToSweep.second.push_back( - std::move(cit->second.weak_ptr)); - ++mapRemovals; - cit = partition.erase(cit); - } - else - { - ++cit; - } - } - else if (cit->second.last_access <= when_expire) - { - // strong, expired - ++cacheRemovals; - if (cit->second.ptr.use_count() == 1) - { - stuffToSweep.first.push_back( - std::move(cit->second.ptr)); - ++mapRemovals; - cit = partition.erase(cit); - } - else - { - // remains weakly cached - cit->second.ptr.reset(); - ++cit; - } - } - else - { - // strong, not expired - ++cit; - } - } - } - - if (mapRemovals || cacheRemovals) - { - JLOG(m_journal.debug()) - << "TaggedCache partition sweep " << m_name - << ": cache = " << partition.size() << "-" << cacheRemovals - << ", map-=" << mapRemovals; - } - - allRemovals += cacheRemovals; - }); - } + std::lock_guard const&); [[nodiscard]] std::thread sweepHelper( @@ -731,45 +312,7 @@ class TaggedCache typename KeyOnlyCacheType::map_type& partition, SweptPointersVector&, std::atomic& allRemovals, - std::lock_guard const&) - { - return std::thread([&, this]() { - int cacheRemovals = 0; - int mapRemovals = 0; - - // Keep references to all the stuff we sweep - // so that we can destroy them outside the lock. - { - auto cit = partition.begin(); - while (cit != partition.end()) - { - if (cit->second.last_access > now) - { - cit->second.last_access = now; - ++cit; - } - else if (cit->second.last_access <= when_expire) - { - cit = partition.erase(cit); - } - else - { - ++cit; - } - } - } - - if (mapRemovals || cacheRemovals) - { - JLOG(m_journal.debug()) - << "TaggedCache partition sweep " << m_name - << ": cache = " << partition.size() << "-" << cacheRemovals - << ", map-=" << mapRemovals; - } - - allRemovals += cacheRemovals; - }); - }; + std::lock_guard const&); beast::Journal m_journal; clock_type& m_clock; diff --git a/include/xrpl/basics/TaggedCache.ipp b/include/xrpl/basics/TaggedCache.ipp new file mode 100644 index 00000000000..ebf70f9af84 --- /dev/null +++ b/include/xrpl/basics/TaggedCache.ipp @@ -0,0 +1,819 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_BASICS_TAGGEDCACHE_IPP_INCLUDED +#define RIPPLE_BASICS_TAGGEDCACHE_IPP_INCLUDED + +#include + +namespace ripple { + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +TaggedCache::TaggedCache( + std::string const& name, + int size, + clock_type::duration expiration, + clock_type& clock, + beast::Journal journal, + beast::insight::Collector::ptr const& collector) + : m_journal(journal) + , m_clock(clock) + , m_stats(name, std::bind(&TaggedCache::collect_metrics, this), collector) + , m_name(name) + , m_target_size(size) + , m_target_age(expiration) + , m_cache_count(0) + , m_hits(0) + , m_misses(0) +{ +} + +/** Return the clock associated with the cache. */ +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +auto +TaggedCache::clock() -> clock_type& +{ + return m_clock; +} + +/** Returns the number of items in the container. */ +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +std::size_t +TaggedCache::size() const +{ + std::lock_guard lock(m_mutex); + return m_cache.size(); +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +void +TaggedCache::setTargetSize(int s) +{ + std::lock_guard lock(m_mutex); + m_target_size = s; + + if (s > 0) + { + for (auto& partition : m_cache.map()) + { + partition.rehash(static_cast( + (s + (s >> 2)) / + (partition.max_load_factor() * m_cache.partitions()) + + 1)); + } + } + + JLOG(m_journal.debug()) << m_name << " target size set to " << s; +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +auto +TaggedCache::getTargetAge() const + -> clock_type::duration +{ + std::lock_guard lock(m_mutex); + return m_target_age; +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +void +TaggedCache::setTargetAge( + clock_type::duration s) +{ + std::lock_guard lock(m_mutex); + m_target_age = s; + JLOG(m_journal.debug()) + << m_name << " target age set to " << m_target_age.count(); +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +int +TaggedCache::getCacheSize() const +{ + std::lock_guard lock(m_mutex); + return m_cache_count; +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +int +TaggedCache::getTrackSize() const +{ + std::lock_guard lock(m_mutex); + return m_cache.size(); +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +float +TaggedCache::getHitRate() +{ + std::lock_guard lock(m_mutex); + auto const total = static_cast(m_hits + m_misses); + return m_hits * (100.0f / std::max(1.0f, total)); +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +void +TaggedCache::clear() +{ + std::lock_guard lock(m_mutex); + m_cache.clear(); + m_cache_count = 0; +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +void +TaggedCache::reset() +{ + std::lock_guard lock(m_mutex); + m_cache.clear(); + m_cache_count = 0; + m_hits = 0; + m_misses = 0; +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +template +bool +TaggedCache::touch_if_exists( + KeyComparable const& key) +{ + std::lock_guard lock(m_mutex); + auto const iter(m_cache.find(key)); + if (iter == m_cache.end()) + { + ++m_stats.misses; + return false; + } + iter->second.touch(m_clock.now()); + ++m_stats.hits; + return true; +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +void +TaggedCache::sweep() +{ + // Keep references to all the stuff we sweep + // For performance, each worker thread should exit before the swept data + // is destroyed but still within the main cache lock. + std::vector allStuffToSweep(m_cache.partitions()); + + clock_type::time_point const now(m_clock.now()); + clock_type::time_point when_expire; + + auto const start = std::chrono::steady_clock::now(); + { + std::lock_guard lock(m_mutex); + + if (m_target_size == 0 || + (static_cast(m_cache.size()) <= m_target_size)) + { + when_expire = now - m_target_age; + } + else + { + when_expire = now - m_target_age * m_target_size / m_cache.size(); + + clock_type::duration const minimumAge(std::chrono::seconds(1)); + if (when_expire > (now - minimumAge)) + when_expire = now - minimumAge; + + JLOG(m_journal.trace()) + << m_name << " is growing fast " << m_cache.size() << " of " + << m_target_size << " aging at " << (now - when_expire).count() + << " of " << m_target_age.count(); + } + + std::vector workers; + workers.reserve(m_cache.partitions()); + std::atomic allRemovals = 0; + + for (std::size_t p = 0; p < m_cache.partitions(); ++p) + { + workers.push_back(sweepHelper( + when_expire, + now, + m_cache.map()[p], + allStuffToSweep[p], + allRemovals, + lock)); + } + for (std::thread& worker : workers) + worker.join(); + + m_cache_count -= allRemovals; + } + // At this point allStuffToSweep will go out of scope outside the lock + // and decrement the reference count on each strong pointer. + JLOG(m_journal.debug()) + << m_name << " TaggedCache sweep lock duration " + << std::chrono::duration_cast( + std::chrono::steady_clock::now() - start) + .count() + << "ms"; +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +bool +TaggedCache::del( + const key_type& key, + bool valid) +{ + // Remove from cache, if !valid, remove from map too. Returns true if + // removed from cache + std::lock_guard lock(m_mutex); + + auto cit = m_cache.find(key); + + if (cit == m_cache.end()) + return false; + + Entry& entry = cit->second; + + bool ret = false; + + if (entry.isCached()) + { + --m_cache_count; + entry.ptr.reset(); + ret = true; + } + + if (!valid || entry.isExpired()) + m_cache.erase(cit); + + return ret; +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +bool +TaggedCache::canonicalize( + const key_type& key, + std::shared_ptr& data, + std::function const&)>&& replace) +{ + // Return canonical value, store if needed, refresh in cache + // Return values: true=we had the data already + std::lock_guard lock(m_mutex); + + auto cit = m_cache.find(key); + + if (cit == m_cache.end()) + { + m_cache.emplace( + std::piecewise_construct, + std::forward_as_tuple(key), + std::forward_as_tuple(m_clock.now(), data)); + ++m_cache_count; + return false; + } + + Entry& entry = cit->second; + entry.touch(m_clock.now()); + + if (entry.isCached()) + { + if (replace(entry.ptr)) + { + entry.ptr = data; + entry.weak_ptr = data; + } + else + { + data = entry.ptr; + } + + return true; + } + + auto cachedData = entry.lock(); + + if (cachedData) + { + if (replace(entry.ptr)) + { + entry.ptr = data; + entry.weak_ptr = data; + } + else + { + entry.ptr = cachedData; + data = cachedData; + } + + ++m_cache_count; + return true; + } + + entry.ptr = data; + entry.weak_ptr = data; + ++m_cache_count; + + return false; +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +bool +TaggedCache:: + canonicalize_replace_cache( + const key_type& key, + std::shared_ptr const& data) +{ + return canonicalize( + key, + const_cast&>(data), + [](std::shared_ptr const&) { return true; }); +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +bool +TaggedCache:: + canonicalize_replace_client(const key_type& key, std::shared_ptr& data) +{ + return canonicalize( + key, data, [](std::shared_ptr const&) { return false; }); +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +std::shared_ptr +TaggedCache::fetch( + const key_type& key) +{ + std::lock_guard l(m_mutex); + auto ret = initialFetch(key, l); + if (!ret) + ++m_misses; + return ret; +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +template +auto +TaggedCache::insert( + key_type const& key, + T const& value) -> std::enable_if_t +{ + auto p = std::make_shared(std::cref(value)); + return canonicalize_replace_client(key, p); +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +template +auto +TaggedCache::insert( + key_type const& key) -> std::enable_if_t +{ + std::lock_guard lock(m_mutex); + clock_type::time_point const now(m_clock.now()); + auto [it, inserted] = m_cache.emplace( + std::piecewise_construct, + std::forward_as_tuple(key), + std::forward_as_tuple(now)); + if (!inserted) + it->second.last_access = now; + return inserted; +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +bool +TaggedCache::retrieve( + const key_type& key, + T& data) +{ + // retrieve the value of the stored data + auto entry = fetch(key); + + if (!entry) + return false; + + data = *entry; + return true; +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +auto +TaggedCache::peekMutex() + -> mutex_type& +{ + return m_mutex; +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +auto +TaggedCache::getKeys() const + -> std::vector +{ + std::vector v; + + { + std::lock_guard lock(m_mutex); + v.reserve(m_cache.size()); + for (auto const& _ : m_cache) + v.push_back(_.first); + } + + return v; +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +double +TaggedCache::rate() const +{ + std::lock_guard lock(m_mutex); + auto const tot = m_hits + m_misses; + if (tot == 0) + return 0; + return double(m_hits) / tot; +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +template +std::shared_ptr +TaggedCache::fetch( + key_type const& digest, + Handler const& h) +{ + { + std::lock_guard l(m_mutex); + if (auto ret = initialFetch(digest, l)) + return ret; + } + + auto sle = h(); + if (!sle) + return {}; + + std::lock_guard l(m_mutex); + ++m_misses; + auto const [it, inserted] = + m_cache.emplace(digest, Entry(m_clock.now(), std::move(sle))); + if (!inserted) + it->second.touch(m_clock.now()); + return it->second.ptr; +} +// End CachedSLEs functions. + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +std::shared_ptr +TaggedCache::initialFetch( + key_type const& key, + std::lock_guard const& l) +{ + auto cit = m_cache.find(key); + if (cit == m_cache.end()) + return {}; + + Entry& entry = cit->second; + if (entry.isCached()) + { + ++m_hits; + entry.touch(m_clock.now()); + return entry.ptr; + } + entry.ptr = entry.lock(); + if (entry.isCached()) + { + // independent of cache size, so not counted as a hit + ++m_cache_count; + entry.touch(m_clock.now()); + return entry.ptr; + } + + m_cache.erase(cit); + return {}; +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +void +TaggedCache::collect_metrics() +{ + m_stats.size.set(getCacheSize()); + + { + beast::insight::Gauge::value_type hit_rate(0); + { + std::lock_guard lock(m_mutex); + auto const total(m_hits + m_misses); + if (total != 0) + hit_rate = (m_hits * 100) / total; + } + m_stats.hit_rate.set(hit_rate); + } +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +std::thread +TaggedCache::sweepHelper( + clock_type::time_point const& when_expire, + [[maybe_unused]] clock_type::time_point const& now, + typename KeyValueCacheType::map_type& partition, + SweptPointersVector& stuffToSweep, + std::atomic& allRemovals, + std::lock_guard const&) +{ + return std::thread([&, this]() { + int cacheRemovals = 0; + int mapRemovals = 0; + + // Keep references to all the stuff we sweep + // so that we can destroy them outside the lock. + stuffToSweep.first.reserve(partition.size()); + stuffToSweep.second.reserve(partition.size()); + { + auto cit = partition.begin(); + while (cit != partition.end()) + { + if (cit->second.isWeak()) + { + // weak + if (cit->second.isExpired()) + { + stuffToSweep.second.push_back( + std::move(cit->second.weak_ptr)); + ++mapRemovals; + cit = partition.erase(cit); + } + else + { + ++cit; + } + } + else if (cit->second.last_access <= when_expire) + { + // strong, expired + ++cacheRemovals; + if (cit->second.ptr.use_count() == 1) + { + stuffToSweep.first.push_back( + std::move(cit->second.ptr)); + ++mapRemovals; + cit = partition.erase(cit); + } + else + { + // remains weakly cached + cit->second.ptr.reset(); + ++cit; + } + } + else + { + // strong, not expired + ++cit; + } + } + } + + if (mapRemovals || cacheRemovals) + { + JLOG(m_journal.debug()) + << "TaggedCache partition sweep " << m_name + << ": cache = " << partition.size() << "-" << cacheRemovals + << ", map-=" << mapRemovals; + } + + allRemovals += cacheRemovals; + }); +} + +template < + class Key, + class T, + bool IsKeyCache, + class Hash, + class KeyEqual, + class Mutex> +std::thread +TaggedCache::sweepHelper( + clock_type::time_point const& when_expire, + clock_type::time_point const& now, + typename KeyOnlyCacheType::map_type& partition, + SweptPointersVector&, + std::atomic& allRemovals, + std::lock_guard const&) +{ + return std::thread([&, this]() { + int cacheRemovals = 0; + int mapRemovals = 0; + + // Keep references to all the stuff we sweep + // so that we can destroy them outside the lock. + { + auto cit = partition.begin(); + while (cit != partition.end()) + { + if (cit->second.last_access > now) + { + cit->second.last_access = now; + ++cit; + } + else if (cit->second.last_access <= when_expire) + { + cit = partition.erase(cit); + } + else + { + ++cit; + } + } + } + + if (mapRemovals || cacheRemovals) + { + JLOG(m_journal.debug()) + << "TaggedCache partition sweep " << m_name + << ": cache = " << partition.size() << "-" << cacheRemovals + << ", map-=" << mapRemovals; + } + + allRemovals += cacheRemovals; + }); +} + +} // namespace ripple + +#endif diff --git a/src/test/basics/KeyCache_test.cpp b/src/test/basics/KeyCache_test.cpp index eab0e87d0a7..3a364da6637 100644 --- a/src/test/basics/KeyCache_test.cpp +++ b/src/test/basics/KeyCache_test.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include diff --git a/src/test/basics/TaggedCache_test.cpp b/src/test/basics/TaggedCache_test.cpp index 6fbba1a4795..83667a1aa55 100644 --- a/src/test/basics/TaggedCache_test.cpp +++ b/src/test/basics/TaggedCache_test.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include diff --git a/src/xrpld/app/ledger/ConsensusTransSetSF.cpp b/src/xrpld/app/ledger/ConsensusTransSetSF.cpp index 4aed7d94bf3..290f4c43bb1 100644 --- a/src/xrpld/app/ledger/ConsensusTransSetSF.cpp +++ b/src/xrpld/app/ledger/ConsensusTransSetSF.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include diff --git a/src/xrpld/app/ledger/LedgerHistory.cpp b/src/xrpld/app/ledger/LedgerHistory.cpp index 30139e2155f..61a49e15f6b 100644 --- a/src/xrpld/app/ledger/LedgerHistory.cpp +++ b/src/xrpld/app/ledger/LedgerHistory.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/src/xrpld/app/ledger/detail/LedgerMaster.cpp b/src/xrpld/app/ledger/detail/LedgerMaster.cpp index d1eeabeb619..ba1cd6f634d 100644 --- a/src/xrpld/app/ledger/detail/LedgerMaster.cpp +++ b/src/xrpld/app/ledger/detail/LedgerMaster.cpp @@ -42,7 +42,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/xrpld/app/ledger/detail/TransactionMaster.cpp b/src/xrpld/app/ledger/detail/TransactionMaster.cpp index e2e1213a37e..1f0b0ea0dc8 100644 --- a/src/xrpld/app/ledger/detail/TransactionMaster.cpp +++ b/src/xrpld/app/ledger/detail/TransactionMaster.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include diff --git a/src/xrpld/app/main/Application.cpp b/src/xrpld/app/main/Application.cpp index a9d66679010..2fcca89c645 100644 --- a/src/xrpld/app/main/Application.cpp +++ b/src/xrpld/app/main/Application.cpp @@ -59,6 +59,7 @@ #include #include #include +#include #include #include #include diff --git a/src/xrpld/app/misc/NetworkOPs.cpp b/src/xrpld/app/misc/NetworkOPs.cpp index 02eb0435b57..a9279f9ff4d 100644 --- a/src/xrpld/app/misc/NetworkOPs.cpp +++ b/src/xrpld/app/misc/NetworkOPs.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include diff --git a/src/xrpld/app/misc/SHAMapStoreImp.h b/src/xrpld/app/misc/SHAMapStoreImp.h index 7d36f092be8..c9ecb61443c 100644 --- a/src/xrpld/app/misc/SHAMapStoreImp.h +++ b/src/xrpld/app/misc/SHAMapStoreImp.h @@ -26,8 +26,8 @@ #include #include #include - #include +#include #include #include #include diff --git a/src/xrpld/app/rdb/backend/detail/Node.cpp b/src/xrpld/app/rdb/backend/detail/Node.cpp index 2ea6bd12c62..33223cef42a 100644 --- a/src/xrpld/app/rdb/backend/detail/Node.cpp +++ b/src/xrpld/app/rdb/backend/detail/Node.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include diff --git a/src/xrpld/ledger/detail/CachedView.cpp b/src/xrpld/ledger/detail/CachedView.cpp index 5502c40e6d5..79ab13ab2cf 100644 --- a/src/xrpld/ledger/detail/CachedView.cpp +++ b/src/xrpld/ledger/detail/CachedView.cpp @@ -18,6 +18,7 @@ //============================================================================== #include +#include #include #include diff --git a/src/xrpld/nodestore/Database.h b/src/xrpld/nodestore/Database.h index bd25046fee2..706d211785c 100644 --- a/src/xrpld/nodestore/Database.h +++ b/src/xrpld/nodestore/Database.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/xrpld/nodestore/detail/DatabaseNodeImp.cpp b/src/xrpld/nodestore/detail/DatabaseNodeImp.cpp index 85e5d3c0da9..3159219964c 100644 --- a/src/xrpld/nodestore/detail/DatabaseNodeImp.cpp +++ b/src/xrpld/nodestore/detail/DatabaseNodeImp.cpp @@ -18,6 +18,7 @@ //============================================================================== #include +#include #include namespace ripple { diff --git a/src/xrpld/rpc/handlers/GetCounts.cpp b/src/xrpld/rpc/handlers/GetCounts.cpp index 690106ebbd2..5e74c9bd527 100644 --- a/src/xrpld/rpc/handlers/GetCounts.cpp +++ b/src/xrpld/rpc/handlers/GetCounts.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/src/xrpld/shamap/FullBelowCache.h b/src/xrpld/shamap/FullBelowCache.h index eed7e6294a0..d7df9379cf8 100644 --- a/src/xrpld/shamap/FullBelowCache.h +++ b/src/xrpld/shamap/FullBelowCache.h @@ -22,6 +22,7 @@ #include #include +#include #include #include #include