20 #include <ripple/app/ledger/InboundLedger.h>
21 #include <ripple/app/main/DBInit.h>
22 #include <ripple/basics/StringUtilities.h>
23 #include <ripple/core/ConfigSections.h>
24 #include <ripple/nodestore/Manager.h>
25 #include <ripple/nodestore/impl/Shard.h>
26 #include <ripple/protocol/digest.h>
28 #include <boost/algorithm/string.hpp>
29 #include <boost/range/adaptor/transformed.hpp>
41 :
Shard(app, db, index,
"", j)
49 boost::filesystem::path
const& dir,
54 , firstSeq_(db.firstLedgerSeq(index))
55 , lastSeq_(
std::max(firstSeq_, db.lastLedgerSeq(index)))
57 index == db.earliestShardIndex() ? lastSeq_ - firstSeq_ + 1
58 : db.ledgersPerShard())
59 , dir_((dir.empty() ? db.getRootDir() : dir) /
std::
to_string(index_))
74 <<
" backend in use, unable to remove directory";
87 boost::filesystem::remove_all(
dir_);
92 <<
". Exception caught in function " << __func__
93 <<
". Error: " << e.
what();
101 std::string const type{get<std::string>(section,
"type",
"nudb")};
105 JLOG(
j_.
error()) <<
"shard " <<
index_ <<
" failed to find factory for "
109 section.set(
"path",
dir_.string());
114 JLOG(
j_.
error()) <<
"shard " <<
index_ <<
" already initialized";
134 JLOG(
j_.
error()) <<
"shard " <<
index_ <<
" not initialized";
156 JLOG(
j_.
error()) <<
"shard " <<
index_ <<
" not initialized";
169 <<
". Exception caught in function " << __func__
170 <<
". Error: " << e.
what();
187 boost::optional<std::uint32_t>
193 <<
" prepare called when not acquiring";
201 <<
" missing acquire SQLite database";
216 if (nodeObject->getHash() !=
finalKey)
228 pCache_->canonicalize_replace_cache(nodeObject->getHash(), nodeObject);
237 <<
". Exception caught in function " << __func__
238 <<
". Error: " << e.
what();
242 nCache_->erase(nodeObject->getHash());
254 auto nodeObject{
pCache_->fetch(hash)};
255 if (!nodeObject && !
nCache_->touch_if_exists(hash))
268 <<
"shard " <<
index_ <<
". Exception caught in function "
269 << __func__ <<
". Error: " << e.
what();
280 <<
"shard " <<
index_ <<
". Corrupt node object at hash "
286 <<
"shard " <<
index_ <<
". Unknown status=" << status
287 <<
" fetching node object at hash " <<
to_string(hash);
295 nodeObject =
pCache_->fetch(hash);
303 pCache_->canonicalize_replace_client(hash, nodeObject);
307 JLOG(
j_.
trace()) <<
"HOS: " << hash <<
" fetch: in shard db";
323 nodeObject =
pCache_->fetch(hash);
324 if (nodeObject ||
nCache_->touch_if_exists(hash))
343 JLOG(
j_.
trace()) <<
"shard " <<
index_ <<
". Ledger already stored";
348 JLOG(
j_.
error()) <<
"shard " <<
index_ <<
". Source ledger sequence "
349 << srcLedger->info().seq <<
". " << msg;
354 if (srcLedger->info().hash.isZero())
355 return fail(
"Invalid hash");
356 if (srcLedger->info().accountHash.isZero())
357 return fail(
"Invalid account hash");
359 auto& srcDB{
const_cast<Database&
>(srcLedger->stateMap().family().db())};
361 return fail(
"Source and destination databases are the same");
365 return fail(
"Failed to lock backend");
369 auto storeBatch = [&]() {
371 for (
auto const& nodeObject : batch)
373 pCache_->canonicalize_replace_cache(
374 nodeObject->getHash(), nodeObject);
375 nCache_->erase(nodeObject->getHash());
376 sz += nodeObject->getData().size();
386 std::string(
". Exception caught in function ") + __func__ +
387 ". Error: " + e.
what());
391 result.
count += batch.size();
401 addRaw(srcLedger->info(), s);
411 if (
auto nodeObject = srcDB.fetchNodeObject(
412 node.getHash().as_uint256(), srcLedger->info().seq))
425 if (srcLedger->stateMap().getHash().isNonZero())
427 if (!srcLedger->stateMap().isValid())
428 return fail(
"Invalid state map");
430 if (next && next->info().parentHash == srcLedger->info().hash)
432 auto have = next->stateMap().snapShot(
false);
433 srcLedger->stateMap().snapShot(
false)->visitDifferences(
437 srcLedger->stateMap().snapShot(
false)->visitNodes(visit);
439 return fail(
"Failed to store state map");
443 if (srcLedger->info().txHash.isNonZero())
445 if (!srcLedger->txMap().isValid())
446 return fail(
"Invalid transaction map");
448 srcLedger->txMap().snapShot(
false)->visitNodes(visit);
450 return fail(
"Failed to store transaction map");
453 if (!batch.
empty() && !storeBatch())
454 return fail(
"Failed to store");
469 auto const ledgerSeq{ledger->info().seq};
470 if (ledgerSeq < firstSeq_ || ledgerSeq >
lastSeq_)
472 JLOG(
j_.
error()) <<
"shard " <<
index_ <<
" invalid ledger sequence "
481 <<
" missing acquire SQLite database";
484 if (boost::icl::contains(
acquireInfo_->storedSeqs, ledgerSeq))
487 JLOG(
j_.
debug()) <<
"shard " <<
index_ <<
" ledger sequence "
488 << ledgerSeq <<
" already stored";
505 JLOG(
j_.
debug()) <<
"shard " <<
index_ <<
" stored ledger sequence "
515 if (ledgerSeq < firstSeq_ || ledgerSeq >
lastSeq_)
524 <<
" missing acquire SQLite database";
527 return boost::icl::contains(
acquireInfo_->storedSeqs, ledgerSeq);
533 boost::optional<Shard::Count> scopedCount;
538 JLOG(
j_.
error()) <<
"shard " <<
index_ <<
" not initialized";
567 std::chrono::steady_clock::time_point
599 bool const writeSQLite,
600 boost::optional<uint256>
const& expectedHash)
607 <<
"shard " <<
index <<
". " << msg
608 << (hash.isZero() ?
"" :
". Ledger hash " +
to_string(hash))
640 nodeObject->getData().data(), nodeObject->getData().size());
642 return fail(
"invalid version");
645 return fail(
"out of range ledger sequences");
648 return fail(
"invalid last ledger hash");
655 return fail(
"missing acquire SQLite database");
658 boost::optional<std::uint32_t>
index;
659 boost::optional<std::string> sHash;
660 soci::blob sociBlob(session);
661 soci::indicator blobPresent;
662 session <<
"SELECT ShardIndex, LastLedgerHash, StoredLedgerSeqs "
664 "WHERE ShardIndex = :index;",
665 soci::into(
index), soci::into(sHash),
666 soci::into(sociBlob, blobPresent), soci::use(
index_);
669 return fail(
"missing or invalid ShardIndex");
672 return fail(
"missing LastLedgerHash");
674 if (!hash.parseHex(*sHash) || hash.isZero())
675 return fail(
"invalid LastLedgerHash");
677 if (blobPresent != soci::i_ok)
678 return fail(
"missing StoredLedgerSeqs");
685 boost::icl::first(storedSeqs) !=
firstSeq_ ||
686 boost::icl::last(storedSeqs) !=
lastSeq_ ||
689 return fail(
"invalid StoredLedgerSeqs");
696 std::string(
". Exception caught in function ") + __func__ +
697 ". Error: " + e.
what());
702 if (expectedHash && *expectedHash != hash)
703 return fail(
"invalid last ledger hash");
709 auto const lastLedgerHash{hash};
712 auto const treeNodeCache{shardFamily.getTreeNodeCache(
lastSeq_)};
717 fullBelowCache->reset();
718 treeNodeCache->reset();
730 return fail(
"invalid ledger");
732 ledger = std::make_shared<Ledger>(
736 if (ledger->info().seq != ledgerSeq)
737 return fail(
"invalid ledger sequence");
738 if (ledger->info().hash != hash)
739 return fail(
"invalid ledger hash");
741 ledger->stateMap().setLedgerSeq(ledgerSeq);
742 ledger->txMap().setLedgerSeq(ledgerSeq);
743 ledger->setImmutable(config);
744 if (!ledger->stateMap().fetchRoot(
745 SHAMapHash{ledger->info().accountHash},
nullptr))
747 return fail(
"missing root STATE node");
749 if (ledger->info().txHash.isNonZero() &&
750 !ledger->txMap().fetchRoot(
753 return fail(
"missing root TXN node");
757 return fail(
"failed to validate ledger");
763 return fail(
"failed storing to SQLite databases");
766 hash = ledger->info().parentHash;
767 next = std::move(ledger);
772 fullBelowCache->reset();
773 treeNodeCache->reset();
834 return fail(
"failed to initialize SQLite databases");
842 std::string(
". Exception caught in function ") + __func__ +
843 ". Error: " + e.
what());
853 using namespace boost::filesystem;
855 auto preexist{
false};
856 auto fail = [
this, &preexist](
std::string const& msg) {
876 auto createAcquireInfo = [
this, &config]() {
880 setup.
startUp = config.standalone() ? config.LOAD : config.START_UP;
897 preexist = exists(
dir_);
905 <<
"INSERT INTO Shard (ShardIndex) "
906 "VALUES (:shardIndex);",
915 boost::optional<std::uint32_t>
index;
916 soci::blob sociBlob(session);
917 soci::indicator blobPresent;
919 session <<
"SELECT ShardIndex, StoredLedgerSeqs "
921 "WHERE ShardIndex = :index;",
922 soci::into(
index), soci::into(sociBlob, blobPresent),
926 return fail(
"invalid acquire SQLite database");
928 if (blobPresent == soci::i_ok)
933 return fail(
"invalid StoredLedgerSeqs");
935 if (boost::icl::first(storedSeqs) <
firstSeq_ ||
936 boost::icl::last(storedSeqs) >
lastSeq_)
938 return fail(
"invalid StoredLedgerSeqs");
954 return fail(
"incompatible, missing backend final key");
959 nodeObject->getData().data(), nodeObject->getData().size());
961 return fail(
"invalid version");
964 return fail(
"out of range ledger sequences");
967 return fail(
"invalid last ledger hash");
981 std::string(
". Exception caught in function ") + __func__ +
982 ". Error: " + e.
what());
1006 setup.
startUp = config.standalone() ? config.LOAD : config.START_UP;
1026 boost::format(
"PRAGMA cache_size=-%d;") %
1033 boost::format(
"PRAGMA cache_size=-%d;") %
1048 boost::format(
"PRAGMA cache_size=-%d;") %
1059 boost::format(
"PRAGMA cache_size=-%d;") %
1066 <<
". Exception caught in function " << __func__
1067 <<
". Error: " << e.
what();
1082 auto const ledgerSeq{ledger->info().seq};
1089 soci::transaction tr(session);
1091 session <<
"DELETE FROM Transactions "
1092 "WHERE LedgerSeq = :seq;",
1093 soci::use(ledgerSeq);
1094 session <<
"DELETE FROM AccountTransactions "
1095 "WHERE LedgerSeq = :seq;",
1096 soci::use(ledgerSeq);
1098 if (ledger->info().txHash.isNonZero())
1101 if (!ledger->txMap().isValid())
1104 <<
" has an invalid transaction map"
1105 <<
" on sequence " << sSeq;
1109 for (
auto const& item : ledger->txs)
1114 auto const txID{item.first->getTransactionID()};
1116 auto const txMeta{std::make_shared<TxMeta>(
1117 txID, ledger->seq(), *item.second)};
1119 session <<
"DELETE FROM AccountTransactions "
1120 "WHERE TransID = :txID;",
1123 auto const& accounts = txMeta->getAffectedAccounts(
j_);
1124 if (!accounts.empty())
1127 auto const s{boost::str(
1128 boost::format(
"('%s','%s',%s,%s)") % sTxID %
"%s" %
1131 sql.
reserve((accounts.size() + 1) * 128);
1133 "INSERT INTO AccountTransactions "
1134 "(TransID, Account, LedgerSeq, TxnSeq) VALUES ";
1135 sql += boost::algorithm::join(
1137 boost::adaptors::transformed(
1148 <<
" account transaction: " << sql;
1153 <<
"shard " <<
index_ <<
" transaction in ledger "
1154 << sSeq <<
" affects no accounts";
1158 item.second->add(s);
1161 item.first->getMetaSQL(
1170 auto const sHash{
to_string(ledger->info().hash)};
1175 soci::transaction tr(session);
1177 auto const sParentHash{
to_string(ledger->info().parentHash)};
1178 auto const sDrops{
to_string(ledger->info().drops)};
1179 auto const sAccountHash{
to_string(ledger->info().accountHash)};
1180 auto const sTxHash{
to_string(ledger->info().txHash)};
1182 session <<
"DELETE FROM Ledgers "
1183 "WHERE LedgerSeq = :seq;",
1184 soci::use(ledgerSeq);
1186 <<
"INSERT OR REPLACE INTO Ledgers ("
1187 "LedgerHash, LedgerSeq, PrevHash, TotalCoins, ClosingTime,"
1188 "PrevClosingTime, CloseTimeRes, CloseFlags, AccountSetHash,"
1191 ":ledgerHash, :ledgerSeq, :prevHash, :totalCoins,"
1192 ":closingTime, :prevClosingTime, :closeTimeRes,"
1193 ":closeFlags, :accountSetHash, :transSetHash);",
1194 soci::use(sHash), soci::use(ledgerSeq), soci::use(sParentHash),
1196 soci::use(ledger->info().closeTime.time_since_epoch().count()),
1198 ledger->info().parentCloseTime.time_since_epoch().count()),
1199 soci::use(ledger->info().closeTimeResolution.count()),
1200 soci::use(ledger->info().closeFlags), soci::use(sAccountHash),
1210 soci::blob sociBlob(session);
1215 if (ledger->info().seq ==
lastSeq_)
1218 session <<
"UPDATE Shard "
1219 "SET LastLedgerHash = :lastLedgerHash,"
1220 "StoredLedgerSeqs = :storedLedgerSeqs "
1221 "WHERE ShardIndex = :shardIndex;",
1222 soci::use(sHash), soci::use(sociBlob), soci::use(
index_);
1226 session <<
"UPDATE Shard "
1227 "SET StoredLedgerSeqs = :storedLedgerSeqs "
1228 "WHERE ShardIndex = :shardIndex;",
1229 soci::use(sociBlob), soci::use(
index_);
1236 <<
". Exception caught in function " << __func__
1237 <<
". Error: " << e.
what();
1251 using namespace boost::filesystem;
1252 for (
auto const& d : directory_iterator(
dir_))
1254 if (is_regular_file(d))
1264 <<
". Exception caught in function " << __func__
1265 <<
". Error: " << e.
what();
1275 JLOG(j.error()) <<
"shard " <<
index <<
". " << msg
1276 << (ledger->info().hash.isZero() ?
""
1277 :
". Ledger hash " +
1279 << (ledger->info().seq == 0 ?
""
1280 :
". Ledger sequence " +
1285 if (ledger->info().hash.isZero())
1286 return fail(
"Invalid ledger hash");
1287 if (ledger->info().accountHash.isZero())
1288 return fail(
"Invalid ledger account hash");
1300 if (ledger->stateMap().getHash().isNonZero())
1302 if (!ledger->stateMap().isValid())
1303 return fail(
"Invalid state map");
1307 if (next && next->info().parentHash == ledger->info().hash)
1308 ledger->stateMap().visitDifferences(&next->stateMap(), visit);
1310 ledger->stateMap().visitNodes(visit);
1315 std::string(
". Exception caught in function ") + __func__ +
1316 ". Error: " + e.
what());
1322 return fail(
"Invalid state map");
1326 if (ledger->info().txHash.isNonZero())
1328 if (!ledger->txMap().isValid())
1329 return fail(
"Invalid transaction map");
1333 ledger->txMap().visitNodes(visit);
1338 std::string(
". Exception caught in function ") + __func__ +
1339 ". Error: " + e.
what());
1344 return fail(
"Invalid transaction map");
1356 JLOG(j.error()) <<
"shard " <<
index <<
". " << msg
1357 <<
". Node object hash " <<
to_string(hash);
1364 switch (
backend_->fetch(hash.data(), &nodeObject))
1368 if (nodeObject->getHash() !=
1370 return fail(
"Node object hash does not match payload");
1373 return fail(
"Missing node object");
1375 return fail(
"Corrupt node object");
1377 return fail(
"Unknown error");
1383 std::string(
". Exception caught in function ") + __func__ +
1384 ". Error: " + e.
what());
1397 JLOG(
j_.
error()) <<
"shard " <<
index_ <<
" not initialized";
1405 else if (
state_ ==
final)