rippled
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
17namespace xrpl {
18namespace 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 {
33 Element(Endpoint const& endpoint_) : endpoint(endpoint_)
34 {
35 }
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, typename list_type::const_iterator>;
69
71
73 boost::transform_iterator<Transform, typename list_type::const_reverse_iterator>;
74
76
78 begin() const
79 {
80 return iterator(m_list.get().cbegin(), Transform());
81 }
82
84 cbegin() const
85 {
86 return iterator(m_list.get().cbegin(), Transform());
87 }
88
90 end() const
91 {
92 return iterator(m_list.get().cend(), Transform());
93 }
94
96 cend() const
97 {
98 return iterator(m_list.get().cend(), Transform());
99 }
100
102 rbegin() const
103 {
104 return reverse_iterator(m_list.get().crbegin(), Transform());
105 }
106
108 crbegin() const
109 {
110 return reverse_iterator(m_list.get().crbegin(), Transform());
111 }
112
114 rend() const
115 {
116 return reverse_iterator(m_list.get().crend(), Transform());
117 }
118
120 crend() const
121 {
122 return reverse_iterator(m_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 m_list.get().erase(m_list.get().iterator_to(e));
131 m_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>
149 {
150 return Hop<IsConst>(list);
151 }
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 hops_t
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 = typename lists_type::value_type;
213
214 explicit Transform() = default;
215
218 list) const
219 {
220 return make_hop<IsConst>(list);
221 }
222 };
223
224 public:
225 using iterator = boost::transform_iterator<Transform<false>, typename lists_type::iterator>;
226
228 boost::transform_iterator<Transform<true>, typename lists_type::const_iterator>;
229
231 boost::transform_iterator<Transform<false>, typename lists_type::reverse_iterator>;
232
234 boost::transform_iterator<Transform<true>, typename lists_type::const_reverse_iterator>;
235
238 {
240 }
241
243 begin() const
244 {
246 }
247
249 cbegin() const
250 {
252 }
253
256 {
258 }
259
261 end() const
262 {
264 }
265
267 cend() const
268 {
270 }
271
274 {
276 }
277
279 rbegin() const
280 {
282 }
283
285 crbegin() const
286 {
288 }
289
292 {
294 }
295
297 rend() const
298 {
300 }
301
303 crend() const
304 {
306 }
307
309 void
310 shuffle();
311
313 histogram() const;
314
315 private:
316 explicit hops_t(Allocator const& alloc);
317
318 void
319 insert(Element& e);
320
321 // Reinsert e at a new hops
322 void
324
325 void
326 remove(Element& e);
327
328 friend class Livecache;
332
334 bool
335 empty() const
336 {
337 return m_cache.empty();
338 }
339
341 typename cache_type::size_type
342 size() const
343 {
344 return m_cache.size();
345 }
346
348 void
349 expire();
350
352 void
353 insert(Endpoint const& ep);
354
356 void
358};
359
360//------------------------------------------------------------------------------
361
362template <class Allocator>
364 : m_journal(journal), m_cache(clock, alloc), hops(alloc)
365{
366}
367
368template <class Allocator>
369void
371{
372 std::size_t n(0);
373 typename cache_type::time_point const expired(
375 for (auto iter(m_cache.chronological.begin());
376 iter != m_cache.chronological.end() && iter.when() <= expired;)
377 {
378 Element& e(iter->second);
379 hops.remove(e);
380 iter = m_cache.erase(iter);
381 ++n;
382 }
383 if (n > 0)
384 {
385 JLOG(m_journal.debug()) << beast::leftw(18) << "Livecache expired " << n
386 << ((n > 1) ? " entries" : " entry");
387 }
388}
389
390template <class Allocator>
391void
393{
394 // The caller already incremented hop, so if we got a
395 // message at maxHops we will store it at maxHops + 1.
396 // This means we won't give out the address to other peers
397 // but we will use it to make connections and hand it out
398 // when redirecting.
399 //
400 XRPL_ASSERT(
401 ep.hops <= (Tuning::maxHops + 1),
402 "xrpl::PeerFinder::Livecache::insert : maximum input hops");
403 auto result = m_cache.emplace(ep.address, ep);
404 Element& e(result.first->second);
405 if (result.second)
406 {
407 hops.insert(e);
408 JLOG(m_journal.debug()) << beast::leftw(18) << "Livecache insert " << ep.address
409 << " at hops " << ep.hops;
410 return;
411 }
412 else if (!result.second && (ep.hops > e.endpoint.hops))
413 {
414 // Drop duplicates at higher hops
415 std::size_t const excess(ep.hops - e.endpoint.hops);
416 JLOG(m_journal.trace()) << beast::leftw(18) << "Livecache drop " << ep.address
417 << " at hops +" << excess;
418 return;
419 }
420
421 m_cache.touch(result.first);
422
423 // Address already in the cache so update metadata
424 if (ep.hops < e.endpoint.hops)
425 {
426 hops.reinsert(e, ep.hops);
427 JLOG(m_journal.debug()) << beast::leftw(18) << "Livecache update " << ep.address
428 << " at hops " << ep.hops;
429 }
430 else
431 {
432 JLOG(m_journal.trace()) << beast::leftw(18) << "Livecache refresh " << ep.address
433 << " at hops " << ep.hops;
434 }
435}
436
437template <class Allocator>
438void
440{
441 typename cache_type::time_point const expired(
443 map["size"] = size();
444 map["hist"] = hops.histogram();
445 beast::PropertyStream::Set set("entries", map);
446 for (auto iter(m_cache.cbegin()); iter != m_cache.cend(); ++iter)
447 {
448 auto const& e(iter->second);
450 item["hops"] = e.endpoint.hops;
451 item["address"] = e.endpoint.address.to_string();
453 ss << (iter.when() - expired).count();
454 item["expires"] = ss.str();
455 }
456}
457
458//------------------------------------------------------------------------------
459
460template <class Allocator>
461void
463{
464 for (auto& list : m_lists)
465 {
467 v.reserve(list.size());
468 std::copy(list.begin(), list.end(), std::back_inserter(v));
469 std::shuffle(v.begin(), v.end(), default_prng());
470 list.clear();
471 for (auto& e : v)
472 list.push_back(e);
473 }
474}
475
476template <class Allocator>
479{
480 std::string s;
481 for (auto const& h : m_hist)
482 {
483 if (!s.empty())
484 s += ", ";
485 s += std::to_string(h);
486 }
487 return s;
488}
489
490template <class Allocator>
492{
493 std::fill(m_hist.begin(), m_hist.end(), 0);
494}
495
496template <class Allocator>
497void
499{
500 XRPL_ASSERT(
502 "xrpl::PeerFinder::Livecache::hops_t::insert : maximum input hops");
503 // This has security implications without a shuffle
504 m_lists[e.endpoint.hops].push_front(e);
505 ++m_hist[e.endpoint.hops];
506}
507
508template <class Allocator>
509void
511{
512 XRPL_ASSERT(
513 numHops <= Tuning::maxHops + 1,
514 "xrpl::PeerFinder::Livecache::hops_t::reinsert : maximum hops input");
515
516 auto& list = m_lists[e.endpoint.hops];
517 list.erase(list.iterator_to(e));
518
519 --m_hist[e.endpoint.hops];
520
521 e.endpoint.hops = numHops;
522 insert(e);
523}
524
525template <class Allocator>
526void
528{
529 --m_hist[e.endpoint.hops];
530
531 auto& list = m_lists[e.endpoint.hops];
532 list.erase(list.iterator_to(e));
533}
534
535} // namespace PeerFinder
536} // namespace xrpl
T back_inserter(T... args)
T begin(T... args)
A version-independent IP address and port combination.
Definition IPEndpoint.h:18
A generic endpoint for log messages.
Definition Journal.h:40
Stream debug() const
Definition Journal.h:301
Stream trace() const
Severity stream access functions.
Definition Journal.h:295
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
const_iterator end() const
Definition Livecache.h:261
void shuffle()
Shuffle each hop list.
Definition Livecache.h:462
boost::transform_iterator< Transform< true >, typename lists_type::const_iterator > const_iterator
Definition Livecache.h:228
const_reverse_iterator rbegin() const
Definition Livecache.h:279
const_iterator begin() const
Definition Livecache.h:243
const_reverse_iterator crbegin() const
Definition Livecache.h:285
boost::transform_iterator< Transform< false >, typename lists_type::reverse_iterator > reverse_iterator
Definition Livecache.h:231
const_iterator cbegin() const
Definition Livecache.h:249
void reinsert(Element &e, std::uint32_t hops)
Definition Livecache.h:510
boost::transform_iterator< Transform< true >, typename lists_type::const_reverse_iterator > const_reverse_iterator
Definition Livecache.h:234
hops_t(Allocator const &alloc)
Definition Livecache.h:491
boost::transform_iterator< Transform< false >, typename lists_type::iterator > iterator
Definition Livecache.h:225
const_reverse_iterator rend() const
Definition Livecache.h:297
const_reverse_iterator crend() const
Definition Livecache.h:303
const_iterator cend() const
Definition Livecache.h:267
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:342
Livecache(clock_type &clock, beast::Journal journal, Allocator alloc=Allocator())
Create the cache.
Definition Livecache.h:363
class xrpl::PeerFinder::Livecache::hops_t hops
void expire()
Erase entries whose time has expired.
Definition Livecache.h:370
void insert(Endpoint const &ep)
Creates or updates an existing Element based on a new message.
Definition Livecache.h:392
bool empty() const
Returns true if the cache is empty.
Definition Livecache.h:335
void onWrite(beast::PropertyStream::Map &map)
Output statistics.
Definition Livecache.h:439
A list of Endpoint at the same hops This is a lightweight wrapper around a reference to the underlyin...
Definition Livecache.h:50
boost::transform_iterator< Transform, typename list_type::const_iterator > iterator
Definition Livecache.h:68
std::reference_wrapper< typename beast::maybe_const< IsConst, list_type >::type > m_list
Definition Livecache.h:141
Hop(typename beast::maybe_const< IsConst, list_type >::type &list)
Definition Livecache.h:135
boost::transform_iterator< Transform, typename list_type::const_reverse_iterator > reverse_iterator
Definition Livecache.h:73
static Hop< IsConst > make_hop(typename beast::maybe_const< 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:41
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:5
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:14
typename std::conditional< IsConst, typename std::remove_const< T >::type const, typename std::remove_const< T >::type >::type type
Definition maybe_const.h:15
Describes a connectable 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:217
typename lists_type::value_type first_argument
Definition Livecache.h:211
Endpoint const & operator()(Element const &e) const
Definition Livecache.h:61
T to_string(T... args)