rippled
Loading...
Searching...
No Matches
Bootcache.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/peerfinder/detail/Bootcache.h>
21#include <xrpld/peerfinder/detail/Tuning.h>
22#include <xrpld/peerfinder/detail/iosformat.h>
23#include <xrpl/basics/Log.h>
24
25namespace ripple {
26namespace PeerFinder {
27
29 : m_store(store)
30 , m_clock(clock)
31 , m_journal(journal)
32 , m_whenUpdate(m_clock.now())
33 , m_needsUpdate(false)
34{
35}
36
38{
39 update();
40}
41
42bool
44{
45 return m_map.empty();
46}
47
48Bootcache::map_type::size_type
50{
51 return m_map.size();
52}
53
56{
57 return const_iterator(m_map.right.begin());
58}
59
62{
63 return const_iterator(m_map.right.begin());
64}
65
68{
69 return const_iterator(m_map.right.end());
70}
71
74{
75 return const_iterator(m_map.right.end());
76}
77
78void
80{
81 m_map.clear();
82 m_needsUpdate = true;
83}
84
85//--------------------------------------------------------------------------
86
87void
89{
90 clear();
91 auto const n(
92 m_store.load([this](beast::IP::Endpoint const& endpoint, int valence) {
93 auto const result(
94 this->m_map.insert(value_type(endpoint, valence)));
95 if (!result.second)
96 {
97 JLOG(this->m_journal.error())
98 << beast::leftw(18) << "Bootcache discard " << endpoint;
99 }
100 }));
101
102 if (n > 0)
103 {
104 JLOG(m_journal.info()) << beast::leftw(18) << "Bootcache loaded " << n
105 << ((n > 1) ? " addresses" : " address");
106 prune();
107 }
108}
109
110bool
111Bootcache::insert(beast::IP::Endpoint const& endpoint)
112{
113 auto const result(m_map.insert(value_type(endpoint, 0)));
114 if (result.second)
115 {
116 JLOG(m_journal.trace())
117 << beast::leftw(18) << "Bootcache insert " << endpoint;
118 prune();
119 flagForUpdate();
120 }
121 return result.second;
122}
123
124bool
125Bootcache::insertStatic(beast::IP::Endpoint const& endpoint)
126{
127 auto result(m_map.insert(value_type(endpoint, staticValence)));
128
129 if (!result.second && (result.first->right.valence() < staticValence))
130 {
131 // An existing entry has too low a valence, replace it
132 m_map.erase(result.first);
133 result = m_map.insert(value_type(endpoint, staticValence));
134 }
135
136 if (result.second)
137 {
138 JLOG(m_journal.trace())
139 << beast::leftw(18) << "Bootcache insert " << endpoint;
140 prune();
141 flagForUpdate();
142 }
143 return result.second;
144}
145
146void
147Bootcache::on_success(beast::IP::Endpoint const& endpoint)
148{
149 auto result(m_map.insert(value_type(endpoint, 1)));
150 if (result.second)
151 {
152 prune();
153 }
154 else
155 {
156 Entry entry(result.first->right);
157 if (entry.valence() < 0)
158 entry.valence() = 0;
159 ++entry.valence();
160 m_map.erase(result.first);
161 result = m_map.insert(value_type(endpoint, entry));
162 XRPL_ASSERT(
163 result.second,
164 "ripple:PeerFinder::Bootcache::on_success : endpoint inserted");
165 }
166 Entry const& entry(result.first->right);
167 JLOG(m_journal.info()) << beast::leftw(18) << "Bootcache connect "
168 << endpoint << " with " << entry.valence()
169 << ((entry.valence() > 1) ? " successes"
170 : " success");
171 flagForUpdate();
172}
173
174void
175Bootcache::on_failure(beast::IP::Endpoint const& endpoint)
176{
177 auto result(m_map.insert(value_type(endpoint, -1)));
178 if (result.second)
179 {
180 prune();
181 }
182 else
183 {
184 Entry entry(result.first->right);
185 if (entry.valence() > 0)
186 entry.valence() = 0;
187 --entry.valence();
188 m_map.erase(result.first);
189 result = m_map.insert(value_type(endpoint, entry));
190 XRPL_ASSERT(
191 result.second,
192 "ripple:PeerFinder::Bootcache::on_failure : endpoint inserted");
193 }
194 Entry const& entry(result.first->right);
195 auto const n(std::abs(entry.valence()));
196 JLOG(m_journal.debug())
197 << beast::leftw(18) << "Bootcache failed " << endpoint << " with " << n
198 << ((n > 1) ? " attempts" : " attempt");
199 flagForUpdate();
200}
201
202void
203Bootcache::periodicActivity()
204{
205 checkUpdate();
206}
207
208//--------------------------------------------------------------------------
209
210void
211Bootcache::onWrite(beast::PropertyStream::Map& map)
212{
213 beast::PropertyStream::Set entries("entries", map);
214 for (auto iter = m_map.right.begin(); iter != m_map.right.end(); ++iter)
215 {
216 beast::PropertyStream::Map entry(entries);
217 entry["endpoint"] = iter->get_left().to_string();
218 entry["valence"] = std::int32_t(iter->get_right().valence());
219 }
220}
221
222// Checks the cache size and prunes if its over the limit.
223void
224Bootcache::prune()
225{
226 if (size() <= Tuning::bootcacheSize)
227 return;
228
229 // Calculate the amount to remove
230 auto count((size() * Tuning::bootcachePrunePercent) / 100);
231 decltype(count) pruned(0);
232
233 // Work backwards because bimap doesn't handle
234 // erasing using a reverse iterator very well.
235 //
236 for (auto iter(m_map.right.end());
237 count-- > 0 && iter != m_map.right.begin();
238 ++pruned)
239 {
240 --iter;
241 beast::IP::Endpoint const& endpoint(iter->get_left());
242 Entry const& entry(iter->get_right());
243 JLOG(m_journal.trace())
244 << beast::leftw(18) << "Bootcache pruned" << endpoint
245 << " at valence " << entry.valence();
246 iter = m_map.right.erase(iter);
247 }
248
249 JLOG(m_journal.debug()) << beast::leftw(18) << "Bootcache pruned " << pruned
250 << " entries total";
251}
252
253// Updates the Store with the current set of entries if needed.
254void
255Bootcache::update()
256{
257 if (!m_needsUpdate)
258 return;
260 list.reserve(m_map.size());
261 for (auto const& e : m_map)
262 {
263 Store::Entry se;
264 se.endpoint = e.get_left();
265 se.valence = e.get_right().valence();
266 list.push_back(se);
267 }
268 m_store.save(list);
269 // Reset the flag and cooldown timer
270 m_needsUpdate = false;
271 m_whenUpdate = m_clock.now() + Tuning::bootcacheCooldownTime;
272}
273
274// Checks the clock and calls update if we are off the cooldown.
275void
276Bootcache::checkUpdate()
277{
278 if (m_needsUpdate && m_whenUpdate < m_clock.now())
279 update();
280}
281
282// Called when changes to an entry will affect the Store.
283void
284Bootcache::flagForUpdate()
285{
286 m_needsUpdate = true;
287 checkUpdate();
288}
289
290} // namespace PeerFinder
291} // namespace ripple
A version-independent IP address and port combination.
Definition: IPEndpoint.h:39
A generic endpoint for log messages.
Definition: Journal.h:59
map_type::size_type size() const
Returns the number of entries in the cache.
Definition: Bootcache.cpp:49
const_iterator cbegin() const
Definition: Bootcache.cpp:61
const_iterator end() const
Definition: Bootcache.cpp:67
const_iterator cend() const
Definition: Bootcache.cpp:73
Bootcache(Store &store, clock_type &clock, beast::Journal journal)
Definition: Bootcache.cpp:28
map_type::value_type value_type
Definition: Bootcache.h:91
const_iterator begin() const
IP::Endpoint iterators that traverse in decreasing valence.
Definition: Bootcache.cpp:55
bool empty() const
Returns true if the cache is empty.
Definition: Bootcache.cpp:43
void load()
Load the persisted data from the Store into the container.
Definition: Bootcache.cpp:88
Abstract persistence for PeerFinder data.
Definition: Store.h:28
virtual std::size_t load(load_callback const &cb)=0
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
T push_back(T... args)
T reserve(T... args)
Left justifies a field at the specified width.
Definition: iosformat.h:34
beast::IP::Endpoint endpoint
Definition: Store.h:44