xrpld
Loading...
Searching...
No Matches
Livecache.h
1#pragma once
2
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/random.h>
5#include <xrpl/beast/container/aged_map.h>
6#include <xrpl/beast/net/IPEndpoint.h>
7#include <xrpl/beast/utility/Journal.h>
8#include <xrpl/beast/utility/PropertyStream.h>
9#include <xrpl/beast/utility/instrumentation.h>
10#include <xrpl/beast/utility/maybe_const.h>
11#include <xrpl/peerfinder/Types.h>
12#include <xrpl/peerfinder/detail/Tuning.h>
13
14#include <boost/intrusive/list.hpp>
15#include <boost/iterator/transform_iterator.hpp>
16
17#include <algorithm>
18#include <array>
19#include <chrono>
20#include <cstddef>
21#include <cstdint>
22#include <functional>
23#include <iomanip>
24#include <ios>
25#include <iterator>
26#include <memory>
27#include <sstream>
28#include <string>
29#include <utility>
30#include <vector>
31
32namespace xrpl::peer_finder {
33
34template <class>
35class Livecache;
36
37namespace detail {
38
40{
41public:
42 explicit LivecacheBase() = default;
43
44protected:
45 struct Element : boost::intrusive::list_base_hook<>
46 {
50
52 };
53
54 using list_type =
55 boost::intrusive::make_list<Element, boost::intrusive::constant_time_size<false>>::type;
56
57public:
63 template <bool IsConst>
64 class Hop
65 {
66 public:
67 // Iterator transformation to extract the endpoint from Element
68 struct Transform
69 {
72
73 explicit Transform() = default;
74
75 Endpoint const&
76 operator()(Element const& e) const
77 {
78 return e.endpoint;
79 }
80 };
81
82 public:
83 using iterator = boost::transform_iterator<Transform, list_type::const_iterator>;
84
86
88 boost::transform_iterator<Transform, list_type::const_reverse_iterator>;
89
91
92 [[nodiscard]] iterator
93 begin() const
94 {
95 return iterator(list_.get().cbegin(), Transform());
96 }
97
98 [[nodiscard]] iterator
99 cbegin() const
100 {
101 return iterator(list_.get().cbegin(), Transform());
102 }
103
104 [[nodiscard]] iterator
105 end() const
106 {
107 return iterator(list_.get().cend(), Transform());
108 }
109
110 [[nodiscard]] iterator
111 cend() const
112 {
113 return iterator(list_.get().cend(), Transform());
114 }
115
116 [[nodiscard]] reverse_iterator
117 rbegin() const
118 {
119 return reverse_iterator(list_.get().crbegin(), Transform());
120 }
121
122 [[nodiscard]] reverse_iterator
123 crbegin() const
124 {
125 return reverse_iterator(list_.get().crbegin(), Transform());
126 }
127
128 [[nodiscard]] reverse_iterator
129 rend() const
130 {
131 return reverse_iterator(list_.get().crend(), Transform());
132 }
133
134 [[nodiscard]] reverse_iterator
135 crend() const
136 {
137 return reverse_iterator(list_.get().crend(), Transform());
138 }
139
140 // move the element to the end of the container
141 void
143 {
144 auto& e(const_cast<Element&>(*pos.base()));
145 list_.get().erase(list_.get().iterator_to(e));
146 list_.get().push_back(e);
147 }
148
149 private:
151 {
152 }
153
154 friend class LivecacheBase;
155
157 };
158
159protected:
160 // Work-around to call Hop's private constructor from Livecache
161 template <bool IsConst>
162 static Hop<IsConst>
167};
168
169} // namespace detail
170
171//------------------------------------------------------------------------------
172
186template <class Allocator = std::allocator<char>>
188{
189private:
192 Element,
195 Allocator>;
196
199
200public:
201 using allocator_type = Allocator;
202
206 Livecache(clock_type& clock, beast::Journal journal, Allocator alloc = Allocator());
207
208 //
209 // Iteration by hops
210 //
211 // The range [begin, end) provides a sequence of list_type
212 // where each list contains endpoints at a given hops.
213 //
214
215 class HopsT
216 {
217 private:
218 // An endpoint at hops=0 represents the local node.
219 // Endpoints coming in at maxHops are stored at maxHops +1,
220 // but not given out (since they would exceed maxHops). They
221 // are used for automatic connection attempts.
222 //
225
226 template <bool IsConst>
228 {
229 using first_argument = lists_type::value_type;
231
232 explicit Transform() = default;
233
239 };
240
241 public:
242 using iterator = boost::transform_iterator<Transform<false>, lists_type::iterator>;
243
245 boost::transform_iterator<Transform<true>, lists_type::const_iterator>;
246
248 boost::transform_iterator<Transform<false>, lists_type::reverse_iterator>;
249
251 boost::transform_iterator<Transform<true>, lists_type::const_reverse_iterator>;
252
255 {
256 return iterator(lists_.begin(), Transform<false>());
257 }
258
259 [[nodiscard]] const_iterator
260 begin() const
261 {
262 return const_iterator(lists_.cbegin(), Transform<true>());
263 }
264
265 [[nodiscard]] const_iterator
266 cbegin() const
267 {
268 return const_iterator(lists_.cbegin(), Transform<true>());
269 }
270
273 {
274 return iterator(lists_.end(), Transform<false>());
275 }
276
277 [[nodiscard]] const_iterator
278 end() const
279 {
280 return const_iterator(lists_.cend(), Transform<true>());
281 }
282
283 [[nodiscard]] const_iterator
284 cend() const
285 {
286 return const_iterator(lists_.cend(), Transform<true>());
287 }
288
291 {
292 return reverse_iterator(lists_.rbegin(), Transform<false>());
293 }
294
295 [[nodiscard]] const_reverse_iterator
296 rbegin() const
297 {
298 return const_reverse_iterator(lists_.crbegin(), Transform<true>());
299 }
300
301 [[nodiscard]] const_reverse_iterator
302 crbegin() const
303 {
304 return const_reverse_iterator(lists_.crbegin(), Transform<true>());
305 }
306
309 {
310 return reverse_iterator(lists_.rend(), Transform<false>());
311 }
312
313 [[nodiscard]] const_reverse_iterator
314 rend() const
315 {
317 }
318
319 [[nodiscard]] const_reverse_iterator
320 crend() const
321 {
323 }
324
328 void
329 shuffle();
330
331 [[nodiscard]] std::string
332 histogram() const;
333
334 private:
335 explicit HopsT(Allocator const& alloc);
336
337 void
338 insert(Element& e);
339
340 // Reinsert e at a new hops
341 void
343
344 void
345 remove(Element& e);
346
347 friend class Livecache;
351
355 [[nodiscard]] bool
356 empty() const
357 {
358 return cache_.empty();
359 }
360
365 size() const
366 {
367 return cache_.size();
368 }
369
373 void
374 expire();
375
379 void
380 insert(Endpoint const& ep);
381
385 void
387};
388
389//------------------------------------------------------------------------------
390
391template <class Allocator>
393 : journal_(journal), cache_(clock, alloc), hops(alloc)
394{
395}
396
397template <class Allocator>
398void
400{
401 std::size_t n(0);
402 typename cache_type::time_point const expired(
404 for (auto iter(cache_.chronological.begin());
405 iter != cache_.chronological.end() && iter.when() <= expired;)
406 {
407 Element& e(iter->second);
408 hops.remove(e);
409 iter = cache_.erase(iter);
410 ++n;
411 }
412 if (n > 0)
413 {
414 JLOG(journal_.debug()) << std::left << std::setw(18) << "Livecache expired " << n
415 << ((n > 1) ? " entries" : " entry");
416 }
417}
418
419template <class Allocator>
420void
422{
423 // The caller already incremented hop, so if we got a
424 // message at maxHops we will store it at maxHops + 1.
425 // This means we won't give out the address to other peers
426 // but we will use it to make connections and hand it out
427 // when redirecting.
428 //
429 XRPL_ASSERT(
430 ep.hops <= (tuning::kMaxHops + 1),
431 "xrpl::peer_finder::Livecache::insert : maximum input hops");
432 auto result = cache_.emplace(ep.address, ep);
433 Element& e(result.first->second);
434 if (result.second)
435 {
436 hops.insert(e);
437 JLOG(journal_.debug()) << std::left << std::setw(18) << "Livecache insert " << ep.address
438 << " at hops " << ep.hops;
439 return;
440 }
441 if (!result.second && (ep.hops > e.endpoint.hops))
442 {
443 // Drop duplicates at higher hops
444 std::size_t const excess(ep.hops - e.endpoint.hops);
445 JLOG(journal_.trace()) << std::left << std::setw(18) << "Livecache drop " << ep.address
446 << " at hops +" << excess;
447 return;
448 }
449
450 cache_.touch(result.first);
451
452 // Address already in the cache so update metadata
453 if (ep.hops < e.endpoint.hops)
454 {
455 hops.reinsert(e, ep.hops);
456 JLOG(journal_.debug()) << std::left << std::setw(18) << "Livecache update " << ep.address
457 << " at hops " << ep.hops;
458 }
459 else
460 {
461 JLOG(journal_.trace()) << std::left << std::setw(18) << "Livecache refresh " << ep.address
462 << " at hops " << ep.hops;
463 }
464}
465
466template <class Allocator>
467void
469{
470 typename cache_type::time_point const expired(
472 map["size"] = size();
473 map["hist"] = hops.histogram();
474 beast::PropertyStream::Set set("entries", map);
475 for (auto iter(cache_.cbegin()); iter != cache_.cend(); ++iter)
476 {
477 auto const& e(iter->second);
479 item["hops"] = e.endpoint.hops;
480 item["address"] = e.endpoint.address.toString();
482 ss << (iter.when() - expired).count();
483 item["expires"] = ss.str();
484 }
485}
486
487//------------------------------------------------------------------------------
488
489template <class Allocator>
490void
492{
493 for (auto& list : lists_)
494 {
496 v.reserve(list.size());
498 std::shuffle(v.begin(), v.end(), defaultPrng());
499 list.clear();
500 for (auto& e : v)
501 list.push_back(e);
502 }
503}
504
505template <class Allocator>
508{
509 std::string s;
510 for (auto const& h : hist_)
511 {
512 if (!s.empty())
513 s += ", ";
514 s += std::to_string(h);
515 }
516 return s;
517}
518
519template <class Allocator>
521{
523}
524
525template <class Allocator>
526void
528{
529 XRPL_ASSERT(
531 "xrpl::peer_finder::Livecache::HopsT::insert : maximum input hops");
532 // This has security implications without a shuffle
533 lists_[e.endpoint.hops].push_front(e);
534 ++hist_[e.endpoint.hops];
535}
536
537template <class Allocator>
538void
540{
541 XRPL_ASSERT(
542 numHops <= tuning::kMaxHops + 1,
543 "xrpl::peer_finder::Livecache::HopsT::reinsert : maximum hops input");
544
545 auto& list = lists_[e.endpoint.hops];
546 list.erase(list.iterator_to(e));
547
548 --hist_[e.endpoint.hops];
549
550 e.endpoint.hops = numHops;
551 insert(e);
552}
553
554template <class Allocator>
555void
557{
558 --hist_[e.endpoint.hops];
559
560 auto& list = lists_[e.endpoint.hops];
561 list.erase(list.iterator_to(e));
562}
563
564} // namespace xrpl::peer_finder
T back_inserter(T... args)
T begin(T... args)
A generic endpoint for log messages.
Definition Journal.h:44
A version-independent IP address and port combination.
Definition IPEndpoint.h:24
void shuffle()
Shuffle each hop list.
Definition Livecache.h:491
HopsT(Allocator const &alloc)
Definition Livecache.h:520
void reinsert(Element &e, std::uint32_t hops)
Definition Livecache.h:539
boost::transform_iterator< Transform< false >, lists_type::reverse_iterator > reverse_iterator
Definition Livecache.h:247
boost::transform_iterator< Transform< true >, lists_type::const_reverse_iterator > const_reverse_iterator
Definition Livecache.h:250
const_reverse_iterator rend() const
Definition Livecache.h:314
const_reverse_iterator crend() const
Definition Livecache.h:320
const_iterator end() const
Definition Livecache.h:278
boost::transform_iterator< Transform< true >, lists_type::const_iterator > const_iterator
Definition Livecache.h:244
std::array< int, 1+tuning::kMaxHops+1 > Histogram
Definition Livecache.h:223
boost::transform_iterator< Transform< false >, lists_type::iterator > iterator
Definition Livecache.h:242
const_iterator cend() const
Definition Livecache.h:284
const_reverse_iterator crbegin() const
Definition Livecache.h:302
const_reverse_iterator rbegin() const
Definition Livecache.h:296
const_iterator cbegin() const
Definition Livecache.h:266
const_iterator begin() const
Definition Livecache.h:260
std::array< list_type, 1+tuning::kMaxHops+1 > lists_type
Definition Livecache.h:224
The Livecache holds the short-lived relayed Endpoint messages.
Definition Livecache.h:188
beast::aged_map< beast::ip::Endpoint, Element, std::chrono::steady_clock, std::less< beast::ip::Endpoint >, Allocator > cache_type
Definition Livecache.h:190
void expire()
Erase entries whose time has expired.
Definition Livecache.h:399
cache_type::size_type size() const
Returns the number of entries in the cache.
Definition Livecache.h:365
void onWrite(beast::PropertyStream::Map &map)
Output statistics.
Definition Livecache.h:468
void insert(Endpoint const &ep)
Creates or updates an existing Element based on a new message.
Definition Livecache.h:421
Livecache(clock_type &clock, beast::Journal journal, Allocator alloc=Allocator())
Create the cache.
Definition Livecache.h:392
class xrpl::peer_finder::Livecache::HopsT hops
bool empty() const
Returns true if the cache is empty.
Definition Livecache.h:356
A list of Endpoint at the same hops This is a lightweight wrapper around a reference to the underlyin...
Definition Livecache.h:65
Hop(beast::MaybeConst< IsConst, list_type >::type &list)
Definition Livecache.h:150
std::reference_wrapper< typename beast::MaybeConst< IsConst, list_type >::type > list_
Definition Livecache.h:156
boost::transform_iterator< Transform, list_type::const_iterator > iterator
Definition Livecache.h:83
boost::transform_iterator< Transform, list_type::const_reverse_iterator > reverse_iterator
Definition Livecache.h:87
boost::intrusive::make_list< Element, boost::intrusive::constant_time_size< false > >::type list_type
Definition Livecache.h:54
static Hop< IsConst > makeHop(beast::MaybeConst< IsConst, list_type >::type &list)
Definition Livecache.h:163
T copy(T... args)
T empty(T... args)
T end(T... args)
T fill(T... args)
T left(T... args)
detail::AgedOrderedContainer< false, true, Key, T, Clock, Compare, Allocator > aged_map
Definition aged_map.h:18
STL namespace.
constexpr std::chrono::seconds kLiveCacheSecondsToLive(30)
beast::AbstractClock< std::chrono::steady_clock > clock_type
bool set(T &target, std::string const &name, Section const &section)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
Dir::ConstIterator const_iterator
Definition Dir.cpp:16
beast::xor_shift_engine & defaultPrng()
Return the default random engine.
T shuffle(T... args)
T reserve(T... args)
T setw(T... args)
T str(T... args)
std:: conditional_t< IsConst, typename std::remove_const< T >::type const, std::remove_const_t< T > > type
Definition maybe_const.h:14
Describes a connectable peer address along with some metadata.
beast::ip::Endpoint address
Hop< IsConst > operator()(beast::MaybeConst< IsConst, lists_type::value_type >::type &list) const
Definition Livecache.h:235
Endpoint const & operator()(Element const &e) const
Definition Livecache.h:76
T to_string(T... args)