xrpld
Loading...
Searching...
No Matches
InboundLedgers.cpp
1#include <xrpld/app/ledger/InboundLedgers.h>
2
3#include <xrpld/app/ledger/InboundLedger.h>
4#include <xrpld/app/ledger/LedgerMaster.h>
5#include <xrpld/app/ledger/LedgerNodeHelpers.h>
6#include <xrpld/app/main/Application.h>
7#include <xrpld/overlay/PeerSet.h>
8
9#include <xrpl/basics/Blob.h>
10#include <xrpl/basics/DecayingSample.h>
11#include <xrpl/basics/Log.h>
12#include <xrpl/basics/UnorderedContainers.h>
13#include <xrpl/basics/base_uint.h>
14#include <xrpl/basics/scope.h>
15#include <xrpl/beast/container/aged_map.h>
16#include <xrpl/beast/container/detail/aged_ordered_container.h>
17#include <xrpl/beast/insight/Collector.h>
18#include <xrpl/beast/utility/instrumentation.h>
19#include <xrpl/core/Job.h>
20#include <xrpl/core/JobQueue.h>
21#include <xrpl/core/PerfLog.h>
22#include <xrpl/json/json_value.h>
23#include <xrpl/protocol/RippleLedgerHash.h>
24#include <xrpl/protocol/Serializer.h>
25#include <xrpl/protocol/jss.h>
26#include <xrpl/server/NetworkOPs.h>
27#include <xrpl/shamap/SHAMapTreeNode.h>
28
29#include <xrpl.pb.h>
30
31#include <chrono>
32#include <cstddef>
33#include <cstdint>
34#include <exception>
35#include <functional>
36#include <memory>
37#include <mutex>
38#include <set>
39#include <string>
40#include <utility>
41#include <vector>
42
43namespace xrpl {
44
46{
47private:
50 // measures ledgers per second, constants are important
53
54public:
55 // How long before we try again to acquire the same ledger
57
59 Application& app,
60 clock_type& clock,
61 beast::insight::Collector::ptr const& collector,
63 : app_(app)
64 , fetchRate_(clock.now())
65 , j_(app.getJournal("InboundLedger"))
66 , clock_(clock)
67 , recentFailures_(clock)
68 , counter_(collector->makeCounter("ledger_fetches"))
69 , peerSetBuilder_(std::move(peerSetBuilder))
70 {
71 }
72
77 acquire(uint256 const& hash, std::uint32_t seq, InboundLedger::Reason reason) override
78 {
79 auto doAcquire = [&, seq, reason]() -> std::shared_ptr<Ledger const> {
80 XRPL_ASSERT(
81 hash.isNonZero(), "xrpl::InboundLedgersImp::acquire::doAcquire : nonzero hash");
82
83 // probably not the right rule
84 if (app_.getOPs().isNeedNetworkLedger() && (reason != InboundLedger::Reason::GENERIC) &&
86 return {};
87
88 bool isNew = true;
90 {
92 if (stopping_)
93 {
94 return {};
95 }
96
97 auto it = ledgers_.find(hash);
98 if (it != ledgers_.end())
99 {
100 isNew = false;
101 inbound = it->second;
102 }
103 else
104 {
106 app_, hash, seq, reason, std::ref(clock_), peerSetBuilder_->build());
107 ledgers_.emplace(hash, inbound);
108 inbound->init(sl);
109 ++counter_;
110 }
111 }
112
113 if (inbound->isFailed())
114 return {};
115
116 if (!isNew)
117 inbound->update(seq);
118
119 if (!inbound->isComplete())
120 return {};
121
122 return inbound->getLedger();
123 };
124 using namespace std::chrono_literals;
126 perf::measureDurationAndLog(doAcquire, "InboundLedgersImp::acquire", 500ms, j_);
127
128 return ledger;
129 }
130
131 void
132 acquireAsync(uint256 const& hash, std::uint32_t seq, InboundLedger::Reason reason) override
133 {
135 try
136 {
137 if (pendingAcquires_.contains(hash))
138 return;
139 pendingAcquires_.insert(hash);
140 ScopeUnlock const unlock(lock);
141 acquire(hash, seq, reason);
142 }
143 catch (std::exception const& e)
144 {
145 JLOG(j_.warn()) << "Exception thrown for acquiring new inbound ledger " << hash << ": "
146 << e.what();
147 }
148 catch (...)
149 {
150 JLOG(j_.warn()) << "Unknown exception thrown for acquiring new inbound ledger " << hash;
151 }
152 pendingAcquires_.erase(hash);
153 }
154
156 find(uint256 const& hash) override
157 {
158 XRPL_ASSERT(hash.isNonZero(), "xrpl::InboundLedgersImp::find : nonzero input");
159
161
162 {
163 ScopedLockType const sl(lock_);
164
165 auto it = ledgers_.find(hash);
166 if (it != ledgers_.end())
167 {
168 ret = it->second;
169 }
170 }
171
172 return ret;
173 }
174
175 /*
176 This gets called when
177 "We got some data from an inbound ledger"
178
179 inboundLedgerTrigger:
180 "What do we do with this partial data?"
181 Figures out what to do with the responses to our requests for information.
182
183 */
184 // means "We got some data from an inbound ledger"
185
186 // VFALCO TODO Remove the dependency on the Peer object.
190 bool
192 LedgerHash const& hash,
195 {
196 if (auto ledger = find(hash))
197 {
198 JLOG(j_.trace()) << "Got data (" << packet->nodes().size()
199 << ") for acquiring ledger: " << hash;
200
201 // Stash the data for later processing and see if we need to
202 // dispatch
203 if (ledger->gotData(std::weak_ptr<Peer>(peer), packet))
204 {
205 app_.getJobQueue().addJob(
206 JtLedgerData, "ProcessLData", [ledger]() { ledger->runData(); });
207 }
208
209 return true;
210 }
211
212 JLOG(j_.trace()) << "Got data for ledger " << hash << " which we're no longer acquiring";
213
214 // If it's state node data, stash it because it still might be
215 // useful.
216 if (packet->type() == protocol::liAS_NODE)
217 {
218 app_.getJobQueue().addJob(
219 JtLedgerData, "GotStaleData", [this, packet]() { gotStaleData(packet); });
220 }
221
222 return false;
223 }
224
225 void
226 logFailure(uint256 const& h, std::uint32_t seq) override
227 {
228 ScopedLockType const sl(lock_);
229
230 recentFailures_.emplace(h, seq);
231 }
232
233 bool
234 isFailure(uint256 const& h) override
235 {
236 ScopedLockType const sl(lock_);
237
239 return recentFailures_.find(h) != recentFailures_.end();
240 }
241
249 void
251 {
252 Serializer s;
253 try
254 {
255 for (auto const& ledgerNode : packetPtr->nodes())
256 {
257 auto const treeNode = getTreeNode(ledgerNode.nodedata());
258 if (!treeNode)
259 return;
260
261 s.erase();
262 treeNode->serializeWithPrefix(s);
263
264 app_.getLedgerMaster().addFetchPack(
265 treeNode->getHash().asUInt256(), std::make_shared<Blob>(s.begin(), s.end()));
266 }
267 }
268 catch (std::exception const&) // NOLINT(bugprone-empty-catch)
269 {
270 }
271 }
272
273 void
274 clearFailures() override
275 {
276 ScopedLockType const sl(lock_);
277
278 recentFailures_.clear();
279 ledgers_.clear();
280 }
281
283 fetchRate() override
284 {
286 return 60 * fetchRate_.value(clock_.now());
287 }
288
289 // Should only be called with an inboundledger that has
290 // a reason of history
291 void
293 {
295 fetchRate_.add(1, clock_.now());
296 }
297
299 getInfo() override
300 {
302
304
305 {
306 ScopedLockType const sl(lock_);
307
308 acqs.reserve(ledgers_.size());
309 for (auto const& it : ledgers_)
310 {
311 XRPL_ASSERT(it.second, "xrpl::InboundLedgersImp::getInfo : non-null ledger");
312 acqs.emplace_back(it);
313 }
314 for (auto const& it : recentFailures_)
315 {
316 if (it.second > 1)
317 {
318 ret[std::to_string(it.second)][jss::failed] = true;
319 }
320 else
321 {
322 ret[to_string(it.first)][jss::failed] = true;
323 }
324 }
325 }
326
327 for (auto const& it : acqs)
328 {
329 // getJson is expensive, so call without the lock
330 std::uint32_t const seq = it.second->getSeq();
331 if (seq > 1)
332 {
333 ret[std::to_string(seq)] = it.second->getJson(0);
334 }
335 else
336 {
337 ret[to_string(it.first)] = it.second->getJson(0);
338 }
339 }
340
341 return ret;
342 }
343
344 void
345 gotFetchPack() override
346 {
348 {
349 ScopedLockType const sl(lock_);
350
351 acquires.reserve(ledgers_.size());
352 for (auto const& it : ledgers_)
353 {
354 XRPL_ASSERT(
355 it.second,
356 "xrpl::InboundLedgersImp::gotFetchPack : non-null "
357 "ledger");
358 acquires.push_back(it.second);
359 }
360 }
361
362 for (auto const& acquire : acquires)
363 {
364 acquire->checkLocal();
365 }
366 }
367
368 void
369 sweep() override
370 {
371 auto const start = clock_.now();
372
373 // Make a list of things to sweep, while holding the lock
375 std::size_t total = 0;
376
377 {
378 ScopedLockType const sl(lock_);
379 auto it = ledgers_.begin();
380 total = ledgers_.size();
381
382 stuffToSweep.reserve(total);
383
384 while (it != ledgers_.end())
385 {
386 auto const la = it->second->getLastAction();
387
388 if (la > start)
389 {
390 it->second->touch();
391 ++it;
392 }
393 else if ((la + std::chrono::minutes(1)) < start)
394 {
395 stuffToSweep.push_back(it->second);
396 // shouldn't cause the actual final delete
397 // since we are holding a reference in the vector.
398 it = ledgers_.erase(it);
399 }
400 else
401 {
402 ++it;
403 }
404 }
405
407 }
408
409 JLOG(j_.debug())
410 << "Swept " << stuffToSweep.size() << " out of " << total
411 << " inbound ledgers. Duration: "
413 << "ms";
414 }
415
416 void
417 stop() override
418 {
419 ScopedLockType const lock(lock_);
420 stopping_ = true;
421 ledgers_.clear();
422 recentFailures_.clear();
423 }
424
426 cacheSize() override
427 {
428 ScopedLockType const lock(lock_);
429 return ledgers_.size();
430 }
431
432private:
434
437
438 bool stopping_ = false;
441
443
445
447
450};
451
452//------------------------------------------------------------------------------
453
456 Application& app,
458 beast::insight::Collector::ptr const& collector)
459{
460 return std::make_unique<InboundLedgersImp>(app, clock, collector, makePeerSetBuilder(app));
461}
462
463} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:44
std::shared_ptr< Collector > ptr
Definition Collector.h:29
A metric for measuring an integral value.
Definition Counter.h:20
Represents a JSON value.
Definition json_value.h:117
bool isNonZero() const
Definition base_uint.h:567
Sampling function using exponential decay to provide a continuous value.
InboundLedgersImp(Application &app, clock_type &clock, beast::insight::Collector::ptr const &collector, std::unique_ptr< PeerSetBuilder > peerSetBuilder)
void gotStaleData(std::shared_ptr< protocol::TMLedgerData > packetPtr) override
We got some data for a ledger we are no longer acquiring Since we paid the price to receive it,...
std::recursive_mutex lock_
static constexpr std::chrono::minutes kReacquireInterval
void onLedgerFetched() override
Called when a complete ledger is obtained.
std::shared_ptr< Ledger const > acquire(uint256 const &hash, std::uint32_t seq, InboundLedger::Reason reason) override
DecayWindow< 30, clock_type > fetchRate_
hash_map< uint256, std::shared_ptr< InboundLedger > > MapType
std::unique_lock< std::recursive_mutex > ScopedLockType
void acquireAsync(uint256 const &hash, std::uint32_t seq, InboundLedger::Reason reason) override
std::size_t fetchRate() override
Returns the rate of historical ledger fetches per minute.
std::set< uint256 > pendingAcquires_
beast::aged_map< uint256, std::uint32_t > recentFailures_
bool gotLedgerData(LedgerHash const &hash, std::shared_ptr< Peer > peer, std::shared_ptr< protocol::TMLedgerData > packet) override
We received a TMLedgerData from a peer.
beast::Journal const j_
json::Value getInfo() override
std::unique_ptr< PeerSetBuilder > peerSetBuilder_
beast::insight::Counter counter_
std::size_t cacheSize() override
void clearFailures() override
bool isFailure(uint256 const &h) override
void logFailure(uint256 const &h, std::uint32_t seq) override
std::shared_ptr< InboundLedger > find(uint256 const &hash) override
Manages the lifetime of inbound ledgers.
beast::AbstractClock< std::chrono::steady_clock > clock_type
Automatically unlocks and re-locks a unique_lock object.
Definition scope.h:197
Blob::iterator begin()
Definition Serializer.h:227
Blob::iterator end()
Definition Serializer.h:232
T duration_cast(T... args)
T emplace_back(T... args)
T make_shared(T... args)
T make_unique(T... args)
std::size_t expire(AgedContainer &c, std::chrono::duration< Rep, Period > const &age)
Expire aged container items past the specified age.
detail::AgedOrderedContainer< false, true, Key, T, Clock, Compare, Allocator > aged_map
Definition aged_map.h:18
@ Object
object value (collection of name/value pairs).
Definition json_value.h:29
STL namespace.
auto measureDurationAndLog(Func &&func, std::string const &actionDescription, std::chrono::duration< Rep, Period > maxDelay, beast::Journal const &journal)
Definition PerfLog.h:162
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:651
SHAMapTreeNodePtr getTreeNode(std::string_view data)
Deserializes a SHAMapTreeNode from wire format data.
@ JtLedgerData
Definition Job.h:52
uint256 LedgerHash
std::unique_ptr< PeerSetBuilder > makePeerSetBuilder(Application &app)
Definition PeerSet.cpp:141
std::unique_ptr< InboundLedgers > makeInboundLedgers(Application &app, InboundLedgers::clock_type &clock, beast::insight::Collector::ptr const &collector)
std::unordered_map< Key, Value, Hash, Pred, Allocator > hash_map
BaseUInt< 256 > uint256
Definition base_uint.h:580
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)