xrpld
Loading...
Searching...
No Matches
Livecache.h
1#pragma once
2
3#include <xrpld/peerfinder/PeerfinderManager.h>
4#include <xrpld/peerfinder/detail/Tuning.h>
5#include <xrpld/peerfinder/detail/iosformat.h>
6
7#include <xrpl/basics/Log.h>
8#include <xrpl/basics/random.h>
9#include <xrpl/beast/container/aged_map.h>
10#include <xrpl/beast/utility/maybe_const.h>
11
12#include <boost/intrusive/list.hpp>
13#include <boost/iterator/transform_iterator.hpp>
14
15#include <algorithm>
16#include <utility>
17
18namespace xrpl::PeerFinder {
19
20template <class>
21class Livecache;
22
23namespace detail {
24
26{
27public:
28 explicit LivecacheBase() = default;
29
30protected:
31 struct Element : boost::intrusive::list_base_hook<>
32 {
36
38 };
39
40 using list_type =
41 boost::intrusive::make_list<Element, boost::intrusive::constant_time_size<false>>::type;
42
43public:
48 template <bool IsConst>
49 class Hop
50 {
51 public:
52 // Iterator transformation to extract the endpoint from Element
53 struct Transform
54 {
57
58 explicit Transform() = default;
59
60 Endpoint const&
61 operator()(Element const& e) const
62 {
63 return e.endpoint;
64 }
65 };
66
67 public:
68 using iterator = boost::transform_iterator<Transform, list_type::const_iterator>;
69
71
73 boost::transform_iterator<Transform, list_type::const_reverse_iterator>;
74
76
77 [[nodiscard]] iterator
78 begin() const
79 {
80 return iterator(list_.get().cbegin(), Transform());
81 }
82
83 [[nodiscard]] iterator
84 cbegin() const
85 {
86 return iterator(list_.get().cbegin(), Transform());
87 }
88
89 [[nodiscard]] iterator
90 end() const
91 {
92 return iterator(list_.get().cend(), Transform());
93 }
94
95 [[nodiscard]] iterator
96 cend() const
97 {
98 return iterator(list_.get().cend(), Transform());
99 }
100
101 [[nodiscard]] reverse_iterator
102 rbegin() const
103 {
104 return reverse_iterator(list_.get().crbegin(), Transform());
105 }
106
107 [[nodiscard]] reverse_iterator
108 crbegin() const
109 {
110 return reverse_iterator(list_.get().crbegin(), Transform());
111 }
112
113 [[nodiscard]] reverse_iterator
114 rend() const
115 {
116 return reverse_iterator(list_.get().crend(), Transform());
117 }
118
119 [[nodiscard]] reverse_iterator
120 crend() const
121 {
122 return reverse_iterator(list_.get().crend(), Transform());
123 }
124
125 // move the element to the end of the container
126 void
128 {
129 auto& e(const_cast<Element&>(*pos.base()));
130 list_.get().erase(list_.get().iterator_to(e));
131 list_.get().push_back(e);
132 }
133
134 private:
136 {
137 }
138
139 friend class LivecacheBase;
140
142 };
143
144protected:
145 // Work-around to call Hop's private constructor from Livecache
146 template <bool IsConst>
147 static Hop<IsConst>
152};
153
154} // namespace detail
155
156//------------------------------------------------------------------------------
157
170template <class Allocator = std::allocator<char>>
172{
173private:
176 Element,
179 Allocator>;
180
183
184public:
185 using allocator_type = Allocator;
186
188 Livecache(clock_type& clock, beast::Journal journal, Allocator alloc = Allocator());
189
190 //
191 // Iteration by hops
192 //
193 // The range [begin, end) provides a sequence of list_type
194 // where each list contains endpoints at a given hops.
195 //
196
197 class HopsT
198 {
199 private:
200 // An endpoint at hops=0 represents the local node.
201 // Endpoints coming in at maxHops are stored at maxHops +1,
202 // but not given out (since they would exceed maxHops). They
203 // are used for automatic connection attempts.
204 //
207
208 template <bool IsConst>
210 {
211 using first_argument = lists_type::value_type;
213
214 explicit Transform() = default;
215
221 };
222
223 public:
224 using iterator = boost::transform_iterator<Transform<false>, lists_type::iterator>;
225
227 boost::transform_iterator<Transform<true>, lists_type::const_iterator>;
228
230 boost::transform_iterator<Transform<false>, lists_type::reverse_iterator>;
231
233 boost::transform_iterator<Transform<true>, lists_type::const_reverse_iterator>;
234
237 {
238 return iterator(lists_.begin(), Transform<false>());
239 }
240
241 [[nodiscard]] const_iterator
242 begin() const
243 {
244 return const_iterator(lists_.cbegin(), Transform<true>());
245 }
246
247 [[nodiscard]] const_iterator
248 cbegin() const
249 {
250 return const_iterator(lists_.cbegin(), Transform<true>());
251 }
252
255 {
256 return iterator(lists_.end(), Transform<false>());
257 }
258
259 [[nodiscard]] const_iterator
260 end() const
261 {
262 return const_iterator(lists_.cend(), Transform<true>());
263 }
264
265 [[nodiscard]] const_iterator
266 cend() const
267 {
268 return const_iterator(lists_.cend(), Transform<true>());
269 }
270
273 {
274 return reverse_iterator(lists_.rbegin(), Transform<false>());
275 }
276
277 [[nodiscard]] const_reverse_iterator
278 rbegin() const
279 {
280 return const_reverse_iterator(lists_.crbegin(), Transform<true>());
281 }
282
283 [[nodiscard]] const_reverse_iterator
284 crbegin() const
285 {
286 return const_reverse_iterator(lists_.crbegin(), Transform<true>());
287 }
288
291 {
292 return reverse_iterator(lists_.rend(), Transform<false>());
293 }
294
295 [[nodiscard]] const_reverse_iterator
296 rend() const
297 {
299 }
300
301 [[nodiscard]] const_reverse_iterator
302 crend() const
303 {
305 }
306
308 void
309 shuffle();
310
311 [[nodiscard]] std::string
312 histogram() const;
313
314 private:
315 explicit HopsT(Allocator const& alloc);
316
317 void
318 insert(Element& e);
319
320 // Reinsert e at a new hops
321 void
323
324 void
325 remove(Element& e);
326
327 friend class Livecache;
331
333 [[nodiscard]] bool
334 empty() const
335 {
336 return cache_.empty();
337 }
338
341 size() const
342 {
343 return cache_.size();
344 }
345
347 void
348 expire();
349
351 void
352 insert(Endpoint const& ep);
353
355 void
357};
358
359//------------------------------------------------------------------------------
360
361template <class Allocator>
363 : journal_(journal), cache_(clock, alloc), hops(alloc)
364{
365}
366
367template <class Allocator>
368void
370{
371 std::size_t n(0);
372 typename cache_type::time_point const expired(
374 for (auto iter(cache_.chronological.begin());
375 iter != cache_.chronological.end() && iter.when() <= expired;)
376 {
377 Element& e(iter->second);
378 hops.remove(e);
379 iter = cache_.erase(iter);
380 ++n;
381 }
382 if (n > 0)
383 {
384 JLOG(journal_.debug()) << beast::Leftw(18) << "Livecache expired " << n
385 << ((n > 1) ? " entries" : " entry");
386 }
387}
388
389template <class Allocator>
390void
392{
393 // The caller already incremented hop, so if we got a
394 // message at maxHops we will store it at maxHops + 1.
395 // This means we won't give out the address to other peers
396 // but we will use it to make connections and hand it out
397 // when redirecting.
398 //
399 XRPL_ASSERT(
400 ep.hops <= (Tuning::kMaxHops + 1),
401 "xrpl::PeerFinder::Livecache::insert : maximum input hops");
402 auto result = cache_.emplace(ep.address, ep);
403 Element& e(result.first->second);
404 if (result.second)
405 {
406 hops.insert(e);
407 JLOG(journal_.debug()) << beast::Leftw(18) << "Livecache insert " << ep.address
408 << " at hops " << ep.hops;
409 return;
410 }
411 if (!result.second && (ep.hops > e.endpoint.hops))
412 {
413 // Drop duplicates at higher hops
414 std::size_t const excess(ep.hops - e.endpoint.hops);
415 JLOG(journal_.trace()) << beast::Leftw(18) << "Livecache drop " << ep.address
416 << " at hops +" << excess;
417 return;
418 }
419
420 cache_.touch(result.first);
421
422 // Address already in the cache so update metadata
423 if (ep.hops < e.endpoint.hops)
424 {
425 hops.reinsert(e, ep.hops);
426 JLOG(journal_.debug()) << beast::Leftw(18) << "Livecache update " << ep.address
427 << " at hops " << ep.hops;
428 }
429 else
430 {
431 JLOG(journal_.trace()) << beast::Leftw(18) << "Livecache refresh " << ep.address
432 << " at hops " << ep.hops;
433 }
434}
435
436template <class Allocator>
437void
439{
440 typename cache_type::time_point const expired(
442 map["size"] = size();
443 map["hist"] = hops.histogram();
444 beast::PropertyStream::Set set("entries", map);
445 for (auto iter(cache_.cbegin()); iter != cache_.cend(); ++iter)
446 {
447 auto const& e(iter->second);
449 item["hops"] = e.endpoint.hops;
450 item["address"] = e.endpoint.address.toString();
452 ss << (iter.when() - expired).count();
453 item["expires"] = ss.str();
454 }
455}
456
457//------------------------------------------------------------------------------
458
459template <class Allocator>
460void
462{
463 for (auto& list : lists_)
464 {
466 v.reserve(list.size());
468 std::shuffle(v.begin(), v.end(), defaultPrng());
469 list.clear();
470 for (auto& e : v)
471 list.push_back(e);
472 }
473}
474
475template <class Allocator>
478{
479 std::string s;
480 for (auto const& h : hist_)
481 {
482 if (!s.empty())
483 s += ", ";
484 s += std::to_string(h);
485 }
486 return s;
487}
488
489template <class Allocator>
491{
493}
494
495template <class Allocator>
496void
498{
499 XRPL_ASSERT(
501 "xrpl::PeerFinder::Livecache::HopsT::insert : maximum input hops");
502 // This has security implications without a shuffle
503 lists_[e.endpoint.hops].push_front(e);
504 ++hist_[e.endpoint.hops];
505}
506
507template <class Allocator>
508void
510{
511 XRPL_ASSERT(
512 numHops <= Tuning::kMaxHops + 1,
513 "xrpl::PeerFinder::Livecache::HopsT::reinsert : maximum hops input");
514
515 auto& list = lists_[e.endpoint.hops];
516 list.erase(list.iterator_to(e));
517
518 --hist_[e.endpoint.hops];
519
520 e.endpoint.hops = numHops;
521 insert(e);
522}
523
524template <class Allocator>
525void
527{
528 --hist_[e.endpoint.hops];
529
530 auto& list = lists_[e.endpoint.hops];
531 list.erase(list.iterator_to(e));
532}
533
534} // namespace xrpl::PeerFinder
T back_inserter(T... args)
T begin(T... args)
A version-independent IP address and port combination.
Definition IPEndpoint.h:17
A generic endpoint for log messages.
Definition Journal.h:38
const_reverse_iterator rbegin() const
Definition Livecache.h:278
const_iterator begin() const
Definition Livecache.h:242
const_iterator cend() const
Definition Livecache.h:266
void reinsert(Element &e, std::uint32_t hops)
Definition Livecache.h:509
boost::transform_iterator< Transform< true >, lists_type::const_iterator > const_iterator
Definition Livecache.h:226
HopsT(Allocator const &alloc)
Definition Livecache.h:490
const_iterator cbegin() const
Definition Livecache.h:248
const_reverse_iterator crbegin() const
Definition Livecache.h:284
boost::transform_iterator< Transform< false >, lists_type::iterator > iterator
Definition Livecache.h:224
const_reverse_iterator crend() const
Definition Livecache.h:302
boost::transform_iterator< Transform< false >, lists_type::reverse_iterator > reverse_iterator
Definition Livecache.h:229
std::array< int, 1+Tuning::kMaxHops+1 > Histogram
Definition Livecache.h:205
boost::transform_iterator< Transform< true >, lists_type::const_reverse_iterator > const_reverse_iterator
Definition Livecache.h:232
const_reverse_iterator rend() const
Definition Livecache.h:296
std::array< list_type, 1+Tuning::kMaxHops+1 > lists_type
Definition Livecache.h:206
void shuffle()
Shuffle each hop list.
Definition Livecache.h:461
const_iterator end() const
Definition Livecache.h:260
std::string histogram() const
Definition Livecache.h:477
The Livecache holds the short-lived relayed Endpoint messages.
Definition Livecache.h:172
cache_type::size_type size() const
Returns the number of entries in the cache.
Definition Livecache.h:341
beast::aged_map< beast::IP::Endpoint, Element, std::chrono::steady_clock, std::less< beast::IP::Endpoint >, Allocator > cache_type
Definition Livecache.h:174
Livecache(clock_type &clock, beast::Journal journal, Allocator alloc=Allocator())
Create the cache.
Definition Livecache.h:362
class xrpl::PeerFinder::Livecache::HopsT hops
void expire()
Erase entries whose time has expired.
Definition Livecache.h:369
void insert(Endpoint const &ep)
Creates or updates an existing Element based on a new message.
Definition Livecache.h:391
bool empty() const
Returns true if the cache is empty.
Definition Livecache.h:334
void onWrite(beast::PropertyStream::Map &map)
Output statistics.
Definition Livecache.h:438
A list of Endpoint at the same hops This is a lightweight wrapper around a reference to the underlyin...
Definition Livecache.h:50
Hop(beast::MaybeConst< IsConst, list_type >::type &list)
Definition Livecache.h:135
boost::transform_iterator< Transform, list_type::const_iterator > iterator
Definition Livecache.h:68
std::reference_wrapper< typename beast::MaybeConst< IsConst, list_type >::type > list_
Definition Livecache.h:141
boost::transform_iterator< Transform, list_type::const_reverse_iterator > reverse_iterator
Definition Livecache.h:72
static Hop< IsConst > makeHop(beast::MaybeConst< IsConst, list_type >::type &list)
Definition Livecache.h:148
boost::intrusive::make_list< Element, boost::intrusive::constant_time_size< false > >::type list_type
Definition Livecache.h:40
T copy(T... args)
T empty(T... args)
T end(T... args)
T fill(T... args)
detail::AgedOrderedContainer< false, true, Key, T, Clock, Compare, Allocator > aged_map
Definition aged_map.h:17
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 str(T... args)
Left justifies a field at the specified width.
Definition iosformat.h:14
std:: conditional_t< IsConst, typename std::remove_const< T >::type const, std::remove_const_t< T > > type
Definition maybe_const.h:12
Describes a connectable peer address along with some metadata.
Hop< IsConst > operator()(beast::MaybeConst< IsConst, lists_type::value_type >::type &list) const
Definition Livecache.h:217
Endpoint const & operator()(Element const &e) const
Definition Livecache.h:61
T to_string(T... args)