rippled
Loading...
Searching...
No Matches
LedgerCleaner.cpp
1#include <xrpld/app/ledger/InboundLedgers.h>
2#include <xrpld/app/ledger/LedgerCleaner.h>
3#include <xrpld/app/ledger/LedgerMaster.h>
4#include <xrpld/app/misc/LoadFeeTrack.h>
5
6#include <xrpl/beast/core/CurrentThreadName.h>
7#include <xrpl/protocol/jss.h>
8
9namespace xrpl {
10
11/*
12
13LedgerCleaner
14
15Cleans up the ledger. Specifically, resolves these issues:
16
171. Older versions could leave the SQLite account and transaction databases in
18 an inconsistent state. The cleaner identifies these inconsistencies and
19 resolves them.
20
212. Upon request, checks for missing nodes in a ledger and triggers a fetch.
22
23*/
24
26{
30
32
34
35 enum class State : char { notCleaning = 0, cleaning };
37 bool shouldExit_ = false;
38
39 // The lowest ledger in the range we're checking.
41
42 // The highest ledger in the range we're checking
44
45 // Check all state/transaction nodes
46 bool checkNodes_ = false;
47
48 // Rewrite SQL databases
49 bool fixTxns_ = false;
50
51 // Number of errors encountered since last success
52 int failures_ = 0;
53
54 //--------------------------------------------------------------------------
55public:
56 LedgerCleanerImp(Application& app, beast::Journal journal) : app_(app), j_(journal)
57 {
58 }
59
61 {
62 if (thread_.joinable())
63 LogicError("LedgerCleanerImp::stop not called.");
64 }
65
66 void
67 start() override
68 {
70 }
71
72 void
73 stop() override
74 {
75 JLOG(j_.info()) << "Stopping";
76 {
78 shouldExit_ = true;
80 }
81 thread_.join();
82 }
83
84 //--------------------------------------------------------------------------
85 //
86 // PropertyStream
87 //
88 //--------------------------------------------------------------------------
89
90 void
92 {
94
95 if (maxRange_ == 0)
96 map["status"] = "idle";
97 else
98 {
99 map["status"] = "running";
100 map["min_ledger"] = minRange_;
101 map["max_ledger"] = maxRange_;
102 map["check_nodes"] = checkNodes_ ? "true" : "false";
103 map["fix_txns"] = fixTxns_ ? "true" : "false";
104 if (failures_ > 0)
105 map["fail_counts"] = failures_;
106 }
107 }
108
109 //--------------------------------------------------------------------------
110 //
111 // LedgerCleaner
112 //
113 //--------------------------------------------------------------------------
114
115 void
116 clean(Json::Value const& params) override
117 {
118 LedgerIndex minRange = 0;
119 LedgerIndex maxRange = 0;
120 app_.getLedgerMaster().getFullValidatedRange(minRange, maxRange);
121
122 {
124
125 maxRange_ = maxRange;
126 minRange_ = minRange;
127 checkNodes_ = false;
128 fixTxns_ = false;
129 failures_ = 0;
130
131 /*
132 JSON Parameters:
133
134 All parameters are optional. By default the cleaner cleans
135 things it thinks are necessary. This behavior can be modified
136 using the following options supplied via JSON RPC:
137
138 "ledger"
139 A single unsigned integer representing an individual
140 ledger to clean.
141
142 "min_ledger", "max_ledger"
143 Unsigned integers representing the starting and ending
144 ledger numbers to clean. If unspecified, clean all ledgers.
145
146 "full"
147 A boolean. When true, means clean everything possible.
148
149 "fix_txns"
150 A boolean value indicating whether or not to fix the
151 transactions in the database as well.
152
153 "check_nodes"
154 A boolean, when set to true means check the nodes.
155
156 "stop"
157 A boolean, when true informs the cleaner to gracefully
158 stop its current activities if any cleaning is taking place.
159 */
160
161 // Quick way to fix a single ledger
162 if (params.isMember(jss::ledger))
163 {
164 maxRange_ = params[jss::ledger].asUInt();
165 minRange_ = params[jss::ledger].asUInt();
166 fixTxns_ = true;
167 checkNodes_ = true;
168 }
169
170 if (params.isMember(jss::max_ledger))
171 maxRange_ = params[jss::max_ledger].asUInt();
172
173 if (params.isMember(jss::min_ledger))
174 minRange_ = params[jss::min_ledger].asUInt();
175
176 if (params.isMember(jss::full))
177 fixTxns_ = checkNodes_ = params[jss::full].asBool();
178
179 if (params.isMember(jss::fix_txns))
180 fixTxns_ = params[jss::fix_txns].asBool();
181
182 if (params.isMember(jss::check_nodes))
183 checkNodes_ = params[jss::check_nodes].asBool();
184
185 if (params.isMember(jss::stop) && params[jss::stop].asBool())
186 minRange_ = maxRange_ = 0;
187
190 }
191 }
192
193 //--------------------------------------------------------------------------
194 //
195 // LedgerCleanerImp
196 //
197 //--------------------------------------------------------------------------
198private:
199 void
201 {
202 beast::setCurrentThreadName("LedgerCleaner");
203 JLOG(j_.debug()) << "Started";
204
205 while (true)
206 {
207 {
210 wakeup_.wait(lock, [this]() { return (shouldExit_ || state_ == State::cleaning); });
211 if (shouldExit_)
212 break;
213 XRPL_ASSERT(state_ == State::cleaning, "xrpl::LedgerCleanerImp::run : is cleaning");
214 }
216 }
217 }
218
219 // VFALCO TODO This should return std::optional<uint256>
222 {
224 try
225 {
226 hash = hashOfSeq(*ledger, index, j_);
227 }
228 catch (SHAMapMissingNode const& mn)
229 {
230 JLOG(j_.warn()) << "Ledger #" << ledger->header().seq << ": " << mn.what();
232 ledger->header().hash, ledger->header().seq, InboundLedger::Reason::GENERIC);
233 }
234 return hash ? *hash : beast::zero; // kludge
235 }
236
244 bool
245 doLedger(LedgerIndex const& ledgerIndex, LedgerHash const& ledgerHash, bool doNodes, bool doTxns)
246 {
247 auto nodeLedger = app_.getInboundLedgers().acquire(ledgerHash, ledgerIndex, InboundLedger::Reason::GENERIC);
248 if (!nodeLedger)
249 {
250 JLOG(j_.debug()) << "Ledger " << ledgerIndex << " not available";
251 app_.getLedgerMaster().clearLedger(ledgerIndex);
253 return false;
254 }
255
256 auto dbLedger = loadByIndex(ledgerIndex, app_);
257 if (!dbLedger || (dbLedger->header().hash != ledgerHash) ||
258 (dbLedger->header().parentHash != nodeLedger->header().parentHash))
259 {
260 // Ideally we'd also check for more than one ledger with that index
261 JLOG(j_.debug()) << "Ledger " << ledgerIndex << " mismatches SQL DB";
262 doTxns = true;
263 }
264
265 if (!app_.getLedgerMaster().fixIndex(ledgerIndex, ledgerHash))
266 {
267 JLOG(j_.debug()) << "ledger " << ledgerIndex << " had wrong entry in history";
268 doTxns = true;
269 }
270
271 if (doNodes && !nodeLedger->walkLedger(app_.journal("Ledger")))
272 {
273 JLOG(j_.debug()) << "Ledger " << ledgerIndex << " is missing nodes";
274 app_.getLedgerMaster().clearLedger(ledgerIndex);
276 return false;
277 }
278
279 if (doTxns && !pendSaveValidated(app_, nodeLedger, true, false))
280 {
281 JLOG(j_.debug()) << "Failed to save ledger " << ledgerIndex;
282 return false;
283 }
284
285 return true;
286 }
287
294 getHash(LedgerIndex const& ledgerIndex, std::shared_ptr<ReadView const>& referenceLedger)
295 {
296 LedgerHash ledgerHash;
297
298 if (!referenceLedger || (referenceLedger->header().seq < ledgerIndex))
299 {
300 referenceLedger = app_.getLedgerMaster().getValidatedLedger();
301 if (!referenceLedger)
302 {
303 JLOG(j_.warn()) << "No validated ledger";
304 return ledgerHash; // Nothing we can do. No validated ledger.
305 }
306 }
307
308 if (referenceLedger->header().seq >= ledgerIndex)
309 {
310 // See if the hash for the ledger we need is in the reference ledger
311 ledgerHash = getLedgerHash(referenceLedger, ledgerIndex);
312 if (ledgerHash.isZero())
313 {
314 // No. Try to get another ledger that might have the hash we
315 // need: compute the index and hash of a ledger that will have
316 // the hash we need.
317 LedgerIndex refIndex = getCandidateLedger(ledgerIndex);
318 LedgerHash refHash = getLedgerHash(referenceLedger, refIndex);
319
320 bool const nonzero(refHash.isNonZero());
321 XRPL_ASSERT(nonzero, "xrpl::LedgerCleanerImp::getHash : nonzero hash");
322 if (nonzero)
323 {
324 // We found the hash and sequence of a better reference
325 // ledger.
326 referenceLedger =
328 if (referenceLedger)
329 ledgerHash = getLedgerHash(referenceLedger, ledgerIndex);
330 }
331 }
332 }
333 else
334 JLOG(j_.warn()) << "Validated ledger is prior to target ledger";
335
336 return ledgerHash;
337 }
338
340 void
342 {
343 auto shouldExit = [this] {
345 return shouldExit_;
346 };
347
349
350 while (!shouldExit())
351 {
352 LedgerIndex ledgerIndex;
353 LedgerHash ledgerHash;
354 bool doNodes;
355 bool doTxns;
356
358 {
359 JLOG(j_.debug()) << "Waiting for load to subside";
361 continue;
362 }
363
364 {
366 if ((minRange_ > maxRange_) || (maxRange_ == 0) || (minRange_ == 0))
367 {
368 minRange_ = maxRange_ = 0;
369 return;
370 }
371 ledgerIndex = maxRange_;
372 doNodes = checkNodes_;
373 doTxns = fixTxns_;
374 }
375
376 ledgerHash = getHash(ledgerIndex, goodLedger);
377
378 bool fail = false;
379 if (ledgerHash.isZero())
380 {
381 JLOG(j_.info()) << "Unable to get hash for ledger " << ledgerIndex;
382 fail = true;
383 }
384 else if (!doLedger(ledgerIndex, ledgerHash, doNodes, doTxns))
385 {
386 JLOG(j_.info()) << "Failed to process ledger " << ledgerIndex;
387 fail = true;
388 }
389
390 if (fail)
391 {
392 {
394 ++failures_;
395 }
396 // Wait for acquiring to catch up to us
398 }
399 else
400 {
401 {
403 if (ledgerIndex == minRange_)
404 ++minRange_;
405 if (ledgerIndex == maxRange_)
406 --maxRange_;
407 failures_ = 0;
408 }
409 // Reduce I/O pressure and wait for acquiring to catch up to us
411 }
412 }
413 }
414};
415
418{
419 return std::make_unique<LedgerCleanerImp>(app, journal);
420}
421
422} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
UInt asUInt() const
bool asBool() const
bool isMember(char const *key) const
Return true if the object has a member named key.
A generic endpoint for log messages.
Definition Journal.h:40
Stream debug() const
Definition Journal.h:300
Stream info() const
Definition Journal.h:306
Stream warn() const
Definition Journal.h:312
virtual beast::Journal journal(std::string const &name)=0
virtual std::shared_ptr< Ledger const > acquire(uint256 const &hash, std::uint32_t seq, InboundLedger::Reason)=0
std::condition_variable wakeup_
LedgerHash getHash(LedgerIndex const &ledgerIndex, std::shared_ptr< ReadView const > &referenceLedger)
Returns the hash of the specified ledger.
void clean(Json::Value const &params) override
Start a long running task to clean the ledger.
void doLedgerCleaner()
Run the ledger cleaner.
void onWrite(beast::PropertyStream::Map &map) override
Subclass override.
beast::Journal const j_
bool doLedger(LedgerIndex const &ledgerIndex, LedgerHash const &ledgerHash, bool doNodes, bool doTxns)
Process a single ledger.
LedgerCleanerImp(Application &app, beast::Journal journal)
LedgerHash getLedgerHash(std::shared_ptr< ReadView const > &ledger, LedgerIndex index)
Check the ledger/transaction databases to make sure they have continuity.
bool fixIndex(LedgerIndex ledgerIndex, LedgerHash const &ledgerHash)
void clearLedger(std::uint32_t seq)
std::shared_ptr< Ledger const > getValidatedLedger()
bool getFullValidatedRange(std::uint32_t &minVal, std::uint32_t &maxVal)
bool isLoadedLocal() const
virtual LoadFeeTrack & getFeeTrack()=0
virtual InboundLedgers & getInboundLedgers()=0
virtual LedgerMaster & getLedgerMaster()=0
bool isZero() const
Definition base_uint.h:508
bool isNonZero() const
Definition base_uint.h:513
T is_same_v
T join(T... args)
T joinable(T... args)
void setCurrentThreadName(std::string_view newThreadName)
Changes the name of the caller thread.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
std::unique_ptr< LedgerCleaner > make_LedgerCleaner(Application &app, beast::Journal journal)
bool pendSaveValidated(Application &app, std::shared_ptr< Ledger const > const &ledger, bool isSynchronous, bool isCurrent)
Save, or arrange to save, a fully-validated ledger Returns false on error.
Definition Ledger.cpp:924
LedgerIndex getCandidateLedger(LedgerIndex requested)
Find a ledger index from which we could easily get the requested ledger.
Definition View.h:399
std::shared_ptr< Ledger > loadByIndex(std::uint32_t ledgerIndex, Application &app, bool acquire)
Definition Ledger.cpp:1026
std::optional< uint256 > hashOfSeq(ReadView const &ledger, LedgerIndex seq, beast::Journal journal)
Return the hash of a ledger by sequence.
Definition View.cpp:878
T sleep_for(T... args)
T what(T... args)