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