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 = boost::intrusive::make_list<Element, boost::intrusive::constant_time_size<false>>::type;
41
42public:
47 template <bool IsConst>
48 class Hop
49 {
50 public:
51 // Iterator transformation to extract the endpoint from Element
52 struct Transform
53 {
56
57 explicit Transform() = default;
58
59 Endpoint const&
60 operator()(Element const& e) const
61 {
62 return e.endpoint;
63 }
64 };
65
66 public:
67 using iterator = boost::transform_iterator<Transform, typename list_type::const_iterator>;
68
70
71 using reverse_iterator = boost::transform_iterator<Transform, typename list_type::const_reverse_iterator>;
72
74
76 begin() const
77 {
78 return iterator(m_list.get().cbegin(), Transform());
79 }
80
82 cbegin() const
83 {
84 return iterator(m_list.get().cbegin(), Transform());
85 }
86
88 end() const
89 {
90 return iterator(m_list.get().cend(), Transform());
91 }
92
94 cend() const
95 {
96 return iterator(m_list.get().cend(), Transform());
97 }
98
100 rbegin() const
101 {
102 return reverse_iterator(m_list.get().crbegin(), Transform());
103 }
104
106 crbegin() const
107 {
108 return reverse_iterator(m_list.get().crbegin(), Transform());
109 }
110
112 rend() const
113 {
114 return reverse_iterator(m_list.get().crend(), Transform());
115 }
116
118 crend() const
119 {
120 return reverse_iterator(m_list.get().crend(), Transform());
121 }
122
123 // move the element to the end of the container
124 void
126 {
127 auto& e(const_cast<Element&>(*pos.base()));
128 m_list.get().erase(m_list.get().iterator_to(e));
129 m_list.get().push_back(e);
130 }
131
132 private:
134 {
135 }
136
137 friend class LivecacheBase;
138
140 };
141
142protected:
143 // Work-around to call Hop's private constructor from Livecache
144 template <bool IsConst>
145 static Hop<IsConst>
147 {
148 return Hop<IsConst>(list);
149 }
150};
151
152} // namespace detail
153
154//------------------------------------------------------------------------------
155
168template <class Allocator = std::allocator<char>>
170{
171private:
172 using cache_type = beast::
173 aged_map<beast::IP::Endpoint, Element, std::chrono::steady_clock, std::less<beast::IP::Endpoint>, Allocator>;
174
177
178public:
179 using allocator_type = Allocator;
180
182 Livecache(clock_type& clock, beast::Journal journal, Allocator alloc = Allocator());
183
184 //
185 // Iteration by hops
186 //
187 // The range [begin, end) provides a sequence of list_type
188 // where each list contains endpoints at a given hops.
189 //
190
191 class hops_t
192 {
193 private:
194 // An endpoint at hops=0 represents the local node.
195 // Endpoints coming in at maxHops are stored at maxHops +1,
196 // but not given out (since they would exceed maxHops). They
197 // are used for automatic connection attempts.
198 //
201
202 template <bool IsConst>
204 {
205 using first_argument = typename lists_type::value_type;
207
208 explicit Transform() = default;
209
212 {
213 return make_hop<IsConst>(list);
214 }
215 };
216
217 public:
218 using iterator = boost::transform_iterator<Transform<false>, typename lists_type::iterator>;
219
220 using const_iterator = boost::transform_iterator<Transform<true>, typename lists_type::const_iterator>;
221
222 using reverse_iterator = boost::transform_iterator<Transform<false>, typename lists_type::reverse_iterator>;
223
225 boost::transform_iterator<Transform<true>, typename lists_type::const_reverse_iterator>;
226
229 {
231 }
232
234 begin() const
235 {
237 }
238
240 cbegin() const
241 {
243 }
244
247 {
249 }
250
252 end() const
253 {
255 }
256
258 cend() const
259 {
261 }
262
265 {
267 }
268
270 rbegin() const
271 {
273 }
274
276 crbegin() const
277 {
279 }
280
283 {
285 }
286
288 rend() const
289 {
291 }
292
294 crend() const
295 {
297 }
298
300 void
301 shuffle();
302
304 histogram() const;
305
306 private:
307 explicit hops_t(Allocator const& alloc);
308
309 void
310 insert(Element& e);
311
312 // Reinsert e at a new hops
313 void
315
316 void
317 remove(Element& e);
318
319 friend class Livecache;
323
325 bool
326 empty() const
327 {
328 return m_cache.empty();
329 }
330
332 typename cache_type::size_type
333 size() const
334 {
335 return m_cache.size();
336 }
337
339 void
340 expire();
341
343 void
344 insert(Endpoint const& ep);
345
347 void
349};
350
351//------------------------------------------------------------------------------
352
353template <class Allocator>
355 : m_journal(journal), m_cache(clock, alloc), hops(alloc)
356{
357}
358
359template <class Allocator>
360void
362{
363 std::size_t n(0);
365 for (auto iter(m_cache.chronological.begin()); iter != m_cache.chronological.end() && iter.when() <= expired;)
366 {
367 Element& e(iter->second);
368 hops.remove(e);
369 iter = m_cache.erase(iter);
370 ++n;
371 }
372 if (n > 0)
373 {
374 JLOG(m_journal.debug()) << beast::leftw(18) << "Livecache expired " << n << ((n > 1) ? " entries" : " entry");
375 }
376}
377
378template <class Allocator>
379void
381{
382 // The caller already incremented hop, so if we got a
383 // message at maxHops we will store it at maxHops + 1.
384 // This means we won't give out the address to other peers
385 // but we will use it to make connections and hand it out
386 // when redirecting.
387 //
388 XRPL_ASSERT(ep.hops <= (Tuning::maxHops + 1), "xrpl::PeerFinder::Livecache::insert : maximum input hops");
389 auto result = m_cache.emplace(ep.address, ep);
390 Element& e(result.first->second);
391 if (result.second)
392 {
393 hops.insert(e);
394 JLOG(m_journal.debug()) << beast::leftw(18) << "Livecache insert " << ep.address << " at hops " << ep.hops;
395 return;
396 }
397 else if (!result.second && (ep.hops > e.endpoint.hops))
398 {
399 // Drop duplicates at higher hops
400 std::size_t const excess(ep.hops - e.endpoint.hops);
401 JLOG(m_journal.trace()) << beast::leftw(18) << "Livecache drop " << ep.address << " at hops +" << excess;
402 return;
403 }
404
405 m_cache.touch(result.first);
406
407 // Address already in the cache so update metadata
408 if (ep.hops < e.endpoint.hops)
409 {
410 hops.reinsert(e, ep.hops);
411 JLOG(m_journal.debug()) << beast::leftw(18) << "Livecache update " << ep.address << " at hops " << ep.hops;
412 }
413 else
414 {
415 JLOG(m_journal.trace()) << beast::leftw(18) << "Livecache refresh " << ep.address << " at hops " << ep.hops;
416 }
417}
418
419template <class Allocator>
420void
422{
424 map["size"] = size();
425 map["hist"] = hops.histogram();
426 beast::PropertyStream::Set set("entries", map);
427 for (auto iter(m_cache.cbegin()); iter != m_cache.cend(); ++iter)
428 {
429 auto const& e(iter->second);
431 item["hops"] = e.endpoint.hops;
432 item["address"] = e.endpoint.address.to_string();
434 ss << (iter.when() - expired).count();
435 item["expires"] = ss.str();
436 }
437}
438
439//------------------------------------------------------------------------------
440
441template <class Allocator>
442void
444{
445 for (auto& list : m_lists)
446 {
448 v.reserve(list.size());
449 std::copy(list.begin(), list.end(), std::back_inserter(v));
450 std::shuffle(v.begin(), v.end(), default_prng());
451 list.clear();
452 for (auto& e : v)
453 list.push_back(e);
454 }
455}
456
457template <class Allocator>
460{
461 std::string s;
462 for (auto const& h : m_hist)
463 {
464 if (!s.empty())
465 s += ", ";
466 s += std::to_string(h);
467 }
468 return s;
469}
470
471template <class Allocator>
473{
474 std::fill(m_hist.begin(), m_hist.end(), 0);
475}
476
477template <class Allocator>
478void
480{
481 XRPL_ASSERT(
482 e.endpoint.hops <= Tuning::maxHops + 1, "xrpl::PeerFinder::Livecache::hops_t::insert : maximum input hops");
483 // This has security implications without a shuffle
484 m_lists[e.endpoint.hops].push_front(e);
485 ++m_hist[e.endpoint.hops];
486}
487
488template <class Allocator>
489void
491{
492 XRPL_ASSERT(numHops <= Tuning::maxHops + 1, "xrpl::PeerFinder::Livecache::hops_t::reinsert : maximum hops input");
493
494 auto& list = m_lists[e.endpoint.hops];
495 list.erase(list.iterator_to(e));
496
497 --m_hist[e.endpoint.hops];
498
499 e.endpoint.hops = numHops;
500 insert(e);
501}
502
503template <class Allocator>
504void
506{
507 --m_hist[e.endpoint.hops];
508
509 auto& list = m_lists[e.endpoint.hops];
510 list.erase(list.iterator_to(e));
511}
512
513} // namespace PeerFinder
514} // namespace xrpl
T back_inserter(T... args)
T begin(T... args)
A generic endpoint for log messages.
Definition Journal.h:40
Stream debug() const
Definition Journal.h:300
Stream trace() const
Severity stream access functions.
Definition Journal.h:294
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:252
void shuffle()
Shuffle each hop list.
Definition Livecache.h:443
boost::transform_iterator< Transform< true >, typename lists_type::const_iterator > const_iterator
Definition Livecache.h:220
const_reverse_iterator rbegin() const
Definition Livecache.h:270
const_iterator begin() const
Definition Livecache.h:234
const_reverse_iterator crbegin() const
Definition Livecache.h:276
boost::transform_iterator< Transform< false >, typename lists_type::reverse_iterator > reverse_iterator
Definition Livecache.h:222
const_iterator cbegin() const
Definition Livecache.h:240
void reinsert(Element &e, std::uint32_t hops)
Definition Livecache.h:490
boost::transform_iterator< Transform< true >, typename lists_type::const_reverse_iterator > const_reverse_iterator
Definition Livecache.h:225
hops_t(Allocator const &alloc)
Definition Livecache.h:472
boost::transform_iterator< Transform< false >, typename lists_type::iterator > iterator
Definition Livecache.h:218
const_reverse_iterator rend() const
Definition Livecache.h:288
const_reverse_iterator crend() const
Definition Livecache.h:294
const_iterator cend() const
Definition Livecache.h:258
The Livecache holds the short-lived relayed Endpoint messages.
Definition Livecache.h:170
cache_type::size_type size() const
Returns the number of entries in the cache.
Definition Livecache.h:333
Livecache(clock_type &clock, beast::Journal journal, Allocator alloc=Allocator())
Create the cache.
Definition Livecache.h:354
class xrpl::PeerFinder::Livecache::hops_t hops
void expire()
Erase entries whose time has expired.
Definition Livecache.h:361
void insert(Endpoint const &ep)
Creates or updates an existing Element based on a new message.
Definition Livecache.h:380
bool empty() const
Returns true if the cache is empty.
Definition Livecache.h:326
void onWrite(beast::PropertyStream::Map &map)
Output statistics.
Definition Livecache.h:421
A list of Endpoint at the same hops This is a lightweight wrapper around a reference to the underlyin...
Definition Livecache.h:49
boost::transform_iterator< Transform, typename list_type::const_iterator > iterator
Definition Livecache.h:67
std::reference_wrapper< typename beast::maybe_const< IsConst, list_type >::type > m_list
Definition Livecache.h:139
Hop(typename beast::maybe_const< IsConst, list_type >::type &list)
Definition Livecache.h:133
boost::transform_iterator< Transform, typename list_type::const_reverse_iterator > reverse_iterator
Definition Livecache.h:71
static Hop< IsConst > make_hop(typename beast::maybe_const< IsConst, list_type >::type &list)
Definition Livecache.h:146
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)
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:13
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:211
typename lists_type::value_type first_argument
Definition Livecache.h:205
Endpoint const & operator()(Element const &e) const
Definition Livecache.h:60
T to_string(T... args)