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/CanProcess.h>
27#include <xrpl/basics/DecayingSample.h>
28#include <xrpl/basics/Log.h>
29#include <xrpl/beast/container/aged_map.h>
30#include <xrpl/beast/core/LexicalCast.h>
31#include <xrpl/protocol/jss.h>
32
33#include <exception>
34#include <memory>
35#include <mutex>
36#include <vector>
37
38namespace ripple {
39
41{
42private:
45 // measures ledgers per second, constants are important
48
49public:
50 // How long before we try again to acquire the same ledger
51 static constexpr std::chrono::minutes const kReacquireInterval{5};
52
54 Application& app,
55 clock_type& clock,
56 beast::insight::Collector::ptr const& collector,
58 : app_(app)
59 , fetchRate_(clock.now())
60 , j_(app.journal("InboundLedger"))
61 , m_clock(clock)
62 , mRecentFailures(clock)
63 , mCounter(collector->make_counter("ledger_fetches"))
64 , mPeerSetBuilder(std::move(peerSetBuilder))
65 {
66 }
67
71 uint256 const& hash,
72 std::uint32_t seq,
73 InboundLedger::Reason reason) override
74 {
75 auto doAcquire = [&, seq, reason]() -> std::shared_ptr<Ledger const> {
76 XRPL_ASSERT(
77 hash.isNonZero(),
78 "ripple::InboundLedgersImp::acquire::doAcquire : nonzero hash");
79
80 bool const needNetworkLedger = app_.getOPs().isNeedNetworkLedger();
81 bool const shouldAcquire = [&]() {
82 if (!needNetworkLedger)
83 return true;
85 return true;
87 return true;
88 return false;
89 }();
90
92 ss << "InboundLedger::acquire: "
93 << "Request: " << to_string(hash) << ", " << seq
94 << " NeedNetworkLedger: " << (needNetworkLedger ? "yes" : "no")
95 << " Reason: " << to_string(reason)
96 << " Should acquire: " << (shouldAcquire ? "true." : "false.");
97
98 /* Acquiring ledgers is somewhat expensive. It requires lots of
99 * computation and network communication. Avoid it when it's not
100 * appropriate. Every validation from a peer for a ledger that
101 * we do not have locally results in a call to this function: even
102 * if we are moments away from validating the same ledger.
103 */
104 bool const shouldBroadcast = [&]() {
105 // If the node is not in "full" state, it needs to sync to
106 // the network, and doesn't have the necessary tx's and
107 // ledger entries to build the ledger.
108 bool const isFull = app_.getOPs().isFull();
109 // If everything else is ok, don't try to acquire the ledger
110 // if the requested seq is in the near future relative to
111 // the validated ledger. If the requested ledger is between
112 // 1 and 19 inclusive ledgers ahead of the valid ledger this
113 // node has not built it yet, but it's possible/likely it
114 // has the tx's necessary to build it and get caught up.
115 // Plus it might not become validated. On the other hand, if
116 // it's more than 20 in the future, this node should request
117 // it so that it can jump ahead and get caught up.
118 LedgerIndex const validSeq =
120 constexpr std::size_t lagLeeway = 20;
121 bool const nearFuture =
122 (seq > validSeq) && (seq < validSeq + lagLeeway);
123 // If everything else is ok, don't try to acquire the ledger
124 // if the request is related to consensus. (Note that
125 // consensus calls usually pass a seq of 0, so nearFuture
126 // will be false other than on a brand new network.)
127 bool const consensus =
129 ss << " Evaluating whether to broadcast requests to peers"
130 << ". full: " << (isFull ? "true" : "false")
131 << ". ledger sequence " << seq
132 << ". Valid sequence: " << validSeq
133 << ". Lag leeway: " << lagLeeway
134 << ". request for near future ledger: "
135 << (nearFuture ? "true" : "false")
136 << ". Consensus: " << (consensus ? "true" : "false");
137
138 // If the node is not synced, send requests.
139 if (!isFull)
140 return true;
141 // If the ledger is in the near future, do NOT send requests.
142 // This node is probably about to build it.
143 if (nearFuture)
144 return false;
145 // If the request is because of consensus, do NOT send requests.
146 // This node is probably about to build it.
147 if (consensus)
148 return false;
149 return true;
150 }();
151 ss << ". Would broadcast to peers? "
152 << (shouldBroadcast ? "true." : "false.");
153
154 if (!shouldAcquire)
155 {
156 JLOG(j_.debug()) << "Abort(rule): " << ss.str();
157 return {};
158 }
159
160 bool isNew = true;
162 {
164 if (stopping_)
165 {
166 JLOG(j_.debug()) << "Abort(stopping): " << ss.str();
167 return {};
168 }
169
170 auto it = mLedgers.find(hash);
171 if (it != mLedgers.end())
172 {
173 isNew = false;
174 inbound = it->second;
175 }
176 else
177 {
178 inbound = std::make_shared<InboundLedger>(
179 app_,
180 hash,
181 seq,
182 reason,
184 mPeerSetBuilder->build());
185 mLedgers.emplace(hash, inbound);
186 inbound->init(sl);
187 ++mCounter;
188 }
189 }
190 ss << " IsNew: " << (isNew ? "true" : "false");
191
192 if (inbound->isFailed())
193 {
194 JLOG(j_.debug()) << "Abort(failed): " << ss.str();
195 return {};
196 }
197
198 if (!isNew)
199 inbound->update(seq);
200
201 if (!inbound->isComplete())
202 {
203 JLOG(j_.debug()) << "InProgress: " << ss.str();
204 return {};
205 }
206
207 JLOG(j_.debug()) << "Complete: " << ss.str();
208 return inbound->getLedger();
209 };
210 using namespace std::chrono_literals;
212 doAcquire, "InboundLedgersImp::acquire", 500ms, j_);
213 }
214
215 void
217 uint256 const& hash,
218 std::uint32_t seq,
219 InboundLedger::Reason reason) override
220 {
221 if (CanProcess const check{acquiresMutex_, pendingAcquires_, hash})
222 {
223 try
224 {
225 acquire(hash, seq, reason);
226 }
227 catch (std::exception const& e)
228 {
229 JLOG(j_.warn())
230 << "Exception thrown for acquiring new inbound ledger "
231 << hash << ": " << e.what();
232 }
233 catch (...)
234 {
235 JLOG(j_.warn()) << "Unknown exception thrown for acquiring new "
236 "inbound ledger "
237 << hash;
238 }
239 }
240 }
241
243 find(uint256 const& hash) override
244 {
245 XRPL_ASSERT(
246 hash.isNonZero(),
247 "ripple::InboundLedgersImp::find : nonzero input");
248
250
251 {
253
254 auto it = mLedgers.find(hash);
255 if (it != mLedgers.end())
256 {
257 ret = it->second;
258 }
259 }
260
261 return ret;
262 }
263
264 /*
265 This gets called when
266 "We got some data from an inbound ledger"
267
268 inboundLedgerTrigger:
269 "What do we do with this partial data?"
270 Figures out what to do with the responses to our requests for information.
271
272 */
273 // means "We got some data from an inbound ledger"
274
275 // VFALCO TODO Remove the dependency on the Peer object.
278 bool
280 LedgerHash const& hash,
283 {
284 if (auto ledger = find(hash))
285 {
286 JLOG(j_.trace()) << "Got data (" << packet->nodes().size()
287 << ") for acquiring ledger: " << hash;
288
289 // Stash the data for later processing and see if we need to
290 // dispatch
291 if (ledger->gotData(std::weak_ptr<Peer>(peer), packet))
293 jtLEDGER_DATA, "processLedgerData", [ledger]() {
294 ledger->runData();
295 });
296
297 return true;
298 }
299
300 JLOG(j_.trace()) << "Got data for ledger " << hash
301 << " which we're no longer acquiring";
302
303 // If it's state node data, stash it because it still might be
304 // useful.
305 if (packet->type() == protocol::liAS_NODE)
306 {
308 jtLEDGER_DATA, "gotStaleData", [this, packet]() {
309 gotStaleData(packet);
310 });
311 }
312
313 return false;
314 }
315
316 void
317 logFailure(uint256 const& h, std::uint32_t seq) override
318 {
320
321 mRecentFailures.emplace(h, seq);
322 }
323
324 bool
325 isFailure(uint256 const& h) override
326 {
328
330 return mRecentFailures.find(h) != mRecentFailures.end();
331 }
332
339 void
341 {
342 Serializer s;
343 try
344 {
345 for (int i = 0; i < packet_ptr->nodes().size(); ++i)
346 {
347 auto const& node = packet_ptr->nodes(i);
348
349 if (!node.has_nodeid() || !node.has_nodedata())
350 return;
351
352 auto newNode =
353 SHAMapTreeNode::makeFromWire(makeSlice(node.nodedata()));
354
355 if (!newNode)
356 return;
357
358 s.erase();
359 newNode->serializeWithPrefix(s);
360
362 newNode->getHash().as_uint256(),
363 std::make_shared<Blob>(s.begin(), s.end()));
364 }
365 }
366 catch (std::exception const&)
367 {
368 }
369 }
370
371 void
372 clearFailures() override
373 {
375
376 mRecentFailures.clear();
377 mLedgers.clear();
378 }
379
381 fetchRate() override
382 {
384 return 60 * fetchRate_.value(m_clock.now());
385 }
386
387 // Should only be called with an inboundledger that has
388 // a reason of history
389 void
391 {
394 }
395
397 getInfo() override
398 {
400
402
403 {
405
406 acqs.reserve(mLedgers.size());
407 for (auto const& it : mLedgers)
408 {
409 XRPL_ASSERT(
410 it.second,
411 "ripple::InboundLedgersImp::getInfo : non-null ledger");
412 acqs.push_back(it);
413 }
414 for (auto const& it : mRecentFailures)
415 {
416 if (it.second > 1)
417 ret[std::to_string(it.second)][jss::failed] = true;
418 else
419 ret[to_string(it.first)][jss::failed] = true;
420 }
421 }
422
423 for (auto const& it : acqs)
424 {
425 // getJson is expensive, so call without the lock
426 std::uint32_t seq = it.second->getSeq();
427 if (seq > 1)
428 ret[std::to_string(seq)] = it.second->getJson(0);
429 else
430 ret[to_string(it.first)] = it.second->getJson(0);
431 }
432
433 return ret;
434 }
435
436 void
437 gotFetchPack() override
438 {
440 {
442
443 acquires.reserve(mLedgers.size());
444 for (auto const& it : mLedgers)
445 {
446 XRPL_ASSERT(
447 it.second,
448 "ripple::InboundLedgersImp::gotFetchPack : non-null "
449 "ledger");
450 acquires.push_back(it.second);
451 }
452 }
453
454 for (auto const& acquire : acquires)
455 {
456 acquire->checkLocal();
457 }
458 }
459
460 void
461 sweep() override
462 {
463 auto const start = m_clock.now();
464
465 // Make a list of things to sweep, while holding the lock
467 std::size_t total;
468
469 {
471 MapType::iterator it(mLedgers.begin());
472 total = mLedgers.size();
473
474 stuffToSweep.reserve(total);
475
476 while (it != mLedgers.end())
477 {
478 auto const la = it->second->getLastAction();
479
480 if (la > start)
481 {
482 it->second->touch();
483 ++it;
484 }
485 else if ((la + std::chrono::minutes(1)) < start)
486 {
487 stuffToSweep.push_back(it->second);
488 // shouldn't cause the actual final delete
489 // since we are holding a reference in the vector.
490 it = mLedgers.erase(it);
491 }
492 else
493 {
494 ++it;
495 }
496 }
497
499 }
500
501 JLOG(j_.debug())
502 << "Swept " << stuffToSweep.size() << " out of " << total
503 << " inbound ledgers. Duration: "
504 << std::chrono::duration_cast<std::chrono::milliseconds>(
505 m_clock.now() - start)
506 .count()
507 << "ms";
508 }
509
510 void
511 stop() override
512 {
513 ScopedLockType lock(mLock);
514 stopping_ = true;
515 mLedgers.clear();
516 mRecentFailures.clear();
517 }
518
520 cacheSize() override
521 {
522 ScopedLockType lock(mLock);
523 return mLedgers.size();
524 }
525
526private:
528
531
532 bool stopping_ = false;
535
537
539
541
544};
545
546//------------------------------------------------------------------------------
547
550 Application& app,
552 beast::insight::Collector::ptr const& collector)
553{
554 return std::make_unique<InboundLedgersImp>(
555 app, clock, collector, make_PeerSetBuilder(app));
556}
557
558} // namespace ripple
T begin(T... args)
RAII class to check if an Item is already being processed on another thread, as indicated by it's pre...
Definition: CanProcess.h:67
Represents a JSON value.
Definition: json_value.h:147
A generic endpoint for log messages.
Definition: Journal.h:59
Stream debug() const
Definition: Journal.h:317
Stream trace() const
Severity stream access functions.
Definition: Journal.h:311
Stream warn() const
Definition: Journal.h:329
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:166
void addFetchPack(uint256 const &hash, std::shared_ptr< Blob > data)
LedgerIndex getValidLedgerIndex()
virtual bool isFull()=0
virtual bool isNeedNetworkLedger()=0
static std::shared_ptr< SHAMapTreeNode > makeFromWire(Slice rawNode)
Blob::iterator begin()
Definition: Serializer.h:252
Blob::iterator end()
Definition: Serializer.h:257
bool isNonZero() const
Definition: base_uint.h:544
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:43
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:184
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
static bool shouldAcquire(std::uint32_t const currentLedger, std::uint32_t const ledgerHistory, std::optional< LedgerIndex > const minimumOnline, std::uint32_t const candidateLedger, beast::Journal j)
std::unique_ptr< PeerSetBuilder > make_PeerSetBuilder(Application &app)
Definition: PeerSet.cpp:182
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:243
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:629
@ jtLEDGER_DATA
Definition: Job.h:66
STL namespace.
T push_back(T... args)
T ref(T... args)
T reserve(T... args)
T size(T... args)
T str(T... args)
T to_string(T... args)
T what(T... args)