Files
rippled/src/libxrpl/peerfinder/Bootcache.cpp

267 lines
6.8 KiB
C++

#include <xrpl/peerfinder/detail/Bootcache.h>
#include <xrpl/basics/Log.h>
#include <xrpl/beast/net/IPEndpoint.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/beast/utility/PropertyStream.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/peerfinder/Types.h>
#include <xrpl/peerfinder/detail/Store.h>
#include <xrpl/peerfinder/detail/Tuning.h>
#include <algorithm>
#include <cstdint>
#include <cstdlib>
#include <iomanip>
#include <ios>
#include <vector>
namespace xrpl::PeerFinder {
Bootcache::Bootcache(Store& store, clock_type& clock, beast::Journal journal)
: store_(store), clock_(clock), journal_(journal), whenUpdate_(clock_.now())
{
}
Bootcache::~Bootcache()
{
update();
}
bool
Bootcache::empty() const
{
return map_.empty();
}
Bootcache::map_type::size_type
Bootcache::size() const
{
return map_.size();
}
Bootcache::const_iterator
Bootcache::begin() const
{
return const_iterator(map_.right.begin());
}
Bootcache::const_iterator
Bootcache::cbegin() const
{
return const_iterator(map_.right.begin());
}
Bootcache::const_iterator
Bootcache::end() const
{
return const_iterator(map_.right.end());
}
Bootcache::const_iterator
Bootcache::cend() const
{
return const_iterator(map_.right.end());
}
void
Bootcache::clear()
{
map_.clear();
needsUpdate_ = true;
}
//--------------------------------------------------------------------------
void
Bootcache::load()
{
clear();
auto const n(store_.load([this](beast::IP::Endpoint const& endpoint, int valence) {
auto const result(this->map_.insert(value_type(endpoint, valence)));
if (!result.second)
{
JLOG(this->journal_.error())
<< std::left << std::setw(18) << "Bootcache discard " << endpoint;
}
}));
if (n > 0)
{
JLOG(journal_.info()) << std::left << std::setw(18) << "Bootcache loaded " << n
<< ((n > 1) ? " addresses" : " address");
prune();
}
}
bool
Bootcache::insert(beast::IP::Endpoint const& endpoint)
{
auto const result(map_.insert(value_type(endpoint, 0)));
if (result.second)
{
JLOG(journal_.trace()) << std::left << std::setw(18) << "Bootcache insert " << endpoint;
prune();
flagForUpdate();
}
return result.second;
}
bool
Bootcache::insertStatic(beast::IP::Endpoint const& endpoint)
{
auto result(map_.insert(value_type(endpoint, kStaticValence)));
if (!result.second && (result.first->right.valence() < kStaticValence))
{
// An existing entry has too low a valence, replace it
map_.erase(result.first);
result = map_.insert(value_type(endpoint, kStaticValence));
}
if (result.second)
{
JLOG(journal_.trace()) << std::left << std::setw(18) << "Bootcache insert " << endpoint;
prune();
flagForUpdate();
}
return result.second;
}
void
Bootcache::onSuccess(beast::IP::Endpoint const& endpoint)
{
auto result(map_.insert(value_type(endpoint, 1)));
if (result.second)
{
prune();
}
else
{
Entry entry(result.first->right);
entry.valence() = std::max(entry.valence(), 0);
++entry.valence();
map_.erase(result.first);
result = map_.insert(value_type(endpoint, entry));
XRPL_ASSERT(result.second, "xrpl::PeerFinder::Bootcache::onSuccess : endpoint inserted");
}
Entry const& entry(result.first->right);
JLOG(journal_.info()) << std::left << std::setw(18) << "Bootcache connect " << endpoint
<< " with " << entry.valence()
<< ((entry.valence() > 1) ? " successes" : " success");
flagForUpdate();
}
void
Bootcache::onFailure(beast::IP::Endpoint const& endpoint)
{
auto result(map_.insert(value_type(endpoint, -1)));
if (result.second)
{
prune();
}
else
{
Entry entry(result.first->right);
entry.valence() = std::min(entry.valence(), 0);
--entry.valence();
map_.erase(result.first);
result = map_.insert(value_type(endpoint, entry));
XRPL_ASSERT(result.second, "xrpl::PeerFinder::Bootcache::onFailure : endpoint inserted");
}
Entry const& entry(result.first->right);
auto const n(std::abs(entry.valence()));
JLOG(journal_.debug()) << std::left << std::setw(18) << "Bootcache failed " << endpoint
<< " with " << n << ((n > 1) ? " attempts" : " attempt");
flagForUpdate();
}
void
Bootcache::periodicActivity()
{
checkUpdate();
}
//--------------------------------------------------------------------------
void
Bootcache::onWrite(beast::PropertyStream::Map& map)
{
beast::PropertyStream::Set entries("entries", map);
for (auto iter = map_.right.begin(); iter != map_.right.end(); ++iter)
{
beast::PropertyStream::Map entry(entries);
entry["endpoint"] = iter->get_left().toString();
entry["valence"] = std::int32_t(iter->get_right().valence());
}
}
// Checks the cache size and prunes if its over the limit.
void
Bootcache::prune()
{
if (size() <= Tuning::kBootcacheSize)
return;
// Calculate the amount to remove
auto count((size() * Tuning::kBootcachePrunePercent) / 100);
decltype(count) pruned(0);
// Work backwards because bimap doesn't handle
// erasing using a reverse iterator very well.
//
for (auto iter(map_.right.end()); count > 0 && iter != map_.right.begin(); ++pruned)
{
--count;
--iter;
beast::IP::Endpoint const& endpoint(iter->get_left());
Entry const& entry(iter->get_right());
JLOG(journal_.trace()) << std::left << std::setw(18) << "Bootcache pruned" << endpoint
<< " at valence " << entry.valence();
iter = map_.right.erase(iter);
}
JLOG(journal_.debug()) << std::left << std::setw(18) << "Bootcache pruned " << pruned
<< " entries total";
}
// Updates the Store with the current set of entries if needed.
void
Bootcache::update()
{
if (!needsUpdate_)
return;
std::vector<Store::Entry> list;
list.reserve(map_.size());
for (auto const& e : map_)
{
Store::Entry se;
se.endpoint = e.get_left();
se.valence = e.get_right().valence();
list.push_back(se);
}
store_.save(list);
// Reset the flag and cooldown timer
needsUpdate_ = false;
whenUpdate_ = clock_.now() + Tuning::kBootcacheCooldownTime;
}
// Checks the clock and calls update if we are off the cooldown.
void
Bootcache::checkUpdate()
{
if (needsUpdate_ && whenUpdate_ < clock_.now())
update();
}
// Called when changes to an entry will affect the Store.
void
Bootcache::flagForUpdate()
{
needsUpdate_ = true;
checkUpdate();
}
} // namespace xrpl::PeerFinder