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
6#include <xrpl/basics/DecayingSample.h>
7#include <xrpl/basics/Log.h>
8#include <xrpl/basics/scope.h>
9#include <xrpl/beast/container/aged_map.h>
10#include <xrpl/core/JobQueue.h>
11#include <xrpl/core/PerfLog.h>
12#include <xrpl/protocol/jss.h>
13
14#include <exception>
15#include <memory>
16#include <mutex>
17#include <vector>
18
19namespace xrpl {
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
51 acquire(uint256 const& hash, std::uint32_t seq, InboundLedger::Reason reason) override
52 {
53 auto doAcquire = [&, seq, reason]() -> std::shared_ptr<Ledger const> {
54 XRPL_ASSERT(hash.isNonZero(), "xrpl::InboundLedgersImp::acquire::doAcquire : nonzero hash");
55
56 // probably not the right rule
59 return {};
60
61 bool isNew = true;
63 {
65 if (stopping_)
66 {
67 return {};
68 }
69
70 auto it = mLedgers.find(hash);
71 if (it != mLedgers.end())
72 {
73 isNew = false;
74 inbound = it->second;
75 }
76 else
77 {
79 app_, hash, seq, reason, std::ref(m_clock), mPeerSetBuilder->build());
80 mLedgers.emplace(hash, inbound);
81 inbound->init(sl);
82 ++mCounter;
83 }
84 }
85
86 if (inbound->isFailed())
87 return {};
88
89 if (!isNew)
90 inbound->update(seq);
91
92 if (!inbound->isComplete())
93 return {};
94
95 return inbound->getLedger();
96 };
97 using namespace std::chrono_literals;
99 perf::measureDurationAndLog(doAcquire, "InboundLedgersImp::acquire", 500ms, j_);
100
101 return ledger;
102 }
103
104 void
105 acquireAsync(uint256 const& hash, std::uint32_t seq, InboundLedger::Reason reason) override
106 {
108 try
109 {
110 if (pendingAcquires_.contains(hash))
111 return;
112 pendingAcquires_.insert(hash);
113 scope_unlock unlock(lock);
114 acquire(hash, seq, reason);
115 }
116 catch (std::exception const& e)
117 {
118 JLOG(j_.warn()) << "Exception thrown for acquiring new inbound ledger " << hash << ": " << e.what();
119 }
120 catch (...)
121 {
122 JLOG(j_.warn()) << "Unknown exception thrown for acquiring new inbound ledger " << hash;
123 }
124 pendingAcquires_.erase(hash);
125 }
126
128 find(uint256 const& hash) override
129 {
130 XRPL_ASSERT(hash.isNonZero(), "xrpl::InboundLedgersImp::find : nonzero input");
131
133
134 {
136
137 auto it = mLedgers.find(hash);
138 if (it != mLedgers.end())
139 {
140 ret = it->second;
141 }
142 }
143
144 return ret;
145 }
146
147 /*
148 This gets called when
149 "We got some data from an inbound ledger"
150
151 inboundLedgerTrigger:
152 "What do we do with this partial data?"
153 Figures out what to do with the responses to our requests for information.
154
155 */
156 // means "We got some data from an inbound ledger"
157
158 // VFALCO TODO Remove the dependency on the Peer object.
161 bool
163 override
164 {
165 if (auto ledger = find(hash))
166 {
167 JLOG(j_.trace()) << "Got data (" << packet->nodes().size() << ") for acquiring ledger: " << hash;
168
169 // Stash the data for later processing and see if we need to
170 // dispatch
171 if (ledger->gotData(std::weak_ptr<Peer>(peer), packet))
172 app_.getJobQueue().addJob(jtLEDGER_DATA, "ProcessLData", [ledger]() { ledger->runData(); });
173
174 return true;
175 }
176
177 JLOG(j_.trace()) << "Got data for ledger " << hash << " which we're no longer acquiring";
178
179 // If it's state node data, stash it because it still might be
180 // useful.
181 if (packet->type() == protocol::liAS_NODE)
182 {
183 app_.getJobQueue().addJob(jtLEDGER_DATA, "GotStaleData", [this, packet]() { gotStaleData(packet); });
184 }
185
186 return false;
187 }
188
189 void
190 logFailure(uint256 const& h, std::uint32_t seq) override
191 {
193
194 mRecentFailures.emplace(h, seq);
195 }
196
197 bool
198 isFailure(uint256 const& h) override
199 {
201
203 return mRecentFailures.find(h) != mRecentFailures.end();
204 }
205
212 void
214 {
215 Serializer s;
216 try
217 {
218 for (int i = 0; i < packet_ptr->nodes().size(); ++i)
219 {
220 auto const& node = packet_ptr->nodes(i);
221
222 if (!node.has_nodeid() || !node.has_nodedata())
223 return;
224
225 auto newNode = SHAMapTreeNode::makeFromWire(makeSlice(node.nodedata()));
226
227 if (!newNode)
228 return;
229
230 s.erase();
231 newNode->serializeWithPrefix(s);
232
234 newNode->getHash().as_uint256(), std::make_shared<Blob>(s.begin(), s.end()));
235 }
236 }
237 catch (std::exception const&)
238 {
239 }
240 }
241
242 void
243 clearFailures() override
244 {
246
247 mRecentFailures.clear();
248 mLedgers.clear();
249 }
250
252 fetchRate() override
253 {
255 return 60 * fetchRate_.value(m_clock.now());
256 }
257
258 // Should only be called with an inboundledger that has
259 // a reason of history
260 void
262 {
265 }
266
268 getInfo() override
269 {
271
273
274 {
276
277 acqs.reserve(mLedgers.size());
278 for (auto const& it : mLedgers)
279 {
280 XRPL_ASSERT(it.second, "xrpl::InboundLedgersImp::getInfo : non-null ledger");
281 acqs.push_back(it);
282 }
283 for (auto const& it : mRecentFailures)
284 {
285 if (it.second > 1)
286 ret[std::to_string(it.second)][jss::failed] = true;
287 else
288 ret[to_string(it.first)][jss::failed] = true;
289 }
290 }
291
292 for (auto const& it : acqs)
293 {
294 // getJson is expensive, so call without the lock
295 std::uint32_t seq = it.second->getSeq();
296 if (seq > 1)
297 ret[std::to_string(seq)] = it.second->getJson(0);
298 else
299 ret[to_string(it.first)] = it.second->getJson(0);
300 }
301
302 return ret;
303 }
304
305 void
306 gotFetchPack() override
307 {
309 {
311
312 acquires.reserve(mLedgers.size());
313 for (auto const& it : mLedgers)
314 {
315 XRPL_ASSERT(
316 it.second,
317 "xrpl::InboundLedgersImp::gotFetchPack : non-null "
318 "ledger");
319 acquires.push_back(it.second);
320 }
321 }
322
323 for (auto const& acquire : acquires)
324 {
325 acquire->checkLocal();
326 }
327 }
328
329 void
330 sweep() override
331 {
332 auto const start = m_clock.now();
333
334 // Make a list of things to sweep, while holding the lock
336 std::size_t total;
337
338 {
340 MapType::iterator it(mLedgers.begin());
341 total = mLedgers.size();
342
343 stuffToSweep.reserve(total);
344
345 while (it != mLedgers.end())
346 {
347 auto const la = it->second->getLastAction();
348
349 if (la > start)
350 {
351 it->second->touch();
352 ++it;
353 }
354 else if ((la + std::chrono::minutes(1)) < start)
355 {
356 stuffToSweep.push_back(it->second);
357 // shouldn't cause the actual final delete
358 // since we are holding a reference in the vector.
359 it = mLedgers.erase(it);
360 }
361 else
362 {
363 ++it;
364 }
365 }
366
368 }
369
370 JLOG(j_.debug()) << "Swept " << stuffToSweep.size() << " out of " << total << " inbound ledgers. Duration: "
371 << std::chrono::duration_cast<std::chrono::milliseconds>(m_clock.now() - start).count()
372 << "ms";
373 }
374
375 void
376 stop() override
377 {
378 ScopedLockType lock(mLock);
379 stopping_ = true;
380 mLedgers.clear();
381 mRecentFailures.clear();
382 }
383
385 cacheSize() override
386 {
387 ScopedLockType lock(mLock);
388 return mLedgers.size();
389 }
390
391private:
393
396
397 bool stopping_ = false;
400
402
404
406
409};
410
411//------------------------------------------------------------------------------
412
415 Application& app,
417 beast::insight::Collector::ptr const& collector)
418{
419 return std::make_unique<InboundLedgersImp>(app, clock, collector, make_PeerSetBuilder(app));
420}
421
422} // namespace xrpl
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:301
Stream trace() const
Severity stream access functions.
Definition Journal.h:295
Stream warn() const
Definition Journal.h:313
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 LedgerMaster & getLedgerMaster()=0
virtual JobQueue & getJobQueue()=0
virtual NetworkOPs & getOPs()=0
Sampling function using exponential decay to provide a continuous value.
void add(double value, time_point now)
double value(time_point now)
InboundLedgersImp(Application &app, clock_type &clock, beast::insight::Collector::ptr const &collector, std::unique_ptr< PeerSetBuilder > peerSetBuilder)
beast::aged_map< uint256, std::uint32_t > mRecentFailures
Json::Value getInfo() override
void onLedgerFetched() override
Called when a complete ledger is obtained.
static constexpr std::chrono::minutes const kReacquireInterval
std::shared_ptr< Ledger const > acquire(uint256 const &hash, std::uint32_t seq, InboundLedger::Reason reason) override
std::recursive_mutex mLock
std::unique_ptr< PeerSetBuilder > mPeerSetBuilder
DecayWindow< 30, clock_type > fetchRate_
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_
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_
std::size_t cacheSize() override
beast::insight::Counter mCounter
void clearFailures() override
bool isFailure(uint256 const &h) 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,...
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.
bool addJob(JobType type, std::string const &name, JobHandler &&jobHandler)
Adds a job to the JobQueue.
Definition JobQueue.h:146
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:227
Blob::iterator end()
Definition Serializer.h:232
bool isNonZero() const
Definition base_uint.h:514
Automatically unlocks and re-locks a unique_lock object.
Definition scope.h:198
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.
STL namespace.
auto measureDurationAndLog(Func &&func, std::string const &actionDescription, std::chrono::duration< Rep, Period > maxDelay, beast::Journal const &journal)
Definition PerfLog.h:159
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:598
std::unique_ptr< InboundLedgers > make_InboundLedgers(Application &app, InboundLedgers::clock_type &clock, beast::insight::Collector::ptr const &collector)
@ jtLEDGER_DATA
Definition Job.h:46
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:214
std::unique_ptr< PeerSetBuilder > make_PeerSetBuilder(Application &app)
Definition PeerSet.cpp:121
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)