rippled
DatabaseShardImp.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2017 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/LedgerMaster.h>
22 #include <ripple/app/misc/NetworkOPs.h>
23 #include <ripple/app/rdb/backend/RelationalDBInterfaceSqlite.h>
24 #include <ripple/basics/ByteUtilities.h>
25 #include <ripple/basics/chrono.h>
26 #include <ripple/basics/random.h>
27 #include <ripple/core/ConfigSections.h>
28 #include <ripple/nodestore/DummyScheduler.h>
29 #include <ripple/nodestore/impl/DatabaseShardImp.h>
30 #include <ripple/overlay/Overlay.h>
31 #include <ripple/overlay/predicates.h>
32 #include <ripple/protocol/HashPrefix.h>
33 
34 #include <boost/algorithm/string/predicate.hpp>
35 
36 #if BOOST_OS_LINUX
37 #include <sys/statvfs.h>
38 #endif
39 
40 namespace ripple {
41 
42 namespace NodeStore {
43 
45  Application& app,
46  Stoppable& parent,
47  std::string const& name,
48  Scheduler& scheduler,
49  int readThreads,
51  : DatabaseShard(
52  name,
53  parent,
54  scheduler,
55  readThreads,
56  app.config().section(ConfigSection::shardDatabase()),
57  j)
58  , app_(app)
59  , parent_(parent)
60  , taskQueue_(std::make_unique<TaskQueue>(*this))
61  , earliestShardIndex_(seqToShardIndex(earliestLedgerSeq()))
62  , avgShardFileSz_(ledgersPerShard_ * kilobytes(192ull))
63  , openFinalLimit_(
64  app.config().getValueFor(SizedItem::openFinalLimit, std::nullopt))
65 {
66  if (app.config().reporting())
67  {
68  Throw<std::runtime_error>(
69  "Attempted to create DatabaseShardImp in reporting mode. Reporting "
70  "does not support shards. Remove shards info from config");
71  }
72 }
73 
74 bool
76 {
77  {
78  std::lock_guard lock(mutex_);
79  if (init_)
80  {
81  JLOG(j_.error()) << "already initialized";
82  return false;
83  }
84 
85  if (!initConfig(lock))
86  {
87  JLOG(j_.error()) << "invalid configuration file settings";
88  return false;
89  }
90 
91  try
92  {
93  using namespace boost::filesystem;
94 
95  // Consolidate the main storage path and all historical paths
96  std::vector<path> paths{dir_};
97  paths.insert(
98  paths.end(), historicalPaths_.begin(), historicalPaths_.end());
99 
100  for (auto const& path : paths)
101  {
102  if (exists(path))
103  {
104  if (!is_directory(path))
105  {
106  JLOG(j_.error()) << path << " must be a directory";
107  return false;
108  }
109  }
110  else if (!create_directories(path))
111  {
112  JLOG(j_.error())
113  << "failed to create path: " + path.string();
114  return false;
115  }
116  }
117 
119  {
120  // Check historical paths for duplicated file systems
121  if (!checkHistoricalPaths())
122  return false;
123  }
124 
125  ctx_ = std::make_unique<nudb::context>();
126  ctx_->start();
127 
128  // Find shards
129  std::uint32_t openFinals{0};
130  for (auto const& path : paths)
131  {
132  for (auto const& it : directory_iterator(path))
133  {
134  // Ignore files
135  if (!is_directory(it))
136  continue;
137 
138  // Ignore nonnumerical directory names
139  auto const shardDir{it.path()};
140  auto dirName{shardDir.stem().string()};
141  if (!std::all_of(
142  dirName.begin(), dirName.end(), [](auto c) {
143  return ::isdigit(static_cast<unsigned char>(c));
144  }))
145  {
146  continue;
147  }
148 
149  // Ignore values below the earliest shard index
150  auto const shardIndex{std::stoul(dirName)};
151  if (shardIndex < earliestShardIndex())
152  {
153  JLOG(j_.debug())
154  << "shard " << shardIndex
155  << " ignored, comes before earliest shard index "
156  << earliestShardIndex();
157  continue;
158  }
159 
160  // Check if a previous import failed
161  if (is_regular_file(shardDir / importMarker_))
162  {
163  JLOG(j_.warn())
164  << "shard " << shardIndex
165  << " previously failed import, removing";
166  remove_all(shardDir);
167  continue;
168  }
169 
170  auto shard{std::make_shared<Shard>(
171  app_, *this, shardIndex, shardDir.parent_path(), j_)};
172  if (!shard->init(scheduler_, *ctx_))
173  {
174  // Remove corrupted or legacy shard
175  shard->removeOnDestroy();
176  JLOG(j_.warn())
177  << "shard " << shardIndex << " removed, "
178  << (shard->isLegacy() ? "legacy" : "corrupted")
179  << " shard";
180  continue;
181  }
182 
183  switch (shard->getState())
184  {
185  case Shard::final:
186  if (++openFinals > openFinalLimit_)
187  shard->tryClose();
188  shards_.emplace(shardIndex, std::move(shard));
189  break;
190 
191  case Shard::complete:
193  shards_.emplace(shardIndex, std::move(shard))
194  .first->second,
195  true,
196  std::nullopt);
197  break;
198 
199  case Shard::acquire:
200  if (acquireIndex_ != 0)
201  {
202  JLOG(j_.error())
203  << "more than one shard being acquired";
204  return false;
205  }
206 
207  shards_.emplace(shardIndex, std::move(shard));
208  acquireIndex_ = shardIndex;
209  break;
210 
211  default:
212  JLOG(j_.error())
213  << "shard " << shardIndex << " invalid state";
214  return false;
215  }
216  }
217  }
218  }
219  catch (std::exception const& e)
220  {
221  JLOG(j_.fatal()) << "Exception caught in function " << __func__
222  << ". Error: " << e.what();
223  return false;
224  }
225 
226  updateStatus(lock);
228  init_ = true;
229  }
230 
231  setFileStats();
232  return true;
233 }
234 
237 {
238  std::optional<std::uint32_t> shardIndex;
239 
240  {
241  std::lock_guard lock(mutex_);
242  assert(init_);
243 
244  if (acquireIndex_ != 0)
245  {
246  if (auto const it{shards_.find(acquireIndex_)}; it != shards_.end())
247  return it->second->prepare();
248 
249  // Should never get here
250  assert(false);
251  return std::nullopt;
252  }
253 
254  if (!canAdd_)
255  return std::nullopt;
256 
257  shardIndex = findAcquireIndex(validLedgerSeq, lock);
258  }
259 
260  if (!shardIndex)
261  {
262  JLOG(j_.debug()) << "no new shards to add";
263  {
264  std::lock_guard lock(mutex_);
265  canAdd_ = false;
266  }
267  return std::nullopt;
268  }
269 
270  auto const pathDesignation = [this, shardIndex = *shardIndex]() {
271  std::lock_guard lock(mutex_);
272  return prepareForNewShard(shardIndex, numHistoricalShards(lock), lock);
273  }();
274 
275  if (!pathDesignation)
276  return std::nullopt;
277 
278  auto const needsHistoricalPath =
279  *pathDesignation == PathDesignation::historical;
280 
281  auto shard = [this, shardIndex, needsHistoricalPath] {
282  std::lock_guard lock(mutex_);
283  return std::make_unique<Shard>(
284  app_,
285  *this,
286  *shardIndex,
287  (needsHistoricalPath ? chooseHistoricalPath(lock) : ""),
288  j_);
289  }();
290 
291  if (!shard->init(scheduler_, *ctx_))
292  return std::nullopt;
293 
294  auto const ledgerSeq{shard->prepare()};
295  {
296  std::lock_guard lock(mutex_);
297  shards_.emplace(*shardIndex, std::move(shard));
298  acquireIndex_ = *shardIndex;
299  }
300  return ledgerSeq;
301 }
302 
303 bool
305 {
306  auto fail = [j = j_, &shardIndexes](
307  std::string const& msg,
308  std::optional<std::uint32_t> shardIndex = std::nullopt) {
309  auto multipleIndexPrequel = [&shardIndexes] {
310  std::vector<std::string> indexesAsString(shardIndexes.size());
312  shardIndexes.begin(),
313  shardIndexes.end(),
314  indexesAsString.begin(),
315  [](uint32_t const index) { return std::to_string(index); });
316 
317  return std::string("shard") +
318  (shardIndexes.size() > 1 ? "s " : " ") +
319  boost::algorithm::join(indexesAsString, ", ");
320  };
321 
322  std::string const prequel = shardIndex
323  ? "shard " + std::to_string(*shardIndex)
324  : multipleIndexPrequel();
325 
326  JLOG(j.error()) << prequel << " " << msg;
327  return false;
328  };
329 
330  std::lock_guard lock(mutex_);
331  assert(init_);
332 
333  if (!canAdd_)
334  return fail("cannot be stored at this time");
335 
336  auto historicalShardsToPrepare = 0;
337 
338  for (auto const shardIndex : shardIndexes)
339  {
340  if (shardIndex < earliestShardIndex())
341  {
342  return fail(
343  "comes before earliest shard index " +
345  shardIndex);
346  }
347 
348  // If we are synced to the network, check if the shard index is
349  // greater or equal to the current or validated shard index.
350  auto seqCheck = [&](std::uint32_t ledgerSeq) {
351  if (ledgerSeq >= earliestLedgerSeq() &&
352  shardIndex >= seqToShardIndex(ledgerSeq))
353  {
354  return fail("invalid index", shardIndex);
355  }
356  return true;
357  };
358  if (!seqCheck(app_.getLedgerMaster().getValidLedgerIndex() + 1) ||
360  {
361  return fail("invalid index", shardIndex);
362  }
363 
364  if (shards_.find(shardIndex) != shards_.end())
365  return fail("is already stored", shardIndex);
366 
367  if (preparedIndexes_.find(shardIndex) != preparedIndexes_.end())
368  return fail("is already queued for import", shardIndex);
369 
370  // Any shard earlier than the two most recent shards
371  // is a historical shard
372  if (shardIndex < shardBoundaryIndex())
373  ++historicalShardsToPrepare;
374  }
375 
376  auto const numHistShards = numHistoricalShards(lock);
377 
378  // Check shard count and available storage space
379  if (numHistShards + historicalShardsToPrepare > maxHistoricalShards_)
380  return fail("maximum number of historical shards reached");
381 
382  if (historicalShardsToPrepare)
383  {
384  // Check available storage space for historical shards
385  if (!sufficientStorage(
386  historicalShardsToPrepare, PathDesignation::historical, lock))
387  return fail("insufficient storage space available");
388  }
389 
390  if (auto const recentShardsToPrepare =
391  shardIndexes.size() - historicalShardsToPrepare;
392  recentShardsToPrepare)
393  {
394  // Check available storage space for recent shards
395  if (!sufficientStorage(
396  recentShardsToPrepare, PathDesignation::none, lock))
397  return fail("insufficient storage space available");
398  }
399 
400  for (auto const shardIndex : shardIndexes)
401  {
402  auto const prepareSuccessful =
403  preparedIndexes_.emplace(shardIndex).second;
404 
405  (void)prepareSuccessful;
406  assert(prepareSuccessful);
407  }
408 
409  return true;
410 }
411 
412 void
414 {
415  std::lock_guard lock(mutex_);
416  assert(init_);
417 
418  preparedIndexes_.erase(shardIndex);
419 }
420 
423 {
425  {
426  std::lock_guard lock(mutex_);
427  assert(init_);
428 
429  for (auto const& shardIndex : preparedIndexes_)
430  rs.insert(shardIndex);
431  }
432 
433  if (rs.empty())
434  return {};
435 
436  return to_string(rs);
437 };
438 
439 bool
441  std::uint32_t shardIndex,
442  boost::filesystem::path const& srcDir)
443 {
444  auto fail = [&](std::string const& msg,
445  std::lock_guard<std::mutex> const& lock) {
446  JLOG(j_.error()) << "shard " << shardIndex << " " << msg;
447 
448  // Remove the failed import shard index so it can be retried
449  preparedIndexes_.erase(shardIndex);
450  return false;
451  };
452 
453  using namespace boost::filesystem;
454  try
455  {
456  if (!is_directory(srcDir) || is_empty(srcDir))
457  {
458  return fail(
459  "invalid source directory " + srcDir.string(),
461  }
462  }
463  catch (std::exception const& e)
464  {
465  return fail(
466  std::string(". Exception caught in function ") + __func__ +
467  ". Error: " + e.what(),
469  }
470 
471  auto const expectedHash{app_.getLedgerMaster().walkHashBySeq(
473  if (!expectedHash)
474  return fail("expected hash not found", std::lock_guard(mutex_));
475 
476  path dstDir;
477  {
478  std::lock_guard lock(mutex_);
479  if (shards_.find(shardIndex) != shards_.end())
480  return fail("already exists", lock);
481 
482  // Check shard was prepared for import
483  if (preparedIndexes_.find(shardIndex) == preparedIndexes_.end())
484  return fail("was not prepared for import", lock);
485 
486  auto const pathDesignation{
487  prepareForNewShard(shardIndex, numHistoricalShards(lock), lock)};
488  if (!pathDesignation)
489  return fail("failed to import", lock);
490 
491  if (*pathDesignation == PathDesignation::historical)
492  dstDir = chooseHistoricalPath(lock);
493  else
494  dstDir = dir_;
495  }
496  dstDir /= std::to_string(shardIndex);
497 
498  auto renameDir = [&](path const& src, path const& dst) {
499  try
500  {
501  rename(src, dst);
502  }
503  catch (std::exception const& e)
504  {
505  return fail(
506  std::string(". Exception caught in function ") + __func__ +
507  ". Error: " + e.what(),
509  }
510  return true;
511  };
512 
513  // Rename source directory to the shard database directory
514  if (!renameDir(srcDir, dstDir))
515  return false;
516 
517  // Create the new shard
518  auto shard{std::make_unique<Shard>(
519  app_, *this, shardIndex, dstDir.parent_path(), j_)};
520 
521  if (!shard->init(scheduler_, *ctx_) || shard->getState() != Shard::complete)
522  {
523  shard.reset();
524  renameDir(dstDir, srcDir);
525  return fail("failed to import", std::lock_guard(mutex_));
526  }
527 
528  auto const [it, inserted] = [&]() {
529  std::lock_guard lock(mutex_);
530  preparedIndexes_.erase(shardIndex);
531  return shards_.emplace(shardIndex, std::move(shard));
532  }();
533 
534  if (!inserted)
535  {
536  shard.reset();
537  renameDir(dstDir, srcDir);
538  return fail("failed to import", std::lock_guard(mutex_));
539  }
540 
541  finalizeShard(it->second, true, expectedHash);
542  return true;
543 }
544 
547 {
548  auto const shardIndex{seqToShardIndex(ledgerSeq)};
549  {
551  {
552  std::lock_guard lock(mutex_);
553  assert(init_);
554 
555  auto const it{shards_.find(shardIndex)};
556  if (it == shards_.end())
557  return nullptr;
558  shard = it->second;
559  }
560 
561  // Ledger must be stored in a final or acquiring shard
562  switch (shard->getState())
563  {
564  case Shard::final:
565  break;
566  case Shard::acquire:
567  if (shard->containsLedger(ledgerSeq))
568  break;
569  [[fallthrough]];
570  default:
571  return nullptr;
572  }
573  }
574 
575  auto const nodeObject{Database::fetchNodeObject(hash, ledgerSeq)};
576  if (!nodeObject)
577  return nullptr;
578 
579  auto fail = [&](std::string const& msg) -> std::shared_ptr<Ledger> {
580  JLOG(j_.error()) << "shard " << shardIndex << " " << msg;
581  return nullptr;
582  };
583 
584  auto ledger{std::make_shared<Ledger>(
585  deserializePrefixedHeader(makeSlice(nodeObject->getData())),
586  app_.config(),
587  *app_.getShardFamily())};
588 
589  if (ledger->info().seq != ledgerSeq)
590  {
591  return fail(
592  "encountered invalid ledger sequence " + std::to_string(ledgerSeq));
593  }
594  if (ledger->info().hash != hash)
595  {
596  return fail(
597  "encountered invalid ledger hash " + to_string(hash) +
598  " on sequence " + std::to_string(ledgerSeq));
599  }
600 
601  ledger->setFull();
602  if (!ledger->stateMap().fetchRoot(
603  SHAMapHash{ledger->info().accountHash}, nullptr))
604  {
605  return fail(
606  "is missing root STATE node on hash " + to_string(hash) +
607  " on sequence " + std::to_string(ledgerSeq));
608  }
609 
610  if (ledger->info().txHash.isNonZero())
611  {
612  if (!ledger->txMap().fetchRoot(
613  SHAMapHash{ledger->info().txHash}, nullptr))
614  {
615  return fail(
616  "is missing root TXN node on hash " + to_string(hash) +
617  " on sequence " + std::to_string(ledgerSeq));
618  }
619  }
620  return ledger;
621 }
622 
623 void
625 {
626  auto const ledgerSeq{ledger->info().seq};
627  if (ledger->info().hash.isZero())
628  {
629  JLOG(j_.error()) << "zero ledger hash for ledger sequence "
630  << ledgerSeq;
631  return;
632  }
633  if (ledger->info().accountHash.isZero())
634  {
635  JLOG(j_.error()) << "zero account hash for ledger sequence "
636  << ledgerSeq;
637  return;
638  }
639  if (ledger->stateMap().getHash().isNonZero() &&
640  !ledger->stateMap().isValid())
641  {
642  JLOG(j_.error()) << "invalid state map for ledger sequence "
643  << ledgerSeq;
644  return;
645  }
646  if (ledger->info().txHash.isNonZero() && !ledger->txMap().isValid())
647  {
648  JLOG(j_.error()) << "invalid transaction map for ledger sequence "
649  << ledgerSeq;
650  return;
651  }
652 
653  auto const shardIndex{seqToShardIndex(ledgerSeq)};
655  {
656  std::lock_guard lock(mutex_);
657  assert(init_);
658 
659  if (shardIndex != acquireIndex_)
660  {
661  JLOG(j_.trace())
662  << "shard " << shardIndex << " is not being acquired";
663  return;
664  }
665 
666  auto const it{shards_.find(shardIndex)};
667  if (it == shards_.end())
668  {
669  JLOG(j_.error())
670  << "shard " << shardIndex << " is not being acquired";
671  return;
672  }
673  shard = it->second;
674  }
675 
676  if (shard->containsLedger(ledgerSeq))
677  {
678  JLOG(j_.trace()) << "shard " << shardIndex << " ledger already stored";
679  return;
680  }
681 
682  setStoredInShard(shard, ledger);
683 }
684 
687 {
688  std::lock_guard lock(mutex_);
689  assert(init_);
690 
691  return status_;
692 }
693 
694 void
696 {
697  // Stop read threads in base before data members are destroyed
698  stopReadThreads();
699 
700  std::lock_guard lock(mutex_);
701 
702  // Notify shards to stop
703  for (auto const& e : shards_)
704  e.second->stop();
705 }
706 
707 void
709 {
711  {
712  std::lock_guard lock(mutex_);
713 
714  shards.reserve(shards_.size());
715  for (auto const& e : shards_)
716  shards.push_back(e.second);
717  shards_.clear();
718  }
719 
720  // All shards should be expired at this point
721  for (auto const& e : shards)
722  {
723  if (!e.expired())
724  {
725  std::string shardIndex;
726  if (auto const shard{e.lock()}; shard)
727  shardIndex = std::to_string(shard->index());
728 
729  JLOG(j_.warn()) << " shard " << shardIndex << " unexpired";
730  }
731  }
732 
733  stopped();
734 }
735 
736 void
738 {
739  {
740  std::lock_guard lock(mutex_);
741  assert(init_);
742 
743  // Only the application local node store can be imported
744  if (&source != &app_.getNodeStore())
745  {
746  assert(false);
747  JLOG(j_.error()) << "invalid source database";
748  return;
749  }
750 
751  std::uint32_t earliestIndex;
752  std::uint32_t latestIndex;
753  {
754  auto loadLedger = [&](bool ascendSort =
757  std::uint32_t ledgerSeq{0};
759  if (ascendSort)
760  {
761  info =
762  dynamic_cast<RelationalDBInterfaceSqlite*>(
765  }
766  else
767  {
768  info =
769  dynamic_cast<RelationalDBInterfaceSqlite*>(
772  }
773  if (info)
774  {
775  ledger = loadLedgerHelper(*info, app_, false);
776  ledgerSeq = info->seq;
777  }
778  if (!ledger || ledgerSeq == 0)
779  {
780  JLOG(j_.error()) << "no suitable ledgers were found in"
781  " the SQLite database to import";
782  return std::nullopt;
783  }
784  return ledgerSeq;
785  };
786 
787  // Find earliest ledger sequence stored
788  auto ledgerSeq{loadLedger()};
789  if (!ledgerSeq)
790  return;
791  earliestIndex = seqToShardIndex(*ledgerSeq);
792 
793  // Consider only complete shards
794  if (ledgerSeq != firstLedgerSeq(earliestIndex))
795  ++earliestIndex;
796 
797  // Find last ledger sequence stored
798  ledgerSeq = loadLedger(false);
799  if (!ledgerSeq)
800  return;
801  latestIndex = seqToShardIndex(*ledgerSeq);
802 
803  // Consider only complete shards
804  if (ledgerSeq != lastLedgerSeq(latestIndex))
805  --latestIndex;
806 
807  if (latestIndex < earliestIndex)
808  {
809  JLOG(j_.error()) << "no suitable ledgers were found in"
810  " the SQLite database to import";
811  return;
812  }
813  }
814 
815  auto numHistShards = this->numHistoricalShards(lock);
816 
817  // Import the shards
818  for (std::uint32_t shardIndex = earliestIndex;
819  shardIndex <= latestIndex;
820  ++shardIndex)
821  {
822  auto const pathDesignation =
823  prepareForNewShard(shardIndex, numHistShards, lock);
824 
825  if (!pathDesignation)
826  break;
827 
828  auto const needsHistoricalPath =
829  *pathDesignation == PathDesignation::historical;
830 
831  // Skip if being acquired
832  if (shardIndex == acquireIndex_)
833  {
834  JLOG(j_.debug())
835  << "shard " << shardIndex << " already being acquired";
836  continue;
837  }
838 
839  // Skip if being imported
840  if (preparedIndexes_.find(shardIndex) != preparedIndexes_.end())
841  {
842  JLOG(j_.debug())
843  << "shard " << shardIndex << " already being imported";
844  continue;
845  }
846 
847  // Skip if stored
848  if (shards_.find(shardIndex) != shards_.end())
849  {
850  JLOG(j_.debug()) << "shard " << shardIndex << " already stored";
851  continue;
852  }
853 
854  // Verify SQLite ledgers are in the node store
855  {
856  auto const firstSeq{firstLedgerSeq(shardIndex)};
857  auto const lastSeq{
858  std::max(firstSeq, lastLedgerSeq(shardIndex))};
859  auto const numLedgers{
860  shardIndex == earliestShardIndex() ? lastSeq - firstSeq + 1
861  : ledgersPerShard_};
862  auto ledgerHashes{
864  firstSeq, lastSeq)};
865  if (ledgerHashes.size() != numLedgers)
866  continue;
867 
868  bool valid{true};
869  for (std::uint32_t n = firstSeq; n <= lastSeq; n += 256)
870  {
871  if (!source.fetchNodeObject(ledgerHashes[n].ledgerHash, n))
872  {
873  JLOG(j_.warn()) << "SQLite ledger sequence " << n
874  << " mismatches node store";
875  valid = false;
876  break;
877  }
878  }
879  if (!valid)
880  continue;
881  }
882 
883  auto const path =
884  needsHistoricalPath ? chooseHistoricalPath(lock) : dir_;
885 
886  // Create the new shard
887  auto shard{
888  std::make_unique<Shard>(app_, *this, shardIndex, path, j_)};
889  if (!shard->init(scheduler_, *ctx_))
890  continue;
891 
892  // Create a marker file to signify an import in progress
893  auto const shardDir{path / std::to_string(shardIndex)};
894  auto const markerFile{shardDir / importMarker_};
895  {
896  std::ofstream ofs{markerFile.string()};
897  if (!ofs.is_open())
898  {
899  JLOG(j_.error()) << "shard " << shardIndex
900  << " failed to create temp marker file";
901  shard->removeOnDestroy();
902  continue;
903  }
904  ofs.close();
905  }
906 
907  // Copy the ledgers from node store
908  std::shared_ptr<Ledger> recentStored;
909  std::optional<uint256> lastLedgerHash;
910 
911  while (auto const ledgerSeq = shard->prepare())
912  {
913  auto ledger{loadByIndex(*ledgerSeq, app_, false)};
914  if (!ledger || ledger->info().seq != ledgerSeq)
915  break;
916 
917  auto const result{shard->storeLedger(ledger, recentStored)};
918  storeStats(result.count, result.size);
919  if (result.error)
920  break;
921 
922  if (!shard->setLedgerStored(ledger))
923  break;
924 
925  if (!lastLedgerHash && ledgerSeq == lastLedgerSeq(shardIndex))
926  lastLedgerHash = ledger->info().hash;
927 
928  recentStored = std::move(ledger);
929  }
930 
931  using namespace boost::filesystem;
932  bool success{false};
933  if (lastLedgerHash && shard->getState() == Shard::complete)
934  {
935  // Store shard final key
936  Serializer s;
938  s.add32(firstLedgerSeq(shardIndex));
939  s.add32(lastLedgerSeq(shardIndex));
940  s.addBitString(*lastLedgerHash);
941  auto const nodeObject{NodeObject::createObject(
942  hotUNKNOWN, std::move(s.modData()), Shard::finalKey)};
943 
944  if (shard->storeNodeObject(nodeObject))
945  {
946  try
947  {
948  // The import process is complete and the
949  // marker file is no longer required
950  remove_all(markerFile);
951 
952  JLOG(j_.debug()) << "shard " << shardIndex
953  << " was successfully imported";
955  shards_.emplace(shardIndex, std::move(shard))
956  .first->second,
957  true,
958  std::nullopt);
959  success = true;
960 
961  if (shardIndex < shardBoundaryIndex())
962  ++numHistShards;
963  }
964  catch (std::exception const& e)
965  {
966  JLOG(j_.fatal()) << "shard index " << shardIndex
967  << ". Exception caught in function "
968  << __func__ << ". Error: " << e.what();
969  }
970  }
971  }
972 
973  if (!success)
974  {
975  JLOG(j_.error())
976  << "shard " << shardIndex << " failed to import";
977  shard->removeOnDestroy();
978  }
979  }
980 
981  updateStatus(lock);
982  }
983 
984  setFileStats();
985 }
986 
989 {
991  {
992  std::lock_guard lock(mutex_);
993  assert(init_);
994 
995  auto const it{shards_.find(acquireIndex_)};
996  if (it == shards_.end())
997  return 0;
998  shard = it->second;
999  }
1000 
1001  return shard->getWriteLoad();
1002 }
1003 
1004 void
1006  NodeObjectType type,
1007  Blob&& data,
1008  uint256 const& hash,
1009  std::uint32_t ledgerSeq)
1010 {
1011  auto const shardIndex{seqToShardIndex(ledgerSeq)};
1012  std::shared_ptr<Shard> shard;
1013  {
1014  std::lock_guard lock(mutex_);
1015  if (shardIndex != acquireIndex_)
1016  {
1017  JLOG(j_.trace())
1018  << "shard " << shardIndex << " is not being acquired";
1019  return;
1020  }
1021 
1022  auto const it{shards_.find(shardIndex)};
1023  if (it == shards_.end())
1024  {
1025  JLOG(j_.error())
1026  << "shard " << shardIndex << " is not being acquired";
1027  return;
1028  }
1029  shard = it->second;
1030  }
1031 
1032  auto const nodeObject{
1033  NodeObject::createObject(type, std::move(data), hash)};
1034  if (shard->storeNodeObject(nodeObject))
1035  storeStats(1, nodeObject->getData().size());
1036 }
1037 
1038 bool
1040 {
1041  auto const ledgerSeq{srcLedger->info().seq};
1042  auto const shardIndex{seqToShardIndex(ledgerSeq)};
1043  std::shared_ptr<Shard> shard;
1044  {
1045  std::lock_guard lock(mutex_);
1046  assert(init_);
1047 
1048  if (shardIndex != acquireIndex_)
1049  {
1050  JLOG(j_.trace())
1051  << "shard " << shardIndex << " is not being acquired";
1052  return false;
1053  }
1054 
1055  auto const it{shards_.find(shardIndex)};
1056  if (it == shards_.end())
1057  {
1058  JLOG(j_.error())
1059  << "shard " << shardIndex << " is not being acquired";
1060  return false;
1061  }
1062  shard = it->second;
1063  }
1064 
1065  auto const result{shard->storeLedger(srcLedger, nullptr)};
1066  storeStats(result.count, result.size);
1067  if (result.error || result.count == 0 || result.size == 0)
1068  return false;
1069 
1070  return setStoredInShard(shard, srcLedger);
1071 }
1072 
1073 void
1075 {
1077  {
1078  std::lock_guard lock(mutex_);
1079  assert(init_);
1080 
1081  shards.reserve(shards_.size());
1082  for (auto const& e : shards_)
1083  shards.push_back(e.second);
1084  }
1085 
1087  openFinals.reserve(openFinalLimit_);
1088 
1089  for (auto const& e : shards)
1090  {
1091  if (auto const shard{e.lock()}; shard && shard->isOpen())
1092  {
1093  shard->sweep();
1094 
1095  if (shard->getState() == Shard::final)
1096  openFinals.emplace_back(std::move(shard));
1097  }
1098  }
1099 
1100  if (openFinals.size() > openFinalLimit_)
1101  {
1102  JLOG(j_.trace()) << "Open shards exceed configured limit of "
1103  << openFinalLimit_ << " by "
1104  << (openFinals.size() - openFinalLimit_);
1105 
1106  // Try to close enough shards to be within the limit.
1107  // Sort ascending on last use so the oldest are removed first.
1108  std::sort(
1109  openFinals.begin(),
1110  openFinals.end(),
1111  [&](std::shared_ptr<Shard> const& lhsShard,
1112  std::shared_ptr<Shard> const& rhsShard) {
1113  return lhsShard->getLastUse() < rhsShard->getLastUse();
1114  });
1115 
1116  for (auto it{openFinals.cbegin()};
1117  it != openFinals.cend() && openFinals.size() > openFinalLimit_;)
1118  {
1119  if ((*it)->tryClose())
1120  it = openFinals.erase(it);
1121  else
1122  ++it;
1123  }
1124  }
1125 }
1126 
1127 bool
1129 {
1130  auto fail = [j = j_](std::string const& msg) {
1131  JLOG(j.error()) << "[" << ConfigSection::shardDatabase() << "] " << msg;
1132  return false;
1133  };
1134 
1135  Config const& config{app_.config()};
1136  Section const& section{config.section(ConfigSection::shardDatabase())};
1137 
1138  {
1139  // The earliest ledger sequence defaults to XRP_LEDGER_EARLIEST_SEQ.
1140  // A custom earliest ledger sequence can be set through the
1141  // configuration file using the 'earliest_seq' field under the
1142  // 'node_db' and 'shard_db' stanzas. If specified, this field must
1143  // have a value greater than zero and be equally assigned in
1144  // both stanzas.
1145 
1146  std::uint32_t shardDBEarliestSeq{0};
1147  get_if_exists<std::uint32_t>(
1148  section, "earliest_seq", shardDBEarliestSeq);
1149 
1150  std::uint32_t nodeDBEarliestSeq{0};
1151  get_if_exists<std::uint32_t>(
1152  config.section(ConfigSection::nodeDatabase()),
1153  "earliest_seq",
1154  nodeDBEarliestSeq);
1155 
1156  if (shardDBEarliestSeq != nodeDBEarliestSeq)
1157  {
1158  return fail(
1159  "and [" + ConfigSection::nodeDatabase() +
1160  "] define different 'earliest_seq' values");
1161  }
1162  }
1163 
1164  using namespace boost::filesystem;
1165  if (!get_if_exists<path>(section, "path", dir_))
1166  return fail("'path' missing");
1167 
1168  {
1169  get_if_exists(section, "max_historical_shards", maxHistoricalShards_);
1170 
1171  Section const& historicalShardPaths =
1172  config.section(SECTION_HISTORICAL_SHARD_PATHS);
1173 
1174  auto values = historicalShardPaths.values();
1175 
1176  std::sort(values.begin(), values.end());
1177  values.erase(std::unique(values.begin(), values.end()), values.end());
1178 
1179  for (auto const& s : values)
1180  {
1181  auto const dir = path(s);
1182  if (dir_ == dir)
1183  {
1184  return fail(
1185  "the 'path' cannot also be in the "
1186  "'historical_shard_path' section");
1187  }
1188 
1190  }
1191  }
1192 
1193  if (section.exists("ledgers_per_shard"))
1194  {
1195  // To be set only in standalone for testing
1196  if (!config.standalone())
1197  return fail("'ledgers_per_shard' only honored in stand alone");
1198 
1199  ledgersPerShard_ = get<std::uint32_t>(section, "ledgers_per_shard");
1200  if (ledgersPerShard_ == 0 || ledgersPerShard_ % 256 != 0)
1201  return fail("'ledgers_per_shard' must be a multiple of 256");
1202 
1205  }
1206 
1207  // NuDB is the default and only supported permanent storage backend
1208  backendName_ = get<std::string>(section, "type", "nudb");
1209  if (!boost::iequals(backendName_, "NuDB"))
1210  return fail("'type' value unsupported");
1211 
1212  return true;
1213 }
1214 
1217  uint256 const& hash,
1218  std::uint32_t ledgerSeq,
1219  FetchReport& fetchReport)
1220 {
1221  auto const shardIndex{seqToShardIndex(ledgerSeq)};
1222  std::shared_ptr<Shard> shard;
1223  {
1224  std::lock_guard lock(mutex_);
1225  auto const it{shards_.find(shardIndex)};
1226  if (it == shards_.end())
1227  return nullptr;
1228  shard = it->second;
1229  }
1230 
1231  return shard->fetchNodeObject(hash, fetchReport);
1232 }
1233 
1236  std::uint32_t validLedgerSeq,
1238 {
1239  if (validLedgerSeq < earliestLedgerSeq())
1240  return std::nullopt;
1241 
1242  auto const maxShardIndex{[this, validLedgerSeq]() {
1243  auto shardIndex{seqToShardIndex(validLedgerSeq)};
1244  if (validLedgerSeq != lastLedgerSeq(shardIndex))
1245  --shardIndex;
1246  return shardIndex;
1247  }()};
1248  auto const maxNumShards{maxShardIndex - earliestShardIndex() + 1};
1249 
1250  // Check if the shard store has all shards
1251  if (shards_.size() >= maxNumShards)
1252  return std::nullopt;
1253 
1254  if (maxShardIndex < 1024 ||
1255  static_cast<float>(shards_.size()) / maxNumShards > 0.5f)
1256  {
1257  // Small or mostly full index space to sample
1258  // Find the available indexes and select one at random
1260  available.reserve(maxNumShards - shards_.size());
1261 
1262  for (auto shardIndex = earliestShardIndex();
1263  shardIndex <= maxShardIndex;
1264  ++shardIndex)
1265  {
1266  if (shards_.find(shardIndex) == shards_.end() &&
1267  preparedIndexes_.find(shardIndex) == preparedIndexes_.end())
1268  {
1269  available.push_back(shardIndex);
1270  }
1271  }
1272 
1273  if (available.empty())
1274  return std::nullopt;
1275 
1276  if (available.size() == 1)
1277  return available.front();
1278 
1279  return available[rand_int(
1280  0u, static_cast<std::uint32_t>(available.size() - 1))];
1281  }
1282 
1283  // Large, sparse index space to sample
1284  // Keep choosing indexes at random until an available one is found
1285  // chances of running more than 30 times is less than 1 in a billion
1286  for (int i = 0; i < 40; ++i)
1287  {
1288  auto const shardIndex{rand_int(earliestShardIndex(), maxShardIndex)};
1289  if (shards_.find(shardIndex) == shards_.end() &&
1290  preparedIndexes_.find(shardIndex) == preparedIndexes_.end())
1291  {
1292  return shardIndex;
1293  }
1294  }
1295 
1296  assert(false);
1297  return std::nullopt;
1298 }
1299 
1300 void
1302  std::shared_ptr<Shard>& shard,
1303  bool writeSQLite,
1304  std::optional<uint256> const& expectedHash)
1305 {
1306  taskQueue_->addTask([this,
1307  wptr = std::weak_ptr<Shard>(shard),
1308  writeSQLite,
1309  expectedHash]() {
1310  if (isStopping())
1311  return;
1312 
1313  auto shard{wptr.lock()};
1314  if (!shard)
1315  {
1316  JLOG(j_.debug()) << "Shard removed before being finalized";
1317  return;
1318  }
1319 
1320  if (!shard->finalize(writeSQLite, expectedHash))
1321  {
1322  if (isStopping())
1323  return;
1324 
1325  // Invalid or corrupt shard, remove it
1326  removeFailedShard(shard);
1327  return;
1328  }
1329 
1330  if (isStopping())
1331  return;
1332 
1333  {
1334  auto const boundaryIndex{shardBoundaryIndex()};
1335 
1336  std::lock_guard lock(mutex_);
1337  updateStatus(lock);
1338 
1339  if (shard->index() < boundaryIndex)
1340  {
1341  // This is a historical shard
1342  if (!historicalPaths_.empty() &&
1343  shard->getDir().parent_path() == dir_)
1344  {
1345  // Shard wasn't placed at a separate historical path
1346  JLOG(j_.warn()) << "shard " << shard->index()
1347  << " is not stored at a historical path";
1348  }
1349  }
1350 
1351  else
1352  {
1353  // Not a historical shard. Shift recent shards if necessary
1354  relocateOutdatedShards(lock);
1355  assert(!boundaryIndex || shard->index() - boundaryIndex <= 1);
1356 
1357  auto& recentShard = shard->index() == boundaryIndex
1360 
1361  // Set the appropriate recent shard index
1362  recentShard = shard->index();
1363 
1364  if (shard->getDir().parent_path() != dir_)
1365  {
1366  JLOG(j_.warn()) << "shard " << shard->index()
1367  << " is not stored at the path";
1368  }
1369  }
1370  }
1371 
1372  setFileStats();
1373 
1374  // Update peers with new shard index
1375  if (!app_.config().standalone() &&
1377  {
1378  protocol::TMPeerShardInfo message;
1379  PublicKey const& publicKey{app_.nodeIdentity().first};
1380  message.set_nodepubkey(publicKey.data(), publicKey.size());
1381  message.set_shardindexes(std::to_string(shard->index()));
1382  app_.overlay().foreach(send_always(std::make_shared<Message>(
1383  message, protocol::mtPEER_SHARD_INFO)));
1384  }
1385  });
1386 }
1387 
1388 void
1390 {
1392  {
1393  std::lock_guard lock(mutex_);
1394  if (shards_.empty())
1395  return;
1396 
1397  shards.reserve(shards_.size());
1398  for (auto const& e : shards_)
1399  shards.push_back(e.second);
1400  }
1401 
1402  std::uint64_t sumSz{0};
1403  std::uint32_t sumFd{0};
1404  std::uint32_t numShards{0};
1405  for (auto const& e : shards)
1406  {
1407  if (auto const shard{e.lock()}; shard)
1408  {
1409  auto const [sz, fd] = shard->getFileInfo();
1410  sumSz += sz;
1411  sumFd += fd;
1412  ++numShards;
1413  }
1414  }
1415 
1416  std::lock_guard lock(mutex_);
1417  fileSz_ = sumSz;
1418  fdRequired_ = sumFd;
1419  avgShardFileSz_ = (numShards == 0 ? fileSz_ : fileSz_ / numShards);
1420 
1421  if (!canAdd_)
1422  return;
1423 
1424  if (auto const count = numHistoricalShards(lock);
1425  count >= maxHistoricalShards_)
1426  {
1428  {
1429  // In order to avoid excessive output, don't produce
1430  // this warning if the server isn't configured to
1431  // store historical shards.
1432  JLOG(j_.warn()) << "maximum number of historical shards reached";
1433  }
1434 
1435  canAdd_ = false;
1436  }
1437  else if (!sufficientStorage(
1438  maxHistoricalShards_ - count,
1440  lock))
1441  {
1442  JLOG(j_.warn())
1443  << "maximum shard store size exceeds available storage space";
1444 
1445  canAdd_ = false;
1446  }
1447 }
1448 
1449 void
1451 {
1452  if (!shards_.empty())
1453  {
1455  for (auto const& e : shards_)
1456  if (e.second->getState() == Shard::final)
1457  rs.insert(e.second->index());
1458  status_ = to_string(rs);
1459  }
1460  else
1461  status_.clear();
1462 }
1463 
1464 bool
1466  std::uint32_t numShards,
1467  PathDesignation pathDesignation,
1468  std::lock_guard<std::mutex> const&) const
1469 {
1470  try
1471  {
1472  std::vector<std::uint64_t> capacities;
1473 
1474  if (pathDesignation == PathDesignation::historical &&
1476  {
1477  capacities.reserve(historicalPaths_.size());
1478 
1479  for (auto const& path : historicalPaths_)
1480  {
1481  // Get the available storage for each historical path
1482  auto const availableSpace =
1483  boost::filesystem::space(path).available;
1484 
1485  capacities.push_back(availableSpace);
1486  }
1487  }
1488  else
1489  {
1490  // Get the available storage for the main shard path
1491  capacities.push_back(boost::filesystem::space(dir_).available);
1492  }
1493 
1494  for (std::uint64_t const capacity : capacities)
1495  {
1496  // Leverage all the historical shard paths to
1497  // see if collectively they can fit the specified
1498  // number of shards. For this to work properly,
1499  // each historical path must correspond to a separate
1500  // physical device or filesystem.
1501 
1502  auto const shardCap = capacity / avgShardFileSz_;
1503  if (numShards <= shardCap)
1504  return true;
1505 
1506  numShards -= shardCap;
1507  }
1508  }
1509  catch (std::exception const& e)
1510  {
1511  JLOG(j_.fatal()) << "Exception caught in function " << __func__
1512  << ". Error: " << e.what();
1513  return false;
1514  }
1515 
1516  return false;
1517 }
1518 
1519 bool
1521  std::shared_ptr<Shard>& shard,
1522  std::shared_ptr<Ledger const> const& ledger)
1523 {
1524  if (!shard->setLedgerStored(ledger))
1525  {
1526  // Invalid or corrupt shard, remove it
1527  removeFailedShard(shard);
1528  return false;
1529  }
1530 
1531  if (shard->getState() == Shard::complete)
1532  {
1533  std::lock_guard lock(mutex_);
1534  if (auto const it{shards_.find(shard->index())}; it != shards_.end())
1535  {
1536  if (shard->index() == acquireIndex_)
1537  acquireIndex_ = 0;
1538 
1539  finalizeShard(it->second, false, std::nullopt);
1540  }
1541  else
1542  {
1543  JLOG(j_.debug())
1544  << "shard " << shard->index() << " is no longer being acquired";
1545  }
1546  }
1547 
1548  setFileStats();
1549  return true;
1550 }
1551 
1552 void
1554 {
1555  {
1556  std::lock_guard lock(mutex_);
1557 
1558  if (shard->index() == acquireIndex_)
1559  acquireIndex_ = 0;
1560 
1561  if (shard->index() == latestShardIndex_)
1562  latestShardIndex_ = std::nullopt;
1563 
1564  if (shard->index() == secondLatestShardIndex_)
1565  secondLatestShardIndex_ = std::nullopt;
1566 
1567  if ((shards_.erase(shard->index()) > 0) &&
1568  shard->getState() == Shard::final)
1569  {
1570  updateStatus(lock);
1571  }
1572  }
1573 
1574  shard->removeOnDestroy();
1575 
1576  // Reset the shared_ptr to invoke the shard's
1577  // destructor and remove it from the server
1578  shard.reset();
1579  setFileStats();
1580 }
1581 
1584 {
1585  auto const validIndex = app_.getLedgerMaster().getValidLedgerIndex();
1586 
1587  if (validIndex < earliestLedgerSeq())
1588  return 0;
1589 
1590  // Shards with an index earlier than the recent shard boundary index
1591  // are considered historical. The three shards at or later than
1592  // this index consist of the two most recently validated shards
1593  // and the shard still in the process of being built by live
1594  // transactions.
1595  return seqToShardIndex(validIndex) - 1;
1596 }
1597 
1600  std::lock_guard<std::mutex> const& lock) const
1601 {
1602  auto const boundaryIndex{shardBoundaryIndex()};
1603  return std::count_if(
1604  shards_.begin(), shards_.end(), [boundaryIndex](auto const& entry) {
1605  return entry.first < boundaryIndex;
1606  });
1607 }
1608 
1609 void
1611  std::lock_guard<std::mutex> const& lock)
1612 {
1613  if (auto& cur = latestShardIndex_, &prev = secondLatestShardIndex_;
1614  cur || prev)
1615  {
1616  auto const latestShardIndex =
1618 
1619  auto const separateHistoricalPath = !historicalPaths_.empty();
1620 
1621  auto const removeShard =
1622  [this](std::uint32_t const shardIndex) -> void {
1623  canAdd_ = false;
1624 
1625  if (auto it = shards_.find(shardIndex); it != shards_.end())
1626  {
1627  if (it->second)
1628  removeFailedShard(it->second);
1629  else
1630  {
1631  JLOG(j_.warn()) << "can't find shard to remove";
1632  }
1633  }
1634  else
1635  {
1636  JLOG(j_.warn()) << "can't find shard to remove";
1637  }
1638  };
1639 
1640  auto const keepShard =
1641  [this, &lock, removeShard, separateHistoricalPath](
1642  std::uint32_t const shardIndex) -> bool {
1644  {
1645  JLOG(j_.error())
1646  << "maximum number of historical shards reached";
1647 
1648  removeShard(shardIndex);
1649  return false;
1650  }
1651  if (separateHistoricalPath &&
1653  {
1654  JLOG(j_.error()) << "insufficient storage space available";
1655 
1656  removeShard(shardIndex);
1657  return false;
1658  }
1659 
1660  return true;
1661  };
1662 
1663  // Move a shard from the main shard path to a historical shard
1664  // path by copying the contents, and creating a new shard.
1665  auto const moveShard = [this,
1666  &lock](std::uint32_t const shardIndex) -> void {
1667  auto const dst = chooseHistoricalPath(lock);
1668 
1669  if (auto it = shards_.find(shardIndex); it != shards_.end())
1670  {
1671  auto& shard{it->second};
1672 
1673  // Close any open file descriptors before moving the shard
1674  // directory. Don't call removeOnDestroy since that would
1675  // attempt to close the fds after the directory has been moved.
1676  if (!shard->tryClose())
1677  {
1678  JLOG(j_.warn())
1679  << "can't close shard to move to historical path";
1680  return;
1681  }
1682 
1683  try
1684  {
1685  // Move the shard directory to the new path
1686  boost::filesystem::rename(
1687  shard->getDir().string(),
1688  dst / std::to_string(shardIndex));
1689  }
1690  catch (...)
1691  {
1692  JLOG(j_.error()) << "shard " << shardIndex
1693  << " failed to move to historical storage";
1694  return;
1695  }
1696 
1697  // Create a shard instance at the new location
1698  shard =
1699  std::make_shared<Shard>(app_, *this, shardIndex, dst, j_);
1700 
1701  // Open the new shard
1702  if (!shard->init(scheduler_, *ctx_))
1703  {
1704  JLOG(j_.error()) << "shard " << shardIndex
1705  << " failed to open in historical storage";
1706  shard->removeOnDestroy();
1707  shard.reset();
1708  }
1709  }
1710  else
1711  {
1712  JLOG(j_.warn())
1713  << "can't find shard to move to historical path";
1714  }
1715  };
1716 
1717  // See if either of the recent shards needs to be updated
1718  bool const curNotSynched =
1719  latestShardIndex_ && *latestShardIndex_ != latestShardIndex;
1720  bool const prevNotSynched = secondLatestShardIndex_ &&
1721  *secondLatestShardIndex_ != latestShardIndex - 1;
1722 
1723  // A new shard has been published. Move outdated
1724  // shards to historical storage as needed
1725  if (curNotSynched || prevNotSynched)
1726  {
1727  if (prev)
1728  {
1729  // Move the formerly second latest shard to historical storage
1730  if (keepShard(*prev) && separateHistoricalPath)
1731  {
1732  moveShard(*prev);
1733  }
1734 
1735  prev = std::nullopt;
1736  }
1737 
1738  if (cur)
1739  {
1740  // The formerly latest shard is now the second latest
1741  if (cur == latestShardIndex - 1)
1742  {
1743  prev = cur;
1744  }
1745 
1746  // The formerly latest shard is no longer a 'recent' shard
1747  else
1748  {
1749  // Move the formerly latest shard to historical storage
1750  if (keepShard(*cur) && separateHistoricalPath)
1751  {
1752  moveShard(*cur);
1753  }
1754  }
1755 
1756  cur = std::nullopt;
1757  }
1758  }
1759  }
1760 }
1761 
1764  std::uint32_t shardIndex,
1766  std::lock_guard<std::mutex> const& lock)
1767 {
1768  // Any shard earlier than the two most recent shards is a historical shard
1769  auto const boundaryIndex{shardBoundaryIndex()};
1770  auto const isHistoricalShard = shardIndex < boundaryIndex;
1771 
1772  auto const designation = isHistoricalShard && !historicalPaths_.empty()
1775 
1776  // Check shard count and available storage space
1777  if (isHistoricalShard && numHistoricalShards >= maxHistoricalShards_)
1778  {
1779  JLOG(j_.error()) << "maximum number of historical shards reached";
1780  canAdd_ = false;
1781  return std::nullopt;
1782  }
1783  if (!sufficientStorage(1, designation, lock))
1784  {
1785  JLOG(j_.error()) << "insufficient storage space available";
1786  canAdd_ = false;
1787  return std::nullopt;
1788  }
1789 
1790  return designation;
1791 }
1792 
1793 boost::filesystem::path
1795 {
1796  // If not configured with separate historical paths,
1797  // use the main path (dir_) by default.
1798  if (historicalPaths_.empty())
1799  return dir_;
1800 
1801  boost::filesystem::path historicalShardPath;
1802  std::vector<boost::filesystem::path> potentialPaths;
1803 
1804  for (boost::filesystem::path const& path : historicalPaths_)
1805  {
1806  if (boost::filesystem::space(path).available >= avgShardFileSz_)
1807  potentialPaths.push_back(path);
1808  }
1809 
1810  if (potentialPaths.empty())
1811  {
1812  JLOG(j_.error()) << "failed to select a historical shard path";
1813  return "";
1814  }
1815 
1816  std::sample(
1817  potentialPaths.begin(),
1818  potentialPaths.end(),
1819  &historicalShardPath,
1820  1,
1821  default_prng());
1822 
1823  return historicalShardPath;
1824 }
1825 
1826 bool
1828 {
1829 #if BOOST_OS_LINUX
1830  // Each historical shard path must correspond
1831  // to a directory on a distinct device or file system.
1832  // Currently, this constraint is enforced only on Linux.
1835 
1836  for (auto const& path : historicalPaths_)
1837  {
1838  struct statvfs buffer;
1839  if (statvfs(path.c_str(), &buffer))
1840  {
1841  JLOG(j_.error())
1842  << "failed to acquire stats for 'historical_shard_path': "
1843  << path;
1844  return false;
1845  }
1846 
1847  filesystemIDs[buffer.f_fsid].push_back(path.string());
1848  }
1849 
1850  bool ret = true;
1851  for (auto const& entry : filesystemIDs)
1852  {
1853  // Check to see if any of the paths are stored on the same file system
1854  if (entry.second.size() > 1)
1855  {
1856  // Two or more historical storage paths
1857  // correspond to the same file system.
1858  JLOG(j_.error())
1859  << "The following paths correspond to the same filesystem: "
1860  << boost::algorithm::join(entry.second, ", ")
1861  << ". Each configured historical storage path should"
1862  " be on a unique device or filesystem.";
1863 
1864  ret = false;
1865  }
1866  }
1867 
1868  return ret;
1869 
1870 #else
1871  // The requirement that each historical storage path
1872  // corresponds to a distinct device or file system is
1873  // enforced only on Linux, so on other platforms
1874  // keep track of the available capacities for each
1875  // path. Issue a warning if we suspect any of the paths
1876  // may violate this requirement.
1877 
1878  // Map byte counts to each path that shares that byte count.
1880  uniqueCapacities(historicalPaths_.size());
1881 
1882  for (auto const& path : historicalPaths_)
1883  uniqueCapacities[boost::filesystem::space(path).available].push_back(
1884  path.string());
1885 
1886  for (auto const& entry : uniqueCapacities)
1887  {
1888  // Check to see if any paths have the same amount of available bytes.
1889  if (entry.second.size() > 1)
1890  {
1891  // Two or more historical storage paths may
1892  // correspond to the same device or file system.
1893  JLOG(j_.warn())
1894  << "Each of the following paths have " << entry.first
1895  << " bytes free, and may be located on the same device"
1896  " or file system: "
1897  << boost::algorithm::join(entry.second, ", ")
1898  << ". Each configured historical storage path should"
1899  " be on a unique device or file system.";
1900  }
1901  }
1902 #endif
1903 
1904  return true;
1905 }
1906 
1907 bool
1909  LedgerIndex ledgerSeq,
1910  std::function<bool(soci::session& session, std::uint32_t index)> const&
1911  callback)
1912 {
1913  std::lock_guard lock(mutex_);
1914  auto shardIndex = seqToShardIndex(ledgerSeq);
1915 
1916  if (shards_.count(shardIndex) &&
1917  shards_[shardIndex]->getState() == Shard::State::final)
1918  {
1919  return shards_[shardIndex]->callForLedgerSQL(callback);
1920  }
1921 
1922  return false;
1923 }
1924 
1925 bool
1927  LedgerIndex ledgerSeq,
1928  std::function<bool(soci::session& session, std::uint32_t index)> const&
1929  callback)
1930 {
1931  std::lock_guard lock(mutex_);
1932  auto shardIndex = seqToShardIndex(ledgerSeq);
1933 
1934  if (shards_.count(shardIndex) &&
1935  shards_[shardIndex]->getState() == Shard::State::final)
1936  {
1937  return shards_[shardIndex]->callForTransactionSQL(callback);
1938  }
1939 
1940  return false;
1941 }
1942 
1943 bool
1945  std::optional<std::uint32_t> minShardIndex,
1946  std::function<bool(Shard& shard)> const& visit)
1947 {
1948  std::lock_guard lock(mutex_);
1949 
1951 
1952  if (!minShardIndex)
1953  it = shards_.begin();
1954  else
1955  it = shards_.lower_bound(*minShardIndex);
1956 
1957  eit = shards_.end();
1958 
1959  for (; it != eit; it++)
1960  {
1961  if (it->second->getState() == Shard::State::final)
1962  {
1963  if (!visit(*it->second))
1964  return false;
1965  }
1966  }
1967 
1968  return true;
1969 }
1970 bool
1972  std::optional<std::uint32_t> minShardIndex,
1973  std::function<bool(soci::session& session, std::uint32_t index)> const&
1974  callback)
1975 {
1976  return iterateShardsForward(
1977  minShardIndex, [&callback](Shard& shard) -> bool {
1978  return shard.callForLedgerSQL(callback);
1979  });
1980 }
1981 
1982 bool
1984  std::optional<std::uint32_t> minShardIndex,
1985  std::function<bool(soci::session& session, std::uint32_t index)> const&
1986  callback)
1987 {
1988  return iterateShardsForward(
1989  minShardIndex, [&callback](Shard& shard) -> bool {
1990  return shard.callForTransactionSQL(callback);
1991  });
1992 }
1993 
1994 bool
1996  std::optional<std::uint32_t> maxShardIndex,
1997  std::function<bool(Shard& shard)> const& visit)
1998 {
1999  std::lock_guard lock(mutex_);
2000 
2001  std::map<std::uint32_t, std::shared_ptr<Shard>>::reverse_iterator it, eit;
2002 
2003  if (!maxShardIndex)
2004  it = shards_.rbegin();
2005  else
2006  it = std::make_reverse_iterator(shards_.upper_bound(*maxShardIndex));
2007 
2008  eit = shards_.rend();
2009 
2010  for (; it != eit; it++)
2011  {
2012  if (it->second->getState() == Shard::State::final &&
2013  (!maxShardIndex || it->first <= *maxShardIndex))
2014  {
2015  if (!visit(*it->second))
2016  return false;
2017  }
2018  }
2019 
2020  return true;
2021 }
2022 
2023 bool
2025  std::optional<std::uint32_t> maxShardIndex,
2026  std::function<bool(soci::session& session, std::uint32_t index)> const&
2027  callback)
2028 {
2029  return iterateShardsBack(maxShardIndex, [&callback](Shard& shard) -> bool {
2030  return shard.callForLedgerSQL(callback);
2031  });
2032 }
2033 
2034 bool
2036  std::optional<std::uint32_t> maxShardIndex,
2037  std::function<bool(soci::session& session, std::uint32_t index)> const&
2038  callback)
2039 {
2040  return iterateShardsBack(maxShardIndex, [&callback](Shard& shard) -> bool {
2041  return shard.callForTransactionSQL(callback);
2042  });
2043 }
2044 
2045 //------------------------------------------------------------------------------
2046 
2049  Application& app,
2050  Stoppable& parent,
2051  Scheduler& scheduler,
2052  int readThreads,
2053  beast::Journal j)
2054 {
2055  // The shard store is optional. Future changes will require it.
2056  Section const& section{
2058  if (section.empty())
2059  return nullptr;
2060 
2061  return std::make_unique<DatabaseShardImp>(
2062  app, parent, "ShardStore", scheduler, readThreads, j);
2063 }
2064 
2065 } // namespace NodeStore
2066 } // namespace ripple
ripple::NodeStore::make_ShardStore
std::unique_ptr< DatabaseShard > make_ShardStore(Application &app, Stoppable &parent, Scheduler &scheduler, int readThreads, beast::Journal j)
Definition: DatabaseShardImp.cpp:2048
beast::Journal::fatal
Stream fatal() const
Definition: Journal.h:339
ripple::SizedItem::openFinalLimit
@ openFinalLimit
ripple::Section
Holds a collection of configuration values.
Definition: BasicConfig.h:43
ripple::Application
Definition: Application.h:102
ripple::NodeStore::DatabaseShardImp::earliestShardIndex_
std::uint32_t earliestShardIndex_
Definition: DatabaseShardImp.h:253
ripple::NodeStore::DatabaseShardImp::ledgersPerShard_
std::uint32_t ledgersPerShard_
Definition: DatabaseShardImp.h:250
ripple::hotUNKNOWN
@ hotUNKNOWN
Definition: NodeObject.h:33
ripple::NodeStore::DatabaseShardImp::lastLedgerSeq
std::uint32_t lastLedgerSeq(std::uint32_t shardIndex) const override
Calculates the last ledger sequence for a given shard index.
Definition: DatabaseShardImp.h:108
ripple::NodeStore::DatabaseShardImp::earliestShardIndex
std::uint32_t earliestShardIndex() const override
Definition: DatabaseShardImp.h:86
ripple::makeSlice
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:240
ripple::NodeStore::DatabaseShardImp::mutex_
std::mutex mutex_
Definition: DatabaseShardImp.h:208
ripple::NodeStore::DatabaseShardImp::app_
Application & app_
Definition: DatabaseShardImp.h:206
ripple::NodeStore::DatabaseShardImp::storeLedger
bool storeLedger(std::shared_ptr< Ledger const > const &srcLedger) override
Store a ledger from a different database.
Definition: DatabaseShardImp.cpp:1039
ripple::NodeStore::Database
Persistency layer for NodeObject.
Definition: Database.h:50
ripple::NodeStore::Shard::acquire
static constexpr State acquire
Definition: Shard.h:62
std::string
STL class.
std::shared_ptr< Ledger >
ripple::loadByIndex
std::shared_ptr< Ledger > loadByIndex(std::uint32_t ledgerIndex, Application &app, bool acquire)
Definition: Ledger.cpp:1061
ripple::SizedItem
SizedItem
Definition: Config.h:48
ripple::NodeStore::DatabaseShardImp::shards_
std::map< std::uint32_t, std::shared_ptr< Shard > > shards_
Definition: DatabaseShardImp.h:218
std::exception
STL class.
std::stoul
T stoul(T... args)
ripple::Stoppable::stopped
void stopped()
Called by derived classes to indicate that the stoppable has stopped.
Definition: Stoppable.cpp:72
ripple::NodeStore::DatabaseShardImp::PathDesignation
PathDesignation
Definition: DatabaseShardImp.h:201
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
ripple::Serializer::modData
Blob & modData()
Definition: Serializer.h:178
std::vector::reserve
T reserve(T... args)
ripple::NodeStore::DatabaseShardImp::removePreShard
void removePreShard(std::uint32_t shardIndex) override
Remove a previously prepared shard index for import.
Definition: DatabaseShardImp.cpp:413
ripple::LedgerMaster::getValidLedgerIndex
LedgerIndex getValidLedgerIndex()
Definition: LedgerMaster.cpp:218
ripple::NodeStore::Database::fdRequired_
int fdRequired_
Definition: Database.h:240
ripple::NodeStore::DatabaseShardImp::fileSz_
std::uint64_t fileSz_
Definition: DatabaseShardImp.h:245
ripple::NodeStore::Shard::State::final
@ final
ripple::InboundLedger::Reason::GENERIC
@ GENERIC
std::vector
STL class.
std::set::find
T find(T... args)
ripple::ConfigSection::shardDatabase
static std::string shardDatabase()
Definition: ConfigSections.h:38
std::vector::size
T size(T... args)
ripple::Application::getRelationalDBInterface
virtual RelationalDBInterface & getRelationalDBInterface()=0
ripple::NodeObjectType
NodeObjectType
The types of node objects.
Definition: NodeObject.h:32
ripple::NodeObject::createObject
static std::shared_ptr< NodeObject > createObject(NodeObjectType type, Blob &&data, uint256 const &hash)
Create an object from fields.
Definition: NodeObject.cpp:37
ripple::NodeStore::DatabaseShardImp::taskQueue_
std::unique_ptr< TaskQueue > taskQueue_
Definition: DatabaseShardImp.h:215
ripple::NodeStore::DatabaseShardImp::setStored
void setStored(std::shared_ptr< Ledger const > const &ledger) override
Notifies the database that the given ledger has been fully acquired and stored.
Definition: DatabaseShardImp.cpp:624
ripple::NodeStore::DatabaseShardImp::updateStatus
void updateStatus(std::lock_guard< std::mutex > const &)
Definition: DatabaseShardImp.cpp:1450
std::set::emplace
T emplace(T... args)
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
std::lock_guard
STL class.
ripple::kilobytes
constexpr auto kilobytes(T value) noexcept
Definition: ByteUtilities.h:27
ripple::NetworkOPs::getOperatingMode
virtual OperatingMode getOperatingMode() const =0
ripple::NodeStore::FetchReport
Contains information about a fetch operation.
Definition: ripple/nodestore/Scheduler.h:32
std::function
std::all_of
T all_of(T... args)
ripple::NodeStore::Shard::finalKey
static const uint256 finalKey
Definition: Shard.h:230
ripple::NodeStore::DatabaseShardImp::iterateTransactionSQLsBack
bool iterateTransactionSQLsBack(std::optional< std::uint32_t > maxShardIndex, std::function< bool(soci::session &session, std::uint32_t index)> const &callback) override
iterateTransactionSQLsBack Checkouts transaction databases for all shards in descending order startin...
Definition: DatabaseShardImp.cpp:2035
ripple::LedgerMaster::walkHashBySeq
std::optional< LedgerHash > walkHashBySeq(std::uint32_t index, InboundLedger::Reason reason)
Walk to a ledger's hash using the skip list.
Definition: LedgerMaster.cpp:1710
ripple::NodeStore::Shard::complete
static constexpr State complete
Definition: Shard.h:63
ripple::getLimitedNewestLedgerInfo
std::optional< LedgerInfo > getLimitedNewestLedgerInfo(soci::session &session, LedgerIndex ledgerFirstIndex, beast::Journal j)
getLimitedNewestLedgerInfo Returns info of newest ledger from ledgers with sequences greather or equa...
Definition: RelationalDBInterface_nodes.cpp:484
ripple::deserializePrefixedHeader
LedgerInfo deserializePrefixedHeader(Slice data, bool hasHash)
Deserialize a ledger header (prefixed with 4 bytes) from a byte array.
Definition: InboundLedger.cpp:293
ripple::NodeStore::DatabaseShardImp::openFinalLimit_
const std::uint32_t openFinalLimit_
Definition: DatabaseShardImp.h:259
std::sort
T sort(T... args)
std::shared_ptr::reset
T reset(T... args)
ripple::SHAMapHash
Definition: SHAMapTreeNode.h:48
ripple::NodeStore::DatabaseShardImp::iterateShardsForward
bool iterateShardsForward(std::optional< std::uint32_t > minShardIndex, std::function< bool(Shard &shard)> const &visit)
iterateShardsForward Visits all shards starting from given in ascending order and calls given callbac...
Definition: DatabaseShardImp.cpp:1944
ripple::Application::getOPs
virtual NetworkOPs & getOPs()=0
ripple::NodeStore::DatabaseShardImp::sweep
void sweep() override
Remove expired entries from the positive and negative caches.
Definition: DatabaseShardImp.cpp:1074
ripple::getLimitedOldestLedgerInfo
std::optional< LedgerInfo > getLimitedOldestLedgerInfo(soci::session &session, LedgerIndex ledgerFirstIndex, beast::Journal j)
getLimitedOldestLedgerInfo Returns info of oldest ledger from ledgers with sequences greather or equa...
Definition: RelationalDBInterface_nodes.cpp:472
ripple::Section::values
std::vector< std::string > const & values() const
Returns all the values in the section.
Definition: BasicConfig.h:76
std::string::clear
T clear(T... args)
ripple::send_always
Sends a message to all peers.
Definition: predicates.h:31
ripple::Stoppable::setParent
void setParent(Stoppable &parent)
Set the parent of this Stoppable.
Definition: Stoppable.cpp:43
ripple::NodeStore::DatabaseShardImp::PathDesignation::historical
@ historical
ripple::get_if_exists
bool get_if_exists(Section const &section, std::string const &name, T &v)
Definition: BasicConfig.h:347
ripple::NodeStore::Shard::version
static constexpr std::uint32_t version
Definition: Shard.h:225
std::vector::push_back
T push_back(T... args)
ripple::NodeStore::DatabaseShardImp::firstLedgerSeq
std::uint32_t firstLedgerSeq(std::uint32_t shardIndex) const override
Calculates the first ledger sequence for a given shard index.
Definition: DatabaseShardImp.h:99
ripple::NodeStore::DatabaseShardImp::fetchNodeObject
std::shared_ptr< NodeObject > fetchNodeObject(uint256 const &hash, std::uint32_t ledgerSeq, FetchReport &fetchReport) override
Definition: DatabaseShardImp.cpp:1216
ripple::NodeStore::DatabaseShardImp::secondLatestShardIndex_
std::optional< std::uint32_t > secondLatestShardIndex_
Definition: DatabaseShardImp.h:272
ripple::NodeStore::DatabaseShardImp::avgShardFileSz_
std::uint64_t avgShardFileSz_
Definition: DatabaseShardImp.h:256
ripple::base_uint< 256 >
ripple::NodeStore::DatabaseShardImp::status_
std::string status_
Definition: DatabaseShardImp.h:233
std::sample
T sample(T... args)
ripple::NodeStore::DatabaseShardImp::getPreShards
std::string getPreShards() override
Get shard indexes being imported.
Definition: DatabaseShardImp.cpp:422
ripple::NodeStore::DatabaseShardImp::getWriteLoad
std::int32_t getWriteLoad() const override
Retrieve the estimated number of pending write operations.
Definition: DatabaseShardImp.cpp:988
ripple::NodeStore::DatabaseShardImp::findAcquireIndex
std::optional< std::uint32_t > findAcquireIndex(std::uint32_t validLedgerSeq, std::lock_guard< std::mutex > const &)
Definition: DatabaseShardImp.cpp:1235
ripple::NodeStore::TaskQueue
Definition: TaskQueue.h:32
ripple::Config::reporting
bool reporting() const
Definition: Config.h:270
ripple::OperatingMode::DISCONNECTED
@ DISCONNECTED
not ready to process requests
ripple::Stoppable
Provides an interface for starting and stopping.
Definition: Stoppable.h:201
ripple::NodeStore::DatabaseShardImp::chooseHistoricalPath
boost::filesystem::path chooseHistoricalPath(std::lock_guard< std::mutex > const &) const
Definition: DatabaseShardImp.cpp:1794
ripple::rand_int
std::enable_if_t< std::is_integral< Integral >::value &&detail::is_engine< Engine >::value, Integral > rand_int(Engine &engine, Integral min, Integral max)
Return a uniformly distributed random integer.
Definition: ripple/basics/random.h:115
ripple::NodeStore::DatabaseShardImp::sufficientStorage
bool sufficientStorage(std::uint32_t numShards, PathDesignation pathDesignation, std::lock_guard< std::mutex > const &) const
Definition: DatabaseShardImp.cpp:1465
ripple::NodeStore::DatabaseShardImp::init_
bool init_
Definition: DatabaseShardImp.h:209
ripple::NodeStore::DatabaseShardImp::getCompleteShards
std::string getCompleteShards() override
Query which complete shards are stored.
Definition: DatabaseShardImp.cpp:686
ripple::NodeStore::DatabaseShardImp::seqToShardIndex
std::uint32_t seqToShardIndex(std::uint32_t ledgerSeq) const override
Calculates the shard index for a given ledger sequence.
Definition: DatabaseShardImp.h:92
ripple::Application::getLedgerMaster
virtual LedgerMaster & getLedgerMaster()=0
ripple::PublicKey
A public key.
Definition: PublicKey.h:59
ripple::Config
Definition: Config.h:67
ripple::RelationalDBInterfaceSqlite
Definition: RelationalDBInterfaceSqlite.h:27
std::ofstream
STL class.
ripple::Application::config
virtual Config & config()=0
ripple::NodeStore::DatabaseShardImp::dir_
boost::filesystem::path dir_
Definition: DatabaseShardImp.h:227
ripple::NodeStore::DatabaseShardImp::callForLedgerSQL
bool callForLedgerSQL(LedgerIndex ledgerSeq, std::function< bool(soci::session &session, std::uint32_t index)> const &callback) override
callForLedgerSQL Checkouts ledger database for shard containing given ledger and calls given callback...
Definition: DatabaseShardImp.cpp:1908
ripple::Config::standalone
bool standalone() const
Definition: Config.h:265
ripple::NodeStore::DatabaseShardImp::removeFailedShard
void removeFailedShard(std::shared_ptr< Shard > &shard)
Definition: DatabaseShardImp.cpp:1553
ripple::Application::nodeIdentity
virtual std::pair< PublicKey, SecretKey > const & nodeIdentity()=0
ripple::NodeStore::DatabaseShard
A collection of historical shards.
Definition: DatabaseShard.h:37
std::to_string
T to_string(T... args)
ripple::NodeStore::DatabaseShardImp::importShard
bool importShard(std::uint32_t shardIndex, boost::filesystem::path const &srcDir) override
Import a shard into the shard database.
Definition: DatabaseShardImp.cpp:440
ripple::default_prng
beast::xor_shift_engine & default_prng()
Return the default random engine.
Definition: ripple/basics/random.h:65
ripple::NodeStore::DatabaseShardImp::store
void store(NodeObjectType type, Blob &&data, uint256 const &hash, std::uint32_t ledgerSeq) override
Store the object.
Definition: DatabaseShardImp.cpp:1005
ripple::NodeStore::DatabaseShardImp::PathDesignation::none
@ none
beast::Journal::error
Stream error() const
Definition: Journal.h:333
ripple::NodeStore::DatabaseShardImp::checkHistoricalPaths
bool checkHistoricalPaths() const
Definition: DatabaseShardImp.cpp:1827
std::set::erase
T erase(T... args)
ripple::NodeStore::DatabaseShardImp::initConfig
bool initConfig(std::lock_guard< std::mutex > const &)
Definition: DatabaseShardImp.cpp:1128
ripple::ConfigSection
Definition: ConfigSections.h:28
ripple::NodeStore::DatabaseShardImp::latestShardIndex_
std::optional< std::uint32_t > latestShardIndex_
Definition: DatabaseShardImp.h:271
ripple::NodeStore::DatabaseShardImp::callForTransactionSQL
bool callForTransactionSQL(LedgerIndex ledgerSeq, std::function< bool(soci::session &session, std::uint32_t index)> const &callback) override
callForTransactionSQL Checkouts transaction database for shard containing given ledger and calls give...
Definition: DatabaseShardImp.cpp:1926
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::NodeStore::seqToShardIndex
constexpr std::uint32_t seqToShardIndex(std::uint32_t ledgerSeq, std::uint32_t ledgersPerShard=DatabaseShard::ledgersPerShardDefault)
Definition: DatabaseShard.h:280
std::uint32_t
ripple::NodeStore::Database::earliestLedgerSeq
std::uint32_t earliestLedgerSeq() const
Definition: Database.h:232
ripple::NodeStore::DatabaseShardImp::setFileStats
void setFileStats()
Definition: DatabaseShardImp.cpp:1389
ripple::NodeStore::DatabaseShardImp::acquireIndex_
std::uint32_t acquireIndex_
Definition: DatabaseShardImp.h:224
ripple::Overlay::foreach
void foreach(Function f) const
Visit every active peer.
Definition: Overlay.h:178
std::map
STL class.
ripple::NodeStore::Scheduler
Scheduling for asynchronous backend activity.
Definition: ripple/nodestore/Scheduler.h:60
ripple::NodeStore::Database::fetchNodeObject
std::shared_ptr< NodeObject > fetchNodeObject(uint256 const &hash, std::uint32_t ledgerSeq=0, FetchType fetchType=FetchType::synchronous)
Fetch a node object.
Definition: Database.cpp:145
std::transform
T transform(T... args)
ripple::NodeStore::Database::storeStats
void storeStats(std::uint64_t count, std::uint64_t sz)
Definition: Database.h:249
ripple::NodeStore::DatabaseShardImp::iterateLedgerSQLsForward
bool iterateLedgerSQLsForward(std::optional< std::uint32_t > minShardIndex, std::function< bool(soci::session &session, std::uint32_t index)> const &callback) override
iterateLedgerSQLsForward Checkouts ledger databases for all shards in ascending order starting from g...
Definition: DatabaseShardImp.cpp:1971
ripple::NodeStore::DatabaseShardImp::preparedIndexes_
std::set< std::uint32_t > preparedIndexes_
Definition: DatabaseShardImp.h:221
ripple::NodeStore::DatabaseShardImp::iterateTransactionSQLsForward
bool iterateTransactionSQLsForward(std::optional< std::uint32_t > minShardIndex, std::function< bool(soci::session &session, std::uint32_t index)> const &callback) override
iterateTransactionSQLsForward Checkouts transaction databases for all shards in ascending order start...
Definition: DatabaseShardImp.cpp:1983
ripple::NodeStore::DatabaseShardImp::init
bool init() override
Initialize the database.
Definition: DatabaseShardImp.cpp:75
ripple::NodeStore::Shard::final
static constexpr State final
Definition: Shard.h:65
std::weak_ptr
STL class.
ripple::Serializer
Definition: Serializer.h:39
ripple::NodeStore::Database::stopReadThreads
void stopReadThreads()
Definition: Database.cpp:78
ripple::NodeStore::DatabaseShardImp::historicalPaths_
std::vector< boost::filesystem::path > historicalPaths_
Definition: DatabaseShardImp.h:242
ripple::NodeStore::DatabaseShardImp::parent_
Stoppable & parent_
Definition: DatabaseShardImp.h:207
ripple::NodeStore::DatabaseShardImp::importMarker_
static constexpr auto importMarker_
Definition: DatabaseShardImp.h:262
std::vector::emplace_back
T emplace_back(T... args)
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::Serializer::addBitString
int addBitString(base_uint< Bits, Tag > const &v)
Definition: Serializer.h:97
ripple::Application::getNodeStore
virtual NodeStore::Database & getNodeStore()=0
ripple::NodeStore::DatabaseShardImp::import
void import(Database &source) override
Import the application local node store.
Definition: DatabaseShardImp.cpp:737
ripple::NodeStore::DatabaseShardImp::maxHistoricalShards_
std::uint32_t maxHistoricalShards_
Definition: DatabaseShardImp.h:239
ripple::Application::getShardFamily
virtual Family * getShardFamily()=0
ripple::NodeStore::Shard::callForTransactionSQL
bool callForTransactionSQL(std::function< bool(soci::session &session, std::uint32_t index)> const &callback)
callForTransactionSQL Checks out transaction database for the shard and calls given callback function...
Definition: Shard.cpp:1234
ripple::NodeStore::Database::j_
const beast::Journal j_
Definition: Database.h:238
ripple::NodeStore::DatabaseShardImp::prepareForNewShard
std::optional< PathDesignation > prepareForNewShard(std::uint32_t shardIndex, std::uint32_t numHistoricalShards, std::lock_guard< std::mutex > const &lock)
Definition: DatabaseShardImp.cpp:1763
ripple::NodeStore::DatabaseShardImp::fetchLedger
std::shared_ptr< Ledger > fetchLedger(uint256 const &hash, std::uint32_t ledgerSeq) override
Fetch a ledger from the shard store.
Definition: DatabaseShardImp.cpp:546
std::vector::begin
T begin(T... args)
std
STL namespace.
ripple::NodeStore::Shard::callForLedgerSQL
bool callForLedgerSQL(std::function< bool(soci::session &session, std::uint32_t index)> const &callback)
callForLedgerSQL Checks out ledger database for the shard and calls given callback function passing s...
Definition: Shard.cpp:1221
ripple::NodeStore::DatabaseShardImp::iterateShardsBack
bool iterateShardsBack(std::optional< std::uint32_t > maxShardIndex, std::function< bool(Shard &shard)> const &visit)
iterateShardsBack Visits all shards starting from given in descending order and calls given callback ...
Definition: DatabaseShardImp.cpp:1995
ripple::NodeStore::DatabaseShardImp::numHistoricalShards
std::uint32_t numHistoricalShards(std::lock_guard< std::mutex > const &lock) const
Definition: DatabaseShardImp.cpp:1599
ripple::LedgerMaster::getCurrentLedgerIndex
LedgerIndex getCurrentLedgerIndex()
Definition: LedgerMaster.cpp:212
ripple::NodeStore::DatabaseShardImp::relocateOutdatedShards
void relocateOutdatedShards(std::lock_guard< std::mutex > const &lock)
Definition: DatabaseShardImp.cpp:1610
ripple::NodeStore::DatabaseShardImp::onChildrenStopped
void onChildrenStopped() override
Override called when all children have stopped.
Definition: DatabaseShardImp.cpp:708
ripple::Application::overlay
virtual Overlay & overlay()=0
ripple::NodeStore::DatabaseShardImp::shardBoundaryIndex
std::uint32_t shardBoundaryIndex() const
Definition: DatabaseShardImp.cpp:1583
std::count_if
T count_if(T... args)
std::vector::empty
T empty(T... args)
ripple::NodeStore::DatabaseShardImp::prepareShards
bool prepareShards(std::vector< std::uint32_t > const &shardIndexes) override
Prepare one or more shard indexes to be imported into the database.
Definition: DatabaseShardImp.cpp:304
std::unique
T unique(T... args)
std::optional< std::uint32_t >
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:40
ripple::NodeStore::DatabaseShardImp::setStoredInShard
bool setStoredInShard(std::shared_ptr< Shard > &shard, std::shared_ptr< Ledger const > const &ledger)
Definition: DatabaseShardImp.cpp:1520
ripple::NodeStore::DatabaseShardImp::canAdd_
bool canAdd_
Definition: DatabaseShardImp.h:230
ripple::Serializer::add32
int add32(std::uint32_t i)
Definition: Serializer.cpp:38
std::vector::end
T end(T... args)
ripple::NodeStore::Database::scheduler_
Scheduler & scheduler_
Definition: Database.h:239
ripple::RangeSet
boost::icl::interval_set< T, std::less, ClosedInterval< T > > RangeSet
A set of closed intervals over the domain T.
Definition: RangeSet.h:69
ripple::NodeStore::DatabaseShardImp::finalizeShard
void finalizeShard(std::shared_ptr< Shard > &shard, bool writeSQLite, std::optional< uint256 > const &expectedHash)
Definition: DatabaseShardImp.cpp:1301
std::max
T max(T... args)
ripple::RelationalDBInterface::getHashesByIndex
virtual std::optional< LedgerHashPair > getHashesByIndex(LedgerIndex ledgerIndex)=0
getHashesByIndex Returns hash of the ledger and hash of parent ledger for the ledger of given sequenc...
ripple::NodeStore::Shard
Definition: Shard.h:52
ripple::NodeStore::DatabaseShardImp::DatabaseShardImp
DatabaseShardImp()=delete
std::unique_ptr
STL class.
std::make_reverse_iterator
T make_reverse_iterator(T... args)
ripple::loadLedgerHelper
std::shared_ptr< Ledger > loadLedgerHelper(LedgerInfo const &info, Application &app, bool acquire)
Definition: Ledger.cpp:1017
std::unordered_map
STL class.
ripple::PublisherStatus::available
@ available
ripple::ConfigSection::nodeDatabase
static std::string nodeDatabase()
Definition: ConfigSections.h:33
std::exception::what
T what(T... args)
ripple::NodeStore::DatabaseShardImp::prepareLedger
std::optional< std::uint32_t > prepareLedger(std::uint32_t validLedgerSeq) override
Prepare to store a new ledger in the shard being acquired.
Definition: DatabaseShardImp.cpp:236
ripple::NodeStore::DatabaseShardImp::onStop
void onStop() override
Override called when the stop notification is issued.
Definition: DatabaseShardImp.cpp:695
ripple::NodeStore::DatabaseShardImp::iterateLedgerSQLsBack
bool iterateLedgerSQLsBack(std::optional< std::uint32_t > maxShardIndex, std::function< bool(soci::session &session, std::uint32_t index)> const &callback) override
iterateLedgerSQLsBack Checkouts ledger databases for all shards in descending order starting from giv...
Definition: DatabaseShardImp.cpp:2024
ripple::NodeStore::DatabaseShardImp::ctx_
std::unique_ptr< nudb::context > ctx_
Definition: DatabaseShardImp.h:212
ripple::BasicConfig::section
Section & section(std::string const &name)
Returns the section with the given name.
Definition: BasicConfig.cpp:138
ripple::Stoppable::isStopping
bool isStopping() const
Returns true if the stoppable should stop.
Definition: Stoppable.cpp:54
ripple::NodeStore::DatabaseShardImp::backendName_
std::string backendName_
Definition: DatabaseShardImp.h:236