rippled
Loading...
Searching...
No Matches
Livecache.h
1#ifndef XRPL_PEERFINDER_LIVECACHE_H_INCLUDED
2#define XRPL_PEERFINDER_LIVECACHE_H_INCLUDED
3
4#include <xrpld/peerfinder/PeerfinderManager.h>
5#include <xrpld/peerfinder/detail/Tuning.h>
6#include <xrpld/peerfinder/detail/iosformat.h>
7
8#include <xrpl/basics/Log.h>
9#include <xrpl/basics/random.h>
10#include <xrpl/beast/container/aged_map.h>
11#include <xrpl/beast/utility/maybe_const.h>
12
13#include <boost/intrusive/list.hpp>
14#include <boost/iterator/transform_iterator.hpp>
15
16#include <algorithm>
17
18namespace ripple {
19namespace PeerFinder {
20
21template <class>
22class Livecache;
23
24namespace detail {
25
27{
28public:
29 explicit LivecacheBase() = default;
30
31protected:
32 struct Element : boost::intrusive::list_base_hook<>
33 {
34 Element(Endpoint const& endpoint_) : endpoint(endpoint_)
35 {
36 }
37
39 };
40
41 using list_type = boost::intrusive::
42 make_list<Element, boost::intrusive::constant_time_size<false>>::type;
43
44public:
49 template <bool IsConst>
50 class Hop
51 {
52 public:
53 // Iterator transformation to extract the endpoint from Element
54 struct Transform
55 {
58
59 explicit Transform() = default;
60
61 Endpoint const&
62 operator()(Element const& e) const
63 {
64 return e.endpoint;
65 }
66 };
67
68 public:
69 using iterator = boost::
70 transform_iterator<Transform, typename list_type::const_iterator>;
71
73
74 using reverse_iterator = boost::transform_iterator<
76 typename list_type::const_reverse_iterator>;
77
79
81 begin() const
82 {
83 return iterator(m_list.get().cbegin(), Transform());
84 }
85
87 cbegin() const
88 {
89 return iterator(m_list.get().cbegin(), Transform());
90 }
91
93 end() const
94 {
95 return iterator(m_list.get().cend(), Transform());
96 }
97
99 cend() const
100 {
101 return iterator(m_list.get().cend(), Transform());
102 }
103
105 rbegin() const
106 {
107 return reverse_iterator(m_list.get().crbegin(), Transform());
108 }
109
111 crbegin() const
112 {
113 return reverse_iterator(m_list.get().crbegin(), Transform());
114 }
115
117 rend() const
118 {
119 return reverse_iterator(m_list.get().crend(), Transform());
120 }
121
123 crend() const
124 {
125 return reverse_iterator(m_list.get().crend(), Transform());
126 }
127
128 // move the element to the end of the container
129 void
131 {
132 auto& e(const_cast<Element&>(*pos.base()));
133 m_list.get().erase(m_list.get().iterator_to(e));
134 m_list.get().push_back(e);
135 }
136
137 private:
138 explicit Hop(
140 : m_list(list)
141 {
142 }
143
144 friend class LivecacheBase;
145
149 };
150
151protected:
152 // Work-around to call Hop's private constructor from Livecache
153 template <bool IsConst>
154 static Hop<IsConst>
156 {
157 return Hop<IsConst>(list);
158 }
159};
160
161} // namespace detail
162
163//------------------------------------------------------------------------------
164
177template <class Allocator = std::allocator<char>>
179{
180private:
183 Element,
186 Allocator>;
187
190
191public:
192 using allocator_type = Allocator;
193
195 Livecache(
196 clock_type& clock,
197 beast::Journal journal,
198 Allocator alloc = Allocator());
199
200 //
201 // Iteration by hops
202 //
203 // The range [begin, end) provides a sequence of list_type
204 // where each list contains endpoints at a given hops.
205 //
206
207 class hops_t
208 {
209 private:
210 // An endpoint at hops=0 represents the local node.
211 // Endpoints coming in at maxHops are stored at maxHops +1,
212 // but not given out (since they would exceed maxHops). They
213 // are used for automatic connection attempts.
214 //
217
218 template <bool IsConst>
220 {
221 using first_argument = typename lists_type::value_type;
223
224 explicit Transform() = default;
225
228 IsConst,
229 typename lists_type::value_type>::type& list) const
230 {
231 return make_hop<IsConst>(list);
232 }
233 };
234
235 public:
236 using iterator = boost::
237 transform_iterator<Transform<false>, typename lists_type::iterator>;
238
239 using const_iterator = boost::transform_iterator<
241 typename lists_type::const_iterator>;
242
243 using reverse_iterator = boost::transform_iterator<
245 typename lists_type::reverse_iterator>;
246
247 using const_reverse_iterator = boost::transform_iterator<
249 typename lists_type::const_reverse_iterator>;
250
253 {
255 }
256
258 begin() const
259 {
261 }
262
264 cbegin() const
265 {
267 }
268
271 {
273 }
274
276 end() const
277 {
279 }
280
282 cend() const
283 {
285 }
286
289 {
291 }
292
294 rbegin() const
295 {
297 }
298
300 crbegin() const
301 {
303 }
304
307 {
309 }
310
312 rend() const
313 {
315 }
316
318 crend() const
319 {
321 }
322
324 void
325 shuffle();
326
328 histogram() const;
329
330 private:
331 explicit hops_t(Allocator const& alloc);
332
333 void
334 insert(Element& e);
335
336 // Reinsert e at a new hops
337 void
339
340 void
341 remove(Element& e);
342
343 friend class Livecache;
347
349 bool
350 empty() const
351 {
352 return m_cache.empty();
353 }
354
356 typename cache_type::size_type
357 size() const
358 {
359 return m_cache.size();
360 }
361
363 void
364 expire();
365
367 void
368 insert(Endpoint const& ep);
369
371 void
373};
374
375//------------------------------------------------------------------------------
376
377template <class Allocator>
379 clock_type& clock,
380 beast::Journal journal,
381 Allocator alloc)
382 : m_journal(journal), m_cache(clock, alloc), hops(alloc)
383{
384}
385
386template <class Allocator>
387void
389{
390 std::size_t n(0);
391 typename cache_type::time_point const expired(
393 for (auto iter(m_cache.chronological.begin());
394 iter != m_cache.chronological.end() && iter.when() <= expired;)
395 {
396 Element& e(iter->second);
397 hops.remove(e);
398 iter = m_cache.erase(iter);
399 ++n;
400 }
401 if (n > 0)
402 {
403 JLOG(m_journal.debug()) << beast::leftw(18) << "Livecache expired " << n
404 << ((n > 1) ? " entries" : " entry");
405 }
406}
407
408template <class Allocator>
409void
411{
412 // The caller already incremented hop, so if we got a
413 // message at maxHops we will store it at maxHops + 1.
414 // This means we won't give out the address to other peers
415 // but we will use it to make connections and hand it out
416 // when redirecting.
417 //
418 XRPL_ASSERT(
419 ep.hops <= (Tuning::maxHops + 1),
420 "ripple::PeerFinder::Livecache::insert : maximum input hops");
421 auto result = m_cache.emplace(ep.address, ep);
422 Element& e(result.first->second);
423 if (result.second)
424 {
425 hops.insert(e);
426 JLOG(m_journal.debug()) << beast::leftw(18) << "Livecache insert "
427 << ep.address << " at hops " << ep.hops;
428 return;
429 }
430 else if (!result.second && (ep.hops > e.endpoint.hops))
431 {
432 // Drop duplicates at higher hops
433 std::size_t const excess(ep.hops - e.endpoint.hops);
434 JLOG(m_journal.trace()) << beast::leftw(18) << "Livecache drop "
435 << ep.address << " at hops +" << excess;
436 return;
437 }
438
439 m_cache.touch(result.first);
440
441 // Address already in the cache so update metadata
442 if (ep.hops < e.endpoint.hops)
443 {
444 hops.reinsert(e, ep.hops);
445 JLOG(m_journal.debug()) << beast::leftw(18) << "Livecache update "
446 << ep.address << " at hops " << ep.hops;
447 }
448 else
449 {
450 JLOG(m_journal.trace()) << beast::leftw(18) << "Livecache refresh "
451 << ep.address << " at hops " << ep.hops;
452 }
453}
454
455template <class Allocator>
456void
458{
459 typename cache_type::time_point const expired(
461 map["size"] = size();
462 map["hist"] = hops.histogram();
463 beast::PropertyStream::Set set("entries", map);
464 for (auto iter(m_cache.cbegin()); iter != m_cache.cend(); ++iter)
465 {
466 auto const& e(iter->second);
468 item["hops"] = e.endpoint.hops;
469 item["address"] = e.endpoint.address.to_string();
471 ss << (iter.when() - expired).count();
472 item["expires"] = ss.str();
473 }
474}
475
476//------------------------------------------------------------------------------
477
478template <class Allocator>
479void
481{
482 for (auto& list : m_lists)
483 {
485 v.reserve(list.size());
486 std::copy(list.begin(), list.end(), std::back_inserter(v));
487 std::shuffle(v.begin(), v.end(), default_prng());
488 list.clear();
489 for (auto& e : v)
490 list.push_back(e);
491 }
492}
493
494template <class Allocator>
497{
498 std::string s;
499 for (auto const& h : m_hist)
500 {
501 if (!s.empty())
502 s += ", ";
503 s += std::to_string(h);
504 }
505 return s;
506}
507
508template <class Allocator>
510{
511 std::fill(m_hist.begin(), m_hist.end(), 0);
512}
513
514template <class Allocator>
515void
517{
518 XRPL_ASSERT(
520 "ripple::PeerFinder::Livecache::hops_t::insert : maximum input hops");
521 // This has security implications without a shuffle
522 m_lists[e.endpoint.hops].push_front(e);
523 ++m_hist[e.endpoint.hops];
524}
525
526template <class Allocator>
527void
529{
530 XRPL_ASSERT(
531 numHops <= Tuning::maxHops + 1,
532 "ripple::PeerFinder::Livecache::hops_t::reinsert : maximum hops input");
533
534 auto& list = m_lists[e.endpoint.hops];
535 list.erase(list.iterator_to(e));
536
537 --m_hist[e.endpoint.hops];
538
539 e.endpoint.hops = numHops;
540 insert(e);
541}
542
543template <class Allocator>
544void
546{
547 --m_hist[e.endpoint.hops];
548
549 auto& list = m_lists[e.endpoint.hops];
550 list.erase(list.iterator_to(e));
551}
552
553} // namespace PeerFinder
554} // namespace ripple
555
556#endif
T back_inserter(T... args)
T begin(T... args)
A version-independent IP address and port combination.
Definition IPEndpoint.h:19
A generic endpoint for log messages.
Definition Journal.h:41
Stream debug() const
Definition Journal.h:309
Stream trace() const
Severity stream access functions.
Definition Journal.h:303
virtual time_point now() const =0
Returns the current time.
Associative container where each element is also indexed by time.
beast::detail::aged_container_iterator< false, Iterator > erase(beast::detail::aged_container_iterator< is_const, Iterator > pos)
void touch(beast::detail::aged_container_iterator< is_const, Iterator > pos)
class beast::detail::aged_ordered_container::chronological_t chronological
auto emplace(Args &&... args) -> typename std::enable_if<!maybe_multi, std::pair< iterator, bool > >::type
typename clock_type::time_point time_point
boost::transform_iterator< Transform< true >, typename lists_type::const_iterator > const_iterator
Definition Livecache.h:241
void shuffle()
Shuffle each hop list.
Definition Livecache.h:480
hops_t(Allocator const &alloc)
Definition Livecache.h:509
const_reverse_iterator crend() const
Definition Livecache.h:318
const_reverse_iterator rend() const
Definition Livecache.h:312
void reinsert(Element &e, std::uint32_t hops)
Definition Livecache.h:528
const_reverse_iterator rbegin() const
Definition Livecache.h:294
boost::transform_iterator< Transform< false >, typename lists_type::reverse_iterator > reverse_iterator
Definition Livecache.h:245
const_iterator cbegin() const
Definition Livecache.h:264
boost::transform_iterator< Transform< false >, typename lists_type::iterator > iterator
Definition Livecache.h:237
const_reverse_iterator crbegin() const
Definition Livecache.h:300
boost::transform_iterator< Transform< true >, typename lists_type::const_reverse_iterator > const_reverse_iterator
Definition Livecache.h:249
The Livecache holds the short-lived relayed Endpoint messages.
Definition Livecache.h:179
cache_type::size_type size() const
Returns the number of entries in the cache.
Definition Livecache.h:357
void expire()
Erase entries whose time has expired.
Definition Livecache.h:388
Livecache(clock_type &clock, beast::Journal journal, Allocator alloc=Allocator())
Create the cache.
Definition Livecache.h:378
void insert(Endpoint const &ep)
Creates or updates an existing Element based on a new message.
Definition Livecache.h:410
void onWrite(beast::PropertyStream::Map &map)
Output statistics.
Definition Livecache.h:457
class ripple::PeerFinder::Livecache::hops_t hops
bool empty() const
Returns true if the cache is empty.
Definition Livecache.h:350
A list of Endpoint at the same hops This is a lightweight wrapper around a reference to the underlyin...
Definition Livecache.h:51
Hop(typename beast::maybe_const< IsConst, list_type >::type &list)
Definition Livecache.h:138
std::reference_wrapper< typename beast::maybe_const< IsConst, list_type >::type > m_list
Definition Livecache.h:148
boost::transform_iterator< Transform, typename list_type::const_iterator > iterator
Definition Livecache.h:70
boost::transform_iterator< Transform, typename list_type::const_reverse_iterator > reverse_iterator
Definition Livecache.h:76
static Hop< IsConst > make_hop(typename beast::maybe_const< IsConst, list_type >::type &list)
Definition Livecache.h:155
boost::intrusive::make_list< Element, boost::intrusive::constant_time_size< false > >::type list_type
Definition Livecache.h:42
T copy(T... args)
T empty(T... args)
T end(T... args)
T fill(T... args)
std::chrono::seconds constexpr liveCacheSecondsToLive(30)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
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,...
@ expired
List is expired, but has the largest non-pending sequence seen so far.
beast::xor_shift_engine & default_prng()
Return the default random engine.
T shuffle(T... args)
T rbegin(T... args)
T rend(T... args)
T reserve(T... args)
T str(T... args)
Left justifies a field at the specified width.
Definition iosformat.h:15
Makes T const or non const depending on a bool.
Definition maybe_const.h:11
typename std::conditional< IsConst, typename std::remove_const< T >::type const, typename std::remove_const< T >::type >::type type
Definition maybe_const.h:16
Describes a connectible peer address along with some metadata.
Hop< IsConst > operator()(typename beast::maybe_const< IsConst, typename lists_type::value_type >::type &list) const
Definition Livecache.h:227
typename lists_type::value_type first_argument
Definition Livecache.h:221
Endpoint const & operator()(Element const &e) const
Definition Livecache.h:62
T to_string(T... args)