rippled
Loading...
Searching...
No Matches
InboundLedgers.cpp
1#include <xrpld/app/ledger/InboundLedgers.h>
2#include <xrpld/app/ledger/LedgerMaster.h>
3#include <xrpld/app/main/Application.h>
4#include <xrpld/app/misc/NetworkOPs.h>
5#include <xrpld/core/JobQueue.h>
6#include <xrpld/perflog/PerfLog.h>
7
8#include <xrpl/basics/DecayingSample.h>
9#include <xrpl/basics/Log.h>
10#include <xrpl/basics/scope.h>
11#include <xrpl/beast/container/aged_map.h>
12#include <xrpl/protocol/jss.h>
13
14#include <exception>
15#include <memory>
16#include <mutex>
17#include <vector>
18
19namespace ripple {
20
22{
23private:
26 // measures ledgers per second, constants are important
29
30public:
31 // How long before we try again to acquire the same ledger
32 static constexpr std::chrono::minutes const kReacquireInterval{5};
33
35 Application& app,
36 clock_type& clock,
37 beast::insight::Collector::ptr const& collector,
39 : app_(app)
40 , fetchRate_(clock.now())
41 , j_(app.journal("InboundLedger"))
42 , m_clock(clock)
43 , mRecentFailures(clock)
44 , mCounter(collector->make_counter("ledger_fetches"))
45 , mPeerSetBuilder(std::move(peerSetBuilder))
46 {
47 }
48
52 uint256 const& hash,
53 std::uint32_t seq,
54 InboundLedger::Reason reason) override
55 {
56 auto doAcquire = [&, seq, reason]() -> std::shared_ptr<Ledger const> {
57 XRPL_ASSERT(
58 hash.isNonZero(),
59 "ripple::InboundLedgersImp::acquire::doAcquire : nonzero hash");
60
61 // probably not the right rule
65 return {};
66
67 bool isNew = true;
69 {
71 if (stopping_)
72 {
73 return {};
74 }
75
76 auto it = mLedgers.find(hash);
77 if (it != mLedgers.end())
78 {
79 isNew = false;
80 inbound = it->second;
81 }
82 else
83 {
85 app_,
86 hash,
87 seq,
88 reason,
90 mPeerSetBuilder->build());
91 mLedgers.emplace(hash, inbound);
92 inbound->init(sl);
93 ++mCounter;
94 }
95 }
96
97 if (inbound->isFailed())
98 return {};
99
100 if (!isNew)
101 inbound->update(seq);
102
103 if (!inbound->isComplete())
104 return {};
105
106 return inbound->getLedger();
107 };
108 using namespace std::chrono_literals;
110 doAcquire, "InboundLedgersImp::acquire", 500ms, j_);
111
112 return ledger;
113 }
114
115 void
117 uint256 const& hash,
118 std::uint32_t seq,
119 InboundLedger::Reason reason) override
120 {
122 try
123 {
124 if (pendingAcquires_.contains(hash))
125 return;
126 pendingAcquires_.insert(hash);
127 scope_unlock unlock(lock);
128 acquire(hash, seq, reason);
129 }
130 catch (std::exception const& e)
131 {
132 JLOG(j_.warn())
133 << "Exception thrown for acquiring new inbound ledger " << hash
134 << ": " << e.what();
135 }
136 catch (...)
137 {
138 JLOG(j_.warn())
139 << "Unknown exception thrown for acquiring new inbound ledger "
140 << hash;
141 }
142 pendingAcquires_.erase(hash);
143 }
144
146 find(uint256 const& hash) override
147 {
148 XRPL_ASSERT(
149 hash.isNonZero(),
150 "ripple::InboundLedgersImp::find : nonzero input");
151
153
154 {
156
157 auto it = mLedgers.find(hash);
158 if (it != mLedgers.end())
159 {
160 ret = it->second;
161 }
162 }
163
164 return ret;
165 }
166
167 /*
168 This gets called when
169 "We got some data from an inbound ledger"
170
171 inboundLedgerTrigger:
172 "What do we do with this partial data?"
173 Figures out what to do with the responses to our requests for information.
174
175 */
176 // means "We got some data from an inbound ledger"
177
178 // VFALCO TODO Remove the dependency on the Peer object.
181 bool
183 LedgerHash const& hash,
186 {
187 if (auto ledger = find(hash))
188 {
189 JLOG(j_.trace()) << "Got data (" << packet->nodes().size()
190 << ") for acquiring ledger: " << hash;
191
192 // Stash the data for later processing and see if we need to
193 // dispatch
194 if (ledger->gotData(std::weak_ptr<Peer>(peer), packet))
196 jtLEDGER_DATA, "processLedgerData", [ledger]() {
197 ledger->runData();
198 });
199
200 return true;
201 }
202
203 JLOG(j_.trace()) << "Got data for ledger " << hash
204 << " which we're no longer acquiring";
205
206 // If it's state node data, stash it because it still might be
207 // useful.
208 if (packet->type() == protocol::liAS_NODE)
209 {
211 jtLEDGER_DATA, "gotStaleData", [this, packet]() {
212 gotStaleData(packet);
213 });
214 }
215
216 return false;
217 }
218
219 void
220 logFailure(uint256 const& h, std::uint32_t seq) override
221 {
223
224 mRecentFailures.emplace(h, seq);
225 }
226
227 bool
228 isFailure(uint256 const& h) override
229 {
231
233 return mRecentFailures.find(h) != mRecentFailures.end();
234 }
235
242 void
244 {
245 Serializer s;
246 try
247 {
248 for (int i = 0; i < packet_ptr->nodes().size(); ++i)
249 {
250 auto const& node = packet_ptr->nodes(i);
251
252 if (!node.has_nodeid() || !node.has_nodedata())
253 return;
254
255 auto newNode =
256 SHAMapTreeNode::makeFromWire(makeSlice(node.nodedata()));
257
258 if (!newNode)
259 return;
260
261 s.erase();
262 newNode->serializeWithPrefix(s);
263
265 newNode->getHash().as_uint256(),
267 }
268 }
269 catch (std::exception const&)
270 {
271 }
272 }
273
274 void
275 clearFailures() override
276 {
278
279 mRecentFailures.clear();
280 mLedgers.clear();
281 }
282
284 fetchRate() override
285 {
287 return 60 * fetchRate_.value(m_clock.now());
288 }
289
290 // Should only be called with an inboundledger that has
291 // a reason of history
292 void
294 {
297 }
298
300 getInfo() override
301 {
303
305
306 {
308
309 acqs.reserve(mLedgers.size());
310 for (auto const& it : mLedgers)
311 {
312 XRPL_ASSERT(
313 it.second,
314 "ripple::InboundLedgersImp::getInfo : non-null ledger");
315 acqs.push_back(it);
316 }
317 for (auto const& it : mRecentFailures)
318 {
319 if (it.second > 1)
320 ret[std::to_string(it.second)][jss::failed] = true;
321 else
322 ret[to_string(it.first)][jss::failed] = true;
323 }
324 }
325
326 for (auto const& it : acqs)
327 {
328 // getJson is expensive, so call without the lock
329 std::uint32_t seq = it.second->getSeq();
330 if (seq > 1)
331 ret[std::to_string(seq)] = it.second->getJson(0);
332 else
333 ret[to_string(it.first)] = it.second->getJson(0);
334 }
335
336 return ret;
337 }
338
339 void
340 gotFetchPack() override
341 {
343 {
345
346 acquires.reserve(mLedgers.size());
347 for (auto const& it : mLedgers)
348 {
349 XRPL_ASSERT(
350 it.second,
351 "ripple::InboundLedgersImp::gotFetchPack : non-null "
352 "ledger");
353 acquires.push_back(it.second);
354 }
355 }
356
357 for (auto const& acquire : acquires)
358 {
359 acquire->checkLocal();
360 }
361 }
362
363 void
364 sweep() override
365 {
366 auto const start = m_clock.now();
367
368 // Make a list of things to sweep, while holding the lock
370 std::size_t total;
371
372 {
374 MapType::iterator it(mLedgers.begin());
375 total = mLedgers.size();
376
377 stuffToSweep.reserve(total);
378
379 while (it != mLedgers.end())
380 {
381 auto const la = it->second->getLastAction();
382
383 if (la > start)
384 {
385 it->second->touch();
386 ++it;
387 }
388 else if ((la + std::chrono::minutes(1)) < start)
389 {
390 stuffToSweep.push_back(it->second);
391 // shouldn't cause the actual final delete
392 // since we are holding a reference in the vector.
393 it = mLedgers.erase(it);
394 }
395 else
396 {
397 ++it;
398 }
399 }
400
402 }
403
404 JLOG(j_.debug())
405 << "Swept " << stuffToSweep.size() << " out of " << total
406 << " inbound ledgers. Duration: "
407 << std::chrono::duration_cast<std::chrono::milliseconds>(
408 m_clock.now() - start)
409 .count()
410 << "ms";
411 }
412
413 void
414 stop() override
415 {
416 ScopedLockType lock(mLock);
417 stopping_ = true;
418 mLedgers.clear();
419 mRecentFailures.clear();
420 }
421
423 cacheSize() override
424 {
425 ScopedLockType lock(mLock);
426 return mLedgers.size();
427 }
428
429private:
431
434
435 bool stopping_ = false;
438
440
442
444
447};
448
449//------------------------------------------------------------------------------
450
453 Application& app,
455 beast::insight::Collector::ptr const& collector)
456{
458 app, clock, collector, make_PeerSetBuilder(app));
459}
460
461} // namespace ripple
T begin(T... args)
Represents a JSON value.
Definition json_value.h:131
A generic endpoint for log messages.
Definition Journal.h:41
Stream debug() const
Definition Journal.h:309
Stream trace() const
Severity stream access functions.
Definition Journal.h:303
Stream warn() const
Definition Journal.h:321
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:20
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:149
void addFetchPack(uint256 const &hash, std::shared_ptr< Blob > data)
virtual bool isNeedNetworkLedger()=0
static intr_ptr::SharedPtr< SHAMapTreeNode > makeFromWire(Slice rawNode)
Blob::iterator begin()
Definition Serializer.h:233
Blob::iterator end()
Definition Serializer.h:238
bool isNonZero() const
Definition base_uint.h:526
Automatically unlocks and re-locks a unique_lock object.
Definition scope.h:212
T clear(T... args)
T emplace(T... args)
T end(T... args)
T erase(T... args)
T find(T... args)
T is_same_v
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:27
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, std::string const &actionDescription, std::chrono::duration< Rep, Period > maxDelay, beast::Journal const &journal)
Definition PerfLog.h:168
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
std::unique_ptr< PeerSetBuilder > make_PeerSetBuilder(Application &app)
Definition PeerSet.cpp:125
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:225
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:611
@ jtLEDGER_DATA
Definition Job.h:47
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)