rippled
LedgerCleaner.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 <ripple/app/ledger/InboundLedgers.h>
21 #include <ripple/app/ledger/LedgerCleaner.h>
22 #include <ripple/app/ledger/LedgerMaster.h>
23 #include <ripple/app/misc/LoadFeeTrack.h>
24 #include <ripple/beast/core/CurrentThreadName.h>
25 #include <ripple/protocol/jss.h>
26 
27 namespace ripple {
28 namespace detail {
29 
30 /*
31 
32 LedgerCleaner
33 
34 Cleans up the ledger. Specifically, resolves these issues:
35 
36 1. Older versions could leave the SQLite account and transaction databases in
37  an inconsistent state. The cleaner identifies these inconsistencies and
38  resolves them.
39 
40 2. Upon request, checks for missing nodes in a ledger and triggers a fetch.
41 
42 */
43 
45 {
48  mutable std::mutex mutex_;
49 
51 
53 
54  enum class State : char { readyToClean = 0, startCleaning, cleaning };
56  bool shouldExit_ = false;
57 
58  // The lowest ledger in the range we're checking.
60 
61  // The highest ledger in the range we're checking
63 
64  // Check all state/transaction nodes
65  bool checkNodes_ = false;
66 
67  // Rewrite SQL databases
68  bool fixTxns_ = false;
69 
70  // Number of errors encountered since last success
71  int failures_ = 0;
72 
73  //--------------------------------------------------------------------------
74 public:
76  Application& app,
77  Stoppable& stoppable,
78  beast::Journal journal)
79  : LedgerCleaner(stoppable), app_(app), j_(journal)
80  {
81  }
82 
83  ~LedgerCleanerImp() override
84  {
85  if (thread_.joinable())
86  LogicError("LedgerCleanerImp::onStop not called.");
87  }
88 
89  //--------------------------------------------------------------------------
90  //
91  // Stoppable
92  //
93  //--------------------------------------------------------------------------
94 
95  void
96  onStart() override
97  {
99  }
100 
101  void
102  onStop() override
103  {
104  JLOG(j_.info()) << "Stopping";
105  {
106  std::lock_guard lock(mutex_);
107  shouldExit_ = true;
109  }
110  thread_.join();
111  }
112 
113  //--------------------------------------------------------------------------
114  //
115  // PropertyStream
116  //
117  //--------------------------------------------------------------------------
118 
119  void
121  {
122  std::lock_guard lock(mutex_);
123 
124  if (maxRange_ == 0)
125  map["status"] = "idle";
126  else
127  {
128  map["status"] = "running";
129  map["min_ledger"] = minRange_;
130  map["max_ledger"] = maxRange_;
131  map["check_nodes"] = checkNodes_ ? "true" : "false";
132  map["fix_txns"] = fixTxns_ ? "true" : "false";
133  if (failures_ > 0)
134  map["fail_counts"] = failures_;
135  }
136  }
137 
138  //--------------------------------------------------------------------------
139  //
140  // LedgerCleaner
141  //
142  //--------------------------------------------------------------------------
143 
144  void
145  doClean(Json::Value const& params) override
146  {
147  LedgerIndex minRange = 0;
148  LedgerIndex maxRange = 0;
149  app_.getLedgerMaster().getFullValidatedRange(minRange, maxRange);
150 
151  {
152  std::lock_guard lock(mutex_);
153 
154  maxRange_ = maxRange;
155  minRange_ = minRange;
156  checkNodes_ = false;
157  fixTxns_ = false;
158  failures_ = 0;
159 
160  /*
161  JSON Parameters:
162 
163  All parameters are optional. By default the cleaner cleans
164  things it thinks are necessary. This behavior can be modified
165  using the following options supplied via JSON RPC:
166 
167  "ledger"
168  A single unsigned integer representing an individual
169  ledger to clean.
170 
171  "min_ledger", "max_ledger"
172  Unsigned integers representing the starting and ending
173  ledger numbers to clean. If unspecified, clean all ledgers.
174 
175  "full"
176  A boolean. When true, means clean everything possible.
177 
178  "fix_txns"
179  A boolean value indicating whether or not to fix the
180  transactions in the database as well.
181 
182  "check_nodes"
183  A boolean, when set to true means check the nodes.
184 
185  "stop"
186  A boolean, when true informs the cleaner to gracefully
187  stop its current activities if any cleaning is taking place.
188  */
189 
190  // Quick way to fix a single ledger
191  if (params.isMember(jss::ledger))
192  {
193  maxRange_ = params[jss::ledger].asUInt();
194  minRange_ = params[jss::ledger].asUInt();
195  fixTxns_ = true;
196  checkNodes_ = true;
197  }
198 
199  if (params.isMember(jss::max_ledger))
200  maxRange_ = params[jss::max_ledger].asUInt();
201 
202  if (params.isMember(jss::min_ledger))
203  minRange_ = params[jss::min_ledger].asUInt();
204 
205  if (params.isMember(jss::full))
206  fixTxns_ = checkNodes_ = params[jss::full].asBool();
207 
208  if (params.isMember(jss::fix_txns))
209  fixTxns_ = params[jss::fix_txns].asBool();
210 
211  if (params.isMember(jss::check_nodes))
212  checkNodes_ = params[jss::check_nodes].asBool();
213 
214  if (params.isMember(jss::stop) && params[jss::stop].asBool())
215  minRange_ = maxRange_ = 0;
216 
218  {
221  }
222  }
223  }
224 
225  //--------------------------------------------------------------------------
226  //
227  // LedgerCleanerImp
228  //
229  //--------------------------------------------------------------------------
230 private:
231  void
233  {
234  JLOG(j_.debug()) << "Initializing";
235  }
236 
237  void
238  run()
239  {
240  beast::setCurrentThreadName("LedgerCleaner");
241  JLOG(j_.debug()) << "Started";
242 
243  init();
244 
245  while (true)
246  {
247  {
249  wakeup_.wait(lock, [this]() {
250  return (shouldExit_ || state_ == State::startCleaning);
251  });
252  if (shouldExit_)
253  break;
254 
256  }
257  doLedgerCleaner();
258  }
259 
260  stopped();
261  }
262 
263  // VFALCO TODO This should return boost::optional<uint256>
264  LedgerHash
266  {
267  boost::optional<LedgerHash> hash;
268  try
269  {
270  hash = hashOfSeq(*ledger, index, j_);
271  }
272  catch (SHAMapMissingNode const& mn)
273  {
274  JLOG(j_.warn())
275  << "Ledger #" << ledger->info().seq << ": " << mn.what();
277  ledger->info().hash,
278  ledger->info().seq,
280  }
281  return hash ? *hash : beast::zero; // kludge
282  }
283 
291  bool
293  LedgerIndex const& ledgerIndex,
294  LedgerHash const& ledgerHash,
295  bool doNodes,
296  bool doTxns)
297  {
298  auto nodeLedger = app_.getInboundLedgers().acquire(
299  ledgerHash, ledgerIndex, InboundLedger::Reason::GENERIC);
300  if (!nodeLedger)
301  {
302  JLOG(j_.debug()) << "Ledger " << ledgerIndex << " not available";
303  app_.getLedgerMaster().clearLedger(ledgerIndex);
305  ledgerHash, ledgerIndex, InboundLedger::Reason::GENERIC);
306  return false;
307  }
308 
309  auto dbLedger = loadByIndex(ledgerIndex, app_);
310  if (!dbLedger || (dbLedger->info().hash != ledgerHash) ||
311  (dbLedger->info().parentHash != nodeLedger->info().parentHash))
312  {
313  // Ideally we'd also check for more than one ledger with that index
314  JLOG(j_.debug())
315  << "Ledger " << ledgerIndex << " mismatches SQL DB";
316  doTxns = true;
317  }
318 
319  if (!app_.getLedgerMaster().fixIndex(ledgerIndex, ledgerHash))
320  {
321  JLOG(j_.debug())
322  << "ledger " << ledgerIndex << " had wrong entry in history";
323  doTxns = true;
324  }
325 
326  if (doNodes && !nodeLedger->walkLedger(app_.journal("Ledger")))
327  {
328  JLOG(j_.debug()) << "Ledger " << ledgerIndex << " is missing nodes";
329  app_.getLedgerMaster().clearLedger(ledgerIndex);
331  ledgerHash, ledgerIndex, InboundLedger::Reason::GENERIC);
332  return false;
333  }
334 
335  if (doTxns && !pendSaveValidated(app_, nodeLedger, true, false))
336  {
337  JLOG(j_.debug()) << "Failed to save ledger " << ledgerIndex;
338  return false;
339  }
340 
341  return true;
342  }
343 
349  LedgerHash
351  LedgerIndex const& ledgerIndex,
352  std::shared_ptr<ReadView const>& referenceLedger)
353  {
354  LedgerHash ledgerHash;
355 
356  if (!referenceLedger || (referenceLedger->info().seq < ledgerIndex))
357  {
358  referenceLedger = app_.getLedgerMaster().getValidatedLedger();
359  if (!referenceLedger)
360  {
361  JLOG(j_.warn()) << "No validated ledger";
362  return ledgerHash; // Nothing we can do. No validated ledger.
363  }
364  }
365 
366  if (referenceLedger->info().seq >= ledgerIndex)
367  {
368  // See if the hash for the ledger we need is in the reference ledger
369  ledgerHash = getLedgerHash(referenceLedger, ledgerIndex);
370  if (ledgerHash.isZero())
371  {
372  // No. Try to get another ledger that might have the hash we
373  // need: compute the index and hash of a ledger that will have
374  // the hash we need.
375  LedgerIndex refIndex = getCandidateLedger(ledgerIndex);
376  LedgerHash refHash = getLedgerHash(referenceLedger, refIndex);
377 
378  bool const nonzero(refHash.isNonZero());
379  assert(nonzero);
380  if (nonzero)
381  {
382  // We found the hash and sequence of a better reference
383  // ledger.
384  referenceLedger = app_.getInboundLedgers().acquire(
385  refHash, refIndex, InboundLedger::Reason::GENERIC);
386  if (referenceLedger)
387  ledgerHash =
388  getLedgerHash(referenceLedger, ledgerIndex);
389  }
390  }
391  }
392  else
393  JLOG(j_.warn()) << "Validated ledger is prior to target ledger";
394 
395  return ledgerHash;
396  }
397 
399  void
401  {
402  auto shouldExit = [this]() {
403  std::lock_guard lock(mutex_);
404  return shouldExit_;
405  };
406 
408 
409  while (!shouldExit())
410  {
411  LedgerIndex ledgerIndex;
412  LedgerHash ledgerHash;
413  bool doNodes;
414  bool doTxns;
415 
416  while (app_.getFeeTrack().isLoadedLocal())
417  {
418  JLOG(j_.debug()) << "Waiting for load to subside";
420  if (shouldExit())
421  return;
422  }
423 
424  {
425  std::lock_guard lock(mutex_);
426  if ((minRange_ > maxRange_) || (maxRange_ == 0) ||
427  (minRange_ == 0))
428  {
429  minRange_ = maxRange_ = 0;
431  return;
432  }
433  ledgerIndex = maxRange_;
434  doNodes = checkNodes_;
435  doTxns = fixTxns_;
436  }
437 
438  ledgerHash = getHash(ledgerIndex, goodLedger);
439 
440  bool fail = false;
441  if (ledgerHash.isZero())
442  {
443  JLOG(j_.info())
444  << "Unable to get hash for ledger " << ledgerIndex;
445  fail = true;
446  }
447  else if (!doLedger(ledgerIndex, ledgerHash, doNodes, doTxns))
448  {
449  JLOG(j_.info()) << "Failed to process ledger " << ledgerIndex;
450  fail = true;
451  }
452 
453  if (fail)
454  {
455  {
456  std::lock_guard lock(mutex_);
457  ++failures_;
458  }
459  // Wait for acquiring to catch up to us
461  }
462  else
463  {
464  {
465  std::lock_guard lock(mutex_);
466  if (ledgerIndex == minRange_)
467  ++minRange_;
468  if (ledgerIndex == maxRange_)
469  --maxRange_;
470  failures_ = 0;
471  }
472  // Reduce I/O pressure and wait for acquiring to catch up to us
474  }
475  }
476  }
477 };
478 
479 //------------------------------------------------------------------------------
480 
482  : Stoppable("LedgerCleaner", parent)
483  , beast::PropertyStream::Source("ledgercleaner")
484 {
485 }
486 
488 
491 {
492  return std::make_unique<LedgerCleanerImp>(app, parent, journal);
493 }
494 
495 } // namespace detail
496 } // namespace ripple
ripple::Application
Definition: Application.h:102
std::this_thread::sleep_for
T sleep_for(T... args)
ripple::detail::LedgerCleaner::LedgerCleaner
LedgerCleaner(Stoppable &parent)
Definition: LedgerCleaner.cpp:481
ripple::detail::LedgerCleanerImp
Definition: LedgerCleaner.cpp:44
ripple::detail::LedgerCleanerImp::mutex_
std::mutex mutex_
Definition: LedgerCleaner.cpp:48
std::shared_ptr
STL class.
ripple::detail::LedgerCleanerImp::app_
Application & app_
Definition: LedgerCleaner.cpp:46
ripple::loadByIndex
std::shared_ptr< Ledger > loadByIndex(std::uint32_t ledgerIndex, Application &app, bool acquire)
Definition: Ledger.cpp:1565
ripple::detail::LedgerCleanerImp::doClean
void doClean(Json::Value const &params) override
Start a long running task to clean the ledger.
Definition: LedgerCleaner.cpp:145
ripple::detail::LedgerCleanerImp::State
State
Definition: LedgerCleaner.cpp:54
ripple::base_uint::isNonZero
bool isNonZero() const
Definition: base_uint.h:444
ripple::Stoppable::stopped
void stopped()
Called by derived classes to indicate that the stoppable has stopped.
Definition: Stoppable.cpp:72
beast::PropertyStream::Map
Definition: PropertyStream.h:236
ripple::detail::LedgerCleanerImp::j_
const beast::Journal j_
Definition: LedgerCleaner.cpp:47
ripple::InboundLedger::Reason::GENERIC
@ GENERIC
std::chrono::seconds
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
std::lock_guard
STL class.
ripple::detail::LedgerCleanerImp::State::readyToClean
@ readyToClean
ripple::detail::LedgerCleanerImp::State::startCleaning
@ startCleaning
ripple::detail::LedgerCleanerImp::wakeup_
std::condition_variable wakeup_
Definition: LedgerCleaner.cpp:50
ripple::detail::LedgerCleanerImp::onStop
void onStop() override
Override called when the stop notification is issued.
Definition: LedgerCleaner.cpp:102
ripple::Application::getInboundLedgers
virtual InboundLedgers & getInboundLedgers()=0
ripple::Application::getFeeTrack
virtual LoadFeeTrack & getFeeTrack()=0
ripple::detail::LedgerCleanerImp::onWrite
void onWrite(beast::PropertyStream::Map &map) override
Subclass override.
Definition: LedgerCleaner.cpp:120
ripple::detail::LedgerCleanerImp::shouldExit_
bool shouldExit_
Definition: LedgerCleaner.cpp:56
Json::Value::asBool
bool asBool() const
Definition: json_value.cpp:619
ripple::SHAMapMissingNode
Definition: SHAMapMissingNode.h:55
ripple::detail::LedgerCleanerImp::minRange_
LedgerIndex minRange_
Definition: LedgerCleaner.cpp:59
ripple::base_uint< 256 >
ripple::LoadFeeTrack::isLoadedLocal
bool isLoadedLocal() const
Definition: LoadFeeTrack.h:123
std::thread::joinable
T joinable(T... args)
ripple::Stoppable
Provides an interface for starting and stopping.
Definition: Stoppable.h:201
ripple::detail::make_LedgerCleaner
std::unique_ptr< LedgerCleaner > make_LedgerCleaner(Application &app, Stoppable &parent, beast::Journal journal)
Definition: LedgerCleaner.cpp:490
ripple::base_uint::isZero
bool isZero() const
Definition: base_uint.h:439
std::thread
STL class.
ripple::detail::LedgerCleanerImp::~LedgerCleanerImp
~LedgerCleanerImp() override
Definition: LedgerCleaner.cpp:83
ripple::detail::LedgerCleaner
Check the ledger/transaction databases to make sure they have continuity.
Definition: LedgerCleaner.h:34
ripple::Application::getLedgerMaster
virtual LedgerMaster & getLedgerMaster()=0
ripple::InboundLedgers::acquire
virtual std::shared_ptr< Ledger const > acquire(uint256 const &hash, std::uint32_t seq, InboundLedger::Reason)=0
ripple::detail::LedgerCleanerImp::getHash
LedgerHash getHash(LedgerIndex const &ledgerIndex, std::shared_ptr< ReadView const > &referenceLedger)
Returns the hash of the specified ledger.
Definition: LedgerCleaner.cpp:350
ripple::LedgerMaster::getFullValidatedRange
bool getFullValidatedRange(std::uint32_t &minVal, std::uint32_t &maxVal)
Definition: LedgerMaster.cpp:601
ripple::LedgerMaster::fixIndex
bool fixIndex(LedgerIndex ledgerIndex, LedgerHash const &ledgerHash)
Definition: LedgerMaster.cpp:521
ripple::detail::LedgerCleanerImp::getLedgerHash
LedgerHash getLedgerHash(std::shared_ptr< ReadView const > &ledger, LedgerIndex index)
Definition: LedgerCleaner.cpp:265
ripple::detail::LedgerCleanerImp::failures_
int failures_
Definition: LedgerCleaner.cpp:71
std::unique_lock
STL class.
ripple::detail::LedgerCleanerImp::maxRange_
LedgerIndex maxRange_
Definition: LedgerCleaner.cpp:62
beast::Journal::info
Stream info() const
Definition: Journal.h:321
Json::Value::isMember
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:932
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
std::uint32_t
ripple::detail::LedgerCleanerImp::state_
State state_
Definition: LedgerCleaner.cpp:55
std::condition_variable::wait
T wait(T... args)
ripple::detail::LedgerCleaner::~LedgerCleaner
virtual ~LedgerCleaner()=0
Destroy the object.
ripple::detail::LedgerCleanerImp::onStart
void onStart() override
Override called during start.
Definition: LedgerCleaner.cpp:96
ripple::detail::LedgerCleanerImp::checkNodes_
bool checkNodes_
Definition: LedgerCleaner.cpp:65
ripple::detail::LedgerCleanerImp::init
void init()
Definition: LedgerCleaner.cpp:232
std::condition_variable::notify_one
T notify_one(T... args)
ripple::hashOfSeq
boost::optional< uint256 > hashOfSeq(ReadView const &ledger, LedgerIndex seq, beast::Journal journal)
Return the hash of a ledger by sequence.
Definition: View.cpp:576
beast::setCurrentThreadName
void setCurrentThreadName(std::string_view name)
Changes the name of the caller thread.
Definition: CurrentThreadName.cpp:119
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::Application::journal
virtual beast::Journal journal(std::string const &name)=0
ripple::detail::LedgerCleanerImp::doLedger
bool doLedger(LedgerIndex const &ledgerIndex, LedgerHash const &ledgerHash, bool doNodes, bool doTxns)
Process a single ledger.
Definition: LedgerCleaner.cpp:292
ripple::detail::LedgerCleanerImp::thread_
std::thread thread_
Definition: LedgerCleaner.cpp:52
ripple::detail::LedgerCleanerImp::State::cleaning
@ cleaning
ripple::LedgerMaster::clearLedger
void clearLedger(std::uint32_t seq)
Definition: LedgerMaster.cpp:593
ripple::LogicError
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
Definition: contract.cpp:48
Json::Value::asUInt
UInt asUInt() const
Definition: json_value.cpp:545
std::condition_variable
ripple::LedgerMaster::getValidatedLedger
std::shared_ptr< Ledger const > getValidatedLedger()
Definition: LedgerMaster.cpp:1622
std::mutex
STL class.
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
ripple::detail::LedgerCleanerImp::fixTxns_
bool fixTxns_
Definition: LedgerCleaner.cpp:68
ripple::detail::LedgerCleanerImp::doLedgerCleaner
void doLedgerCleaner()
Run the ledger cleaner.
Definition: LedgerCleaner.cpp:400
ripple::getCandidateLedger
LedgerIndex getCandidateLedger(LedgerIndex requested)
Find a ledger index from which we could easily get the requested ledger.
Definition: View.h:187
ripple::pendSaveValidated
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:1123
std::unique_ptr
STL class.
ripple::detail::LedgerCleanerImp::LedgerCleanerImp
LedgerCleanerImp(Application &app, Stoppable &stoppable, beast::Journal journal)
Definition: LedgerCleaner.cpp:75
std::thread::join
T join(T... args)
std::runtime_error::what
T what(T... args)
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::detail::LedgerCleanerImp::run
void run()
Definition: LedgerCleaner.cpp:238
beast
Definition: base_uint.h:585