rippled
Loading...
Searching...
No Matches
InboundLedgers.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/app/ledger/InboundLedgers.h>
21#include <xrpld/app/ledger/LedgerMaster.h>
22#include <xrpld/app/main/Application.h>
23#include <xrpld/app/misc/NetworkOPs.h>
24#include <xrpld/core/JobQueue.h>
25#include <xrpld/perflog/PerfLog.h>
26#include <xrpl/basics/DecayingSample.h>
27#include <xrpl/basics/Log.h>
28#include <xrpl/basics/scope.h>
29#include <xrpl/beast/container/aged_map.h>
30#include <xrpl/protocol/jss.h>
31
32#include <exception>
33#include <memory>
34#include <mutex>
35#include <vector>
36
37namespace ripple {
38
40{
41private:
44 // measures ledgers per second, constants are important
47
48public:
49 // How long before we try again to acquire the same ledger
50 static constexpr std::chrono::minutes const kReacquireInterval{5};
51
53 Application& app,
54 clock_type& clock,
55 beast::insight::Collector::ptr const& collector,
57 : app_(app)
58 , fetchRate_(clock.now())
59 , j_(app.journal("InboundLedger"))
60 , m_clock(clock)
61 , mRecentFailures(clock)
62 , mCounter(collector->make_counter("ledger_fetches"))
63 , mPeerSetBuilder(std::move(peerSetBuilder))
64 {
65 }
66
70 uint256 const& hash,
71 std::uint32_t seq,
72 InboundLedger::Reason reason) override
73 {
74 auto doAcquire = [&, seq, reason]() -> std::shared_ptr<Ledger const> {
75 XRPL_ASSERT(
76 hash.isNonZero(),
77 "ripple::InboundLedgersImp::acquire::doAcquire : nonzero hash");
78
79 // probably not the right rule
83 return {};
84
85 bool isNew = true;
87 {
89 if (stopping_)
90 {
91 return {};
92 }
93
94 auto it = mLedgers.find(hash);
95 if (it != mLedgers.end())
96 {
97 isNew = false;
98 inbound = it->second;
99 }
100 else
101 {
102 inbound = std::make_shared<InboundLedger>(
103 app_,
104 hash,
105 seq,
106 reason,
108 mPeerSetBuilder->build());
109 mLedgers.emplace(hash, inbound);
110 inbound->init(sl);
111 ++mCounter;
112 }
113 }
114
115 if (inbound->isFailed())
116 return {};
117
118 if (!isNew)
119 inbound->update(seq);
120
121 if (!inbound->isComplete())
122 return {};
123
124 return inbound->getLedger();
125 };
126 using namespace std::chrono_literals;
128 doAcquire, "InboundLedgersImp::acquire", 500ms, j_);
129
130 return ledger;
131 }
132
133 void
135 uint256 const& hash,
136 std::uint32_t seq,
137 InboundLedger::Reason reason) override
138 {
140 try
141 {
142 if (pendingAcquires_.contains(hash))
143 return;
144 pendingAcquires_.insert(hash);
145 scope_unlock unlock(lock);
146 acquire(hash, seq, reason);
147 }
148 catch (std::exception const& e)
149 {
150 JLOG(j_.warn())
151 << "Exception thrown for acquiring new inbound ledger " << hash
152 << ": " << e.what();
153 }
154 catch (...)
155 {
156 JLOG(j_.warn())
157 << "Unknown exception thrown for acquiring new inbound ledger "
158 << hash;
159 }
160 pendingAcquires_.erase(hash);
161 }
162
164 find(uint256 const& hash) override
165 {
166 XRPL_ASSERT(
167 hash.isNonZero(),
168 "ripple::InboundLedgersImp::find : nonzero input");
169
171
172 {
174
175 auto it = mLedgers.find(hash);
176 if (it != mLedgers.end())
177 {
178 ret = it->second;
179 }
180 }
181
182 return ret;
183 }
184
185 /*
186 This gets called when
187 "We got some data from an inbound ledger"
188
189 inboundLedgerTrigger:
190 "What do we do with this partial data?"
191 Figures out what to do with the responses to our requests for information.
192
193 */
194 // means "We got some data from an inbound ledger"
195
196 // VFALCO TODO Remove the dependency on the Peer object.
199 bool
201 LedgerHash const& hash,
204 {
205 if (auto ledger = find(hash))
206 {
207 JLOG(j_.trace()) << "Got data (" << packet->nodes().size()
208 << ") for acquiring ledger: " << hash;
209
210 // Stash the data for later processing and see if we need to
211 // dispatch
212 if (ledger->gotData(std::weak_ptr<Peer>(peer), packet))
214 jtLEDGER_DATA, "processLedgerData", [ledger]() {
215 ledger->runData();
216 });
217
218 return true;
219 }
220
221 JLOG(j_.trace()) << "Got data for ledger " << hash
222 << " which we're no longer acquiring";
223
224 // If it's state node data, stash it because it still might be
225 // useful.
226 if (packet->type() == protocol::liAS_NODE)
227 {
229 jtLEDGER_DATA, "gotStaleData", [this, packet]() {
230 gotStaleData(packet);
231 });
232 }
233
234 return false;
235 }
236
237 void
238 logFailure(uint256 const& h, std::uint32_t seq) override
239 {
241
242 mRecentFailures.emplace(h, seq);
243 }
244
245 bool
246 isFailure(uint256 const& h) override
247 {
249
251 return mRecentFailures.find(h) != mRecentFailures.end();
252 }
253
260 void
262 {
263 Serializer s;
264 try
265 {
266 for (int i = 0; i < packet_ptr->nodes().size(); ++i)
267 {
268 auto const& node = packet_ptr->nodes(i);
269
270 if (!node.has_nodeid() || !node.has_nodedata())
271 return;
272
273 auto newNode =
274 SHAMapTreeNode::makeFromWire(makeSlice(node.nodedata()));
275
276 if (!newNode)
277 return;
278
279 s.erase();
280 newNode->serializeWithPrefix(s);
281
283 newNode->getHash().as_uint256(),
284 std::make_shared<Blob>(s.begin(), s.end()));
285 }
286 }
287 catch (std::exception const&)
288 {
289 }
290 }
291
292 void
293 clearFailures() override
294 {
296
297 mRecentFailures.clear();
298 mLedgers.clear();
299 }
300
302 fetchRate() override
303 {
305 return 60 * fetchRate_.value(m_clock.now());
306 }
307
308 // Should only be called with an inboundledger that has
309 // a reason of history
310 void
312 {
315 }
316
318 getInfo() override
319 {
321
323
324 {
326
327 acqs.reserve(mLedgers.size());
328 for (auto const& it : mLedgers)
329 {
330 XRPL_ASSERT(
331 it.second,
332 "ripple::InboundLedgersImp::getInfo : non-null ledger");
333 acqs.push_back(it);
334 }
335 for (auto const& it : mRecentFailures)
336 {
337 if (it.second > 1)
338 ret[std::to_string(it.second)][jss::failed] = true;
339 else
340 ret[to_string(it.first)][jss::failed] = true;
341 }
342 }
343
344 for (auto const& it : acqs)
345 {
346 // getJson is expensive, so call without the lock
347 std::uint32_t seq = it.second->getSeq();
348 if (seq > 1)
349 ret[std::to_string(seq)] = it.second->getJson(0);
350 else
351 ret[to_string(it.first)] = it.second->getJson(0);
352 }
353
354 return ret;
355 }
356
357 void
358 gotFetchPack() override
359 {
361 {
363
364 acquires.reserve(mLedgers.size());
365 for (auto const& it : mLedgers)
366 {
367 XRPL_ASSERT(
368 it.second,
369 "ripple::InboundLedgersImp::gotFetchPack : non-null "
370 "ledger");
371 acquires.push_back(it.second);
372 }
373 }
374
375 for (auto const& acquire : acquires)
376 {
377 acquire->checkLocal();
378 }
379 }
380
381 void
382 sweep() override
383 {
384 auto const start = m_clock.now();
385
386 // Make a list of things to sweep, while holding the lock
388 std::size_t total;
389
390 {
392 MapType::iterator it(mLedgers.begin());
393 total = mLedgers.size();
394
395 stuffToSweep.reserve(total);
396
397 while (it != mLedgers.end())
398 {
399 auto const la = it->second->getLastAction();
400
401 if (la > start)
402 {
403 it->second->touch();
404 ++it;
405 }
406 else if ((la + std::chrono::minutes(1)) < start)
407 {
408 stuffToSweep.push_back(it->second);
409 // shouldn't cause the actual final delete
410 // since we are holding a reference in the vector.
411 it = mLedgers.erase(it);
412 }
413 else
414 {
415 ++it;
416 }
417 }
418
420 }
421
422 JLOG(j_.debug())
423 << "Swept " << stuffToSweep.size() << " out of " << total
424 << " inbound ledgers. Duration: "
425 << std::chrono::duration_cast<std::chrono::milliseconds>(
426 m_clock.now() - start)
427 .count()
428 << "ms";
429 }
430
431 void
432 stop() override
433 {
434 ScopedLockType lock(mLock);
435 stopping_ = true;
436 mLedgers.clear();
437 mRecentFailures.clear();
438 }
439
441 cacheSize() override
442 {
443 ScopedLockType lock(mLock);
444 return mLedgers.size();
445 }
446
447private:
449
452
453 bool stopping_ = false;
456
458
460
462
465};
466
467//------------------------------------------------------------------------------
468
471 Application& app,
473 beast::insight::Collector::ptr const& collector)
474{
475 return std::make_unique<InboundLedgersImp>(
476 app, clock, collector, make_PeerSetBuilder(app));
477}
478
479} // namespace ripple
T begin(T... args)
Represents a JSON value.
Definition: json_value.h:148
A generic endpoint for log messages.
Definition: Journal.h:60
Stream debug() const
Definition: Journal.h:328
Stream trace() const
Severity stream access functions.
Definition: Journal.h:322
Stream warn() const
Definition: Journal.h:340
virtual time_point now() const =0
Returns the current time.
Associative container where each element is also indexed by time.
A metric for measuring an integral value.
Definition: Counter.h:39
virtual JobQueue & getJobQueue()=0
virtual NetworkOPs & getOPs()=0
virtual LedgerMaster & getLedgerMaster()=0
Sampling function using exponential decay to provide a continuous value.
double value(time_point now)
void add(double value, time_point now)
DecayWindow< 30, clock_type > fetchRate_
std::size_t cacheSize() override
std::recursive_mutex mLock
Json::Value getInfo() override
beast::Journal const j_
void logFailure(uint256 const &h, std::uint32_t seq) override
void gotStaleData(std::shared_ptr< protocol::TMLedgerData > packet_ptr) override
We got some data for a ledger we are no longer acquiring Since we paid the price to receive it,...
beast::aged_map< uint256, std::uint32_t > mRecentFailures
std::set< uint256 > pendingAcquires_
beast::insight::Counter mCounter
std::unique_ptr< PeerSetBuilder > mPeerSetBuilder
std::shared_ptr< Ledger const > acquire(uint256 const &hash, std::uint32_t seq, InboundLedger::Reason reason) override
void acquireAsync(uint256 const &hash, std::uint32_t seq, InboundLedger::Reason reason) override
InboundLedgersImp(Application &app, clock_type &clock, beast::insight::Collector::ptr const &collector, std::unique_ptr< PeerSetBuilder > peerSetBuilder)
static constexpr std::chrono::minutes const kReacquireInterval
std::size_t fetchRate() override
Returns the rate of historical ledger fetches per minute.
std::shared_ptr< InboundLedger > find(uint256 const &hash) override
bool gotLedgerData(LedgerHash const &hash, std::shared_ptr< Peer > peer, std::shared_ptr< protocol::TMLedgerData > packet) override
We received a TMLedgerData from a peer.
void onLedgerFetched() override
Called when a complete ledger is obtained.
bool isFailure(uint256 const &h) override
Manages the lifetime of inbound ledgers.
bool addJob(JobType type, std::string const &name, JobHandler &&jobHandler)
Adds a job to the JobQueue.
Definition: JobQueue.h:165
void addFetchPack(uint256 const &hash, std::shared_ptr< Blob > data)
virtual bool isNeedNetworkLedger()=0
static std::shared_ptr< SHAMapTreeNode > makeFromWire(Slice rawNode)
Blob::iterator begin()
Definition: Serializer.h:253
Blob::iterator end()
Definition: Serializer.h:258
bool isNonZero() const
Definition: base_uint.h:545
Automatically unlocks and re-locks a unique_lock object.
Definition: scope.h:231
T clear(T... args)
T emplace(T... args)
T end(T... args)
T erase(T... args)
T find(T... args)
@ objectValue
object value (collection of name/value pairs).
Definition: json_value.h:44
std::enable_if< is_aged_container< AgedContainer >::value, std::size_t >::type expire(AgedContainer &c, std::chrono::duration< Rep, Period > const &age)
Expire aged container items past the specified age.
auto measureDurationAndLog(Func &&func, const std::string &actionDescription, std::chrono::duration< Rep, Period > maxDelay, const beast::Journal &journal)
Definition: PerfLog.h:186
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
std::unique_ptr< PeerSetBuilder > make_PeerSetBuilder(Application &app)
Definition: PeerSet.cpp:144
std::unique_ptr< InboundLedgers > make_InboundLedgers(Application &app, InboundLedgers::clock_type &clock, beast::insight::Collector::ptr const &collector)
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition: Slice.h:244
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
@ jtLEDGER_DATA
Definition: Job.h:65
STL namespace.
T push_back(T... args)
T ref(T... args)
T reserve(T... args)
T size(T... args)
T to_string(T... args)
T what(T... args)